Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * i.MX6 Video Data Order Adapter (VDOA)
0004  *
0005  * Copyright (C) 2014 Philipp Zabel
0006  * Copyright (C) 2016 Pengutronix, Michael Tretter <kernel@pengutronix.de>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/device.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/module.h>
0013 #include <linux/mod_devicetable.h>
0014 #include <linux/dma-mapping.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/videodev2.h>
0017 #include <linux/slab.h>
0018 
0019 #include "imx-vdoa.h"
0020 
0021 #define VDOA_NAME "imx-vdoa"
0022 
0023 #define VDOAC       0x00
0024 #define VDOASRR     0x04
0025 #define VDOAIE      0x08
0026 #define VDOAIST     0x0c
0027 #define VDOAFP      0x10
0028 #define VDOAIEBA00  0x14
0029 #define VDOAIEBA01  0x18
0030 #define VDOAIEBA02  0x1c
0031 #define VDOAIEBA10  0x20
0032 #define VDOAIEBA11  0x24
0033 #define VDOAIEBA12  0x28
0034 #define VDOASL      0x2c
0035 #define VDOAIUBO    0x30
0036 #define VDOAVEBA0   0x34
0037 #define VDOAVEBA1   0x38
0038 #define VDOAVEBA2   0x3c
0039 #define VDOAVUBO    0x40
0040 #define VDOASR      0x44
0041 
0042 #define VDOAC_ISEL      BIT(6)
0043 #define VDOAC_PFS       BIT(5)
0044 #define VDOAC_SO        BIT(4)
0045 #define VDOAC_SYNC      BIT(3)
0046 #define VDOAC_NF        BIT(2)
0047 #define VDOAC_BNDM_MASK     0x3
0048 #define VDOAC_BAND_HEIGHT_8 0x0
0049 #define VDOAC_BAND_HEIGHT_16    0x1
0050 #define VDOAC_BAND_HEIGHT_32    0x2
0051 
0052 #define VDOASRR_START       BIT(1)
0053 #define VDOASRR_SWRST       BIT(0)
0054 
0055 #define VDOAIE_EITERR       BIT(1)
0056 #define VDOAIE_EIEOT        BIT(0)
0057 
0058 #define VDOAIST_TERR        BIT(1)
0059 #define VDOAIST_EOT     BIT(0)
0060 
0061 #define VDOAFP_FH_MASK      (0x1fff << 16)
0062 #define VDOAFP_FW_MASK      (0x3fff)
0063 
0064 #define VDOASL_VSLY_MASK    (0x3fff << 16)
0065 #define VDOASL_ISLY_MASK    (0x7fff)
0066 
0067 #define VDOASR_ERRW     BIT(4)
0068 #define VDOASR_EOB      BIT(3)
0069 #define VDOASR_CURRENT_FRAME    (0x3 << 1)
0070 #define VDOASR_CURRENT_BUFFER   BIT(1)
0071 
0072 enum {
0073     V4L2_M2M_SRC = 0,
0074     V4L2_M2M_DST = 1,
0075 };
0076 
0077 struct vdoa_data {
0078     struct vdoa_ctx     *curr_ctx;
0079     struct device       *dev;
0080     struct clk      *vdoa_clk;
0081     void __iomem        *regs;
0082 };
0083 
0084 struct vdoa_q_data {
0085     unsigned int    width;
0086     unsigned int    height;
0087     unsigned int    bytesperline;
0088     unsigned int    sizeimage;
0089     u32     pixelformat;
0090 };
0091 
0092 struct vdoa_ctx {
0093     struct vdoa_data    *vdoa;
0094     struct completion   completion;
0095     struct vdoa_q_data  q_data[2];
0096     unsigned int        submitted_job;
0097     unsigned int        completed_job;
0098 };
0099 
0100 static irqreturn_t vdoa_irq_handler(int irq, void *data)
0101 {
0102     struct vdoa_data *vdoa = data;
0103     struct vdoa_ctx *curr_ctx;
0104     u32 val;
0105 
0106     /* Disable interrupts */
0107     writel(0, vdoa->regs + VDOAIE);
0108 
0109     curr_ctx = vdoa->curr_ctx;
0110     if (!curr_ctx) {
0111         dev_warn(vdoa->dev,
0112             "Instance released before the end of transaction\n");
0113         return IRQ_HANDLED;
0114     }
0115 
0116     val = readl(vdoa->regs + VDOAIST);
0117     writel(val, vdoa->regs + VDOAIST);
0118     if (val & VDOAIST_TERR) {
0119         val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW;
0120         dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read");
0121     } else if (!(val & VDOAIST_EOT)) {
0122         dev_warn(vdoa->dev, "Spurious interrupt\n");
0123     }
0124     curr_ctx->completed_job++;
0125     complete(&curr_ctx->completion);
0126 
0127     return IRQ_HANDLED;
0128 }
0129 
0130 int vdoa_wait_for_completion(struct vdoa_ctx *ctx)
0131 {
0132     struct vdoa_data *vdoa = ctx->vdoa;
0133 
0134     if (ctx->submitted_job == ctx->completed_job)
0135         return 0;
0136 
0137     if (!wait_for_completion_timeout(&ctx->completion,
0138                      msecs_to_jiffies(300))) {
0139         dev_err(vdoa->dev,
0140             "Timeout waiting for transfer result\n");
0141         return -ETIMEDOUT;
0142     }
0143 
0144     return 0;
0145 }
0146 EXPORT_SYMBOL(vdoa_wait_for_completion);
0147 
0148 void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src)
0149 {
0150     struct vdoa_q_data *src_q_data, *dst_q_data;
0151     struct vdoa_data *vdoa = ctx->vdoa;
0152     u32 val;
0153 
0154     if (vdoa->curr_ctx)
0155         vdoa_wait_for_completion(vdoa->curr_ctx);
0156 
0157     vdoa->curr_ctx = ctx;
0158 
0159     reinit_completion(&ctx->completion);
0160     ctx->submitted_job++;
0161 
0162     src_q_data = &ctx->q_data[V4L2_M2M_SRC];
0163     dst_q_data = &ctx->q_data[V4L2_M2M_DST];
0164 
0165     /* Progressive, no sync, 1 frame per run */
0166     if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV)
0167         val = VDOAC_PFS;
0168     else
0169         val = 0;
0170     writel(val, vdoa->regs + VDOAC);
0171 
0172     writel(dst_q_data->height << 16 | dst_q_data->width,
0173            vdoa->regs + VDOAFP);
0174 
0175     val = dst;
0176     writel(val, vdoa->regs + VDOAIEBA00);
0177 
0178     writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline,
0179            vdoa->regs + VDOASL);
0180 
0181     if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 ||
0182         dst_q_data->pixelformat == V4L2_PIX_FMT_NV21)
0183         val = dst_q_data->bytesperline * dst_q_data->height;
0184     else
0185         val = 0;
0186     writel(val, vdoa->regs + VDOAIUBO);
0187 
0188     val = src;
0189     writel(val, vdoa->regs + VDOAVEBA0);
0190     val = round_up(src_q_data->bytesperline * src_q_data->height, 4096);
0191     writel(val, vdoa->regs + VDOAVUBO);
0192 
0193     /* Enable interrupts and start transfer */
0194     writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE);
0195     writel(VDOASRR_START, vdoa->regs + VDOASRR);
0196 }
0197 EXPORT_SYMBOL(vdoa_device_run);
0198 
0199 struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa)
0200 {
0201     struct vdoa_ctx *ctx;
0202     int err;
0203 
0204     ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
0205     if (!ctx)
0206         return NULL;
0207 
0208     err = clk_prepare_enable(vdoa->vdoa_clk);
0209     if (err) {
0210         kfree(ctx);
0211         return NULL;
0212     }
0213 
0214     init_completion(&ctx->completion);
0215     ctx->vdoa = vdoa;
0216 
0217     return ctx;
0218 }
0219 EXPORT_SYMBOL(vdoa_context_create);
0220 
0221 void vdoa_context_destroy(struct vdoa_ctx *ctx)
0222 {
0223     struct vdoa_data *vdoa = ctx->vdoa;
0224 
0225     if (vdoa->curr_ctx == ctx) {
0226         vdoa_wait_for_completion(vdoa->curr_ctx);
0227         vdoa->curr_ctx = NULL;
0228     }
0229 
0230     clk_disable_unprepare(vdoa->vdoa_clk);
0231     kfree(ctx);
0232 }
0233 EXPORT_SYMBOL(vdoa_context_destroy);
0234 
0235 int vdoa_context_configure(struct vdoa_ctx *ctx,
0236                unsigned int width, unsigned int height,
0237                u32 pixelformat)
0238 {
0239     struct vdoa_q_data *src_q_data;
0240     struct vdoa_q_data *dst_q_data;
0241 
0242     if (width < 16 || width  > 8192 || width % 16 != 0 ||
0243         height < 16 || height > 4096 || height % 16 != 0)
0244         return -EINVAL;
0245 
0246     if (pixelformat != V4L2_PIX_FMT_YUYV &&
0247         pixelformat != V4L2_PIX_FMT_NV12)
0248         return -EINVAL;
0249 
0250     /* If no context is passed, only check if the format is valid */
0251     if (!ctx)
0252         return 0;
0253 
0254     src_q_data = &ctx->q_data[V4L2_M2M_SRC];
0255     dst_q_data = &ctx->q_data[V4L2_M2M_DST];
0256 
0257     src_q_data->width = width;
0258     src_q_data->height = height;
0259     src_q_data->bytesperline = width;
0260     src_q_data->sizeimage =
0261         round_up(src_q_data->bytesperline * height, 4096) +
0262         src_q_data->bytesperline * height / 2;
0263 
0264     dst_q_data->width = width;
0265     dst_q_data->height = height;
0266     dst_q_data->pixelformat = pixelformat;
0267     switch (pixelformat) {
0268     case V4L2_PIX_FMT_YUYV:
0269         dst_q_data->bytesperline = width * 2;
0270         dst_q_data->sizeimage = dst_q_data->bytesperline * height;
0271         break;
0272     case V4L2_PIX_FMT_NV12:
0273     default:
0274         dst_q_data->bytesperline = width;
0275         dst_q_data->sizeimage =
0276             dst_q_data->bytesperline * height * 3 / 2;
0277         break;
0278     }
0279 
0280     return 0;
0281 }
0282 EXPORT_SYMBOL(vdoa_context_configure);
0283 
0284 static int vdoa_probe(struct platform_device *pdev)
0285 {
0286     struct vdoa_data *vdoa;
0287     int ret;
0288 
0289     ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
0290     if (ret) {
0291         dev_err(&pdev->dev, "DMA enable failed\n");
0292         return ret;
0293     }
0294 
0295     vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL);
0296     if (!vdoa)
0297         return -ENOMEM;
0298 
0299     vdoa->dev = &pdev->dev;
0300 
0301     vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL);
0302     if (IS_ERR(vdoa->vdoa_clk)) {
0303         dev_err(vdoa->dev, "Failed to get clock\n");
0304         return PTR_ERR(vdoa->vdoa_clk);
0305     }
0306 
0307     vdoa->regs = devm_platform_ioremap_resource(pdev, 0);
0308     if (IS_ERR(vdoa->regs))
0309         return PTR_ERR(vdoa->regs);
0310 
0311     ret = platform_get_irq(pdev, 0);
0312     if (ret < 0)
0313         return ret;
0314     ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
0315                     vdoa_irq_handler, IRQF_ONESHOT,
0316                     "vdoa", vdoa);
0317     if (ret < 0) {
0318         dev_err(vdoa->dev, "Failed to get irq\n");
0319         return ret;
0320     }
0321 
0322     platform_set_drvdata(pdev, vdoa);
0323 
0324     return 0;
0325 }
0326 
0327 static int vdoa_remove(struct platform_device *pdev)
0328 {
0329     return 0;
0330 }
0331 
0332 static const struct of_device_id vdoa_dt_ids[] = {
0333     { .compatible = "fsl,imx6q-vdoa" },
0334     {}
0335 };
0336 MODULE_DEVICE_TABLE(of, vdoa_dt_ids);
0337 
0338 static struct platform_driver vdoa_driver = {
0339     .probe      = vdoa_probe,
0340     .remove     = vdoa_remove,
0341     .driver     = {
0342         .name   = VDOA_NAME,
0343         .of_match_table = vdoa_dt_ids,
0344     },
0345 };
0346 
0347 module_platform_driver(vdoa_driver);
0348 
0349 MODULE_DESCRIPTION("Video Data Order Adapter");
0350 MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
0351 MODULE_ALIAS("platform:imx-vdoa");
0352 MODULE_LICENSE("GPL");