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