xref: /linux/drivers/media/platform/chips-media/coda/imx-vdoa.c (revision d2c9a99135da931377240942d44f3dea104cedb8)
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