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