imx-vdoa.c (8009B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * i.MX6 Video Data Order Adapter (VDOA) 4 * 5 * Copyright (C) 2014 Philipp Zabel 6 * 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 0x00 24#define VDOASRR 0x04 25#define VDOAIE 0x08 26#define VDOAIST 0x0c 27#define VDOAFP 0x10 28#define VDOAIEBA00 0x14 29#define VDOAIEBA01 0x18 30#define VDOAIEBA02 0x1c 31#define VDOAIEBA10 0x20 32#define VDOAIEBA11 0x24 33#define VDOAIEBA12 0x28 34#define VDOASL 0x2c 35#define VDOAIUBO 0x30 36#define VDOAVEBA0 0x34 37#define VDOAVEBA1 0x38 38#define VDOAVEBA2 0x3c 39#define VDOAVUBO 0x40 40#define VDOASR 0x44 41 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 0x3 48#define VDOAC_BAND_HEIGHT_8 0x0 49#define VDOAC_BAND_HEIGHT_16 0x1 50#define VDOAC_BAND_HEIGHT_32 0x2 51 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 else 169 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 else 185 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 int vdoa_remove(struct platform_device *pdev) 328{ 329 return 0; 330} 331 332static const struct of_device_id vdoa_dt_ids[] = { 333 { .compatible = "fsl,imx6q-vdoa" }, 334 {} 335}; 336MODULE_DEVICE_TABLE(of, vdoa_dt_ids); 337 338static struct platform_driver vdoa_driver = { 339 .probe = vdoa_probe, 340 .remove = vdoa_remove, 341 .driver = { 342 .name = VDOA_NAME, 343 .of_match_table = vdoa_dt_ids, 344 }, 345}; 346 347module_platform_driver(vdoa_driver); 348 349MODULE_DESCRIPTION("Video Data Order Adapter"); 350MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>"); 351MODULE_ALIAS("platform:imx-vdoa"); 352MODULE_LICENSE("GPL");