1 | // SPDX-License-Identifier: GPL-2.0-or-later |
---|---|
2 | /* |
3 | * Copyright (C) 2012-2016 Mentor Graphics Inc. |
4 | * |
5 | * Queued image conversion support, with tiling and rotation. |
6 | */ |
7 | |
8 | #include <linux/interrupt.h> |
9 | #include <linux/dma-mapping.h> |
10 | #include <linux/math.h> |
11 | |
12 | #include <video/imx-ipu-image-convert.h> |
13 | |
14 | #include "ipu-prv.h" |
15 | |
16 | /* |
17 | * The IC Resizer has a restriction that the output frame from the |
18 | * resizer must be 1024 or less in both width (pixels) and height |
19 | * (lines). |
20 | * |
21 | * The image converter attempts to split up a conversion when |
22 | * the desired output (converted) frame resolution exceeds the |
23 | * IC resizer limit of 1024 in either dimension. |
24 | * |
25 | * If either dimension of the output frame exceeds the limit, the |
26 | * dimension is split into 1, 2, or 4 equal stripes, for a maximum |
27 | * of 4*4 or 16 tiles. A conversion is then carried out for each |
28 | * tile (but taking care to pass the full frame stride length to |
29 | * the DMA channel's parameter memory!). IDMA double-buffering is used |
30 | * to convert each tile back-to-back when possible (see note below |
31 | * when double_buffering boolean is set). |
32 | * |
33 | * Note that the input frame must be split up into the same number |
34 | * of tiles as the output frame: |
35 | * |
36 | * +---------+-----+ |
37 | * +-----+---+ | A | B | |
38 | * | A | B | | | | |
39 | * +-----+---+ --> +---------+-----+ |
40 | * | C | D | | C | D | |
41 | * +-----+---+ | | | |
42 | * +---------+-----+ |
43 | * |
44 | * Clockwise 90° rotations are handled by first rescaling into a |
45 | * reusable temporary tile buffer and then rotating with the 8x8 |
46 | * block rotator, writing to the correct destination: |
47 | * |
48 | * +-----+-----+ |
49 | * | | | |
50 | * +-----+---+ +---------+ | C | A | |
51 | * | A | B | | A,B, | | | | | |
52 | * +-----+---+ --> | C,D | | --> | | | |
53 | * | C | D | +---------+ +-----+-----+ |
54 | * +-----+---+ | D | B | |
55 | * | | | |
56 | * +-----+-----+ |
57 | * |
58 | * If the 8x8 block rotator is used, horizontal or vertical flipping |
59 | * is done during the rotation step, otherwise flipping is done |
60 | * during the scaling step. |
61 | * With rotation or flipping, tile order changes between input and |
62 | * output image. Tiles are numbered row major from top left to bottom |
63 | * right for both input and output image. |
64 | */ |
65 | |
66 | #define MAX_STRIPES_W 4 |
67 | #define MAX_STRIPES_H 4 |
68 | #define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) |
69 | |
70 | #define MIN_W 16 |
71 | #define MIN_H 8 |
72 | #define MAX_W 4096 |
73 | #define MAX_H 4096 |
74 | |
75 | enum ipu_image_convert_type { |
76 | IMAGE_CONVERT_IN = 0, |
77 | IMAGE_CONVERT_OUT, |
78 | }; |
79 | |
80 | struct ipu_image_convert_dma_buf { |
81 | void *virt; |
82 | dma_addr_t phys; |
83 | unsigned long len; |
84 | }; |
85 | |
86 | struct ipu_image_convert_dma_chan { |
87 | int in; |
88 | int out; |
89 | int rot_in; |
90 | int rot_out; |
91 | int vdi_in_p; |
92 | int vdi_in; |
93 | int vdi_in_n; |
94 | }; |
95 | |
96 | /* dimensions of one tile */ |
97 | struct ipu_image_tile { |
98 | u32 width; |
99 | u32 height; |
100 | u32 left; |
101 | u32 top; |
102 | /* size and strides are in bytes */ |
103 | u32 size; |
104 | u32 stride; |
105 | u32 rot_stride; |
106 | /* start Y or packed offset of this tile */ |
107 | u32 offset; |
108 | /* offset from start to tile in U plane, for planar formats */ |
109 | u32 u_off; |
110 | /* offset from start to tile in V plane, for planar formats */ |
111 | u32 v_off; |
112 | }; |
113 | |
114 | struct ipu_image_convert_image { |
115 | struct ipu_image base; |
116 | enum ipu_image_convert_type type; |
117 | |
118 | const struct ipu_image_pixfmt *fmt; |
119 | unsigned int stride; |
120 | |
121 | /* # of rows (horizontal stripes) if dest height is > 1024 */ |
122 | unsigned int num_rows; |
123 | /* # of columns (vertical stripes) if dest width is > 1024 */ |
124 | unsigned int num_cols; |
125 | |
126 | struct ipu_image_tile tile[MAX_TILES]; |
127 | }; |
128 | |
129 | struct ipu_image_pixfmt { |
130 | u32 fourcc; /* V4L2 fourcc */ |
131 | int bpp; /* total bpp */ |
132 | int uv_width_dec; /* decimation in width for U/V planes */ |
133 | int uv_height_dec; /* decimation in height for U/V planes */ |
134 | bool planar; /* planar format */ |
135 | bool uv_swapped; /* U and V planes are swapped */ |
136 | bool uv_packed; /* partial planar (U and V in same plane) */ |
137 | }; |
138 | |
139 | struct ipu_image_convert_ctx; |
140 | struct ipu_image_convert_chan; |
141 | struct ipu_image_convert_priv; |
142 | |
143 | enum eof_irq_mask { |
144 | EOF_IRQ_IN = BIT(0), |
145 | EOF_IRQ_ROT_IN = BIT(1), |
146 | EOF_IRQ_OUT = BIT(2), |
147 | EOF_IRQ_ROT_OUT = BIT(3), |
148 | }; |
149 | |
150 | #define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT) |
151 | #define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \ |
152 | EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT) |
153 | |
154 | struct ipu_image_convert_ctx { |
155 | struct ipu_image_convert_chan *chan; |
156 | |
157 | ipu_image_convert_cb_t complete; |
158 | void *complete_context; |
159 | |
160 | /* Source/destination image data and rotation mode */ |
161 | struct ipu_image_convert_image in; |
162 | struct ipu_image_convert_image out; |
163 | struct ipu_ic_csc csc; |
164 | enum ipu_rotate_mode rot_mode; |
165 | u32 downsize_coeff_h; |
166 | u32 downsize_coeff_v; |
167 | u32 image_resize_coeff_h; |
168 | u32 image_resize_coeff_v; |
169 | u32 resize_coeffs_h[MAX_STRIPES_W]; |
170 | u32 resize_coeffs_v[MAX_STRIPES_H]; |
171 | |
172 | /* intermediate buffer for rotation */ |
173 | struct ipu_image_convert_dma_buf rot_intermediate[2]; |
174 | |
175 | /* current buffer number for double buffering */ |
176 | int cur_buf_num; |
177 | |
178 | bool aborting; |
179 | struct completion aborted; |
180 | |
181 | /* can we use double-buffering for this conversion operation? */ |
182 | bool double_buffering; |
183 | /* num_rows * num_cols */ |
184 | unsigned int num_tiles; |
185 | /* next tile to process */ |
186 | unsigned int next_tile; |
187 | /* where to place converted tile in dest image */ |
188 | unsigned int out_tile_map[MAX_TILES]; |
189 | |
190 | /* mask of completed EOF irqs at every tile conversion */ |
191 | enum eof_irq_mask eof_mask; |
192 | |
193 | struct list_head list; |
194 | }; |
195 | |
196 | struct ipu_image_convert_chan { |
197 | struct ipu_image_convert_priv *priv; |
198 | |
199 | enum ipu_ic_task ic_task; |
200 | const struct ipu_image_convert_dma_chan *dma_ch; |
201 | |
202 | struct ipu_ic *ic; |
203 | struct ipuv3_channel *in_chan; |
204 | struct ipuv3_channel *out_chan; |
205 | struct ipuv3_channel *rotation_in_chan; |
206 | struct ipuv3_channel *rotation_out_chan; |
207 | |
208 | /* the IPU end-of-frame irqs */ |
209 | int in_eof_irq; |
210 | int rot_in_eof_irq; |
211 | int out_eof_irq; |
212 | int rot_out_eof_irq; |
213 | |
214 | spinlock_t irqlock; |
215 | |
216 | /* list of convert contexts */ |
217 | struct list_head ctx_list; |
218 | /* queue of conversion runs */ |
219 | struct list_head pending_q; |
220 | /* queue of completed runs */ |
221 | struct list_head done_q; |
222 | |
223 | /* the current conversion run */ |
224 | struct ipu_image_convert_run *current_run; |
225 | }; |
226 | |
227 | struct ipu_image_convert_priv { |
228 | struct ipu_image_convert_chan chan[IC_NUM_TASKS]; |
229 | struct ipu_soc *ipu; |
230 | }; |
231 | |
232 | static const struct ipu_image_convert_dma_chan |
233 | image_convert_dma_chan[IC_NUM_TASKS] = { |
234 | [IC_TASK_VIEWFINDER] = { |
235 | .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, |
236 | .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, |
237 | .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, |
238 | .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, |
239 | .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, |
240 | .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, |
241 | .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, |
242 | }, |
243 | [IC_TASK_POST_PROCESSOR] = { |
244 | .in = IPUV3_CHANNEL_MEM_IC_PP, |
245 | .out = IPUV3_CHANNEL_IC_PP_MEM, |
246 | .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, |
247 | .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, |
248 | }, |
249 | }; |
250 | |
251 | static const struct ipu_image_pixfmt image_convert_formats[] = { |
252 | { |
253 | .fourcc = V4L2_PIX_FMT_RGB565, |
254 | .bpp = 16, |
255 | }, { |
256 | .fourcc = V4L2_PIX_FMT_RGB24, |
257 | .bpp = 24, |
258 | }, { |
259 | .fourcc = V4L2_PIX_FMT_BGR24, |
260 | .bpp = 24, |
261 | }, { |
262 | .fourcc = V4L2_PIX_FMT_RGB32, |
263 | .bpp = 32, |
264 | }, { |
265 | .fourcc = V4L2_PIX_FMT_BGR32, |
266 | .bpp = 32, |
267 | }, { |
268 | .fourcc = V4L2_PIX_FMT_XRGB32, |
269 | .bpp = 32, |
270 | }, { |
271 | .fourcc = V4L2_PIX_FMT_XBGR32, |
272 | .bpp = 32, |
273 | }, { |
274 | .fourcc = V4L2_PIX_FMT_BGRX32, |
275 | .bpp = 32, |
276 | }, { |
277 | .fourcc = V4L2_PIX_FMT_RGBX32, |
278 | .bpp = 32, |
279 | }, { |
280 | .fourcc = V4L2_PIX_FMT_YUYV, |
281 | .bpp = 16, |
282 | .uv_width_dec = 2, |
283 | .uv_height_dec = 1, |
284 | }, { |
285 | .fourcc = V4L2_PIX_FMT_UYVY, |
286 | .bpp = 16, |
287 | .uv_width_dec = 2, |
288 | .uv_height_dec = 1, |
289 | }, { |
290 | .fourcc = V4L2_PIX_FMT_YUV420, |
291 | .bpp = 12, |
292 | .planar = true, |
293 | .uv_width_dec = 2, |
294 | .uv_height_dec = 2, |
295 | }, { |
296 | .fourcc = V4L2_PIX_FMT_YVU420, |
297 | .bpp = 12, |
298 | .planar = true, |
299 | .uv_width_dec = 2, |
300 | .uv_height_dec = 2, |
301 | .uv_swapped = true, |
302 | }, { |
303 | .fourcc = V4L2_PIX_FMT_NV12, |
304 | .bpp = 12, |
305 | .planar = true, |
306 | .uv_width_dec = 2, |
307 | .uv_height_dec = 2, |
308 | .uv_packed = true, |
309 | }, { |
310 | .fourcc = V4L2_PIX_FMT_YUV422P, |
311 | .bpp = 16, |
312 | .planar = true, |
313 | .uv_width_dec = 2, |
314 | .uv_height_dec = 1, |
315 | }, { |
316 | .fourcc = V4L2_PIX_FMT_NV16, |
317 | .bpp = 16, |
318 | .planar = true, |
319 | .uv_width_dec = 2, |
320 | .uv_height_dec = 1, |
321 | .uv_packed = true, |
322 | }, |
323 | }; |
324 | |
325 | static const struct ipu_image_pixfmt *get_format(u32 fourcc) |
326 | { |
327 | const struct ipu_image_pixfmt *ret = NULL; |
328 | unsigned int i; |
329 | |
330 | for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { |
331 | if (image_convert_formats[i].fourcc == fourcc) { |
332 | ret = &image_convert_formats[i]; |
333 | break; |
334 | } |
335 | } |
336 | |
337 | return ret; |
338 | } |
339 | |
340 | static void dump_format(struct ipu_image_convert_ctx *ctx, |
341 | struct ipu_image_convert_image *ic_image) |
342 | { |
343 | struct ipu_image_convert_chan *chan = ctx->chan; |
344 | struct ipu_image_convert_priv *priv = chan->priv; |
345 | |
346 | dev_dbg(priv->ipu->dev, |
347 | "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", |
348 | chan->ic_task, ctx, |
349 | ic_image->type == IMAGE_CONVERT_OUT ? "Output": "Input", |
350 | ic_image->base.pix.width, ic_image->base.pix.height, |
351 | ic_image->num_cols, ic_image->num_rows, |
352 | ic_image->fmt->fourcc & 0xff, |
353 | (ic_image->fmt->fourcc >> 8) & 0xff, |
354 | (ic_image->fmt->fourcc >> 16) & 0xff, |
355 | (ic_image->fmt->fourcc >> 24) & 0xff); |
356 | } |
357 | |
358 | static void free_dma_buf(struct ipu_image_convert_priv *priv, |
359 | struct ipu_image_convert_dma_buf *buf) |
360 | { |
361 | if (buf->virt) |
362 | dma_free_coherent(dev: priv->ipu->dev, |
363 | size: buf->len, cpu_addr: buf->virt, dma_handle: buf->phys); |
364 | buf->virt = NULL; |
365 | buf->phys = 0; |
366 | } |
367 | |
368 | static int alloc_dma_buf(struct ipu_image_convert_priv *priv, |
369 | struct ipu_image_convert_dma_buf *buf, |
370 | int size) |
371 | { |
372 | buf->len = PAGE_ALIGN(size); |
373 | buf->virt = dma_alloc_coherent(dev: priv->ipu->dev, size: buf->len, dma_handle: &buf->phys, |
374 | GFP_DMA | GFP_KERNEL); |
375 | if (!buf->virt) { |
376 | dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); |
377 | return -ENOMEM; |
378 | } |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | static inline int num_stripes(int dim) |
384 | { |
385 | return (dim - 1) / 1024 + 1; |
386 | } |
387 | |
388 | /* |
389 | * Calculate downsizing coefficients, which are the same for all tiles, |
390 | * and initial bilinear resizing coefficients, which are used to find the |
391 | * best seam positions. |
392 | * Also determine the number of tiles necessary to guarantee that no tile |
393 | * is larger than 1024 pixels in either dimension at the output and between |
394 | * IC downsizing and main processing sections. |
395 | */ |
396 | static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, |
397 | struct ipu_image *in, |
398 | struct ipu_image *out) |
399 | { |
400 | u32 downsized_width = in->rect.width; |
401 | u32 downsized_height = in->rect.height; |
402 | u32 downsize_coeff_v = 0; |
403 | u32 downsize_coeff_h = 0; |
404 | u32 resized_width = out->rect.width; |
405 | u32 resized_height = out->rect.height; |
406 | u32 resize_coeff_h; |
407 | u32 resize_coeff_v; |
408 | u32 cols; |
409 | u32 rows; |
410 | |
411 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
412 | resized_width = out->rect.height; |
413 | resized_height = out->rect.width; |
414 | } |
415 | |
416 | /* Do not let invalid input lead to an endless loop below */ |
417 | if (WARN_ON(resized_width == 0 || resized_height == 0)) |
418 | return -EINVAL; |
419 | |
420 | while (downsized_width >= resized_width * 2) { |
421 | downsized_width >>= 1; |
422 | downsize_coeff_h++; |
423 | } |
424 | |
425 | while (downsized_height >= resized_height * 2) { |
426 | downsized_height >>= 1; |
427 | downsize_coeff_v++; |
428 | } |
429 | |
430 | /* |
431 | * Calculate the bilinear resizing coefficients that could be used if |
432 | * we were converting with a single tile. The bottom right output pixel |
433 | * should sample as close as possible to the bottom right input pixel |
434 | * out of the decimator, but not overshoot it: |
435 | */ |
436 | resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); |
437 | resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); |
438 | |
439 | /* |
440 | * Both the output of the IC downsizing section before being passed to |
441 | * the IC main processing section and the final output of the IC main |
442 | * processing section must be <= 1024 pixels in both dimensions. |
443 | */ |
444 | cols = num_stripes(max_t(u32, downsized_width, resized_width)); |
445 | rows = num_stripes(max_t(u32, downsized_height, resized_height)); |
446 | |
447 | dev_dbg(ctx->chan->priv->ipu->dev, |
448 | "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", |
449 | __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, |
450 | resize_coeff_v, cols, rows); |
451 | |
452 | if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || |
453 | resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) |
454 | return -EINVAL; |
455 | |
456 | ctx->downsize_coeff_h = downsize_coeff_h; |
457 | ctx->downsize_coeff_v = downsize_coeff_v; |
458 | ctx->image_resize_coeff_h = resize_coeff_h; |
459 | ctx->image_resize_coeff_v = resize_coeff_v; |
460 | ctx->in.num_cols = cols; |
461 | ctx->in.num_rows = rows; |
462 | |
463 | return 0; |
464 | } |
465 | |
466 | #define round_closest(x, y) round_down((x) + (y)/2, (y)) |
467 | |
468 | /* |
469 | * Find the best aligned seam position for the given column / row index. |
470 | * Rotation and image offsets are out of scope. |
471 | * |
472 | * @index: column / row index, used to calculate valid interval |
473 | * @in_edge: input right / bottom edge |
474 | * @out_edge: output right / bottom edge |
475 | * @in_align: input alignment, either horizontal 8-byte line start address |
476 | * alignment, or pixel alignment due to image format |
477 | * @out_align: output alignment, either horizontal 8-byte line start address |
478 | * alignment, or pixel alignment due to image format or rotator |
479 | * block size |
480 | * @in_burst: horizontal input burst size in case of horizontal flip |
481 | * @out_burst: horizontal output burst size or rotator block size |
482 | * @downsize_coeff: downsizing section coefficient |
483 | * @resize_coeff: main processing section resizing coefficient |
484 | * @_in_seam: aligned input seam position return value |
485 | * @_out_seam: aligned output seam position return value |
486 | */ |
487 | static void find_best_seam(struct ipu_image_convert_ctx *ctx, |
488 | unsigned int index, |
489 | unsigned int in_edge, |
490 | unsigned int out_edge, |
491 | unsigned int in_align, |
492 | unsigned int out_align, |
493 | unsigned int in_burst, |
494 | unsigned int out_burst, |
495 | unsigned int downsize_coeff, |
496 | unsigned int resize_coeff, |
497 | u32 *_in_seam, |
498 | u32 *_out_seam) |
499 | { |
500 | struct device *dev = ctx->chan->priv->ipu->dev; |
501 | unsigned int out_pos; |
502 | /* Input / output seam position candidates */ |
503 | unsigned int out_seam = 0; |
504 | unsigned int in_seam = 0; |
505 | unsigned int min_diff = UINT_MAX; |
506 | unsigned int out_start; |
507 | unsigned int out_end; |
508 | unsigned int in_start; |
509 | unsigned int in_end; |
510 | |
511 | /* Start within 1024 pixels of the right / bottom edge */ |
512 | out_start = max_t(int, index * out_align, out_edge - 1024); |
513 | /* End before having to add more columns to the left / rows above */ |
514 | out_end = min_t(unsigned int, out_edge, index * 1024 + 1); |
515 | |
516 | /* |
517 | * Limit input seam position to make sure that the downsized input tile |
518 | * to the right or bottom does not exceed 1024 pixels. |
519 | */ |
520 | in_start = max_t(int, index * in_align, |
521 | in_edge - (1024 << downsize_coeff)); |
522 | in_end = min_t(unsigned int, in_edge, |
523 | index * (1024 << downsize_coeff) + 1); |
524 | |
525 | /* |
526 | * Output tiles must start at a multiple of 8 bytes horizontally and |
527 | * possibly at an even line horizontally depending on the pixel format. |
528 | * Only consider output aligned positions for the seam. |
529 | */ |
530 | out_start = round_up(out_start, out_align); |
531 | for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { |
532 | unsigned int in_pos; |
533 | unsigned int in_pos_aligned; |
534 | unsigned int in_pos_rounded; |
535 | unsigned int diff; |
536 | |
537 | /* |
538 | * Tiles in the right row / bottom column may not be allowed to |
539 | * overshoot horizontally / vertically. out_burst may be the |
540 | * actual DMA burst size, or the rotator block size. |
541 | */ |
542 | if ((out_burst > 1) && (out_edge - out_pos) % out_burst) |
543 | continue; |
544 | |
545 | /* |
546 | * Input sample position, corresponding to out_pos, 19.13 fixed |
547 | * point. |
548 | */ |
549 | in_pos = (out_pos * resize_coeff) << downsize_coeff; |
550 | /* |
551 | * The closest input sample position that we could actually |
552 | * start the input tile at, 19.13 fixed point. |
553 | */ |
554 | in_pos_aligned = round_closest(in_pos, 8192U * in_align); |
555 | /* Convert 19.13 fixed point to integer */ |
556 | in_pos_rounded = in_pos_aligned / 8192U; |
557 | |
558 | if (in_pos_rounded < in_start) |
559 | continue; |
560 | if (in_pos_rounded >= in_end) |
561 | break; |
562 | |
563 | if ((in_burst > 1) && |
564 | (in_edge - in_pos_rounded) % in_burst) |
565 | continue; |
566 | |
567 | diff = abs_diff(in_pos, in_pos_aligned); |
568 | if (diff < min_diff) { |
569 | in_seam = in_pos_rounded; |
570 | out_seam = out_pos; |
571 | min_diff = diff; |
572 | } |
573 | } |
574 | |
575 | *_out_seam = out_seam; |
576 | *_in_seam = in_seam; |
577 | |
578 | dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", |
579 | __func__, out_seam, out_align, out_start, out_end, |
580 | in_seam, in_align, in_start, in_end, min_diff / 8192, |
581 | DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); |
582 | } |
583 | |
584 | /* |
585 | * Tile left edges are required to be aligned to multiples of 8 bytes |
586 | * by the IDMAC. |
587 | */ |
588 | static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) |
589 | { |
590 | if (fmt->planar) |
591 | return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; |
592 | else |
593 | return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; |
594 | } |
595 | |
596 | /* |
597 | * Tile top edge alignment is only limited by chroma subsampling. |
598 | */ |
599 | static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) |
600 | { |
601 | return fmt->uv_height_dec > 1 ? 2 : 1; |
602 | } |
603 | |
604 | static inline u32 tile_width_align(enum ipu_image_convert_type type, |
605 | const struct ipu_image_pixfmt *fmt, |
606 | enum ipu_rotate_mode rot_mode) |
607 | { |
608 | if (type == IMAGE_CONVERT_IN) { |
609 | /* |
610 | * The IC burst reads 8 pixels at a time. Reading beyond the |
611 | * end of the line is usually acceptable. Those pixels are |
612 | * ignored, unless the IC has to write the scaled line in |
613 | * reverse. |
614 | */ |
615 | return (!ipu_rot_mode_is_irt(rot_mode) && |
616 | (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; |
617 | } |
618 | |
619 | /* |
620 | * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled |
621 | * formats to guarantee 8-byte aligned line start addresses in the |
622 | * chroma planes when IRT is used. Align to 8x8 pixel IRT block size |
623 | * for all other formats. |
624 | */ |
625 | return (ipu_rot_mode_is_irt(rot_mode) && |
626 | fmt->planar && !fmt->uv_packed) ? |
627 | 8 * fmt->uv_width_dec : 8; |
628 | } |
629 | |
630 | static inline u32 tile_height_align(enum ipu_image_convert_type type, |
631 | const struct ipu_image_pixfmt *fmt, |
632 | enum ipu_rotate_mode rot_mode) |
633 | { |
634 | if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) |
635 | return 2; |
636 | |
637 | /* |
638 | * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled |
639 | * formats to guarantee 8-byte aligned line start addresses in the |
640 | * chroma planes when IRT is used. Align to 8x8 pixel IRT block size |
641 | * for all other formats. |
642 | */ |
643 | return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; |
644 | } |
645 | |
646 | /* |
647 | * Fill in left position and width and for all tiles in an input column, and |
648 | * for all corresponding output tiles. If the 90° rotator is used, the output |
649 | * tiles are in a row, and output tile top position and height are set. |
650 | */ |
651 | static void fill_tile_column(struct ipu_image_convert_ctx *ctx, |
652 | unsigned int col, |
653 | struct ipu_image_convert_image *in, |
654 | unsigned int in_left, unsigned int in_width, |
655 | struct ipu_image_convert_image *out, |
656 | unsigned int out_left, unsigned int out_width) |
657 | { |
658 | unsigned int row, tile_idx; |
659 | struct ipu_image_tile *in_tile, *out_tile; |
660 | |
661 | for (row = 0; row < in->num_rows; row++) { |
662 | tile_idx = in->num_cols * row + col; |
663 | in_tile = &in->tile[tile_idx]; |
664 | out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; |
665 | |
666 | in_tile->left = in_left; |
667 | in_tile->width = in_width; |
668 | |
669 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
670 | out_tile->top = out_left; |
671 | out_tile->height = out_width; |
672 | } else { |
673 | out_tile->left = out_left; |
674 | out_tile->width = out_width; |
675 | } |
676 | } |
677 | } |
678 | |
679 | /* |
680 | * Fill in top position and height and for all tiles in an input row, and |
681 | * for all corresponding output tiles. If the 90° rotator is used, the output |
682 | * tiles are in a column, and output tile left position and width are set. |
683 | */ |
684 | static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, |
685 | struct ipu_image_convert_image *in, |
686 | unsigned int in_top, unsigned int in_height, |
687 | struct ipu_image_convert_image *out, |
688 | unsigned int out_top, unsigned int out_height) |
689 | { |
690 | unsigned int col, tile_idx; |
691 | struct ipu_image_tile *in_tile, *out_tile; |
692 | |
693 | for (col = 0; col < in->num_cols; col++) { |
694 | tile_idx = in->num_cols * row + col; |
695 | in_tile = &in->tile[tile_idx]; |
696 | out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; |
697 | |
698 | in_tile->top = in_top; |
699 | in_tile->height = in_height; |
700 | |
701 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
702 | out_tile->left = out_top; |
703 | out_tile->width = out_height; |
704 | } else { |
705 | out_tile->top = out_top; |
706 | out_tile->height = out_height; |
707 | } |
708 | } |
709 | } |
710 | |
711 | /* |
712 | * Find the best horizontal and vertical seam positions to split into tiles. |
713 | * Minimize the fractional part of the input sampling position for the |
714 | * top / left pixels of each tile. |
715 | */ |
716 | static void find_seams(struct ipu_image_convert_ctx *ctx, |
717 | struct ipu_image_convert_image *in, |
718 | struct ipu_image_convert_image *out) |
719 | { |
720 | struct device *dev = ctx->chan->priv->ipu->dev; |
721 | unsigned int resized_width = out->base.rect.width; |
722 | unsigned int resized_height = out->base.rect.height; |
723 | unsigned int col; |
724 | unsigned int row; |
725 | unsigned int in_left_align = tile_left_align(fmt: in->fmt); |
726 | unsigned int in_top_align = tile_top_align(fmt: in->fmt); |
727 | unsigned int out_left_align = tile_left_align(fmt: out->fmt); |
728 | unsigned int out_top_align = tile_top_align(fmt: out->fmt); |
729 | unsigned int out_width_align = tile_width_align(type: out->type, fmt: out->fmt, |
730 | rot_mode: ctx->rot_mode); |
731 | unsigned int out_height_align = tile_height_align(type: out->type, fmt: out->fmt, |
732 | rot_mode: ctx->rot_mode); |
733 | unsigned int in_right = in->base.rect.width; |
734 | unsigned int in_bottom = in->base.rect.height; |
735 | unsigned int out_right = out->base.rect.width; |
736 | unsigned int out_bottom = out->base.rect.height; |
737 | unsigned int flipped_out_left; |
738 | unsigned int flipped_out_top; |
739 | |
740 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
741 | /* Switch width/height and align top left to IRT block size */ |
742 | resized_width = out->base.rect.height; |
743 | resized_height = out->base.rect.width; |
744 | out_left_align = out_height_align; |
745 | out_top_align = out_width_align; |
746 | out_width_align = out_left_align; |
747 | out_height_align = out_top_align; |
748 | out_right = out->base.rect.height; |
749 | out_bottom = out->base.rect.width; |
750 | } |
751 | |
752 | for (col = in->num_cols - 1; col > 0; col--) { |
753 | bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || |
754 | !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); |
755 | bool allow_out_overshoot = (col < in->num_cols - 1) && |
756 | !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); |
757 | unsigned int in_left; |
758 | unsigned int out_left; |
759 | |
760 | /* |
761 | * Align input width to burst length if the scaling step flips |
762 | * horizontally. |
763 | */ |
764 | |
765 | find_best_seam(ctx, index: col, |
766 | in_edge: in_right, out_edge: out_right, |
767 | in_align: in_left_align, out_align: out_left_align, |
768 | in_burst: allow_in_overshoot ? 1 : 8 /* burst length */, |
769 | out_burst: allow_out_overshoot ? 1 : out_width_align, |
770 | downsize_coeff: ctx->downsize_coeff_h, resize_coeff: ctx->image_resize_coeff_h, |
771 | in_seam: &in_left, out_seam: &out_left); |
772 | |
773 | if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) |
774 | flipped_out_left = resized_width - out_right; |
775 | else |
776 | flipped_out_left = out_left; |
777 | |
778 | fill_tile_column(ctx, col, in, in_left, in_width: in_right - in_left, |
779 | out, out_left: flipped_out_left, out_width: out_right - out_left); |
780 | |
781 | dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, |
782 | in_left, in_right - in_left, |
783 | flipped_out_left, out_right - out_left); |
784 | |
785 | in_right = in_left; |
786 | out_right = out_left; |
787 | } |
788 | |
789 | flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? |
790 | resized_width - out_right : 0; |
791 | |
792 | fill_tile_column(ctx, col: 0, in, in_left: 0, in_width: in_right, |
793 | out, out_left: flipped_out_left, out_width: out_right); |
794 | |
795 | dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, |
796 | in_right, flipped_out_left, out_right); |
797 | |
798 | for (row = in->num_rows - 1; row > 0; row--) { |
799 | bool allow_overshoot = row < in->num_rows - 1; |
800 | unsigned int in_top; |
801 | unsigned int out_top; |
802 | |
803 | find_best_seam(ctx, index: row, |
804 | in_edge: in_bottom, out_edge: out_bottom, |
805 | in_align: in_top_align, out_align: out_top_align, |
806 | in_burst: 1, out_burst: allow_overshoot ? 1 : out_height_align, |
807 | downsize_coeff: ctx->downsize_coeff_v, resize_coeff: ctx->image_resize_coeff_v, |
808 | in_seam: &in_top, out_seam: &out_top); |
809 | |
810 | if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ |
811 | ipu_rot_mode_is_irt(ctx->rot_mode)) |
812 | flipped_out_top = resized_height - out_bottom; |
813 | else |
814 | flipped_out_top = out_top; |
815 | |
816 | fill_tile_row(ctx, row, in, in_top, in_height: in_bottom - in_top, |
817 | out, out_top: flipped_out_top, out_height: out_bottom - out_top); |
818 | |
819 | dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, |
820 | in_top, in_bottom - in_top, |
821 | flipped_out_top, out_bottom - out_top); |
822 | |
823 | in_bottom = in_top; |
824 | out_bottom = out_top; |
825 | } |
826 | |
827 | if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ |
828 | ipu_rot_mode_is_irt(ctx->rot_mode)) |
829 | flipped_out_top = resized_height - out_bottom; |
830 | else |
831 | flipped_out_top = 0; |
832 | |
833 | fill_tile_row(ctx, row: 0, in, in_top: 0, in_height: in_bottom, |
834 | out, out_top: flipped_out_top, out_height: out_bottom); |
835 | |
836 | dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, |
837 | in_bottom, flipped_out_top, out_bottom); |
838 | } |
839 | |
840 | static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, |
841 | struct ipu_image_convert_image *image) |
842 | { |
843 | struct ipu_image_convert_chan *chan = ctx->chan; |
844 | struct ipu_image_convert_priv *priv = chan->priv; |
845 | unsigned int max_width = 1024; |
846 | unsigned int max_height = 1024; |
847 | unsigned int i; |
848 | |
849 | if (image->type == IMAGE_CONVERT_IN) { |
850 | /* Up to 4096x4096 input tile size */ |
851 | max_width <<= ctx->downsize_coeff_h; |
852 | max_height <<= ctx->downsize_coeff_v; |
853 | } |
854 | |
855 | for (i = 0; i < ctx->num_tiles; i++) { |
856 | struct ipu_image_tile *tile; |
857 | const unsigned int row = i / image->num_cols; |
858 | const unsigned int col = i % image->num_cols; |
859 | |
860 | if (image->type == IMAGE_CONVERT_OUT) |
861 | tile = &image->tile[ctx->out_tile_map[i]]; |
862 | else |
863 | tile = &image->tile[i]; |
864 | |
865 | tile->size = ((tile->height * image->fmt->bpp) >> 3) * |
866 | tile->width; |
867 | |
868 | if (image->fmt->planar) { |
869 | tile->stride = tile->width; |
870 | tile->rot_stride = tile->height; |
871 | } else { |
872 | tile->stride = |
873 | (image->fmt->bpp * tile->width) >> 3; |
874 | tile->rot_stride = |
875 | (image->fmt->bpp * tile->height) >> 3; |
876 | } |
877 | |
878 | dev_dbg(priv->ipu->dev, |
879 | "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", |
880 | chan->ic_task, ctx, |
881 | image->type == IMAGE_CONVERT_IN ? "Input": "Output", |
882 | row, col, |
883 | tile->width, tile->height, tile->left, tile->top); |
884 | |
885 | if (!tile->width || tile->width > max_width || |
886 | !tile->height || tile->height > max_height) { |
887 | dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", |
888 | image->type == IMAGE_CONVERT_IN ? "input": |
889 | "output", tile->width, tile->height); |
890 | return -EINVAL; |
891 | } |
892 | } |
893 | |
894 | return 0; |
895 | } |
896 | |
897 | /* |
898 | * Use the rotation transformation to find the tile coordinates |
899 | * (row, col) of a tile in the destination frame that corresponds |
900 | * to the given tile coordinates of a source frame. The destination |
901 | * coordinate is then converted to a tile index. |
902 | */ |
903 | static int transform_tile_index(struct ipu_image_convert_ctx *ctx, |
904 | int src_row, int src_col) |
905 | { |
906 | struct ipu_image_convert_chan *chan = ctx->chan; |
907 | struct ipu_image_convert_priv *priv = chan->priv; |
908 | struct ipu_image_convert_image *s_image = &ctx->in; |
909 | struct ipu_image_convert_image *d_image = &ctx->out; |
910 | int dst_row, dst_col; |
911 | |
912 | /* with no rotation it's a 1:1 mapping */ |
913 | if (ctx->rot_mode == IPU_ROTATE_NONE) |
914 | return src_row * s_image->num_cols + src_col; |
915 | |
916 | /* |
917 | * before doing the transform, first we have to translate |
918 | * source row,col for an origin in the center of s_image |
919 | */ |
920 | src_row = src_row * 2 - (s_image->num_rows - 1); |
921 | src_col = src_col * 2 - (s_image->num_cols - 1); |
922 | |
923 | /* do the rotation transform */ |
924 | if (ctx->rot_mode & IPU_ROT_BIT_90) { |
925 | dst_col = -src_row; |
926 | dst_row = src_col; |
927 | } else { |
928 | dst_col = src_col; |
929 | dst_row = src_row; |
930 | } |
931 | |
932 | /* apply flip */ |
933 | if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) |
934 | dst_col = -dst_col; |
935 | if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) |
936 | dst_row = -dst_row; |
937 | |
938 | dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", |
939 | chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); |
940 | |
941 | /* |
942 | * finally translate dest row,col using an origin in upper |
943 | * left of d_image |
944 | */ |
945 | dst_row += d_image->num_rows - 1; |
946 | dst_col += d_image->num_cols - 1; |
947 | dst_row /= 2; |
948 | dst_col /= 2; |
949 | |
950 | return dst_row * d_image->num_cols + dst_col; |
951 | } |
952 | |
953 | /* |
954 | * Fill the out_tile_map[] with transformed destination tile indeces. |
955 | */ |
956 | static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) |
957 | { |
958 | struct ipu_image_convert_image *s_image = &ctx->in; |
959 | unsigned int row, col, tile = 0; |
960 | |
961 | for (row = 0; row < s_image->num_rows; row++) { |
962 | for (col = 0; col < s_image->num_cols; col++) { |
963 | ctx->out_tile_map[tile] = |
964 | transform_tile_index(ctx, src_row: row, src_col: col); |
965 | tile++; |
966 | } |
967 | } |
968 | } |
969 | |
970 | static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, |
971 | struct ipu_image_convert_image *image) |
972 | { |
973 | struct ipu_image_convert_chan *chan = ctx->chan; |
974 | struct ipu_image_convert_priv *priv = chan->priv; |
975 | const struct ipu_image_pixfmt *fmt = image->fmt; |
976 | unsigned int row, col, tile = 0; |
977 | u32 H, top, y_stride, uv_stride; |
978 | u32 uv_row_off, uv_col_off, uv_off, u_off, v_off; |
979 | u32 y_row_off, y_col_off, y_off; |
980 | u32 y_size, uv_size; |
981 | |
982 | /* setup some convenience vars */ |
983 | H = image->base.pix.height; |
984 | |
985 | y_stride = image->stride; |
986 | uv_stride = y_stride / fmt->uv_width_dec; |
987 | if (fmt->uv_packed) |
988 | uv_stride *= 2; |
989 | |
990 | y_size = H * y_stride; |
991 | uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); |
992 | |
993 | for (row = 0; row < image->num_rows; row++) { |
994 | top = image->tile[tile].top; |
995 | y_row_off = top * y_stride; |
996 | uv_row_off = (top * uv_stride) / fmt->uv_height_dec; |
997 | |
998 | for (col = 0; col < image->num_cols; col++) { |
999 | y_col_off = image->tile[tile].left; |
1000 | uv_col_off = y_col_off / fmt->uv_width_dec; |
1001 | if (fmt->uv_packed) |
1002 | uv_col_off *= 2; |
1003 | |
1004 | y_off = y_row_off + y_col_off; |
1005 | uv_off = uv_row_off + uv_col_off; |
1006 | |
1007 | u_off = y_size - y_off + uv_off; |
1008 | v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; |
1009 | if (fmt->uv_swapped) |
1010 | swap(u_off, v_off); |
1011 | |
1012 | image->tile[tile].offset = y_off; |
1013 | image->tile[tile].u_off = u_off; |
1014 | image->tile[tile++].v_off = v_off; |
1015 | |
1016 | if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { |
1017 | dev_err(priv->ipu->dev, |
1018 | "task %u: ctx %p: %s@[%d,%d]: " |
1019 | "y_off %08x, u_off %08x, v_off %08x\n", |
1020 | chan->ic_task, ctx, |
1021 | image->type == IMAGE_CONVERT_IN ? |
1022 | "Input": "Output", row, col, |
1023 | y_off, u_off, v_off); |
1024 | return -EINVAL; |
1025 | } |
1026 | } |
1027 | } |
1028 | |
1029 | return 0; |
1030 | } |
1031 | |
1032 | static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, |
1033 | struct ipu_image_convert_image *image) |
1034 | { |
1035 | struct ipu_image_convert_chan *chan = ctx->chan; |
1036 | struct ipu_image_convert_priv *priv = chan->priv; |
1037 | const struct ipu_image_pixfmt *fmt = image->fmt; |
1038 | unsigned int row, col, tile = 0; |
1039 | u32 bpp, stride, offset; |
1040 | u32 row_off, col_off; |
1041 | |
1042 | /* setup some convenience vars */ |
1043 | stride = image->stride; |
1044 | bpp = fmt->bpp; |
1045 | |
1046 | for (row = 0; row < image->num_rows; row++) { |
1047 | row_off = image->tile[tile].top * stride; |
1048 | |
1049 | for (col = 0; col < image->num_cols; col++) { |
1050 | col_off = (image->tile[tile].left * bpp) >> 3; |
1051 | |
1052 | offset = row_off + col_off; |
1053 | |
1054 | image->tile[tile].offset = offset; |
1055 | image->tile[tile].u_off = 0; |
1056 | image->tile[tile++].v_off = 0; |
1057 | |
1058 | if (offset & 0x7) { |
1059 | dev_err(priv->ipu->dev, |
1060 | "task %u: ctx %p: %s@[%d,%d]: " |
1061 | "phys %08x\n", |
1062 | chan->ic_task, ctx, |
1063 | image->type == IMAGE_CONVERT_IN ? |
1064 | "Input": "Output", row, col, |
1065 | row_off + col_off); |
1066 | return -EINVAL; |
1067 | } |
1068 | } |
1069 | } |
1070 | |
1071 | return 0; |
1072 | } |
1073 | |
1074 | static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, |
1075 | struct ipu_image_convert_image *image) |
1076 | { |
1077 | if (image->fmt->planar) |
1078 | return calc_tile_offsets_planar(ctx, image); |
1079 | |
1080 | return calc_tile_offsets_packed(ctx, image); |
1081 | } |
1082 | |
1083 | /* |
1084 | * Calculate the resizing ratio for the IC main processing section given input |
1085 | * size, fixed downsizing coefficient, and output size. |
1086 | * Either round to closest for the next tile's first pixel to minimize seams |
1087 | * and distortion (for all but right column / bottom row), or round down to |
1088 | * avoid sampling beyond the edges of the input image for this tile's last |
1089 | * pixel. |
1090 | * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. |
1091 | */ |
1092 | static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, |
1093 | u32 output_size, bool allow_overshoot) |
1094 | { |
1095 | u32 downsized = input_size >> downsize_coeff; |
1096 | |
1097 | if (allow_overshoot) |
1098 | return DIV_ROUND_CLOSEST(8192 * downsized, output_size); |
1099 | else |
1100 | return 8192 * (downsized - 1) / (output_size - 1); |
1101 | } |
1102 | |
1103 | /* |
1104 | * Slightly modify resize coefficients per tile to hide the bilinear |
1105 | * interpolator reset at tile borders, shifting the right / bottom edge |
1106 | * by up to a half input pixel. This removes noticeable seams between |
1107 | * tiles at higher upscaling factors. |
1108 | */ |
1109 | static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) |
1110 | { |
1111 | struct ipu_image_convert_chan *chan = ctx->chan; |
1112 | struct ipu_image_convert_priv *priv = chan->priv; |
1113 | struct ipu_image_tile *in_tile, *out_tile; |
1114 | unsigned int col, row, tile_idx; |
1115 | unsigned int last_output; |
1116 | |
1117 | for (col = 0; col < ctx->in.num_cols; col++) { |
1118 | bool closest = (col < ctx->in.num_cols - 1) && |
1119 | !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); |
1120 | u32 resized_width; |
1121 | u32 resize_coeff_h; |
1122 | u32 in_width; |
1123 | |
1124 | tile_idx = col; |
1125 | in_tile = &ctx->in.tile[tile_idx]; |
1126 | out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; |
1127 | |
1128 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1129 | resized_width = out_tile->height; |
1130 | else |
1131 | resized_width = out_tile->width; |
1132 | |
1133 | resize_coeff_h = calc_resize_coeff(input_size: in_tile->width, |
1134 | downsize_coeff: ctx->downsize_coeff_h, |
1135 | output_size: resized_width, allow_overshoot: closest); |
1136 | |
1137 | dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", |
1138 | __func__, col, resize_coeff_h); |
1139 | |
1140 | /* |
1141 | * With the horizontal scaling factor known, round up resized |
1142 | * width (output width or height) to burst size. |
1143 | */ |
1144 | resized_width = round_up(resized_width, 8); |
1145 | |
1146 | /* |
1147 | * Calculate input width from the last accessed input pixel |
1148 | * given resized width and scaling coefficients. Round up to |
1149 | * burst size. |
1150 | */ |
1151 | last_output = resized_width - 1; |
1152 | if (closest && ((last_output * resize_coeff_h) % 8192)) |
1153 | last_output++; |
1154 | in_width = round_up( |
1155 | (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) |
1156 | << ctx->downsize_coeff_h, 8); |
1157 | |
1158 | for (row = 0; row < ctx->in.num_rows; row++) { |
1159 | tile_idx = row * ctx->in.num_cols + col; |
1160 | in_tile = &ctx->in.tile[tile_idx]; |
1161 | out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; |
1162 | |
1163 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1164 | out_tile->height = resized_width; |
1165 | else |
1166 | out_tile->width = resized_width; |
1167 | |
1168 | in_tile->width = in_width; |
1169 | } |
1170 | |
1171 | ctx->resize_coeffs_h[col] = resize_coeff_h; |
1172 | } |
1173 | |
1174 | for (row = 0; row < ctx->in.num_rows; row++) { |
1175 | bool closest = (row < ctx->in.num_rows - 1) && |
1176 | !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); |
1177 | u32 resized_height; |
1178 | u32 resize_coeff_v; |
1179 | u32 in_height; |
1180 | |
1181 | tile_idx = row * ctx->in.num_cols; |
1182 | in_tile = &ctx->in.tile[tile_idx]; |
1183 | out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; |
1184 | |
1185 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1186 | resized_height = out_tile->width; |
1187 | else |
1188 | resized_height = out_tile->height; |
1189 | |
1190 | resize_coeff_v = calc_resize_coeff(input_size: in_tile->height, |
1191 | downsize_coeff: ctx->downsize_coeff_v, |
1192 | output_size: resized_height, allow_overshoot: closest); |
1193 | |
1194 | dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", |
1195 | __func__, row, resize_coeff_v); |
1196 | |
1197 | /* |
1198 | * With the vertical scaling factor known, round up resized |
1199 | * height (output width or height) to IDMAC limitations. |
1200 | */ |
1201 | resized_height = round_up(resized_height, 2); |
1202 | |
1203 | /* |
1204 | * Calculate input width from the last accessed input pixel |
1205 | * given resized height and scaling coefficients. Align to |
1206 | * IDMAC restrictions. |
1207 | */ |
1208 | last_output = resized_height - 1; |
1209 | if (closest && ((last_output * resize_coeff_v) % 8192)) |
1210 | last_output++; |
1211 | in_height = round_up( |
1212 | (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) |
1213 | << ctx->downsize_coeff_v, 2); |
1214 | |
1215 | for (col = 0; col < ctx->in.num_cols; col++) { |
1216 | tile_idx = row * ctx->in.num_cols + col; |
1217 | in_tile = &ctx->in.tile[tile_idx]; |
1218 | out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; |
1219 | |
1220 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1221 | out_tile->width = resized_height; |
1222 | else |
1223 | out_tile->height = resized_height; |
1224 | |
1225 | in_tile->height = in_height; |
1226 | } |
1227 | |
1228 | ctx->resize_coeffs_v[row] = resize_coeff_v; |
1229 | } |
1230 | } |
1231 | |
1232 | /* |
1233 | * return the number of runs in given queue (pending_q or done_q) |
1234 | * for this context. hold irqlock when calling. |
1235 | */ |
1236 | static int get_run_count(struct ipu_image_convert_ctx *ctx, |
1237 | struct list_head *q) |
1238 | { |
1239 | struct ipu_image_convert_run *run; |
1240 | int count = 0; |
1241 | |
1242 | lockdep_assert_held(&ctx->chan->irqlock); |
1243 | |
1244 | list_for_each_entry(run, q, list) { |
1245 | if (run->ctx == ctx) |
1246 | count++; |
1247 | } |
1248 | |
1249 | return count; |
1250 | } |
1251 | |
1252 | static void convert_stop(struct ipu_image_convert_run *run) |
1253 | { |
1254 | struct ipu_image_convert_ctx *ctx = run->ctx; |
1255 | struct ipu_image_convert_chan *chan = ctx->chan; |
1256 | struct ipu_image_convert_priv *priv = chan->priv; |
1257 | |
1258 | dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", |
1259 | __func__, chan->ic_task, ctx, run); |
1260 | |
1261 | /* disable IC tasks and the channels */ |
1262 | ipu_ic_task_disable(ic: chan->ic); |
1263 | ipu_idmac_disable_channel(channel: chan->in_chan); |
1264 | ipu_idmac_disable_channel(channel: chan->out_chan); |
1265 | |
1266 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
1267 | ipu_idmac_disable_channel(channel: chan->rotation_in_chan); |
1268 | ipu_idmac_disable_channel(channel: chan->rotation_out_chan); |
1269 | ipu_idmac_unlink(src: chan->out_chan, sink: chan->rotation_in_chan); |
1270 | } |
1271 | |
1272 | ipu_ic_disable(ic: chan->ic); |
1273 | } |
1274 | |
1275 | static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, |
1276 | struct ipuv3_channel *channel, |
1277 | struct ipu_image_convert_image *image, |
1278 | enum ipu_rotate_mode rot_mode, |
1279 | bool rot_swap_width_height, |
1280 | unsigned int tile) |
1281 | { |
1282 | struct ipu_image_convert_chan *chan = ctx->chan; |
1283 | unsigned int burst_size; |
1284 | u32 width, height, stride; |
1285 | dma_addr_t addr0, addr1 = 0; |
1286 | struct ipu_image tile_image; |
1287 | unsigned int tile_idx[2]; |
1288 | |
1289 | if (image->type == IMAGE_CONVERT_OUT) { |
1290 | tile_idx[0] = ctx->out_tile_map[tile]; |
1291 | tile_idx[1] = ctx->out_tile_map[1]; |
1292 | } else { |
1293 | tile_idx[0] = tile; |
1294 | tile_idx[1] = 1; |
1295 | } |
1296 | |
1297 | if (rot_swap_width_height) { |
1298 | width = image->tile[tile_idx[0]].height; |
1299 | height = image->tile[tile_idx[0]].width; |
1300 | stride = image->tile[tile_idx[0]].rot_stride; |
1301 | addr0 = ctx->rot_intermediate[0].phys; |
1302 | if (ctx->double_buffering) |
1303 | addr1 = ctx->rot_intermediate[1].phys; |
1304 | } else { |
1305 | width = image->tile[tile_idx[0]].width; |
1306 | height = image->tile[tile_idx[0]].height; |
1307 | stride = image->stride; |
1308 | addr0 = image->base.phys0 + |
1309 | image->tile[tile_idx[0]].offset; |
1310 | if (ctx->double_buffering) |
1311 | addr1 = image->base.phys0 + |
1312 | image->tile[tile_idx[1]].offset; |
1313 | } |
1314 | |
1315 | ipu_cpmem_zero(ch: channel); |
1316 | |
1317 | memset(&tile_image, 0, sizeof(tile_image)); |
1318 | tile_image.pix.width = tile_image.rect.width = width; |
1319 | tile_image.pix.height = tile_image.rect.height = height; |
1320 | tile_image.pix.bytesperline = stride; |
1321 | tile_image.pix.pixelformat = image->fmt->fourcc; |
1322 | tile_image.phys0 = addr0; |
1323 | tile_image.phys1 = addr1; |
1324 | if (image->fmt->planar && !rot_swap_width_height) { |
1325 | tile_image.u_offset = image->tile[tile_idx[0]].u_off; |
1326 | tile_image.v_offset = image->tile[tile_idx[0]].v_off; |
1327 | } |
1328 | |
1329 | ipu_cpmem_set_image(ch: channel, image: &tile_image); |
1330 | |
1331 | if (rot_mode) |
1332 | ipu_cpmem_set_rotation(ch: channel, rot: rot_mode); |
1333 | |
1334 | /* |
1335 | * Skip writing U and V components to odd rows in the output |
1336 | * channels for planar 4:2:0. |
1337 | */ |
1338 | if ((channel == chan->out_chan || |
1339 | channel == chan->rotation_out_chan) && |
1340 | image->fmt->planar && image->fmt->uv_height_dec == 2) |
1341 | ipu_cpmem_skip_odd_chroma_rows(ch: channel); |
1342 | |
1343 | if (channel == chan->rotation_in_chan || |
1344 | channel == chan->rotation_out_chan) { |
1345 | burst_size = 8; |
1346 | ipu_cpmem_set_block_mode(ch: channel); |
1347 | } else |
1348 | burst_size = (width % 16) ? 8 : 16; |
1349 | |
1350 | ipu_cpmem_set_burstsize(ch: channel, burstsize: burst_size); |
1351 | |
1352 | ipu_ic_task_idma_init(ic: chan->ic, channel, width, height, |
1353 | burst_size, rot: rot_mode); |
1354 | |
1355 | /* |
1356 | * Setting a non-zero AXI ID collides with the PRG AXI snooping, so |
1357 | * only do this when there is no PRG present. |
1358 | */ |
1359 | if (!channel->ipu->prg_priv) |
1360 | ipu_cpmem_set_axi_id(ch: channel, id: 1); |
1361 | |
1362 | ipu_idmac_set_double_buffer(channel, doublebuffer: ctx->double_buffering); |
1363 | } |
1364 | |
1365 | static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) |
1366 | { |
1367 | struct ipu_image_convert_ctx *ctx = run->ctx; |
1368 | struct ipu_image_convert_chan *chan = ctx->chan; |
1369 | struct ipu_image_convert_priv *priv = chan->priv; |
1370 | struct ipu_image_convert_image *s_image = &ctx->in; |
1371 | struct ipu_image_convert_image *d_image = &ctx->out; |
1372 | unsigned int dst_tile = ctx->out_tile_map[tile]; |
1373 | unsigned int dest_width, dest_height; |
1374 | unsigned int col, row; |
1375 | u32 rsc; |
1376 | int ret; |
1377 | |
1378 | dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", |
1379 | __func__, chan->ic_task, ctx, run, tile, dst_tile); |
1380 | |
1381 | /* clear EOF irq mask */ |
1382 | ctx->eof_mask = 0; |
1383 | |
1384 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
1385 | /* swap width/height for resizer */ |
1386 | dest_width = d_image->tile[dst_tile].height; |
1387 | dest_height = d_image->tile[dst_tile].width; |
1388 | } else { |
1389 | dest_width = d_image->tile[dst_tile].width; |
1390 | dest_height = d_image->tile[dst_tile].height; |
1391 | } |
1392 | |
1393 | row = tile / s_image->num_cols; |
1394 | col = tile % s_image->num_cols; |
1395 | |
1396 | rsc = (ctx->downsize_coeff_v << 30) | |
1397 | (ctx->resize_coeffs_v[row] << 16) | |
1398 | (ctx->downsize_coeff_h << 14) | |
1399 | (ctx->resize_coeffs_h[col]); |
1400 | |
1401 | dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", |
1402 | __func__, s_image->tile[tile].width, |
1403 | s_image->tile[tile].height, dest_width, dest_height, rsc); |
1404 | |
1405 | /* setup the IC resizer and CSC */ |
1406 | ret = ipu_ic_task_init_rsc(ic: chan->ic, csc: &ctx->csc, |
1407 | in_width: s_image->tile[tile].width, |
1408 | in_height: s_image->tile[tile].height, |
1409 | out_width: dest_width, |
1410 | out_height: dest_height, |
1411 | rsc); |
1412 | if (ret) { |
1413 | dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); |
1414 | return ret; |
1415 | } |
1416 | |
1417 | /* init the source MEM-->IC PP IDMAC channel */ |
1418 | init_idmac_channel(ctx, channel: chan->in_chan, image: s_image, |
1419 | rot_mode: IPU_ROTATE_NONE, rot_swap_width_height: false, tile); |
1420 | |
1421 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
1422 | /* init the IC PP-->MEM IDMAC channel */ |
1423 | init_idmac_channel(ctx, channel: chan->out_chan, image: d_image, |
1424 | rot_mode: IPU_ROTATE_NONE, rot_swap_width_height: true, tile); |
1425 | |
1426 | /* init the MEM-->IC PP ROT IDMAC channel */ |
1427 | init_idmac_channel(ctx, channel: chan->rotation_in_chan, image: d_image, |
1428 | rot_mode: ctx->rot_mode, rot_swap_width_height: true, tile); |
1429 | |
1430 | /* init the destination IC PP ROT-->MEM IDMAC channel */ |
1431 | init_idmac_channel(ctx, channel: chan->rotation_out_chan, image: d_image, |
1432 | rot_mode: IPU_ROTATE_NONE, rot_swap_width_height: false, tile); |
1433 | |
1434 | /* now link IC PP-->MEM to MEM-->IC PP ROT */ |
1435 | ipu_idmac_link(src: chan->out_chan, sink: chan->rotation_in_chan); |
1436 | } else { |
1437 | /* init the destination IC PP-->MEM IDMAC channel */ |
1438 | init_idmac_channel(ctx, channel: chan->out_chan, image: d_image, |
1439 | rot_mode: ctx->rot_mode, rot_swap_width_height: false, tile); |
1440 | } |
1441 | |
1442 | /* enable the IC */ |
1443 | ipu_ic_enable(ic: chan->ic); |
1444 | |
1445 | /* set buffers ready */ |
1446 | ipu_idmac_select_buffer(channel: chan->in_chan, buf_num: 0); |
1447 | ipu_idmac_select_buffer(channel: chan->out_chan, buf_num: 0); |
1448 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1449 | ipu_idmac_select_buffer(channel: chan->rotation_out_chan, buf_num: 0); |
1450 | if (ctx->double_buffering) { |
1451 | ipu_idmac_select_buffer(channel: chan->in_chan, buf_num: 1); |
1452 | ipu_idmac_select_buffer(channel: chan->out_chan, buf_num: 1); |
1453 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1454 | ipu_idmac_select_buffer(channel: chan->rotation_out_chan, buf_num: 1); |
1455 | } |
1456 | |
1457 | /* enable the channels! */ |
1458 | ipu_idmac_enable_channel(channel: chan->in_chan); |
1459 | ipu_idmac_enable_channel(channel: chan->out_chan); |
1460 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
1461 | ipu_idmac_enable_channel(channel: chan->rotation_in_chan); |
1462 | ipu_idmac_enable_channel(channel: chan->rotation_out_chan); |
1463 | } |
1464 | |
1465 | ipu_ic_task_enable(ic: chan->ic); |
1466 | |
1467 | ipu_cpmem_dump(ch: chan->in_chan); |
1468 | ipu_cpmem_dump(ch: chan->out_chan); |
1469 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
1470 | ipu_cpmem_dump(ch: chan->rotation_in_chan); |
1471 | ipu_cpmem_dump(ch: chan->rotation_out_chan); |
1472 | } |
1473 | |
1474 | ipu_dump(ipu: priv->ipu); |
1475 | |
1476 | return 0; |
1477 | } |
1478 | |
1479 | /* hold irqlock when calling */ |
1480 | static int do_run(struct ipu_image_convert_run *run) |
1481 | { |
1482 | struct ipu_image_convert_ctx *ctx = run->ctx; |
1483 | struct ipu_image_convert_chan *chan = ctx->chan; |
1484 | |
1485 | lockdep_assert_held(&chan->irqlock); |
1486 | |
1487 | ctx->in.base.phys0 = run->in_phys; |
1488 | ctx->out.base.phys0 = run->out_phys; |
1489 | |
1490 | ctx->cur_buf_num = 0; |
1491 | ctx->next_tile = 1; |
1492 | |
1493 | /* remove run from pending_q and set as current */ |
1494 | list_del(entry: &run->list); |
1495 | chan->current_run = run; |
1496 | |
1497 | return convert_start(run, tile: 0); |
1498 | } |
1499 | |
1500 | /* hold irqlock when calling */ |
1501 | static void run_next(struct ipu_image_convert_chan *chan) |
1502 | { |
1503 | struct ipu_image_convert_priv *priv = chan->priv; |
1504 | struct ipu_image_convert_run *run, *tmp; |
1505 | int ret; |
1506 | |
1507 | lockdep_assert_held(&chan->irqlock); |
1508 | |
1509 | list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { |
1510 | /* skip contexts that are aborting */ |
1511 | if (run->ctx->aborting) { |
1512 | dev_dbg(priv->ipu->dev, |
1513 | "%s: task %u: skipping aborting ctx %p run %p\n", |
1514 | __func__, chan->ic_task, run->ctx, run); |
1515 | continue; |
1516 | } |
1517 | |
1518 | ret = do_run(run); |
1519 | if (!ret) |
1520 | break; |
1521 | |
1522 | /* |
1523 | * something went wrong with start, add the run |
1524 | * to done q and continue to the next run in the |
1525 | * pending q. |
1526 | */ |
1527 | run->status = ret; |
1528 | list_add_tail(new: &run->list, head: &chan->done_q); |
1529 | chan->current_run = NULL; |
1530 | } |
1531 | } |
1532 | |
1533 | static void empty_done_q(struct ipu_image_convert_chan *chan) |
1534 | { |
1535 | struct ipu_image_convert_priv *priv = chan->priv; |
1536 | struct ipu_image_convert_run *run; |
1537 | unsigned long flags; |
1538 | |
1539 | spin_lock_irqsave(&chan->irqlock, flags); |
1540 | |
1541 | while (!list_empty(head: &chan->done_q)) { |
1542 | run = list_entry(chan->done_q.next, |
1543 | struct ipu_image_convert_run, |
1544 | list); |
1545 | |
1546 | list_del(entry: &run->list); |
1547 | |
1548 | dev_dbg(priv->ipu->dev, |
1549 | "%s: task %u: completing ctx %p run %p with %d\n", |
1550 | __func__, chan->ic_task, run->ctx, run, run->status); |
1551 | |
1552 | /* call the completion callback and free the run */ |
1553 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
1554 | run->ctx->complete(run, run->ctx->complete_context); |
1555 | spin_lock_irqsave(&chan->irqlock, flags); |
1556 | } |
1557 | |
1558 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
1559 | } |
1560 | |
1561 | /* |
1562 | * the bottom half thread clears out the done_q, calling the |
1563 | * completion handler for each. |
1564 | */ |
1565 | static irqreturn_t do_bh(int irq, void *dev_id) |
1566 | { |
1567 | struct ipu_image_convert_chan *chan = dev_id; |
1568 | struct ipu_image_convert_priv *priv = chan->priv; |
1569 | struct ipu_image_convert_ctx *ctx; |
1570 | unsigned long flags; |
1571 | |
1572 | dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, |
1573 | chan->ic_task); |
1574 | |
1575 | empty_done_q(chan); |
1576 | |
1577 | spin_lock_irqsave(&chan->irqlock, flags); |
1578 | |
1579 | /* |
1580 | * the done_q is cleared out, signal any contexts |
1581 | * that are aborting that abort can complete. |
1582 | */ |
1583 | list_for_each_entry(ctx, &chan->ctx_list, list) { |
1584 | if (ctx->aborting) { |
1585 | dev_dbg(priv->ipu->dev, |
1586 | "%s: task %u: signaling abort for ctx %p\n", |
1587 | __func__, chan->ic_task, ctx); |
1588 | complete_all(&ctx->aborted); |
1589 | } |
1590 | } |
1591 | |
1592 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
1593 | |
1594 | dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, |
1595 | chan->ic_task); |
1596 | |
1597 | return IRQ_HANDLED; |
1598 | } |
1599 | |
1600 | static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) |
1601 | { |
1602 | unsigned int cur_tile = ctx->next_tile - 1; |
1603 | unsigned int next_tile = ctx->next_tile; |
1604 | |
1605 | if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != |
1606 | ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || |
1607 | ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != |
1608 | ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || |
1609 | ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || |
1610 | ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || |
1611 | ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || |
1612 | ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) |
1613 | return true; |
1614 | |
1615 | return false; |
1616 | } |
1617 | |
1618 | /* hold irqlock when calling */ |
1619 | static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run) |
1620 | { |
1621 | struct ipu_image_convert_ctx *ctx = run->ctx; |
1622 | struct ipu_image_convert_chan *chan = ctx->chan; |
1623 | struct ipu_image_tile *src_tile, *dst_tile; |
1624 | struct ipu_image_convert_image *s_image = &ctx->in; |
1625 | struct ipu_image_convert_image *d_image = &ctx->out; |
1626 | struct ipuv3_channel *outch; |
1627 | unsigned int dst_idx; |
1628 | |
1629 | lockdep_assert_held(&chan->irqlock); |
1630 | |
1631 | outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? |
1632 | chan->rotation_out_chan : chan->out_chan; |
1633 | |
1634 | /* |
1635 | * It is difficult to stop the channel DMA before the channels |
1636 | * enter the paused state. Without double-buffering the channels |
1637 | * are always in a paused state when the EOF irq occurs, so it |
1638 | * is safe to stop the channels now. For double-buffering we |
1639 | * just ignore the abort until the operation completes, when it |
1640 | * is safe to shut down. |
1641 | */ |
1642 | if (ctx->aborting && !ctx->double_buffering) { |
1643 | convert_stop(run); |
1644 | run->status = -EIO; |
1645 | goto done; |
1646 | } |
1647 | |
1648 | if (ctx->next_tile == ctx->num_tiles) { |
1649 | /* |
1650 | * the conversion is complete |
1651 | */ |
1652 | convert_stop(run); |
1653 | run->status = 0; |
1654 | goto done; |
1655 | } |
1656 | |
1657 | /* |
1658 | * not done, place the next tile buffers. |
1659 | */ |
1660 | if (!ctx->double_buffering) { |
1661 | if (ic_settings_changed(ctx)) { |
1662 | convert_stop(run); |
1663 | convert_start(run, tile: ctx->next_tile); |
1664 | } else { |
1665 | src_tile = &s_image->tile[ctx->next_tile]; |
1666 | dst_idx = ctx->out_tile_map[ctx->next_tile]; |
1667 | dst_tile = &d_image->tile[dst_idx]; |
1668 | |
1669 | ipu_cpmem_set_buffer(ch: chan->in_chan, bufnum: 0, |
1670 | buf: s_image->base.phys0 + |
1671 | src_tile->offset); |
1672 | ipu_cpmem_set_buffer(ch: outch, bufnum: 0, |
1673 | buf: d_image->base.phys0 + |
1674 | dst_tile->offset); |
1675 | if (s_image->fmt->planar) |
1676 | ipu_cpmem_set_uv_offset(ch: chan->in_chan, |
1677 | u_off: src_tile->u_off, |
1678 | v_off: src_tile->v_off); |
1679 | if (d_image->fmt->planar) |
1680 | ipu_cpmem_set_uv_offset(ch: outch, |
1681 | u_off: dst_tile->u_off, |
1682 | v_off: dst_tile->v_off); |
1683 | |
1684 | ipu_idmac_select_buffer(channel: chan->in_chan, buf_num: 0); |
1685 | ipu_idmac_select_buffer(channel: outch, buf_num: 0); |
1686 | } |
1687 | } else if (ctx->next_tile < ctx->num_tiles - 1) { |
1688 | |
1689 | src_tile = &s_image->tile[ctx->next_tile + 1]; |
1690 | dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; |
1691 | dst_tile = &d_image->tile[dst_idx]; |
1692 | |
1693 | ipu_cpmem_set_buffer(ch: chan->in_chan, bufnum: ctx->cur_buf_num, |
1694 | buf: s_image->base.phys0 + src_tile->offset); |
1695 | ipu_cpmem_set_buffer(ch: outch, bufnum: ctx->cur_buf_num, |
1696 | buf: d_image->base.phys0 + dst_tile->offset); |
1697 | |
1698 | ipu_idmac_select_buffer(channel: chan->in_chan, buf_num: ctx->cur_buf_num); |
1699 | ipu_idmac_select_buffer(channel: outch, buf_num: ctx->cur_buf_num); |
1700 | |
1701 | ctx->cur_buf_num ^= 1; |
1702 | } |
1703 | |
1704 | ctx->eof_mask = 0; /* clear EOF irq mask for next tile */ |
1705 | ctx->next_tile++; |
1706 | return IRQ_HANDLED; |
1707 | done: |
1708 | list_add_tail(new: &run->list, head: &chan->done_q); |
1709 | chan->current_run = NULL; |
1710 | run_next(chan); |
1711 | return IRQ_WAKE_THREAD; |
1712 | } |
1713 | |
1714 | static irqreturn_t eof_irq(int irq, void *data) |
1715 | { |
1716 | struct ipu_image_convert_chan *chan = data; |
1717 | struct ipu_image_convert_priv *priv = chan->priv; |
1718 | struct ipu_image_convert_ctx *ctx; |
1719 | struct ipu_image_convert_run *run; |
1720 | irqreturn_t ret = IRQ_HANDLED; |
1721 | bool tile_complete = false; |
1722 | unsigned long flags; |
1723 | |
1724 | spin_lock_irqsave(&chan->irqlock, flags); |
1725 | |
1726 | /* get current run and its context */ |
1727 | run = chan->current_run; |
1728 | if (!run) { |
1729 | ret = IRQ_NONE; |
1730 | goto out; |
1731 | } |
1732 | |
1733 | ctx = run->ctx; |
1734 | |
1735 | if (irq == chan->in_eof_irq) { |
1736 | ctx->eof_mask |= EOF_IRQ_IN; |
1737 | } else if (irq == chan->out_eof_irq) { |
1738 | ctx->eof_mask |= EOF_IRQ_OUT; |
1739 | } else if (irq == chan->rot_in_eof_irq || |
1740 | irq == chan->rot_out_eof_irq) { |
1741 | if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { |
1742 | /* this was NOT a rotation op, shouldn't happen */ |
1743 | dev_err(priv->ipu->dev, |
1744 | "Unexpected rotation interrupt\n"); |
1745 | goto out; |
1746 | } |
1747 | ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ? |
1748 | EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT; |
1749 | } else { |
1750 | dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq); |
1751 | ret = IRQ_NONE; |
1752 | goto out; |
1753 | } |
1754 | |
1755 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) |
1756 | tile_complete = (ctx->eof_mask == EOF_IRQ_ROT_COMPLETE); |
1757 | else |
1758 | tile_complete = (ctx->eof_mask == EOF_IRQ_COMPLETE); |
1759 | |
1760 | if (tile_complete) |
1761 | ret = do_tile_complete(run); |
1762 | out: |
1763 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
1764 | return ret; |
1765 | } |
1766 | |
1767 | /* |
1768 | * try to force the completion of runs for this ctx. Called when |
1769 | * abort wait times out in ipu_image_convert_abort(). |
1770 | */ |
1771 | static void force_abort(struct ipu_image_convert_ctx *ctx) |
1772 | { |
1773 | struct ipu_image_convert_chan *chan = ctx->chan; |
1774 | struct ipu_image_convert_run *run; |
1775 | unsigned long flags; |
1776 | |
1777 | spin_lock_irqsave(&chan->irqlock, flags); |
1778 | |
1779 | run = chan->current_run; |
1780 | if (run && run->ctx == ctx) { |
1781 | convert_stop(run); |
1782 | run->status = -EIO; |
1783 | list_add_tail(new: &run->list, head: &chan->done_q); |
1784 | chan->current_run = NULL; |
1785 | run_next(chan); |
1786 | } |
1787 | |
1788 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
1789 | |
1790 | empty_done_q(chan); |
1791 | } |
1792 | |
1793 | static void release_ipu_resources(struct ipu_image_convert_chan *chan) |
1794 | { |
1795 | if (chan->in_eof_irq >= 0) |
1796 | free_irq(chan->in_eof_irq, chan); |
1797 | if (chan->rot_in_eof_irq >= 0) |
1798 | free_irq(chan->rot_in_eof_irq, chan); |
1799 | if (chan->out_eof_irq >= 0) |
1800 | free_irq(chan->out_eof_irq, chan); |
1801 | if (chan->rot_out_eof_irq >= 0) |
1802 | free_irq(chan->rot_out_eof_irq, chan); |
1803 | |
1804 | if (!IS_ERR_OR_NULL(ptr: chan->in_chan)) |
1805 | ipu_idmac_put(chan->in_chan); |
1806 | if (!IS_ERR_OR_NULL(ptr: chan->out_chan)) |
1807 | ipu_idmac_put(chan->out_chan); |
1808 | if (!IS_ERR_OR_NULL(ptr: chan->rotation_in_chan)) |
1809 | ipu_idmac_put(chan->rotation_in_chan); |
1810 | if (!IS_ERR_OR_NULL(ptr: chan->rotation_out_chan)) |
1811 | ipu_idmac_put(chan->rotation_out_chan); |
1812 | if (!IS_ERR_OR_NULL(ptr: chan->ic)) |
1813 | ipu_ic_put(ic: chan->ic); |
1814 | |
1815 | chan->in_chan = chan->out_chan = chan->rotation_in_chan = |
1816 | chan->rotation_out_chan = NULL; |
1817 | chan->in_eof_irq = -1; |
1818 | chan->rot_in_eof_irq = -1; |
1819 | chan->out_eof_irq = -1; |
1820 | chan->rot_out_eof_irq = -1; |
1821 | } |
1822 | |
1823 | static int get_eof_irq(struct ipu_image_convert_chan *chan, |
1824 | struct ipuv3_channel *channel) |
1825 | { |
1826 | struct ipu_image_convert_priv *priv = chan->priv; |
1827 | int ret, irq; |
1828 | |
1829 | irq = ipu_idmac_channel_irq(ipu: priv->ipu, channel, irq: IPU_IRQ_EOF); |
1830 | |
1831 | ret = request_threaded_irq(irq, handler: eof_irq, thread_fn: do_bh, flags: 0, name: "ipu-ic", dev: chan); |
1832 | if (ret < 0) { |
1833 | dev_err(priv->ipu->dev, "could not acquire irq %d\n", irq); |
1834 | return ret; |
1835 | } |
1836 | |
1837 | return irq; |
1838 | } |
1839 | |
1840 | static int get_ipu_resources(struct ipu_image_convert_chan *chan) |
1841 | { |
1842 | const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; |
1843 | struct ipu_image_convert_priv *priv = chan->priv; |
1844 | int ret; |
1845 | |
1846 | /* get IC */ |
1847 | chan->ic = ipu_ic_get(ipu: priv->ipu, task: chan->ic_task); |
1848 | if (IS_ERR(ptr: chan->ic)) { |
1849 | dev_err(priv->ipu->dev, "could not acquire IC\n"); |
1850 | ret = PTR_ERR(ptr: chan->ic); |
1851 | goto err; |
1852 | } |
1853 | |
1854 | /* get IDMAC channels */ |
1855 | chan->in_chan = ipu_idmac_get(ipu: priv->ipu, channel: dma->in); |
1856 | chan->out_chan = ipu_idmac_get(ipu: priv->ipu, channel: dma->out); |
1857 | if (IS_ERR(ptr: chan->in_chan) || IS_ERR(ptr: chan->out_chan)) { |
1858 | dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); |
1859 | ret = -EBUSY; |
1860 | goto err; |
1861 | } |
1862 | |
1863 | chan->rotation_in_chan = ipu_idmac_get(ipu: priv->ipu, channel: dma->rot_in); |
1864 | chan->rotation_out_chan = ipu_idmac_get(ipu: priv->ipu, channel: dma->rot_out); |
1865 | if (IS_ERR(ptr: chan->rotation_in_chan) || IS_ERR(ptr: chan->rotation_out_chan)) { |
1866 | dev_err(priv->ipu->dev, |
1867 | "could not acquire idmac rotation channels\n"); |
1868 | ret = -EBUSY; |
1869 | goto err; |
1870 | } |
1871 | |
1872 | /* acquire the EOF interrupts */ |
1873 | ret = get_eof_irq(chan, channel: chan->in_chan); |
1874 | if (ret < 0) { |
1875 | chan->in_eof_irq = -1; |
1876 | goto err; |
1877 | } |
1878 | chan->in_eof_irq = ret; |
1879 | |
1880 | ret = get_eof_irq(chan, channel: chan->rotation_in_chan); |
1881 | if (ret < 0) { |
1882 | chan->rot_in_eof_irq = -1; |
1883 | goto err; |
1884 | } |
1885 | chan->rot_in_eof_irq = ret; |
1886 | |
1887 | ret = get_eof_irq(chan, channel: chan->out_chan); |
1888 | if (ret < 0) { |
1889 | chan->out_eof_irq = -1; |
1890 | goto err; |
1891 | } |
1892 | chan->out_eof_irq = ret; |
1893 | |
1894 | ret = get_eof_irq(chan, channel: chan->rotation_out_chan); |
1895 | if (ret < 0) { |
1896 | chan->rot_out_eof_irq = -1; |
1897 | goto err; |
1898 | } |
1899 | chan->rot_out_eof_irq = ret; |
1900 | |
1901 | return 0; |
1902 | err: |
1903 | release_ipu_resources(chan); |
1904 | return ret; |
1905 | } |
1906 | |
1907 | static int fill_image(struct ipu_image_convert_ctx *ctx, |
1908 | struct ipu_image_convert_image *ic_image, |
1909 | struct ipu_image *image, |
1910 | enum ipu_image_convert_type type) |
1911 | { |
1912 | struct ipu_image_convert_priv *priv = ctx->chan->priv; |
1913 | |
1914 | ic_image->base = *image; |
1915 | ic_image->type = type; |
1916 | |
1917 | ic_image->fmt = get_format(fourcc: image->pix.pixelformat); |
1918 | if (!ic_image->fmt) { |
1919 | dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", |
1920 | type == IMAGE_CONVERT_OUT ? "Output": "Input"); |
1921 | return -EINVAL; |
1922 | } |
1923 | |
1924 | if (ic_image->fmt->planar) |
1925 | ic_image->stride = ic_image->base.pix.width; |
1926 | else |
1927 | ic_image->stride = ic_image->base.pix.bytesperline; |
1928 | |
1929 | return 0; |
1930 | } |
1931 | |
1932 | /* borrowed from drivers/media/v4l2-core/v4l2-common.c */ |
1933 | static unsigned int clamp_align(unsigned int x, unsigned int min, |
1934 | unsigned int max, unsigned int align) |
1935 | { |
1936 | /* Bits that must be zero to be aligned */ |
1937 | unsigned int mask = ~((1 << align) - 1); |
1938 | |
1939 | /* Clamp to aligned min and max */ |
1940 | x = clamp(x, (min + ~mask) & mask, max & mask); |
1941 | |
1942 | /* Round to nearest aligned value */ |
1943 | if (align) |
1944 | x = (x + (1 << (align - 1))) & mask; |
1945 | |
1946 | return x; |
1947 | } |
1948 | |
1949 | /* Adjusts input/output images to IPU restrictions */ |
1950 | void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, |
1951 | enum ipu_rotate_mode rot_mode) |
1952 | { |
1953 | const struct ipu_image_pixfmt *infmt, *outfmt; |
1954 | u32 w_align_out, h_align_out; |
1955 | u32 w_align_in, h_align_in; |
1956 | |
1957 | infmt = get_format(fourcc: in->pix.pixelformat); |
1958 | outfmt = get_format(fourcc: out->pix.pixelformat); |
1959 | |
1960 | /* set some default pixel formats if needed */ |
1961 | if (!infmt) { |
1962 | in->pix.pixelformat = V4L2_PIX_FMT_RGB24; |
1963 | infmt = get_format(V4L2_PIX_FMT_RGB24); |
1964 | } |
1965 | if (!outfmt) { |
1966 | out->pix.pixelformat = V4L2_PIX_FMT_RGB24; |
1967 | outfmt = get_format(V4L2_PIX_FMT_RGB24); |
1968 | } |
1969 | |
1970 | /* image converter does not handle fields */ |
1971 | in->pix.field = out->pix.field = V4L2_FIELD_NONE; |
1972 | |
1973 | /* resizer cannot downsize more than 4:1 */ |
1974 | if (ipu_rot_mode_is_irt(rot_mode)) { |
1975 | out->pix.height = max_t(__u32, out->pix.height, |
1976 | in->pix.width / 4); |
1977 | out->pix.width = max_t(__u32, out->pix.width, |
1978 | in->pix.height / 4); |
1979 | } else { |
1980 | out->pix.width = max_t(__u32, out->pix.width, |
1981 | in->pix.width / 4); |
1982 | out->pix.height = max_t(__u32, out->pix.height, |
1983 | in->pix.height / 4); |
1984 | } |
1985 | |
1986 | /* align input width/height */ |
1987 | w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, |
1988 | rot_mode)); |
1989 | h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, |
1990 | rot_mode)); |
1991 | in->pix.width = clamp_align(x: in->pix.width, MIN_W, MAX_W, |
1992 | align: w_align_in); |
1993 | in->pix.height = clamp_align(x: in->pix.height, MIN_H, MAX_H, |
1994 | align: h_align_in); |
1995 | |
1996 | /* align output width/height */ |
1997 | w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, |
1998 | rot_mode)); |
1999 | h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, |
2000 | rot_mode)); |
2001 | out->pix.width = clamp_align(x: out->pix.width, MIN_W, MAX_W, |
2002 | align: w_align_out); |
2003 | out->pix.height = clamp_align(x: out->pix.height, MIN_H, MAX_H, |
2004 | align: h_align_out); |
2005 | |
2006 | /* set input/output strides and image sizes */ |
2007 | in->pix.bytesperline = infmt->planar ? |
2008 | clamp_align(x: in->pix.width, min: 2 << w_align_in, MAX_W, |
2009 | align: w_align_in) : |
2010 | clamp_align(x: (in->pix.width * infmt->bpp) >> 3, |
2011 | min: ((2 << w_align_in) * infmt->bpp) >> 3, |
2012 | max: (MAX_W * infmt->bpp) >> 3, |
2013 | align: w_align_in); |
2014 | in->pix.sizeimage = infmt->planar ? |
2015 | (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : |
2016 | in->pix.height * in->pix.bytesperline; |
2017 | out->pix.bytesperline = outfmt->planar ? out->pix.width : |
2018 | (out->pix.width * outfmt->bpp) >> 3; |
2019 | out->pix.sizeimage = outfmt->planar ? |
2020 | (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : |
2021 | out->pix.height * out->pix.bytesperline; |
2022 | } |
2023 | EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); |
2024 | |
2025 | /* |
2026 | * this is used by ipu_image_convert_prepare() to verify set input and |
2027 | * output images are valid before starting the conversion. Clients can |
2028 | * also call it before calling ipu_image_convert_prepare(). |
2029 | */ |
2030 | int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, |
2031 | enum ipu_rotate_mode rot_mode) |
2032 | { |
2033 | struct ipu_image testin, testout; |
2034 | |
2035 | testin = *in; |
2036 | testout = *out; |
2037 | |
2038 | ipu_image_convert_adjust(&testin, &testout, rot_mode); |
2039 | |
2040 | if (testin.pix.width != in->pix.width || |
2041 | testin.pix.height != in->pix.height || |
2042 | testout.pix.width != out->pix.width || |
2043 | testout.pix.height != out->pix.height) |
2044 | return -EINVAL; |
2045 | |
2046 | return 0; |
2047 | } |
2048 | EXPORT_SYMBOL_GPL(ipu_image_convert_verify); |
2049 | |
2050 | /* |
2051 | * Call ipu_image_convert_prepare() to prepare for the conversion of |
2052 | * given images and rotation mode. Returns a new conversion context. |
2053 | */ |
2054 | struct ipu_image_convert_ctx * |
2055 | ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, |
2056 | struct ipu_image *in, struct ipu_image *out, |
2057 | enum ipu_rotate_mode rot_mode, |
2058 | ipu_image_convert_cb_t complete, |
2059 | void *complete_context) |
2060 | { |
2061 | struct ipu_image_convert_priv *priv = ipu->image_convert_priv; |
2062 | struct ipu_image_convert_image *s_image, *d_image; |
2063 | struct ipu_image_convert_chan *chan; |
2064 | struct ipu_image_convert_ctx *ctx; |
2065 | unsigned long flags; |
2066 | unsigned int i; |
2067 | bool get_res; |
2068 | int ret; |
2069 | |
2070 | if (!in || !out || !complete || |
2071 | (ic_task != IC_TASK_VIEWFINDER && |
2072 | ic_task != IC_TASK_POST_PROCESSOR)) |
2073 | return ERR_PTR(error: -EINVAL); |
2074 | |
2075 | /* verify the in/out images before continuing */ |
2076 | ret = ipu_image_convert_verify(in, out, rot_mode); |
2077 | if (ret) { |
2078 | dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", |
2079 | __func__); |
2080 | return ERR_PTR(error: ret); |
2081 | } |
2082 | |
2083 | chan = &priv->chan[ic_task]; |
2084 | |
2085 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
2086 | if (!ctx) |
2087 | return ERR_PTR(error: -ENOMEM); |
2088 | |
2089 | dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, |
2090 | chan->ic_task, ctx); |
2091 | |
2092 | ctx->chan = chan; |
2093 | init_completion(x: &ctx->aborted); |
2094 | |
2095 | ctx->rot_mode = rot_mode; |
2096 | |
2097 | /* Sets ctx->in.num_rows/cols as well */ |
2098 | ret = calc_image_resize_coefficients(ctx, in, out); |
2099 | if (ret) |
2100 | goto out_free; |
2101 | |
2102 | s_image = &ctx->in; |
2103 | d_image = &ctx->out; |
2104 | |
2105 | /* set tiling and rotation */ |
2106 | if (ipu_rot_mode_is_irt(rot_mode)) { |
2107 | d_image->num_rows = s_image->num_cols; |
2108 | d_image->num_cols = s_image->num_rows; |
2109 | } else { |
2110 | d_image->num_rows = s_image->num_rows; |
2111 | d_image->num_cols = s_image->num_cols; |
2112 | } |
2113 | |
2114 | ctx->num_tiles = d_image->num_cols * d_image->num_rows; |
2115 | |
2116 | ret = fill_image(ctx, ic_image: s_image, image: in, type: IMAGE_CONVERT_IN); |
2117 | if (ret) |
2118 | goto out_free; |
2119 | ret = fill_image(ctx, ic_image: d_image, image: out, type: IMAGE_CONVERT_OUT); |
2120 | if (ret) |
2121 | goto out_free; |
2122 | |
2123 | calc_out_tile_map(ctx); |
2124 | |
2125 | find_seams(ctx, in: s_image, out: d_image); |
2126 | |
2127 | ret = calc_tile_dimensions(ctx, image: s_image); |
2128 | if (ret) |
2129 | goto out_free; |
2130 | |
2131 | ret = calc_tile_offsets(ctx, image: s_image); |
2132 | if (ret) |
2133 | goto out_free; |
2134 | |
2135 | calc_tile_dimensions(ctx, image: d_image); |
2136 | ret = calc_tile_offsets(ctx, image: d_image); |
2137 | if (ret) |
2138 | goto out_free; |
2139 | |
2140 | calc_tile_resize_coefficients(ctx); |
2141 | |
2142 | ret = ipu_ic_calc_csc(csc: &ctx->csc, |
2143 | in_enc: s_image->base.pix.ycbcr_enc, |
2144 | in_quant: s_image->base.pix.quantization, |
2145 | in_cs: ipu_pixelformat_to_colorspace(pixelformat: s_image->fmt->fourcc), |
2146 | out_enc: d_image->base.pix.ycbcr_enc, |
2147 | out_quant: d_image->base.pix.quantization, |
2148 | out_cs: ipu_pixelformat_to_colorspace(pixelformat: d_image->fmt->fourcc)); |
2149 | if (ret) |
2150 | goto out_free; |
2151 | |
2152 | dump_format(ctx, ic_image: s_image); |
2153 | dump_format(ctx, ic_image: d_image); |
2154 | |
2155 | ctx->complete = complete; |
2156 | ctx->complete_context = complete_context; |
2157 | |
2158 | /* |
2159 | * Can we use double-buffering for this operation? If there is |
2160 | * only one tile (the whole image can be converted in a single |
2161 | * operation) there's no point in using double-buffering. Also, |
2162 | * the IPU's IDMAC channels allow only a single U and V plane |
2163 | * offset shared between both buffers, but these offsets change |
2164 | * for every tile, and therefore would have to be updated for |
2165 | * each buffer which is not possible. So double-buffering is |
2166 | * impossible when either the source or destination images are |
2167 | * a planar format (YUV420, YUV422P, etc.). Further, differently |
2168 | * sized tiles or different resizing coefficients per tile |
2169 | * prevent double-buffering as well. |
2170 | */ |
2171 | ctx->double_buffering = (ctx->num_tiles > 1 && |
2172 | !s_image->fmt->planar && |
2173 | !d_image->fmt->planar); |
2174 | for (i = 1; i < ctx->num_tiles; i++) { |
2175 | if (ctx->in.tile[i].width != ctx->in.tile[0].width || |
2176 | ctx->in.tile[i].height != ctx->in.tile[0].height || |
2177 | ctx->out.tile[i].width != ctx->out.tile[0].width || |
2178 | ctx->out.tile[i].height != ctx->out.tile[0].height) { |
2179 | ctx->double_buffering = false; |
2180 | break; |
2181 | } |
2182 | } |
2183 | for (i = 1; i < ctx->in.num_cols; i++) { |
2184 | if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { |
2185 | ctx->double_buffering = false; |
2186 | break; |
2187 | } |
2188 | } |
2189 | for (i = 1; i < ctx->in.num_rows; i++) { |
2190 | if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { |
2191 | ctx->double_buffering = false; |
2192 | break; |
2193 | } |
2194 | } |
2195 | |
2196 | if (ipu_rot_mode_is_irt(ctx->rot_mode)) { |
2197 | unsigned long intermediate_size = d_image->tile[0].size; |
2198 | |
2199 | for (i = 1; i < ctx->num_tiles; i++) { |
2200 | if (d_image->tile[i].size > intermediate_size) |
2201 | intermediate_size = d_image->tile[i].size; |
2202 | } |
2203 | |
2204 | ret = alloc_dma_buf(priv, buf: &ctx->rot_intermediate[0], |
2205 | size: intermediate_size); |
2206 | if (ret) |
2207 | goto out_free; |
2208 | if (ctx->double_buffering) { |
2209 | ret = alloc_dma_buf(priv, |
2210 | buf: &ctx->rot_intermediate[1], |
2211 | size: intermediate_size); |
2212 | if (ret) |
2213 | goto out_free_dmabuf0; |
2214 | } |
2215 | } |
2216 | |
2217 | spin_lock_irqsave(&chan->irqlock, flags); |
2218 | |
2219 | get_res = list_empty(head: &chan->ctx_list); |
2220 | |
2221 | list_add_tail(new: &ctx->list, head: &chan->ctx_list); |
2222 | |
2223 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
2224 | |
2225 | if (get_res) { |
2226 | ret = get_ipu_resources(chan); |
2227 | if (ret) |
2228 | goto out_free_dmabuf1; |
2229 | } |
2230 | |
2231 | return ctx; |
2232 | |
2233 | out_free_dmabuf1: |
2234 | free_dma_buf(priv, buf: &ctx->rot_intermediate[1]); |
2235 | spin_lock_irqsave(&chan->irqlock, flags); |
2236 | list_del(entry: &ctx->list); |
2237 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
2238 | out_free_dmabuf0: |
2239 | free_dma_buf(priv, buf: &ctx->rot_intermediate[0]); |
2240 | out_free: |
2241 | kfree(objp: ctx); |
2242 | return ERR_PTR(error: ret); |
2243 | } |
2244 | EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); |
2245 | |
2246 | /* |
2247 | * Carry out a single image conversion run. Only the physaddr's of the input |
2248 | * and output image buffers are needed. The conversion context must have |
2249 | * been created previously with ipu_image_convert_prepare(). |
2250 | */ |
2251 | int ipu_image_convert_queue(struct ipu_image_convert_run *run) |
2252 | { |
2253 | struct ipu_image_convert_chan *chan; |
2254 | struct ipu_image_convert_priv *priv; |
2255 | struct ipu_image_convert_ctx *ctx; |
2256 | unsigned long flags; |
2257 | int ret = 0; |
2258 | |
2259 | if (!run || !run->ctx || !run->in_phys || !run->out_phys) |
2260 | return -EINVAL; |
2261 | |
2262 | ctx = run->ctx; |
2263 | chan = ctx->chan; |
2264 | priv = chan->priv; |
2265 | |
2266 | dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, |
2267 | chan->ic_task, ctx, run); |
2268 | |
2269 | INIT_LIST_HEAD(list: &run->list); |
2270 | |
2271 | spin_lock_irqsave(&chan->irqlock, flags); |
2272 | |
2273 | if (ctx->aborting) { |
2274 | ret = -EIO; |
2275 | goto unlock; |
2276 | } |
2277 | |
2278 | list_add_tail(new: &run->list, head: &chan->pending_q); |
2279 | |
2280 | if (!chan->current_run) { |
2281 | ret = do_run(run); |
2282 | if (ret) |
2283 | chan->current_run = NULL; |
2284 | } |
2285 | unlock: |
2286 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
2287 | return ret; |
2288 | } |
2289 | EXPORT_SYMBOL_GPL(ipu_image_convert_queue); |
2290 | |
2291 | /* Abort any active or pending conversions for this context */ |
2292 | static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) |
2293 | { |
2294 | struct ipu_image_convert_chan *chan = ctx->chan; |
2295 | struct ipu_image_convert_priv *priv = chan->priv; |
2296 | struct ipu_image_convert_run *run, *active_run, *tmp; |
2297 | unsigned long flags; |
2298 | int run_count, ret; |
2299 | |
2300 | spin_lock_irqsave(&chan->irqlock, flags); |
2301 | |
2302 | /* move all remaining pending runs in this context to done_q */ |
2303 | list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { |
2304 | if (run->ctx != ctx) |
2305 | continue; |
2306 | run->status = -EIO; |
2307 | list_move_tail(list: &run->list, head: &chan->done_q); |
2308 | } |
2309 | |
2310 | run_count = get_run_count(ctx, q: &chan->done_q); |
2311 | active_run = (chan->current_run && chan->current_run->ctx == ctx) ? |
2312 | chan->current_run : NULL; |
2313 | |
2314 | if (active_run) |
2315 | reinit_completion(x: &ctx->aborted); |
2316 | |
2317 | ctx->aborting = true; |
2318 | |
2319 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
2320 | |
2321 | if (!run_count && !active_run) { |
2322 | dev_dbg(priv->ipu->dev, |
2323 | "%s: task %u: no abort needed for ctx %p\n", |
2324 | __func__, chan->ic_task, ctx); |
2325 | return; |
2326 | } |
2327 | |
2328 | if (!active_run) { |
2329 | empty_done_q(chan); |
2330 | return; |
2331 | } |
2332 | |
2333 | dev_dbg(priv->ipu->dev, |
2334 | "%s: task %u: wait for completion: %d runs\n", |
2335 | __func__, chan->ic_task, run_count); |
2336 | |
2337 | ret = wait_for_completion_timeout(x: &ctx->aborted, |
2338 | timeout: msecs_to_jiffies(m: 10000)); |
2339 | if (ret == 0) { |
2340 | dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); |
2341 | force_abort(ctx); |
2342 | } |
2343 | } |
2344 | |
2345 | void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) |
2346 | { |
2347 | __ipu_image_convert_abort(ctx); |
2348 | ctx->aborting = false; |
2349 | } |
2350 | EXPORT_SYMBOL_GPL(ipu_image_convert_abort); |
2351 | |
2352 | /* Unprepare image conversion context */ |
2353 | void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) |
2354 | { |
2355 | struct ipu_image_convert_chan *chan = ctx->chan; |
2356 | struct ipu_image_convert_priv *priv = chan->priv; |
2357 | unsigned long flags; |
2358 | bool put_res; |
2359 | |
2360 | /* make sure no runs are hanging around */ |
2361 | __ipu_image_convert_abort(ctx); |
2362 | |
2363 | dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, |
2364 | chan->ic_task, ctx); |
2365 | |
2366 | spin_lock_irqsave(&chan->irqlock, flags); |
2367 | |
2368 | list_del(entry: &ctx->list); |
2369 | |
2370 | put_res = list_empty(head: &chan->ctx_list); |
2371 | |
2372 | spin_unlock_irqrestore(lock: &chan->irqlock, flags); |
2373 | |
2374 | if (put_res) |
2375 | release_ipu_resources(chan); |
2376 | |
2377 | free_dma_buf(priv, buf: &ctx->rot_intermediate[1]); |
2378 | free_dma_buf(priv, buf: &ctx->rot_intermediate[0]); |
2379 | |
2380 | kfree(objp: ctx); |
2381 | } |
2382 | EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); |
2383 | |
2384 | /* |
2385 | * "Canned" asynchronous single image conversion. Allocates and returns |
2386 | * a new conversion run. On successful return the caller must free the |
2387 | * run and call ipu_image_convert_unprepare() after conversion completes. |
2388 | */ |
2389 | struct ipu_image_convert_run * |
2390 | ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, |
2391 | struct ipu_image *in, struct ipu_image *out, |
2392 | enum ipu_rotate_mode rot_mode, |
2393 | ipu_image_convert_cb_t complete, |
2394 | void *complete_context) |
2395 | { |
2396 | struct ipu_image_convert_ctx *ctx; |
2397 | struct ipu_image_convert_run *run; |
2398 | int ret; |
2399 | |
2400 | ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, |
2401 | complete, complete_context); |
2402 | if (IS_ERR(ptr: ctx)) |
2403 | return ERR_CAST(ptr: ctx); |
2404 | |
2405 | run = kzalloc(sizeof(*run), GFP_KERNEL); |
2406 | if (!run) { |
2407 | ipu_image_convert_unprepare(ctx); |
2408 | return ERR_PTR(error: -ENOMEM); |
2409 | } |
2410 | |
2411 | run->ctx = ctx; |
2412 | run->in_phys = in->phys0; |
2413 | run->out_phys = out->phys0; |
2414 | |
2415 | ret = ipu_image_convert_queue(run); |
2416 | if (ret) { |
2417 | ipu_image_convert_unprepare(ctx); |
2418 | kfree(objp: run); |
2419 | return ERR_PTR(error: ret); |
2420 | } |
2421 | |
2422 | return run; |
2423 | } |
2424 | EXPORT_SYMBOL_GPL(ipu_image_convert); |
2425 | |
2426 | int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) |
2427 | { |
2428 | struct ipu_image_convert_priv *priv; |
2429 | int i; |
2430 | |
2431 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
2432 | if (!priv) |
2433 | return -ENOMEM; |
2434 | |
2435 | ipu->image_convert_priv = priv; |
2436 | priv->ipu = ipu; |
2437 | |
2438 | for (i = 0; i < IC_NUM_TASKS; i++) { |
2439 | struct ipu_image_convert_chan *chan = &priv->chan[i]; |
2440 | |
2441 | chan->ic_task = i; |
2442 | chan->priv = priv; |
2443 | chan->dma_ch = &image_convert_dma_chan[i]; |
2444 | chan->in_eof_irq = -1; |
2445 | chan->rot_in_eof_irq = -1; |
2446 | chan->out_eof_irq = -1; |
2447 | chan->rot_out_eof_irq = -1; |
2448 | |
2449 | spin_lock_init(&chan->irqlock); |
2450 | INIT_LIST_HEAD(list: &chan->ctx_list); |
2451 | INIT_LIST_HEAD(list: &chan->pending_q); |
2452 | INIT_LIST_HEAD(list: &chan->done_q); |
2453 | } |
2454 | |
2455 | return 0; |
2456 | } |
2457 | |
2458 | void ipu_image_convert_exit(struct ipu_soc *ipu) |
2459 | { |
2460 | } |
2461 |
Definitions
- ipu_image_convert_type
- ipu_image_convert_dma_buf
- ipu_image_convert_dma_chan
- ipu_image_tile
- ipu_image_convert_image
- ipu_image_pixfmt
- eof_irq_mask
- ipu_image_convert_ctx
- ipu_image_convert_chan
- ipu_image_convert_priv
- image_convert_dma_chan
- image_convert_formats
- get_format
- dump_format
- free_dma_buf
- alloc_dma_buf
- num_stripes
- calc_image_resize_coefficients
- find_best_seam
- tile_left_align
- tile_top_align
- tile_width_align
- tile_height_align
- fill_tile_column
- fill_tile_row
- find_seams
- calc_tile_dimensions
- transform_tile_index
- calc_out_tile_map
- calc_tile_offsets_planar
- calc_tile_offsets_packed
- calc_tile_offsets
- calc_resize_coeff
- calc_tile_resize_coefficients
- get_run_count
- convert_stop
- init_idmac_channel
- convert_start
- do_run
- run_next
- empty_done_q
- do_bh
- ic_settings_changed
- do_tile_complete
- eof_irq
- force_abort
- release_ipu_resources
- get_eof_irq
- get_ipu_resources
- fill_image
- clamp_align
- ipu_image_convert_adjust
- ipu_image_convert_verify
- ipu_image_convert_prepare
- ipu_image_convert_queue
- __ipu_image_convert_abort
- ipu_image_convert_abort
- ipu_image_convert_unprepare
- ipu_image_convert
- ipu_image_convert_init
Improve your Profiling and Debugging skills
Find out more