0001
0002
0003
0004
0005
0006
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
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
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
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
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");