1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC |
4 | * |
5 | * This subdevice handles capture of video frames from the CSI or VDIC, |
6 | * which are routed directly to the Image Converter preprocess tasks, |
7 | * for resizing, colorspace conversion, and rotation. |
8 | * |
9 | * Copyright (c) 2012-2017 Mentor Graphics Inc. |
10 | */ |
11 | #include <linux/delay.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/module.h> |
14 | #include <linux/sched.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/spinlock.h> |
17 | #include <linux/timer.h> |
18 | #include <media/v4l2-ctrls.h> |
19 | #include <media/v4l2-device.h> |
20 | #include <media/v4l2-ioctl.h> |
21 | #include <media/v4l2-mc.h> |
22 | #include <media/v4l2-subdev.h> |
23 | #include <media/imx.h> |
24 | #include "imx-media.h" |
25 | #include "imx-ic.h" |
26 | |
27 | /* |
28 | * Min/Max supported width and heights. |
29 | * |
30 | * We allow planar output, so we have to align width at the source pad |
31 | * by 16 pixels to meet IDMAC alignment requirements for possible planar |
32 | * output. |
33 | * |
34 | * TODO: move this into pad format negotiation, if capture device |
35 | * has not requested a planar format, we should allow 8 pixel |
36 | * alignment at the source pad. |
37 | */ |
38 | #define MIN_W_SINK 32 |
39 | #define MIN_H_SINK 32 |
40 | #define MAX_W_SINK 4096 |
41 | #define MAX_H_SINK 4096 |
42 | #define W_ALIGN_SINK 3 /* multiple of 8 pixels */ |
43 | #define H_ALIGN_SINK 1 /* multiple of 2 lines */ |
44 | |
45 | #define MAX_W_SRC 1024 |
46 | #define MAX_H_SRC 1024 |
47 | #define W_ALIGN_SRC 1 /* multiple of 2 pixels */ |
48 | #define H_ALIGN_SRC 1 /* multiple of 2 lines */ |
49 | |
50 | #define S_ALIGN 1 /* multiple of 2 */ |
51 | |
52 | struct prp_priv { |
53 | struct imx_ic_priv *ic_priv; |
54 | struct media_pad pad[PRPENCVF_NUM_PADS]; |
55 | /* the video device at output pad */ |
56 | struct imx_media_video_dev *vdev; |
57 | |
58 | /* lock to protect all members below */ |
59 | struct mutex lock; |
60 | |
61 | /* IPU units we require */ |
62 | struct ipu_ic *ic; |
63 | struct ipuv3_channel *out_ch; |
64 | struct ipuv3_channel *rot_in_ch; |
65 | struct ipuv3_channel *rot_out_ch; |
66 | |
67 | /* active vb2 buffers to send to video dev sink */ |
68 | struct imx_media_buffer *active_vb2_buf[2]; |
69 | struct imx_media_dma_buf underrun_buf; |
70 | |
71 | int ipu_buf_num; /* ipu double buffer index: 0-1 */ |
72 | |
73 | /* the sink for the captured frames */ |
74 | struct media_entity *sink; |
75 | /* the source subdev */ |
76 | struct v4l2_subdev *src_sd; |
77 | |
78 | struct v4l2_mbus_framefmt format_mbus[PRPENCVF_NUM_PADS]; |
79 | const struct imx_media_pixfmt *cc[PRPENCVF_NUM_PADS]; |
80 | struct v4l2_fract frame_interval; |
81 | |
82 | struct imx_media_dma_buf rot_buf[2]; |
83 | |
84 | /* controls */ |
85 | struct v4l2_ctrl_handler ctrl_hdlr; |
86 | int rotation; /* degrees */ |
87 | bool hflip; |
88 | bool vflip; |
89 | |
90 | /* derived from rotation, hflip, vflip controls */ |
91 | enum ipu_rotate_mode rot_mode; |
92 | |
93 | spinlock_t irqlock; /* protect eof_irq handler */ |
94 | |
95 | struct timer_list eof_timeout_timer; |
96 | int eof_irq; |
97 | int nfb4eof_irq; |
98 | |
99 | int stream_count; |
100 | u32 frame_sequence; /* frame sequence counter */ |
101 | bool last_eof; /* waiting for last EOF at stream off */ |
102 | bool nfb4eof; /* NFB4EOF encountered during streaming */ |
103 | bool interweave_swap; /* swap top/bottom lines when interweaving */ |
104 | struct completion last_eof_comp; |
105 | }; |
106 | |
107 | static const struct prp_channels { |
108 | u32 out_ch; |
109 | u32 rot_in_ch; |
110 | u32 rot_out_ch; |
111 | } prp_channel[] = { |
112 | [IC_TASK_ENCODER] = { |
113 | .out_ch = IPUV3_CHANNEL_IC_PRP_ENC_MEM, |
114 | .rot_in_ch = IPUV3_CHANNEL_MEM_ROT_ENC, |
115 | .rot_out_ch = IPUV3_CHANNEL_ROT_ENC_MEM, |
116 | }, |
117 | [IC_TASK_VIEWFINDER] = { |
118 | .out_ch = IPUV3_CHANNEL_IC_PRP_VF_MEM, |
119 | .rot_in_ch = IPUV3_CHANNEL_MEM_ROT_VF, |
120 | .rot_out_ch = IPUV3_CHANNEL_ROT_VF_MEM, |
121 | }, |
122 | }; |
123 | |
124 | static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd) |
125 | { |
126 | struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); |
127 | |
128 | return ic_priv->task_priv; |
129 | } |
130 | |
131 | static void prp_put_ipu_resources(struct prp_priv *priv) |
132 | { |
133 | if (priv->ic) |
134 | ipu_ic_put(ic: priv->ic); |
135 | priv->ic = NULL; |
136 | |
137 | if (priv->out_ch) |
138 | ipu_idmac_put(priv->out_ch); |
139 | priv->out_ch = NULL; |
140 | |
141 | if (priv->rot_in_ch) |
142 | ipu_idmac_put(priv->rot_in_ch); |
143 | priv->rot_in_ch = NULL; |
144 | |
145 | if (priv->rot_out_ch) |
146 | ipu_idmac_put(priv->rot_out_ch); |
147 | priv->rot_out_ch = NULL; |
148 | } |
149 | |
150 | static int prp_get_ipu_resources(struct prp_priv *priv) |
151 | { |
152 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
153 | struct ipu_ic *ic; |
154 | struct ipuv3_channel *out_ch, *rot_in_ch, *rot_out_ch; |
155 | int ret, task = ic_priv->task_id; |
156 | |
157 | ic = ipu_ic_get(ipu: ic_priv->ipu, task); |
158 | if (IS_ERR(ptr: ic)) { |
159 | v4l2_err(&ic_priv->sd, "failed to get IC\n" ); |
160 | ret = PTR_ERR(ptr: ic); |
161 | goto out; |
162 | } |
163 | priv->ic = ic; |
164 | |
165 | out_ch = ipu_idmac_get(ipu: ic_priv->ipu, channel: prp_channel[task].out_ch); |
166 | if (IS_ERR(ptr: out_ch)) { |
167 | v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n" , |
168 | prp_channel[task].out_ch); |
169 | ret = PTR_ERR(ptr: out_ch); |
170 | goto out; |
171 | } |
172 | priv->out_ch = out_ch; |
173 | |
174 | rot_in_ch = ipu_idmac_get(ipu: ic_priv->ipu, channel: prp_channel[task].rot_in_ch); |
175 | if (IS_ERR(ptr: rot_in_ch)) { |
176 | v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n" , |
177 | prp_channel[task].rot_in_ch); |
178 | ret = PTR_ERR(ptr: rot_in_ch); |
179 | goto out; |
180 | } |
181 | priv->rot_in_ch = rot_in_ch; |
182 | |
183 | rot_out_ch = ipu_idmac_get(ipu: ic_priv->ipu, channel: prp_channel[task].rot_out_ch); |
184 | if (IS_ERR(ptr: rot_out_ch)) { |
185 | v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n" , |
186 | prp_channel[task].rot_out_ch); |
187 | ret = PTR_ERR(ptr: rot_out_ch); |
188 | goto out; |
189 | } |
190 | priv->rot_out_ch = rot_out_ch; |
191 | |
192 | return 0; |
193 | out: |
194 | prp_put_ipu_resources(priv); |
195 | return ret; |
196 | } |
197 | |
198 | static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) |
199 | { |
200 | struct imx_media_video_dev *vdev = priv->vdev; |
201 | struct imx_media_buffer *done, *next; |
202 | struct vb2_buffer *vb; |
203 | dma_addr_t phys; |
204 | |
205 | done = priv->active_vb2_buf[priv->ipu_buf_num]; |
206 | if (done) { |
207 | done->vbuf.field = vdev->fmt.field; |
208 | done->vbuf.sequence = priv->frame_sequence; |
209 | vb = &done->vbuf.vb2_buf; |
210 | vb->timestamp = ktime_get_ns(); |
211 | vb2_buffer_done(vb, state: priv->nfb4eof ? |
212 | VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); |
213 | } |
214 | |
215 | priv->frame_sequence++; |
216 | priv->nfb4eof = false; |
217 | |
218 | /* get next queued buffer */ |
219 | next = imx_media_capture_device_next_buf(vdev); |
220 | if (next) { |
221 | phys = vb2_dma_contig_plane_dma_addr(vb: &next->vbuf.vb2_buf, plane_no: 0); |
222 | priv->active_vb2_buf[priv->ipu_buf_num] = next; |
223 | } else { |
224 | phys = priv->underrun_buf.phys; |
225 | priv->active_vb2_buf[priv->ipu_buf_num] = NULL; |
226 | } |
227 | |
228 | if (ipu_idmac_buffer_is_ready(channel: ch, buf_num: priv->ipu_buf_num)) |
229 | ipu_idmac_clear_buffer(channel: ch, buf_num: priv->ipu_buf_num); |
230 | |
231 | if (priv->interweave_swap && ch == priv->out_ch) |
232 | phys += vdev->fmt.bytesperline; |
233 | |
234 | ipu_cpmem_set_buffer(ch, bufnum: priv->ipu_buf_num, buf: phys); |
235 | } |
236 | |
237 | static irqreturn_t prp_eof_interrupt(int irq, void *dev_id) |
238 | { |
239 | struct prp_priv *priv = dev_id; |
240 | struct ipuv3_channel *channel; |
241 | |
242 | spin_lock(lock: &priv->irqlock); |
243 | |
244 | if (priv->last_eof) { |
245 | complete(&priv->last_eof_comp); |
246 | priv->last_eof = false; |
247 | goto unlock; |
248 | } |
249 | |
250 | channel = (ipu_rot_mode_is_irt(priv->rot_mode)) ? |
251 | priv->rot_out_ch : priv->out_ch; |
252 | |
253 | prp_vb2_buf_done(priv, ch: channel); |
254 | |
255 | /* select new IPU buf */ |
256 | ipu_idmac_select_buffer(channel, buf_num: priv->ipu_buf_num); |
257 | /* toggle IPU double-buffer index */ |
258 | priv->ipu_buf_num ^= 1; |
259 | |
260 | /* bump the EOF timeout timer */ |
261 | mod_timer(timer: &priv->eof_timeout_timer, |
262 | expires: jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); |
263 | |
264 | unlock: |
265 | spin_unlock(lock: &priv->irqlock); |
266 | return IRQ_HANDLED; |
267 | } |
268 | |
269 | static irqreturn_t prp_nfb4eof_interrupt(int irq, void *dev_id) |
270 | { |
271 | struct prp_priv *priv = dev_id; |
272 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
273 | |
274 | spin_lock(lock: &priv->irqlock); |
275 | |
276 | /* |
277 | * this is not an unrecoverable error, just mark |
278 | * the next captured frame with vb2 error flag. |
279 | */ |
280 | priv->nfb4eof = true; |
281 | |
282 | v4l2_err(&ic_priv->sd, "NFB4EOF\n" ); |
283 | |
284 | spin_unlock(lock: &priv->irqlock); |
285 | |
286 | return IRQ_HANDLED; |
287 | } |
288 | |
289 | /* |
290 | * EOF timeout timer function. |
291 | */ |
292 | /* |
293 | * EOF timeout timer function. This is an unrecoverable condition |
294 | * without a stream restart. |
295 | */ |
296 | static void prp_eof_timeout(struct timer_list *t) |
297 | { |
298 | struct prp_priv *priv = from_timer(priv, t, eof_timeout_timer); |
299 | struct imx_media_video_dev *vdev = priv->vdev; |
300 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
301 | |
302 | v4l2_err(&ic_priv->sd, "EOF timeout\n" ); |
303 | |
304 | /* signal a fatal error to capture device */ |
305 | imx_media_capture_device_error(vdev); |
306 | } |
307 | |
308 | static void prp_setup_vb2_buf(struct prp_priv *priv, dma_addr_t *phys) |
309 | { |
310 | struct imx_media_video_dev *vdev = priv->vdev; |
311 | struct imx_media_buffer *buf; |
312 | int i; |
313 | |
314 | for (i = 0; i < 2; i++) { |
315 | buf = imx_media_capture_device_next_buf(vdev); |
316 | if (buf) { |
317 | priv->active_vb2_buf[i] = buf; |
318 | phys[i] = vb2_dma_contig_plane_dma_addr( |
319 | vb: &buf->vbuf.vb2_buf, plane_no: 0); |
320 | } else { |
321 | priv->active_vb2_buf[i] = NULL; |
322 | phys[i] = priv->underrun_buf.phys; |
323 | } |
324 | } |
325 | } |
326 | |
327 | static void prp_unsetup_vb2_buf(struct prp_priv *priv, |
328 | enum vb2_buffer_state return_status) |
329 | { |
330 | struct imx_media_buffer *buf; |
331 | int i; |
332 | |
333 | /* return any remaining active frames with return_status */ |
334 | for (i = 0; i < 2; i++) { |
335 | buf = priv->active_vb2_buf[i]; |
336 | if (buf) { |
337 | struct vb2_buffer *vb = &buf->vbuf.vb2_buf; |
338 | |
339 | vb->timestamp = ktime_get_ns(); |
340 | vb2_buffer_done(vb, state: return_status); |
341 | } |
342 | } |
343 | } |
344 | |
345 | static int prp_setup_channel(struct prp_priv *priv, |
346 | struct ipuv3_channel *channel, |
347 | enum ipu_rotate_mode rot_mode, |
348 | dma_addr_t addr0, dma_addr_t addr1, |
349 | bool rot_swap_width_height) |
350 | { |
351 | struct imx_media_video_dev *vdev = priv->vdev; |
352 | const struct imx_media_pixfmt *outcc; |
353 | struct v4l2_mbus_framefmt *outfmt; |
354 | unsigned int burst_size; |
355 | struct ipu_image image; |
356 | bool interweave; |
357 | int ret; |
358 | |
359 | outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; |
360 | outcc = vdev->cc; |
361 | |
362 | ipu_cpmem_zero(ch: channel); |
363 | |
364 | memset(&image, 0, sizeof(image)); |
365 | image.pix = vdev->fmt; |
366 | image.rect = vdev->compose; |
367 | |
368 | /* |
369 | * If the field type at capture interface is interlaced, and |
370 | * the output IDMAC pad is sequential, enable interweave at |
371 | * the IDMAC output channel. |
372 | */ |
373 | interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && |
374 | V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); |
375 | priv->interweave_swap = interweave && |
376 | image.pix.field == V4L2_FIELD_INTERLACED_BT; |
377 | |
378 | if (rot_swap_width_height) { |
379 | swap(image.pix.width, image.pix.height); |
380 | swap(image.rect.width, image.rect.height); |
381 | /* recalc stride using swapped width */ |
382 | image.pix.bytesperline = outcc->planar ? |
383 | image.pix.width : |
384 | (image.pix.width * outcc->bpp) >> 3; |
385 | } |
386 | |
387 | if (priv->interweave_swap && channel == priv->out_ch) { |
388 | /* start interweave scan at 1st top line (2nd line) */ |
389 | image.rect.top = 1; |
390 | } |
391 | |
392 | image.phys0 = addr0; |
393 | image.phys1 = addr1; |
394 | |
395 | /* |
396 | * Skip writing U and V components to odd rows in the output |
397 | * channels for planar 4:2:0 (but not when enabling IDMAC |
398 | * interweaving, they are incompatible). |
399 | */ |
400 | if ((channel == priv->out_ch && !interweave) || |
401 | channel == priv->rot_out_ch) { |
402 | switch (image.pix.pixelformat) { |
403 | case V4L2_PIX_FMT_YUV420: |
404 | case V4L2_PIX_FMT_YVU420: |
405 | case V4L2_PIX_FMT_NV12: |
406 | ipu_cpmem_skip_odd_chroma_rows(ch: channel); |
407 | break; |
408 | } |
409 | } |
410 | |
411 | ret = ipu_cpmem_set_image(ch: channel, image: &image); |
412 | if (ret) |
413 | return ret; |
414 | |
415 | if (channel == priv->rot_in_ch || |
416 | channel == priv->rot_out_ch) { |
417 | burst_size = 8; |
418 | ipu_cpmem_set_block_mode(ch: channel); |
419 | } else { |
420 | burst_size = (image.pix.width & 0xf) ? 8 : 16; |
421 | } |
422 | |
423 | ipu_cpmem_set_burstsize(ch: channel, burstsize: burst_size); |
424 | |
425 | if (rot_mode) |
426 | ipu_cpmem_set_rotation(ch: channel, rot: rot_mode); |
427 | |
428 | if (interweave && channel == priv->out_ch) |
429 | ipu_cpmem_interlaced_scan(ch: channel, |
430 | stride: priv->interweave_swap ? |
431 | -image.pix.bytesperline : |
432 | image.pix.bytesperline, |
433 | pixelformat: image.pix.pixelformat); |
434 | |
435 | ret = ipu_ic_task_idma_init(ic: priv->ic, channel, |
436 | width: image.pix.width, height: image.pix.height, |
437 | burst_size, rot: rot_mode); |
438 | if (ret) |
439 | return ret; |
440 | |
441 | ipu_cpmem_set_axi_id(ch: channel, id: 1); |
442 | |
443 | ipu_idmac_set_double_buffer(channel, doublebuffer: true); |
444 | |
445 | return 0; |
446 | } |
447 | |
448 | static int prp_setup_rotation(struct prp_priv *priv) |
449 | { |
450 | struct imx_media_video_dev *vdev = priv->vdev; |
451 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
452 | const struct imx_media_pixfmt *outcc, *incc; |
453 | struct v4l2_mbus_framefmt *infmt; |
454 | struct v4l2_pix_format *outfmt; |
455 | struct ipu_ic_csc csc; |
456 | dma_addr_t phys[2]; |
457 | int ret; |
458 | |
459 | infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; |
460 | outfmt = &vdev->fmt; |
461 | incc = priv->cc[PRPENCVF_SINK_PAD]; |
462 | outcc = vdev->cc; |
463 | |
464 | ret = ipu_ic_calc_csc(csc: &csc, |
465 | in_enc: infmt->ycbcr_enc, in_quant: infmt->quantization, |
466 | in_cs: incc->cs, |
467 | out_enc: outfmt->ycbcr_enc, out_quant: outfmt->quantization, |
468 | out_cs: outcc->cs); |
469 | if (ret) { |
470 | v4l2_err(&ic_priv->sd, "ipu_ic_calc_csc failed, %d\n" , |
471 | ret); |
472 | return ret; |
473 | } |
474 | |
475 | ret = imx_media_alloc_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->rot_buf[0], |
476 | size: outfmt->sizeimage); |
477 | if (ret) { |
478 | v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[0], %d\n" , ret); |
479 | return ret; |
480 | } |
481 | ret = imx_media_alloc_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->rot_buf[1], |
482 | size: outfmt->sizeimage); |
483 | if (ret) { |
484 | v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[1], %d\n" , ret); |
485 | goto free_rot0; |
486 | } |
487 | |
488 | ret = ipu_ic_task_init(ic: priv->ic, csc: &csc, |
489 | in_width: infmt->width, in_height: infmt->height, |
490 | out_width: outfmt->height, out_height: outfmt->width); |
491 | if (ret) { |
492 | v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n" , ret); |
493 | goto free_rot1; |
494 | } |
495 | |
496 | /* init the IC-PRP-->MEM IDMAC channel */ |
497 | ret = prp_setup_channel(priv, channel: priv->out_ch, rot_mode: IPU_ROTATE_NONE, |
498 | addr0: priv->rot_buf[0].phys, addr1: priv->rot_buf[1].phys, |
499 | rot_swap_width_height: true); |
500 | if (ret) { |
501 | v4l2_err(&ic_priv->sd, |
502 | "prp_setup_channel(out_ch) failed, %d\n" , ret); |
503 | goto free_rot1; |
504 | } |
505 | |
506 | /* init the MEM-->IC-PRP ROT IDMAC channel */ |
507 | ret = prp_setup_channel(priv, channel: priv->rot_in_ch, rot_mode: priv->rot_mode, |
508 | addr0: priv->rot_buf[0].phys, addr1: priv->rot_buf[1].phys, |
509 | rot_swap_width_height: true); |
510 | if (ret) { |
511 | v4l2_err(&ic_priv->sd, |
512 | "prp_setup_channel(rot_in_ch) failed, %d\n" , ret); |
513 | goto free_rot1; |
514 | } |
515 | |
516 | prp_setup_vb2_buf(priv, phys); |
517 | |
518 | /* init the destination IC-PRP ROT-->MEM IDMAC channel */ |
519 | ret = prp_setup_channel(priv, channel: priv->rot_out_ch, rot_mode: IPU_ROTATE_NONE, |
520 | addr0: phys[0], addr1: phys[1], |
521 | rot_swap_width_height: false); |
522 | if (ret) { |
523 | v4l2_err(&ic_priv->sd, |
524 | "prp_setup_channel(rot_out_ch) failed, %d\n" , ret); |
525 | goto unsetup_vb2; |
526 | } |
527 | |
528 | /* now link IC-PRP-->MEM to MEM-->IC-PRP ROT */ |
529 | ipu_idmac_link(src: priv->out_ch, sink: priv->rot_in_ch); |
530 | |
531 | /* enable the IC */ |
532 | ipu_ic_enable(ic: priv->ic); |
533 | |
534 | /* set buffers ready */ |
535 | ipu_idmac_select_buffer(channel: priv->out_ch, buf_num: 0); |
536 | ipu_idmac_select_buffer(channel: priv->out_ch, buf_num: 1); |
537 | ipu_idmac_select_buffer(channel: priv->rot_out_ch, buf_num: 0); |
538 | ipu_idmac_select_buffer(channel: priv->rot_out_ch, buf_num: 1); |
539 | |
540 | /* enable the channels */ |
541 | ipu_idmac_enable_channel(channel: priv->out_ch); |
542 | ipu_idmac_enable_channel(channel: priv->rot_in_ch); |
543 | ipu_idmac_enable_channel(channel: priv->rot_out_ch); |
544 | |
545 | /* and finally enable the IC PRP task */ |
546 | ipu_ic_task_enable(ic: priv->ic); |
547 | |
548 | return 0; |
549 | |
550 | unsetup_vb2: |
551 | prp_unsetup_vb2_buf(priv, return_status: VB2_BUF_STATE_QUEUED); |
552 | free_rot1: |
553 | imx_media_free_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->rot_buf[1]); |
554 | free_rot0: |
555 | imx_media_free_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->rot_buf[0]); |
556 | return ret; |
557 | } |
558 | |
559 | static void prp_unsetup_rotation(struct prp_priv *priv) |
560 | { |
561 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
562 | |
563 | ipu_ic_task_disable(ic: priv->ic); |
564 | |
565 | ipu_idmac_disable_channel(channel: priv->out_ch); |
566 | ipu_idmac_disable_channel(channel: priv->rot_in_ch); |
567 | ipu_idmac_disable_channel(channel: priv->rot_out_ch); |
568 | |
569 | ipu_idmac_unlink(src: priv->out_ch, sink: priv->rot_in_ch); |
570 | |
571 | ipu_ic_disable(ic: priv->ic); |
572 | |
573 | imx_media_free_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->rot_buf[0]); |
574 | imx_media_free_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->rot_buf[1]); |
575 | } |
576 | |
577 | static int prp_setup_norotation(struct prp_priv *priv) |
578 | { |
579 | struct imx_media_video_dev *vdev = priv->vdev; |
580 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
581 | const struct imx_media_pixfmt *outcc, *incc; |
582 | struct v4l2_mbus_framefmt *infmt; |
583 | struct v4l2_pix_format *outfmt; |
584 | struct ipu_ic_csc csc; |
585 | dma_addr_t phys[2]; |
586 | int ret; |
587 | |
588 | infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; |
589 | outfmt = &vdev->fmt; |
590 | incc = priv->cc[PRPENCVF_SINK_PAD]; |
591 | outcc = vdev->cc; |
592 | |
593 | ret = ipu_ic_calc_csc(csc: &csc, |
594 | in_enc: infmt->ycbcr_enc, in_quant: infmt->quantization, |
595 | in_cs: incc->cs, |
596 | out_enc: outfmt->ycbcr_enc, out_quant: outfmt->quantization, |
597 | out_cs: outcc->cs); |
598 | if (ret) { |
599 | v4l2_err(&ic_priv->sd, "ipu_ic_calc_csc failed, %d\n" , |
600 | ret); |
601 | return ret; |
602 | } |
603 | |
604 | ret = ipu_ic_task_init(ic: priv->ic, csc: &csc, |
605 | in_width: infmt->width, in_height: infmt->height, |
606 | out_width: outfmt->width, out_height: outfmt->height); |
607 | if (ret) { |
608 | v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n" , ret); |
609 | return ret; |
610 | } |
611 | |
612 | prp_setup_vb2_buf(priv, phys); |
613 | |
614 | /* init the IC PRP-->MEM IDMAC channel */ |
615 | ret = prp_setup_channel(priv, channel: priv->out_ch, rot_mode: priv->rot_mode, |
616 | addr0: phys[0], addr1: phys[1], rot_swap_width_height: false); |
617 | if (ret) { |
618 | v4l2_err(&ic_priv->sd, |
619 | "prp_setup_channel(out_ch) failed, %d\n" , ret); |
620 | goto unsetup_vb2; |
621 | } |
622 | |
623 | ipu_cpmem_dump(ch: priv->out_ch); |
624 | ipu_ic_dump(ic: priv->ic); |
625 | ipu_dump(ipu: ic_priv->ipu); |
626 | |
627 | ipu_ic_enable(ic: priv->ic); |
628 | |
629 | /* set buffers ready */ |
630 | ipu_idmac_select_buffer(channel: priv->out_ch, buf_num: 0); |
631 | ipu_idmac_select_buffer(channel: priv->out_ch, buf_num: 1); |
632 | |
633 | /* enable the channels */ |
634 | ipu_idmac_enable_channel(channel: priv->out_ch); |
635 | |
636 | /* enable the IC task */ |
637 | ipu_ic_task_enable(ic: priv->ic); |
638 | |
639 | return 0; |
640 | |
641 | unsetup_vb2: |
642 | prp_unsetup_vb2_buf(priv, return_status: VB2_BUF_STATE_QUEUED); |
643 | return ret; |
644 | } |
645 | |
646 | static void prp_unsetup_norotation(struct prp_priv *priv) |
647 | { |
648 | ipu_ic_task_disable(ic: priv->ic); |
649 | ipu_idmac_disable_channel(channel: priv->out_ch); |
650 | ipu_ic_disable(ic: priv->ic); |
651 | } |
652 | |
653 | static void prp_unsetup(struct prp_priv *priv, |
654 | enum vb2_buffer_state state) |
655 | { |
656 | if (ipu_rot_mode_is_irt(priv->rot_mode)) |
657 | prp_unsetup_rotation(priv); |
658 | else |
659 | prp_unsetup_norotation(priv); |
660 | |
661 | prp_unsetup_vb2_buf(priv, return_status: state); |
662 | } |
663 | |
664 | static int prp_start(struct prp_priv *priv) |
665 | { |
666 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
667 | struct imx_media_video_dev *vdev = priv->vdev; |
668 | int ret; |
669 | |
670 | ret = prp_get_ipu_resources(priv); |
671 | if (ret) |
672 | return ret; |
673 | |
674 | ret = imx_media_alloc_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->underrun_buf, |
675 | size: vdev->fmt.sizeimage); |
676 | if (ret) |
677 | goto out_put_ipu; |
678 | |
679 | priv->ipu_buf_num = 0; |
680 | |
681 | /* init EOF completion waitq */ |
682 | init_completion(x: &priv->last_eof_comp); |
683 | priv->frame_sequence = 0; |
684 | priv->last_eof = false; |
685 | priv->nfb4eof = false; |
686 | |
687 | if (ipu_rot_mode_is_irt(priv->rot_mode)) |
688 | ret = prp_setup_rotation(priv); |
689 | else |
690 | ret = prp_setup_norotation(priv); |
691 | if (ret) |
692 | goto out_free_underrun; |
693 | |
694 | priv->nfb4eof_irq = ipu_idmac_channel_irq(ipu: ic_priv->ipu, |
695 | channel: priv->out_ch, |
696 | irq: IPU_IRQ_NFB4EOF); |
697 | ret = devm_request_irq(dev: ic_priv->ipu_dev, irq: priv->nfb4eof_irq, |
698 | handler: prp_nfb4eof_interrupt, irqflags: 0, |
699 | devname: "imx-ic-prp-nfb4eof" , dev_id: priv); |
700 | if (ret) { |
701 | v4l2_err(&ic_priv->sd, |
702 | "Error registering NFB4EOF irq: %d\n" , ret); |
703 | goto out_unsetup; |
704 | } |
705 | |
706 | if (ipu_rot_mode_is_irt(priv->rot_mode)) |
707 | priv->eof_irq = ipu_idmac_channel_irq( |
708 | ipu: ic_priv->ipu, channel: priv->rot_out_ch, irq: IPU_IRQ_EOF); |
709 | else |
710 | priv->eof_irq = ipu_idmac_channel_irq( |
711 | ipu: ic_priv->ipu, channel: priv->out_ch, irq: IPU_IRQ_EOF); |
712 | |
713 | ret = devm_request_irq(dev: ic_priv->ipu_dev, irq: priv->eof_irq, |
714 | handler: prp_eof_interrupt, irqflags: 0, |
715 | devname: "imx-ic-prp-eof" , dev_id: priv); |
716 | if (ret) { |
717 | v4l2_err(&ic_priv->sd, |
718 | "Error registering eof irq: %d\n" , ret); |
719 | goto out_free_nfb4eof_irq; |
720 | } |
721 | |
722 | /* start upstream */ |
723 | ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); |
724 | ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; |
725 | if (ret) { |
726 | v4l2_err(&ic_priv->sd, |
727 | "upstream stream on failed: %d\n" , ret); |
728 | goto out_free_eof_irq; |
729 | } |
730 | |
731 | /* start the EOF timeout timer */ |
732 | mod_timer(timer: &priv->eof_timeout_timer, |
733 | expires: jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); |
734 | |
735 | return 0; |
736 | |
737 | out_free_eof_irq: |
738 | devm_free_irq(dev: ic_priv->ipu_dev, irq: priv->eof_irq, dev_id: priv); |
739 | out_free_nfb4eof_irq: |
740 | devm_free_irq(dev: ic_priv->ipu_dev, irq: priv->nfb4eof_irq, dev_id: priv); |
741 | out_unsetup: |
742 | prp_unsetup(priv, state: VB2_BUF_STATE_QUEUED); |
743 | out_free_underrun: |
744 | imx_media_free_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->underrun_buf); |
745 | out_put_ipu: |
746 | prp_put_ipu_resources(priv); |
747 | return ret; |
748 | } |
749 | |
750 | static void prp_stop(struct prp_priv *priv) |
751 | { |
752 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
753 | unsigned long flags; |
754 | int ret; |
755 | |
756 | /* mark next EOF interrupt as the last before stream off */ |
757 | spin_lock_irqsave(&priv->irqlock, flags); |
758 | priv->last_eof = true; |
759 | spin_unlock_irqrestore(lock: &priv->irqlock, flags); |
760 | |
761 | /* |
762 | * and then wait for interrupt handler to mark completion. |
763 | */ |
764 | ret = wait_for_completion_timeout( |
765 | x: &priv->last_eof_comp, |
766 | timeout: msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); |
767 | if (ret == 0) |
768 | v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n" ); |
769 | |
770 | /* stop upstream */ |
771 | ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); |
772 | if (ret && ret != -ENOIOCTLCMD) |
773 | v4l2_warn(&ic_priv->sd, |
774 | "upstream stream off failed: %d\n" , ret); |
775 | |
776 | devm_free_irq(dev: ic_priv->ipu_dev, irq: priv->eof_irq, dev_id: priv); |
777 | devm_free_irq(dev: ic_priv->ipu_dev, irq: priv->nfb4eof_irq, dev_id: priv); |
778 | |
779 | prp_unsetup(priv, state: VB2_BUF_STATE_ERROR); |
780 | |
781 | imx_media_free_dma_buf(dev: ic_priv->ipu_dev, buf: &priv->underrun_buf); |
782 | |
783 | /* cancel the EOF timeout timer */ |
784 | del_timer_sync(timer: &priv->eof_timeout_timer); |
785 | |
786 | prp_put_ipu_resources(priv); |
787 | } |
788 | |
789 | static struct v4l2_mbus_framefmt * |
790 | __prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, |
791 | unsigned int pad, enum v4l2_subdev_format_whence which) |
792 | { |
793 | if (which == V4L2_SUBDEV_FORMAT_TRY) |
794 | return v4l2_subdev_state_get_format(sd_state, pad); |
795 | else |
796 | return &priv->format_mbus[pad]; |
797 | } |
798 | |
799 | /* |
800 | * Applies IC resizer and IDMAC alignment restrictions to output |
801 | * rectangle given the input rectangle, and depending on given |
802 | * rotation mode. |
803 | * |
804 | * The IC resizer cannot downsize more than 4:1. Note also that |
805 | * for 90 or 270 rotation, _both_ output width and height must |
806 | * be aligned by W_ALIGN_SRC, because the intermediate rotation |
807 | * buffer swaps output width/height, and the final output buffer |
808 | * does not. |
809 | * |
810 | * Returns true if the output rectangle was modified. |
811 | */ |
812 | static bool prp_bound_align_output(struct v4l2_mbus_framefmt *outfmt, |
813 | struct v4l2_mbus_framefmt *infmt, |
814 | enum ipu_rotate_mode rot_mode) |
815 | { |
816 | u32 orig_width = outfmt->width; |
817 | u32 orig_height = outfmt->height; |
818 | |
819 | if (ipu_rot_mode_is_irt(rot_mode)) |
820 | v4l_bound_align_image(width: &outfmt->width, |
821 | wmin: infmt->height / 4, MAX_H_SRC, |
822 | W_ALIGN_SRC, |
823 | height: &outfmt->height, |
824 | hmin: infmt->width / 4, MAX_W_SRC, |
825 | W_ALIGN_SRC, S_ALIGN); |
826 | else |
827 | v4l_bound_align_image(width: &outfmt->width, |
828 | wmin: infmt->width / 4, MAX_W_SRC, |
829 | W_ALIGN_SRC, |
830 | height: &outfmt->height, |
831 | hmin: infmt->height / 4, MAX_H_SRC, |
832 | H_ALIGN_SRC, S_ALIGN); |
833 | |
834 | return outfmt->width != orig_width || outfmt->height != orig_height; |
835 | } |
836 | |
837 | /* |
838 | * V4L2 subdev operations. |
839 | */ |
840 | |
841 | static int prp_enum_mbus_code(struct v4l2_subdev *sd, |
842 | struct v4l2_subdev_state *sd_state, |
843 | struct v4l2_subdev_mbus_code_enum *code) |
844 | { |
845 | if (code->pad >= PRPENCVF_NUM_PADS) |
846 | return -EINVAL; |
847 | |
848 | return imx_media_enum_ipu_formats(code: &code->code, index: code->index, |
849 | fmt_sel: PIXFMT_SEL_YUV_RGB); |
850 | } |
851 | |
852 | static int prp_get_fmt(struct v4l2_subdev *sd, |
853 | struct v4l2_subdev_state *sd_state, |
854 | struct v4l2_subdev_format *sdformat) |
855 | { |
856 | struct prp_priv *priv = sd_to_priv(sd); |
857 | struct v4l2_mbus_framefmt *fmt; |
858 | int ret = 0; |
859 | |
860 | if (sdformat->pad >= PRPENCVF_NUM_PADS) |
861 | return -EINVAL; |
862 | |
863 | mutex_lock(&priv->lock); |
864 | |
865 | fmt = __prp_get_fmt(priv, sd_state, pad: sdformat->pad, which: sdformat->which); |
866 | if (!fmt) { |
867 | ret = -EINVAL; |
868 | goto out; |
869 | } |
870 | |
871 | sdformat->format = *fmt; |
872 | out: |
873 | mutex_unlock(lock: &priv->lock); |
874 | return ret; |
875 | } |
876 | |
877 | static void prp_try_fmt(struct prp_priv *priv, |
878 | struct v4l2_subdev_state *sd_state, |
879 | struct v4l2_subdev_format *sdformat, |
880 | const struct imx_media_pixfmt **cc) |
881 | { |
882 | struct v4l2_mbus_framefmt *infmt; |
883 | |
884 | *cc = imx_media_find_ipu_format(code: sdformat->format.code, |
885 | fmt_sel: PIXFMT_SEL_YUV_RGB); |
886 | if (!*cc) { |
887 | u32 code; |
888 | |
889 | imx_media_enum_ipu_formats(code: &code, index: 0, fmt_sel: PIXFMT_SEL_YUV_RGB); |
890 | *cc = imx_media_find_ipu_format(code, fmt_sel: PIXFMT_SEL_YUV_RGB); |
891 | |
892 | sdformat->format.code = (*cc)->codes[0]; |
893 | } |
894 | |
895 | infmt = __prp_get_fmt(priv, sd_state, pad: PRPENCVF_SINK_PAD, |
896 | which: sdformat->which); |
897 | |
898 | if (sdformat->pad == PRPENCVF_SRC_PAD) { |
899 | sdformat->format.field = infmt->field; |
900 | |
901 | prp_bound_align_output(outfmt: &sdformat->format, infmt, |
902 | rot_mode: priv->rot_mode); |
903 | |
904 | /* propagate colorimetry from sink */ |
905 | sdformat->format.colorspace = infmt->colorspace; |
906 | sdformat->format.xfer_func = infmt->xfer_func; |
907 | } else { |
908 | v4l_bound_align_image(width: &sdformat->format.width, |
909 | MIN_W_SINK, MAX_W_SINK, W_ALIGN_SINK, |
910 | height: &sdformat->format.height, |
911 | MIN_H_SINK, MAX_H_SINK, H_ALIGN_SINK, |
912 | S_ALIGN); |
913 | |
914 | if (sdformat->format.field == V4L2_FIELD_ANY) |
915 | sdformat->format.field = V4L2_FIELD_NONE; |
916 | } |
917 | |
918 | imx_media_try_colorimetry(tryfmt: &sdformat->format, ic_route: true); |
919 | } |
920 | |
921 | static int prp_set_fmt(struct v4l2_subdev *sd, |
922 | struct v4l2_subdev_state *sd_state, |
923 | struct v4l2_subdev_format *sdformat) |
924 | { |
925 | struct prp_priv *priv = sd_to_priv(sd); |
926 | const struct imx_media_pixfmt *cc; |
927 | struct v4l2_mbus_framefmt *fmt; |
928 | int ret = 0; |
929 | |
930 | if (sdformat->pad >= PRPENCVF_NUM_PADS) |
931 | return -EINVAL; |
932 | |
933 | mutex_lock(&priv->lock); |
934 | |
935 | if (priv->stream_count > 0) { |
936 | ret = -EBUSY; |
937 | goto out; |
938 | } |
939 | |
940 | prp_try_fmt(priv, sd_state, sdformat, cc: &cc); |
941 | |
942 | fmt = __prp_get_fmt(priv, sd_state, pad: sdformat->pad, which: sdformat->which); |
943 | *fmt = sdformat->format; |
944 | |
945 | /* propagate a default format to source pad */ |
946 | if (sdformat->pad == PRPENCVF_SINK_PAD) { |
947 | const struct imx_media_pixfmt *outcc; |
948 | struct v4l2_mbus_framefmt *outfmt; |
949 | struct v4l2_subdev_format format; |
950 | |
951 | format.pad = PRPENCVF_SRC_PAD; |
952 | format.which = sdformat->which; |
953 | format.format = sdformat->format; |
954 | prp_try_fmt(priv, sd_state, sdformat: &format, cc: &outcc); |
955 | |
956 | outfmt = __prp_get_fmt(priv, sd_state, pad: PRPENCVF_SRC_PAD, |
957 | which: sdformat->which); |
958 | *outfmt = format.format; |
959 | if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) |
960 | priv->cc[PRPENCVF_SRC_PAD] = outcc; |
961 | } |
962 | |
963 | if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) |
964 | priv->cc[sdformat->pad] = cc; |
965 | |
966 | out: |
967 | mutex_unlock(lock: &priv->lock); |
968 | return ret; |
969 | } |
970 | |
971 | static int prp_enum_frame_size(struct v4l2_subdev *sd, |
972 | struct v4l2_subdev_state *sd_state, |
973 | struct v4l2_subdev_frame_size_enum *fse) |
974 | { |
975 | struct prp_priv *priv = sd_to_priv(sd); |
976 | struct v4l2_subdev_format format = {}; |
977 | const struct imx_media_pixfmt *cc; |
978 | int ret = 0; |
979 | |
980 | if (fse->pad >= PRPENCVF_NUM_PADS || fse->index != 0) |
981 | return -EINVAL; |
982 | |
983 | mutex_lock(&priv->lock); |
984 | |
985 | format.pad = fse->pad; |
986 | format.which = fse->which; |
987 | format.format.code = fse->code; |
988 | format.format.width = 1; |
989 | format.format.height = 1; |
990 | prp_try_fmt(priv, sd_state, sdformat: &format, cc: &cc); |
991 | fse->min_width = format.format.width; |
992 | fse->min_height = format.format.height; |
993 | |
994 | if (format.format.code != fse->code) { |
995 | ret = -EINVAL; |
996 | goto out; |
997 | } |
998 | |
999 | format.format.code = fse->code; |
1000 | format.format.width = -1; |
1001 | format.format.height = -1; |
1002 | prp_try_fmt(priv, sd_state, sdformat: &format, cc: &cc); |
1003 | fse->max_width = format.format.width; |
1004 | fse->max_height = format.format.height; |
1005 | out: |
1006 | mutex_unlock(lock: &priv->lock); |
1007 | return ret; |
1008 | } |
1009 | |
1010 | static int prp_link_setup(struct media_entity *entity, |
1011 | const struct media_pad *local, |
1012 | const struct media_pad *remote, u32 flags) |
1013 | { |
1014 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); |
1015 | struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); |
1016 | struct prp_priv *priv = ic_priv->task_priv; |
1017 | struct v4l2_subdev *remote_sd; |
1018 | int ret = 0; |
1019 | |
1020 | dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s" , |
1021 | ic_priv->sd.name, remote->entity->name, local->entity->name); |
1022 | |
1023 | mutex_lock(&priv->lock); |
1024 | |
1025 | if (local->flags & MEDIA_PAD_FL_SINK) { |
1026 | if (!is_media_entity_v4l2_subdev(entity: remote->entity)) { |
1027 | ret = -EINVAL; |
1028 | goto out; |
1029 | } |
1030 | |
1031 | remote_sd = media_entity_to_v4l2_subdev(remote->entity); |
1032 | |
1033 | if (flags & MEDIA_LNK_FL_ENABLED) { |
1034 | if (priv->src_sd) { |
1035 | ret = -EBUSY; |
1036 | goto out; |
1037 | } |
1038 | priv->src_sd = remote_sd; |
1039 | } else { |
1040 | priv->src_sd = NULL; |
1041 | } |
1042 | |
1043 | goto out; |
1044 | } |
1045 | |
1046 | /* this is the source pad */ |
1047 | |
1048 | /* the remote must be the device node */ |
1049 | if (!is_media_entity_v4l2_video_device(entity: remote->entity)) { |
1050 | ret = -EINVAL; |
1051 | goto out; |
1052 | } |
1053 | |
1054 | if (flags & MEDIA_LNK_FL_ENABLED) { |
1055 | if (priv->sink) { |
1056 | ret = -EBUSY; |
1057 | goto out; |
1058 | } |
1059 | } else { |
1060 | priv->sink = NULL; |
1061 | goto out; |
1062 | } |
1063 | |
1064 | priv->sink = remote->entity; |
1065 | out: |
1066 | mutex_unlock(lock: &priv->lock); |
1067 | return ret; |
1068 | } |
1069 | |
1070 | static int prp_s_ctrl(struct v4l2_ctrl *ctrl) |
1071 | { |
1072 | struct prp_priv *priv = container_of(ctrl->handler, |
1073 | struct prp_priv, ctrl_hdlr); |
1074 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
1075 | enum ipu_rotate_mode rot_mode; |
1076 | int rotation, ret = 0; |
1077 | bool hflip, vflip; |
1078 | |
1079 | mutex_lock(&priv->lock); |
1080 | |
1081 | rotation = priv->rotation; |
1082 | hflip = priv->hflip; |
1083 | vflip = priv->vflip; |
1084 | |
1085 | switch (ctrl->id) { |
1086 | case V4L2_CID_HFLIP: |
1087 | hflip = (ctrl->val == 1); |
1088 | break; |
1089 | case V4L2_CID_VFLIP: |
1090 | vflip = (ctrl->val == 1); |
1091 | break; |
1092 | case V4L2_CID_ROTATE: |
1093 | rotation = ctrl->val; |
1094 | break; |
1095 | default: |
1096 | v4l2_err(&ic_priv->sd, "Invalid control\n" ); |
1097 | ret = -EINVAL; |
1098 | goto out; |
1099 | } |
1100 | |
1101 | ret = ipu_degrees_to_rot_mode(mode: &rot_mode, degrees: rotation, hflip, vflip); |
1102 | if (ret) |
1103 | goto out; |
1104 | |
1105 | if (rot_mode != priv->rot_mode) { |
1106 | struct v4l2_mbus_framefmt outfmt, infmt; |
1107 | |
1108 | /* can't change rotation mid-streaming */ |
1109 | if (priv->stream_count > 0) { |
1110 | ret = -EBUSY; |
1111 | goto out; |
1112 | } |
1113 | |
1114 | outfmt = priv->format_mbus[PRPENCVF_SRC_PAD]; |
1115 | infmt = priv->format_mbus[PRPENCVF_SINK_PAD]; |
1116 | |
1117 | if (prp_bound_align_output(outfmt: &outfmt, infmt: &infmt, rot_mode)) { |
1118 | ret = -EINVAL; |
1119 | goto out; |
1120 | } |
1121 | |
1122 | priv->rot_mode = rot_mode; |
1123 | priv->rotation = rotation; |
1124 | priv->hflip = hflip; |
1125 | priv->vflip = vflip; |
1126 | } |
1127 | |
1128 | out: |
1129 | mutex_unlock(lock: &priv->lock); |
1130 | return ret; |
1131 | } |
1132 | |
1133 | static const struct v4l2_ctrl_ops prp_ctrl_ops = { |
1134 | .s_ctrl = prp_s_ctrl, |
1135 | }; |
1136 | |
1137 | static int prp_init_controls(struct prp_priv *priv) |
1138 | { |
1139 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
1140 | struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr; |
1141 | int ret; |
1142 | |
1143 | v4l2_ctrl_handler_init(hdlr, 3); |
1144 | |
1145 | v4l2_ctrl_new_std(hdl: hdlr, ops: &prp_ctrl_ops, V4L2_CID_HFLIP, |
1146 | min: 0, max: 1, step: 1, def: 0); |
1147 | v4l2_ctrl_new_std(hdl: hdlr, ops: &prp_ctrl_ops, V4L2_CID_VFLIP, |
1148 | min: 0, max: 1, step: 1, def: 0); |
1149 | v4l2_ctrl_new_std(hdl: hdlr, ops: &prp_ctrl_ops, V4L2_CID_ROTATE, |
1150 | min: 0, max: 270, step: 90, def: 0); |
1151 | |
1152 | ic_priv->sd.ctrl_handler = hdlr; |
1153 | |
1154 | if (hdlr->error) { |
1155 | ret = hdlr->error; |
1156 | goto out_free; |
1157 | } |
1158 | |
1159 | v4l2_ctrl_handler_setup(hdl: hdlr); |
1160 | return 0; |
1161 | |
1162 | out_free: |
1163 | v4l2_ctrl_handler_free(hdl: hdlr); |
1164 | return ret; |
1165 | } |
1166 | |
1167 | static int prp_s_stream(struct v4l2_subdev *sd, int enable) |
1168 | { |
1169 | struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); |
1170 | struct prp_priv *priv = ic_priv->task_priv; |
1171 | int ret = 0; |
1172 | |
1173 | mutex_lock(&priv->lock); |
1174 | |
1175 | if (!priv->src_sd || !priv->sink) { |
1176 | ret = -EPIPE; |
1177 | goto out; |
1178 | } |
1179 | |
1180 | /* |
1181 | * enable/disable streaming only if stream_count is |
1182 | * going from 0 to 1 / 1 to 0. |
1183 | */ |
1184 | if (priv->stream_count != !enable) |
1185 | goto update_count; |
1186 | |
1187 | dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n" , sd->name, |
1188 | enable ? "ON" : "OFF" ); |
1189 | |
1190 | if (enable) |
1191 | ret = prp_start(priv); |
1192 | else |
1193 | prp_stop(priv); |
1194 | if (ret) |
1195 | goto out; |
1196 | |
1197 | update_count: |
1198 | priv->stream_count += enable ? 1 : -1; |
1199 | if (priv->stream_count < 0) |
1200 | priv->stream_count = 0; |
1201 | out: |
1202 | mutex_unlock(lock: &priv->lock); |
1203 | return ret; |
1204 | } |
1205 | |
1206 | static int prp_get_frame_interval(struct v4l2_subdev *sd, |
1207 | struct v4l2_subdev_state *sd_state, |
1208 | struct v4l2_subdev_frame_interval *fi) |
1209 | { |
1210 | struct prp_priv *priv = sd_to_priv(sd); |
1211 | |
1212 | /* |
1213 | * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 |
1214 | * subdev active state API. |
1215 | */ |
1216 | if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
1217 | return -EINVAL; |
1218 | |
1219 | if (fi->pad >= PRPENCVF_NUM_PADS) |
1220 | return -EINVAL; |
1221 | |
1222 | mutex_lock(&priv->lock); |
1223 | fi->interval = priv->frame_interval; |
1224 | mutex_unlock(lock: &priv->lock); |
1225 | |
1226 | return 0; |
1227 | } |
1228 | |
1229 | static int prp_set_frame_interval(struct v4l2_subdev *sd, |
1230 | struct v4l2_subdev_state *sd_state, |
1231 | struct v4l2_subdev_frame_interval *fi) |
1232 | { |
1233 | struct prp_priv *priv = sd_to_priv(sd); |
1234 | |
1235 | /* |
1236 | * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 |
1237 | * subdev active state API. |
1238 | */ |
1239 | if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
1240 | return -EINVAL; |
1241 | |
1242 | if (fi->pad >= PRPENCVF_NUM_PADS) |
1243 | return -EINVAL; |
1244 | |
1245 | mutex_lock(&priv->lock); |
1246 | |
1247 | /* No limits on valid frame intervals */ |
1248 | if (fi->interval.numerator == 0 || fi->interval.denominator == 0) |
1249 | fi->interval = priv->frame_interval; |
1250 | else |
1251 | priv->frame_interval = fi->interval; |
1252 | |
1253 | mutex_unlock(lock: &priv->lock); |
1254 | |
1255 | return 0; |
1256 | } |
1257 | |
1258 | static int prp_registered(struct v4l2_subdev *sd) |
1259 | { |
1260 | struct prp_priv *priv = sd_to_priv(sd); |
1261 | struct imx_ic_priv *ic_priv = priv->ic_priv; |
1262 | int i, ret; |
1263 | u32 code; |
1264 | |
1265 | /* set a default mbus format */ |
1266 | imx_media_enum_ipu_formats(code: &code, index: 0, fmt_sel: PIXFMT_SEL_YUV); |
1267 | |
1268 | for (i = 0; i < PRPENCVF_NUM_PADS; i++) { |
1269 | ret = imx_media_init_mbus_fmt(mbus: &priv->format_mbus[i], |
1270 | IMX_MEDIA_DEF_PIX_WIDTH, |
1271 | IMX_MEDIA_DEF_PIX_HEIGHT, code, |
1272 | field: V4L2_FIELD_NONE, cc: &priv->cc[i]); |
1273 | if (ret) |
1274 | return ret; |
1275 | } |
1276 | |
1277 | /* init default frame interval */ |
1278 | priv->frame_interval.numerator = 1; |
1279 | priv->frame_interval.denominator = 30; |
1280 | |
1281 | priv->vdev = imx_media_capture_device_init(dev: ic_priv->ipu_dev, |
1282 | src_sd: &ic_priv->sd, |
1283 | pad: PRPENCVF_SRC_PAD, legacy_api: true); |
1284 | if (IS_ERR(ptr: priv->vdev)) |
1285 | return PTR_ERR(ptr: priv->vdev); |
1286 | |
1287 | ret = imx_media_capture_device_register(vdev: priv->vdev, link_flags: 0); |
1288 | if (ret) |
1289 | goto remove_vdev; |
1290 | |
1291 | ret = prp_init_controls(priv); |
1292 | if (ret) |
1293 | goto unreg_vdev; |
1294 | |
1295 | return 0; |
1296 | |
1297 | unreg_vdev: |
1298 | imx_media_capture_device_unregister(vdev: priv->vdev); |
1299 | remove_vdev: |
1300 | imx_media_capture_device_remove(vdev: priv->vdev); |
1301 | return ret; |
1302 | } |
1303 | |
1304 | static void prp_unregistered(struct v4l2_subdev *sd) |
1305 | { |
1306 | struct prp_priv *priv = sd_to_priv(sd); |
1307 | |
1308 | imx_media_capture_device_unregister(vdev: priv->vdev); |
1309 | imx_media_capture_device_remove(vdev: priv->vdev); |
1310 | |
1311 | v4l2_ctrl_handler_free(hdl: &priv->ctrl_hdlr); |
1312 | } |
1313 | |
1314 | static const struct v4l2_subdev_pad_ops prp_pad_ops = { |
1315 | .enum_mbus_code = prp_enum_mbus_code, |
1316 | .enum_frame_size = prp_enum_frame_size, |
1317 | .get_fmt = prp_get_fmt, |
1318 | .set_fmt = prp_set_fmt, |
1319 | .get_frame_interval = prp_get_frame_interval, |
1320 | .set_frame_interval = prp_set_frame_interval, |
1321 | }; |
1322 | |
1323 | static const struct v4l2_subdev_video_ops prp_video_ops = { |
1324 | .s_stream = prp_s_stream, |
1325 | }; |
1326 | |
1327 | static const struct media_entity_operations prp_entity_ops = { |
1328 | .link_setup = prp_link_setup, |
1329 | .link_validate = v4l2_subdev_link_validate, |
1330 | }; |
1331 | |
1332 | static const struct v4l2_subdev_ops prp_subdev_ops = { |
1333 | .video = &prp_video_ops, |
1334 | .pad = &prp_pad_ops, |
1335 | }; |
1336 | |
1337 | static const struct v4l2_subdev_internal_ops prp_internal_ops = { |
1338 | .init_state = imx_media_init_state, |
1339 | .registered = prp_registered, |
1340 | .unregistered = prp_unregistered, |
1341 | }; |
1342 | |
1343 | static int prp_init(struct imx_ic_priv *ic_priv) |
1344 | { |
1345 | struct prp_priv *priv; |
1346 | int i, ret; |
1347 | |
1348 | priv = devm_kzalloc(dev: ic_priv->ipu_dev, size: sizeof(*priv), GFP_KERNEL); |
1349 | if (!priv) |
1350 | return -ENOMEM; |
1351 | |
1352 | ic_priv->task_priv = priv; |
1353 | priv->ic_priv = ic_priv; |
1354 | |
1355 | spin_lock_init(&priv->irqlock); |
1356 | timer_setup(&priv->eof_timeout_timer, prp_eof_timeout, 0); |
1357 | |
1358 | mutex_init(&priv->lock); |
1359 | |
1360 | for (i = 0; i < PRPENCVF_NUM_PADS; i++) { |
1361 | priv->pad[i].flags = (i == PRPENCVF_SINK_PAD) ? |
1362 | MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; |
1363 | } |
1364 | |
1365 | ret = media_entity_pads_init(entity: &ic_priv->sd.entity, num_pads: PRPENCVF_NUM_PADS, |
1366 | pads: priv->pad); |
1367 | if (ret) |
1368 | mutex_destroy(lock: &priv->lock); |
1369 | |
1370 | return ret; |
1371 | } |
1372 | |
1373 | static void prp_remove(struct imx_ic_priv *ic_priv) |
1374 | { |
1375 | struct prp_priv *priv = ic_priv->task_priv; |
1376 | |
1377 | mutex_destroy(lock: &priv->lock); |
1378 | } |
1379 | |
1380 | struct imx_ic_ops imx_ic_prpencvf_ops = { |
1381 | .subdev_ops = &prp_subdev_ops, |
1382 | .internal_ops = &prp_internal_ops, |
1383 | .entity_ops = &prp_entity_ops, |
1384 | .init = prp_init, |
1385 | .remove = prp_remove, |
1386 | }; |
1387 | |