347 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * i.MX6 Video Data Order Adapter (VDOA)4 *5 * Copyright (C) 2014 Philipp Zabel6 * Copyright (C) 2016 Pengutronix, Michael Tretter <kernel@pengutronix.de>7 */8 9#include <linux/clk.h>10#include <linux/device.h>11#include <linux/interrupt.h>12#include <linux/module.h>13#include <linux/mod_devicetable.h>14#include <linux/dma-mapping.h>15#include <linux/platform_device.h>16#include <linux/videodev2.h>17#include <linux/slab.h>18 19#include "imx-vdoa.h"20 21#define VDOA_NAME "imx-vdoa"22 23#define VDOAC 0x0024#define VDOASRR 0x0425#define VDOAIE 0x0826#define VDOAIST 0x0c27#define VDOAFP 0x1028#define VDOAIEBA00 0x1429#define VDOAIEBA01 0x1830#define VDOAIEBA02 0x1c31#define VDOAIEBA10 0x2032#define VDOAIEBA11 0x2433#define VDOAIEBA12 0x2834#define VDOASL 0x2c35#define VDOAIUBO 0x3036#define VDOAVEBA0 0x3437#define VDOAVEBA1 0x3838#define VDOAVEBA2 0x3c39#define VDOAVUBO 0x4040#define VDOASR 0x4441 42#define VDOAC_ISEL BIT(6)43#define VDOAC_PFS BIT(5)44#define VDOAC_SO BIT(4)45#define VDOAC_SYNC BIT(3)46#define VDOAC_NF BIT(2)47#define VDOAC_BNDM_MASK 0x348#define VDOAC_BAND_HEIGHT_8 0x049#define VDOAC_BAND_HEIGHT_16 0x150#define VDOAC_BAND_HEIGHT_32 0x251 52#define VDOASRR_START BIT(1)53#define VDOASRR_SWRST BIT(0)54 55#define VDOAIE_EITERR BIT(1)56#define VDOAIE_EIEOT BIT(0)57 58#define VDOAIST_TERR BIT(1)59#define VDOAIST_EOT BIT(0)60 61#define VDOAFP_FH_MASK (0x1fff << 16)62#define VDOAFP_FW_MASK (0x3fff)63 64#define VDOASL_VSLY_MASK (0x3fff << 16)65#define VDOASL_ISLY_MASK (0x7fff)66 67#define VDOASR_ERRW BIT(4)68#define VDOASR_EOB BIT(3)69#define VDOASR_CURRENT_FRAME (0x3 << 1)70#define VDOASR_CURRENT_BUFFER BIT(1)71 72enum {73 V4L2_M2M_SRC = 0,74 V4L2_M2M_DST = 1,75};76 77struct vdoa_data {78 struct vdoa_ctx *curr_ctx;79 struct device *dev;80 struct clk *vdoa_clk;81 void __iomem *regs;82};83 84struct vdoa_q_data {85 unsigned int width;86 unsigned int height;87 unsigned int bytesperline;88 unsigned int sizeimage;89 u32 pixelformat;90};91 92struct vdoa_ctx {93 struct vdoa_data *vdoa;94 struct completion completion;95 struct vdoa_q_data q_data[2];96 unsigned int submitted_job;97 unsigned int completed_job;98};99 100static irqreturn_t vdoa_irq_handler(int irq, void *data)101{102 struct vdoa_data *vdoa = data;103 struct vdoa_ctx *curr_ctx;104 u32 val;105 106 /* Disable interrupts */107 writel(0, vdoa->regs + VDOAIE);108 109 curr_ctx = vdoa->curr_ctx;110 if (!curr_ctx) {111 dev_warn(vdoa->dev,112 "Instance released before the end of transaction\n");113 return IRQ_HANDLED;114 }115 116 val = readl(vdoa->regs + VDOAIST);117 writel(val, vdoa->regs + VDOAIST);118 if (val & VDOAIST_TERR) {119 val = readl(vdoa->regs + VDOASR) & VDOASR_ERRW;120 dev_err(vdoa->dev, "AXI %s error\n", val ? "write" : "read");121 } else if (!(val & VDOAIST_EOT)) {122 dev_warn(vdoa->dev, "Spurious interrupt\n");123 }124 curr_ctx->completed_job++;125 complete(&curr_ctx->completion);126 127 return IRQ_HANDLED;128}129 130int vdoa_wait_for_completion(struct vdoa_ctx *ctx)131{132 struct vdoa_data *vdoa = ctx->vdoa;133 134 if (ctx->submitted_job == ctx->completed_job)135 return 0;136 137 if (!wait_for_completion_timeout(&ctx->completion,138 msecs_to_jiffies(300))) {139 dev_err(vdoa->dev,140 "Timeout waiting for transfer result\n");141 return -ETIMEDOUT;142 }143 144 return 0;145}146EXPORT_SYMBOL(vdoa_wait_for_completion);147 148void vdoa_device_run(struct vdoa_ctx *ctx, dma_addr_t dst, dma_addr_t src)149{150 struct vdoa_q_data *src_q_data, *dst_q_data;151 struct vdoa_data *vdoa = ctx->vdoa;152 u32 val;153 154 if (vdoa->curr_ctx)155 vdoa_wait_for_completion(vdoa->curr_ctx);156 157 vdoa->curr_ctx = ctx;158 159 reinit_completion(&ctx->completion);160 ctx->submitted_job++;161 162 src_q_data = &ctx->q_data[V4L2_M2M_SRC];163 dst_q_data = &ctx->q_data[V4L2_M2M_DST];164 165 /* Progressive, no sync, 1 frame per run */166 if (dst_q_data->pixelformat == V4L2_PIX_FMT_YUYV)167 val = VDOAC_PFS;168 else169 val = 0;170 writel(val, vdoa->regs + VDOAC);171 172 writel(dst_q_data->height << 16 | dst_q_data->width,173 vdoa->regs + VDOAFP);174 175 val = dst;176 writel(val, vdoa->regs + VDOAIEBA00);177 178 writel(src_q_data->bytesperline << 16 | dst_q_data->bytesperline,179 vdoa->regs + VDOASL);180 181 if (dst_q_data->pixelformat == V4L2_PIX_FMT_NV12 ||182 dst_q_data->pixelformat == V4L2_PIX_FMT_NV21)183 val = dst_q_data->bytesperline * dst_q_data->height;184 else185 val = 0;186 writel(val, vdoa->regs + VDOAIUBO);187 188 val = src;189 writel(val, vdoa->regs + VDOAVEBA0);190 val = round_up(src_q_data->bytesperline * src_q_data->height, 4096);191 writel(val, vdoa->regs + VDOAVUBO);192 193 /* Enable interrupts and start transfer */194 writel(VDOAIE_EITERR | VDOAIE_EIEOT, vdoa->regs + VDOAIE);195 writel(VDOASRR_START, vdoa->regs + VDOASRR);196}197EXPORT_SYMBOL(vdoa_device_run);198 199struct vdoa_ctx *vdoa_context_create(struct vdoa_data *vdoa)200{201 struct vdoa_ctx *ctx;202 int err;203 204 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);205 if (!ctx)206 return NULL;207 208 err = clk_prepare_enable(vdoa->vdoa_clk);209 if (err) {210 kfree(ctx);211 return NULL;212 }213 214 init_completion(&ctx->completion);215 ctx->vdoa = vdoa;216 217 return ctx;218}219EXPORT_SYMBOL(vdoa_context_create);220 221void vdoa_context_destroy(struct vdoa_ctx *ctx)222{223 struct vdoa_data *vdoa = ctx->vdoa;224 225 if (vdoa->curr_ctx == ctx) {226 vdoa_wait_for_completion(vdoa->curr_ctx);227 vdoa->curr_ctx = NULL;228 }229 230 clk_disable_unprepare(vdoa->vdoa_clk);231 kfree(ctx);232}233EXPORT_SYMBOL(vdoa_context_destroy);234 235int vdoa_context_configure(struct vdoa_ctx *ctx,236 unsigned int width, unsigned int height,237 u32 pixelformat)238{239 struct vdoa_q_data *src_q_data;240 struct vdoa_q_data *dst_q_data;241 242 if (width < 16 || width > 8192 || width % 16 != 0 ||243 height < 16 || height > 4096 || height % 16 != 0)244 return -EINVAL;245 246 if (pixelformat != V4L2_PIX_FMT_YUYV &&247 pixelformat != V4L2_PIX_FMT_NV12)248 return -EINVAL;249 250 /* If no context is passed, only check if the format is valid */251 if (!ctx)252 return 0;253 254 src_q_data = &ctx->q_data[V4L2_M2M_SRC];255 dst_q_data = &ctx->q_data[V4L2_M2M_DST];256 257 src_q_data->width = width;258 src_q_data->height = height;259 src_q_data->bytesperline = width;260 src_q_data->sizeimage =261 round_up(src_q_data->bytesperline * height, 4096) +262 src_q_data->bytesperline * height / 2;263 264 dst_q_data->width = width;265 dst_q_data->height = height;266 dst_q_data->pixelformat = pixelformat;267 switch (pixelformat) {268 case V4L2_PIX_FMT_YUYV:269 dst_q_data->bytesperline = width * 2;270 dst_q_data->sizeimage = dst_q_data->bytesperline * height;271 break;272 case V4L2_PIX_FMT_NV12:273 default:274 dst_q_data->bytesperline = width;275 dst_q_data->sizeimage =276 dst_q_data->bytesperline * height * 3 / 2;277 break;278 }279 280 return 0;281}282EXPORT_SYMBOL(vdoa_context_configure);283 284static int vdoa_probe(struct platform_device *pdev)285{286 struct vdoa_data *vdoa;287 int ret;288 289 ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));290 if (ret) {291 dev_err(&pdev->dev, "DMA enable failed\n");292 return ret;293 }294 295 vdoa = devm_kzalloc(&pdev->dev, sizeof(*vdoa), GFP_KERNEL);296 if (!vdoa)297 return -ENOMEM;298 299 vdoa->dev = &pdev->dev;300 301 vdoa->vdoa_clk = devm_clk_get(vdoa->dev, NULL);302 if (IS_ERR(vdoa->vdoa_clk)) {303 dev_err(vdoa->dev, "Failed to get clock\n");304 return PTR_ERR(vdoa->vdoa_clk);305 }306 307 vdoa->regs = devm_platform_ioremap_resource(pdev, 0);308 if (IS_ERR(vdoa->regs))309 return PTR_ERR(vdoa->regs);310 311 ret = platform_get_irq(pdev, 0);312 if (ret < 0)313 return ret;314 ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,315 vdoa_irq_handler, IRQF_ONESHOT,316 "vdoa", vdoa);317 if (ret < 0) {318 dev_err(vdoa->dev, "Failed to get irq\n");319 return ret;320 }321 322 platform_set_drvdata(pdev, vdoa);323 324 return 0;325}326 327static const struct of_device_id vdoa_dt_ids[] = {328 { .compatible = "fsl,imx6q-vdoa" },329 {}330};331MODULE_DEVICE_TABLE(of, vdoa_dt_ids);332 333static struct platform_driver vdoa_driver = {334 .probe = vdoa_probe,335 .driver = {336 .name = VDOA_NAME,337 .of_match_table = vdoa_dt_ids,338 },339};340 341module_platform_driver(vdoa_driver);342 343MODULE_DESCRIPTION("Video Data Order Adapter");344MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");345MODULE_ALIAS("platform:imx-vdoa");346MODULE_LICENSE("GPL");347