0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/interrupt.h>
0009 #include <linux/dma-mapping.h>
0010 #include <video/imx-ipu-image-convert.h>
0011 #include "ipu-prv.h"
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063 #define MAX_STRIPES_W 4
0064 #define MAX_STRIPES_H 4
0065 #define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
0066
0067 #define MIN_W 16
0068 #define MIN_H 8
0069 #define MAX_W 4096
0070 #define MAX_H 4096
0071
0072 enum ipu_image_convert_type {
0073 IMAGE_CONVERT_IN = 0,
0074 IMAGE_CONVERT_OUT,
0075 };
0076
0077 struct ipu_image_convert_dma_buf {
0078 void *virt;
0079 dma_addr_t phys;
0080 unsigned long len;
0081 };
0082
0083 struct ipu_image_convert_dma_chan {
0084 int in;
0085 int out;
0086 int rot_in;
0087 int rot_out;
0088 int vdi_in_p;
0089 int vdi_in;
0090 int vdi_in_n;
0091 };
0092
0093
0094 struct ipu_image_tile {
0095 u32 width;
0096 u32 height;
0097 u32 left;
0098 u32 top;
0099
0100 u32 size;
0101 u32 stride;
0102 u32 rot_stride;
0103
0104 u32 offset;
0105
0106 u32 u_off;
0107
0108 u32 v_off;
0109 };
0110
0111 struct ipu_image_convert_image {
0112 struct ipu_image base;
0113 enum ipu_image_convert_type type;
0114
0115 const struct ipu_image_pixfmt *fmt;
0116 unsigned int stride;
0117
0118
0119 unsigned int num_rows;
0120
0121 unsigned int num_cols;
0122
0123 struct ipu_image_tile tile[MAX_TILES];
0124 };
0125
0126 struct ipu_image_pixfmt {
0127 u32 fourcc;
0128 int bpp;
0129 int uv_width_dec;
0130 int uv_height_dec;
0131 bool planar;
0132 bool uv_swapped;
0133 bool uv_packed;
0134 };
0135
0136 struct ipu_image_convert_ctx;
0137 struct ipu_image_convert_chan;
0138 struct ipu_image_convert_priv;
0139
0140 enum eof_irq_mask {
0141 EOF_IRQ_IN = BIT(0),
0142 EOF_IRQ_ROT_IN = BIT(1),
0143 EOF_IRQ_OUT = BIT(2),
0144 EOF_IRQ_ROT_OUT = BIT(3),
0145 };
0146
0147 #define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT)
0148 #define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \
0149 EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT)
0150
0151 struct ipu_image_convert_ctx {
0152 struct ipu_image_convert_chan *chan;
0153
0154 ipu_image_convert_cb_t complete;
0155 void *complete_context;
0156
0157
0158 struct ipu_image_convert_image in;
0159 struct ipu_image_convert_image out;
0160 struct ipu_ic_csc csc;
0161 enum ipu_rotate_mode rot_mode;
0162 u32 downsize_coeff_h;
0163 u32 downsize_coeff_v;
0164 u32 image_resize_coeff_h;
0165 u32 image_resize_coeff_v;
0166 u32 resize_coeffs_h[MAX_STRIPES_W];
0167 u32 resize_coeffs_v[MAX_STRIPES_H];
0168
0169
0170 struct ipu_image_convert_dma_buf rot_intermediate[2];
0171
0172
0173 int cur_buf_num;
0174
0175 bool aborting;
0176 struct completion aborted;
0177
0178
0179 bool double_buffering;
0180
0181 unsigned int num_tiles;
0182
0183 unsigned int next_tile;
0184
0185 unsigned int out_tile_map[MAX_TILES];
0186
0187
0188 enum eof_irq_mask eof_mask;
0189
0190 struct list_head list;
0191 };
0192
0193 struct ipu_image_convert_chan {
0194 struct ipu_image_convert_priv *priv;
0195
0196 enum ipu_ic_task ic_task;
0197 const struct ipu_image_convert_dma_chan *dma_ch;
0198
0199 struct ipu_ic *ic;
0200 struct ipuv3_channel *in_chan;
0201 struct ipuv3_channel *out_chan;
0202 struct ipuv3_channel *rotation_in_chan;
0203 struct ipuv3_channel *rotation_out_chan;
0204
0205
0206 int in_eof_irq;
0207 int rot_in_eof_irq;
0208 int out_eof_irq;
0209 int rot_out_eof_irq;
0210
0211 spinlock_t irqlock;
0212
0213
0214 struct list_head ctx_list;
0215
0216 struct list_head pending_q;
0217
0218 struct list_head done_q;
0219
0220
0221 struct ipu_image_convert_run *current_run;
0222 };
0223
0224 struct ipu_image_convert_priv {
0225 struct ipu_image_convert_chan chan[IC_NUM_TASKS];
0226 struct ipu_soc *ipu;
0227 };
0228
0229 static const struct ipu_image_convert_dma_chan
0230 image_convert_dma_chan[IC_NUM_TASKS] = {
0231 [IC_TASK_VIEWFINDER] = {
0232 .in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
0233 .out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
0234 .rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
0235 .rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
0236 .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV,
0237 .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR,
0238 .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT,
0239 },
0240 [IC_TASK_POST_PROCESSOR] = {
0241 .in = IPUV3_CHANNEL_MEM_IC_PP,
0242 .out = IPUV3_CHANNEL_IC_PP_MEM,
0243 .rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
0244 .rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
0245 },
0246 };
0247
0248 static const struct ipu_image_pixfmt image_convert_formats[] = {
0249 {
0250 .fourcc = V4L2_PIX_FMT_RGB565,
0251 .bpp = 16,
0252 }, {
0253 .fourcc = V4L2_PIX_FMT_RGB24,
0254 .bpp = 24,
0255 }, {
0256 .fourcc = V4L2_PIX_FMT_BGR24,
0257 .bpp = 24,
0258 }, {
0259 .fourcc = V4L2_PIX_FMT_RGB32,
0260 .bpp = 32,
0261 }, {
0262 .fourcc = V4L2_PIX_FMT_BGR32,
0263 .bpp = 32,
0264 }, {
0265 .fourcc = V4L2_PIX_FMT_XRGB32,
0266 .bpp = 32,
0267 }, {
0268 .fourcc = V4L2_PIX_FMT_XBGR32,
0269 .bpp = 32,
0270 }, {
0271 .fourcc = V4L2_PIX_FMT_BGRX32,
0272 .bpp = 32,
0273 }, {
0274 .fourcc = V4L2_PIX_FMT_RGBX32,
0275 .bpp = 32,
0276 }, {
0277 .fourcc = V4L2_PIX_FMT_YUYV,
0278 .bpp = 16,
0279 .uv_width_dec = 2,
0280 .uv_height_dec = 1,
0281 }, {
0282 .fourcc = V4L2_PIX_FMT_UYVY,
0283 .bpp = 16,
0284 .uv_width_dec = 2,
0285 .uv_height_dec = 1,
0286 }, {
0287 .fourcc = V4L2_PIX_FMT_YUV420,
0288 .bpp = 12,
0289 .planar = true,
0290 .uv_width_dec = 2,
0291 .uv_height_dec = 2,
0292 }, {
0293 .fourcc = V4L2_PIX_FMT_YVU420,
0294 .bpp = 12,
0295 .planar = true,
0296 .uv_width_dec = 2,
0297 .uv_height_dec = 2,
0298 .uv_swapped = true,
0299 }, {
0300 .fourcc = V4L2_PIX_FMT_NV12,
0301 .bpp = 12,
0302 .planar = true,
0303 .uv_width_dec = 2,
0304 .uv_height_dec = 2,
0305 .uv_packed = true,
0306 }, {
0307 .fourcc = V4L2_PIX_FMT_YUV422P,
0308 .bpp = 16,
0309 .planar = true,
0310 .uv_width_dec = 2,
0311 .uv_height_dec = 1,
0312 }, {
0313 .fourcc = V4L2_PIX_FMT_NV16,
0314 .bpp = 16,
0315 .planar = true,
0316 .uv_width_dec = 2,
0317 .uv_height_dec = 1,
0318 .uv_packed = true,
0319 },
0320 };
0321
0322 static const struct ipu_image_pixfmt *get_format(u32 fourcc)
0323 {
0324 const struct ipu_image_pixfmt *ret = NULL;
0325 unsigned int i;
0326
0327 for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) {
0328 if (image_convert_formats[i].fourcc == fourcc) {
0329 ret = &image_convert_formats[i];
0330 break;
0331 }
0332 }
0333
0334 return ret;
0335 }
0336
0337 static void dump_format(struct ipu_image_convert_ctx *ctx,
0338 struct ipu_image_convert_image *ic_image)
0339 {
0340 struct ipu_image_convert_chan *chan = ctx->chan;
0341 struct ipu_image_convert_priv *priv = chan->priv;
0342
0343 dev_dbg(priv->ipu->dev,
0344 "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n",
0345 chan->ic_task, ctx,
0346 ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
0347 ic_image->base.pix.width, ic_image->base.pix.height,
0348 ic_image->num_cols, ic_image->num_rows,
0349 ic_image->fmt->fourcc & 0xff,
0350 (ic_image->fmt->fourcc >> 8) & 0xff,
0351 (ic_image->fmt->fourcc >> 16) & 0xff,
0352 (ic_image->fmt->fourcc >> 24) & 0xff);
0353 }
0354
0355 int ipu_image_convert_enum_format(int index, u32 *fourcc)
0356 {
0357 const struct ipu_image_pixfmt *fmt;
0358
0359 if (index >= (int)ARRAY_SIZE(image_convert_formats))
0360 return -EINVAL;
0361
0362
0363 fmt = &image_convert_formats[index];
0364 *fourcc = fmt->fourcc;
0365 return 0;
0366 }
0367 EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
0368
0369 static void free_dma_buf(struct ipu_image_convert_priv *priv,
0370 struct ipu_image_convert_dma_buf *buf)
0371 {
0372 if (buf->virt)
0373 dma_free_coherent(priv->ipu->dev,
0374 buf->len, buf->virt, buf->phys);
0375 buf->virt = NULL;
0376 buf->phys = 0;
0377 }
0378
0379 static int alloc_dma_buf(struct ipu_image_convert_priv *priv,
0380 struct ipu_image_convert_dma_buf *buf,
0381 int size)
0382 {
0383 buf->len = PAGE_ALIGN(size);
0384 buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
0385 GFP_DMA | GFP_KERNEL);
0386 if (!buf->virt) {
0387 dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
0388 return -ENOMEM;
0389 }
0390
0391 return 0;
0392 }
0393
0394 static inline int num_stripes(int dim)
0395 {
0396 return (dim - 1) / 1024 + 1;
0397 }
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407 static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx,
0408 struct ipu_image *in,
0409 struct ipu_image *out)
0410 {
0411 u32 downsized_width = in->rect.width;
0412 u32 downsized_height = in->rect.height;
0413 u32 downsize_coeff_v = 0;
0414 u32 downsize_coeff_h = 0;
0415 u32 resized_width = out->rect.width;
0416 u32 resized_height = out->rect.height;
0417 u32 resize_coeff_h;
0418 u32 resize_coeff_v;
0419 u32 cols;
0420 u32 rows;
0421
0422 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
0423 resized_width = out->rect.height;
0424 resized_height = out->rect.width;
0425 }
0426
0427
0428 if (WARN_ON(resized_width == 0 || resized_height == 0))
0429 return -EINVAL;
0430
0431 while (downsized_width >= resized_width * 2) {
0432 downsized_width >>= 1;
0433 downsize_coeff_h++;
0434 }
0435
0436 while (downsized_height >= resized_height * 2) {
0437 downsized_height >>= 1;
0438 downsize_coeff_v++;
0439 }
0440
0441
0442
0443
0444
0445
0446
0447 resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1);
0448 resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1);
0449
0450
0451
0452
0453
0454
0455 cols = num_stripes(max_t(u32, downsized_width, resized_width));
0456 rows = num_stripes(max_t(u32, downsized_height, resized_height));
0457
0458 dev_dbg(ctx->chan->priv->ipu->dev,
0459 "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n",
0460 __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v,
0461 resize_coeff_v, cols, rows);
0462
0463 if (downsize_coeff_h > 2 || downsize_coeff_v > 2 ||
0464 resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff)
0465 return -EINVAL;
0466
0467 ctx->downsize_coeff_h = downsize_coeff_h;
0468 ctx->downsize_coeff_v = downsize_coeff_v;
0469 ctx->image_resize_coeff_h = resize_coeff_h;
0470 ctx->image_resize_coeff_v = resize_coeff_v;
0471 ctx->in.num_cols = cols;
0472 ctx->in.num_rows = rows;
0473
0474 return 0;
0475 }
0476
0477 #define round_closest(x, y) round_down((x) + (y)/2, (y))
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498 static void find_best_seam(struct ipu_image_convert_ctx *ctx,
0499 unsigned int index,
0500 unsigned int in_edge,
0501 unsigned int out_edge,
0502 unsigned int in_align,
0503 unsigned int out_align,
0504 unsigned int in_burst,
0505 unsigned int out_burst,
0506 unsigned int downsize_coeff,
0507 unsigned int resize_coeff,
0508 u32 *_in_seam,
0509 u32 *_out_seam)
0510 {
0511 struct device *dev = ctx->chan->priv->ipu->dev;
0512 unsigned int out_pos;
0513
0514 unsigned int out_seam = 0;
0515 unsigned int in_seam = 0;
0516 unsigned int min_diff = UINT_MAX;
0517 unsigned int out_start;
0518 unsigned int out_end;
0519 unsigned int in_start;
0520 unsigned int in_end;
0521
0522
0523 out_start = max_t(int, index * out_align, out_edge - 1024);
0524
0525 out_end = min_t(unsigned int, out_edge, index * 1024 + 1);
0526
0527
0528
0529
0530
0531 in_start = max_t(int, index * in_align,
0532 in_edge - (1024 << downsize_coeff));
0533 in_end = min_t(unsigned int, in_edge,
0534 index * (1024 << downsize_coeff) + 1);
0535
0536
0537
0538
0539
0540
0541 out_start = round_up(out_start, out_align);
0542 for (out_pos = out_start; out_pos < out_end; out_pos += out_align) {
0543 unsigned int in_pos;
0544 unsigned int in_pos_aligned;
0545 unsigned int in_pos_rounded;
0546 unsigned int abs_diff;
0547
0548
0549
0550
0551
0552
0553 if ((out_burst > 1) && (out_edge - out_pos) % out_burst)
0554 continue;
0555
0556
0557
0558
0559
0560 in_pos = (out_pos * resize_coeff) << downsize_coeff;
0561
0562
0563
0564
0565 in_pos_aligned = round_closest(in_pos, 8192U * in_align);
0566
0567 in_pos_rounded = in_pos_aligned / 8192U;
0568
0569 if (in_pos_rounded < in_start)
0570 continue;
0571 if (in_pos_rounded >= in_end)
0572 break;
0573
0574 if ((in_burst > 1) &&
0575 (in_edge - in_pos_rounded) % in_burst)
0576 continue;
0577
0578 if (in_pos < in_pos_aligned)
0579 abs_diff = in_pos_aligned - in_pos;
0580 else
0581 abs_diff = in_pos - in_pos_aligned;
0582
0583 if (abs_diff < min_diff) {
0584 in_seam = in_pos_rounded;
0585 out_seam = out_pos;
0586 min_diff = abs_diff;
0587 }
0588 }
0589
0590 *_out_seam = out_seam;
0591 *_in_seam = in_seam;
0592
0593 dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n",
0594 __func__, out_seam, out_align, out_start, out_end,
0595 in_seam, in_align, in_start, in_end, min_diff / 8192,
0596 DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192));
0597 }
0598
0599
0600
0601
0602
0603 static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt)
0604 {
0605 if (fmt->planar)
0606 return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec;
0607 else
0608 return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8;
0609 }
0610
0611
0612
0613
0614 static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt)
0615 {
0616 return fmt->uv_height_dec > 1 ? 2 : 1;
0617 }
0618
0619 static inline u32 tile_width_align(enum ipu_image_convert_type type,
0620 const struct ipu_image_pixfmt *fmt,
0621 enum ipu_rotate_mode rot_mode)
0622 {
0623 if (type == IMAGE_CONVERT_IN) {
0624
0625
0626
0627
0628
0629
0630 return (!ipu_rot_mode_is_irt(rot_mode) &&
0631 (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2;
0632 }
0633
0634
0635
0636
0637
0638
0639
0640 return (ipu_rot_mode_is_irt(rot_mode) &&
0641 fmt->planar && !fmt->uv_packed) ?
0642 8 * fmt->uv_width_dec : 8;
0643 }
0644
0645 static inline u32 tile_height_align(enum ipu_image_convert_type type,
0646 const struct ipu_image_pixfmt *fmt,
0647 enum ipu_rotate_mode rot_mode)
0648 {
0649 if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode))
0650 return 2;
0651
0652
0653
0654
0655
0656
0657
0658 return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8;
0659 }
0660
0661
0662
0663
0664
0665
0666 static void fill_tile_column(struct ipu_image_convert_ctx *ctx,
0667 unsigned int col,
0668 struct ipu_image_convert_image *in,
0669 unsigned int in_left, unsigned int in_width,
0670 struct ipu_image_convert_image *out,
0671 unsigned int out_left, unsigned int out_width)
0672 {
0673 unsigned int row, tile_idx;
0674 struct ipu_image_tile *in_tile, *out_tile;
0675
0676 for (row = 0; row < in->num_rows; row++) {
0677 tile_idx = in->num_cols * row + col;
0678 in_tile = &in->tile[tile_idx];
0679 out_tile = &out->tile[ctx->out_tile_map[tile_idx]];
0680
0681 in_tile->left = in_left;
0682 in_tile->width = in_width;
0683
0684 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
0685 out_tile->top = out_left;
0686 out_tile->height = out_width;
0687 } else {
0688 out_tile->left = out_left;
0689 out_tile->width = out_width;
0690 }
0691 }
0692 }
0693
0694
0695
0696
0697
0698
0699 static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row,
0700 struct ipu_image_convert_image *in,
0701 unsigned int in_top, unsigned int in_height,
0702 struct ipu_image_convert_image *out,
0703 unsigned int out_top, unsigned int out_height)
0704 {
0705 unsigned int col, tile_idx;
0706 struct ipu_image_tile *in_tile, *out_tile;
0707
0708 for (col = 0; col < in->num_cols; col++) {
0709 tile_idx = in->num_cols * row + col;
0710 in_tile = &in->tile[tile_idx];
0711 out_tile = &out->tile[ctx->out_tile_map[tile_idx]];
0712
0713 in_tile->top = in_top;
0714 in_tile->height = in_height;
0715
0716 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
0717 out_tile->left = out_top;
0718 out_tile->width = out_height;
0719 } else {
0720 out_tile->top = out_top;
0721 out_tile->height = out_height;
0722 }
0723 }
0724 }
0725
0726
0727
0728
0729
0730
0731 static void find_seams(struct ipu_image_convert_ctx *ctx,
0732 struct ipu_image_convert_image *in,
0733 struct ipu_image_convert_image *out)
0734 {
0735 struct device *dev = ctx->chan->priv->ipu->dev;
0736 unsigned int resized_width = out->base.rect.width;
0737 unsigned int resized_height = out->base.rect.height;
0738 unsigned int col;
0739 unsigned int row;
0740 unsigned int in_left_align = tile_left_align(in->fmt);
0741 unsigned int in_top_align = tile_top_align(in->fmt);
0742 unsigned int out_left_align = tile_left_align(out->fmt);
0743 unsigned int out_top_align = tile_top_align(out->fmt);
0744 unsigned int out_width_align = tile_width_align(out->type, out->fmt,
0745 ctx->rot_mode);
0746 unsigned int out_height_align = tile_height_align(out->type, out->fmt,
0747 ctx->rot_mode);
0748 unsigned int in_right = in->base.rect.width;
0749 unsigned int in_bottom = in->base.rect.height;
0750 unsigned int out_right = out->base.rect.width;
0751 unsigned int out_bottom = out->base.rect.height;
0752 unsigned int flipped_out_left;
0753 unsigned int flipped_out_top;
0754
0755 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
0756
0757 resized_width = out->base.rect.height;
0758 resized_height = out->base.rect.width;
0759 out_left_align = out_height_align;
0760 out_top_align = out_width_align;
0761 out_width_align = out_left_align;
0762 out_height_align = out_top_align;
0763 out_right = out->base.rect.height;
0764 out_bottom = out->base.rect.width;
0765 }
0766
0767 for (col = in->num_cols - 1; col > 0; col--) {
0768 bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) ||
0769 !(ctx->rot_mode & IPU_ROT_BIT_HFLIP);
0770 bool allow_out_overshoot = (col < in->num_cols - 1) &&
0771 !(ctx->rot_mode & IPU_ROT_BIT_HFLIP);
0772 unsigned int in_left;
0773 unsigned int out_left;
0774
0775
0776
0777
0778
0779
0780 find_best_seam(ctx, col,
0781 in_right, out_right,
0782 in_left_align, out_left_align,
0783 allow_in_overshoot ? 1 : 8 ,
0784 allow_out_overshoot ? 1 : out_width_align,
0785 ctx->downsize_coeff_h, ctx->image_resize_coeff_h,
0786 &in_left, &out_left);
0787
0788 if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
0789 flipped_out_left = resized_width - out_right;
0790 else
0791 flipped_out_left = out_left;
0792
0793 fill_tile_column(ctx, col, in, in_left, in_right - in_left,
0794 out, flipped_out_left, out_right - out_left);
0795
0796 dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col,
0797 in_left, in_right - in_left,
0798 flipped_out_left, out_right - out_left);
0799
0800 in_right = in_left;
0801 out_right = out_left;
0802 }
0803
0804 flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ?
0805 resized_width - out_right : 0;
0806
0807 fill_tile_column(ctx, 0, in, 0, in_right,
0808 out, flipped_out_left, out_right);
0809
0810 dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__,
0811 in_right, flipped_out_left, out_right);
0812
0813 for (row = in->num_rows - 1; row > 0; row--) {
0814 bool allow_overshoot = row < in->num_rows - 1;
0815 unsigned int in_top;
0816 unsigned int out_top;
0817
0818 find_best_seam(ctx, row,
0819 in_bottom, out_bottom,
0820 in_top_align, out_top_align,
0821 1, allow_overshoot ? 1 : out_height_align,
0822 ctx->downsize_coeff_v, ctx->image_resize_coeff_v,
0823 &in_top, &out_top);
0824
0825 if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^
0826 ipu_rot_mode_is_irt(ctx->rot_mode))
0827 flipped_out_top = resized_height - out_bottom;
0828 else
0829 flipped_out_top = out_top;
0830
0831 fill_tile_row(ctx, row, in, in_top, in_bottom - in_top,
0832 out, flipped_out_top, out_bottom - out_top);
0833
0834 dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row,
0835 in_top, in_bottom - in_top,
0836 flipped_out_top, out_bottom - out_top);
0837
0838 in_bottom = in_top;
0839 out_bottom = out_top;
0840 }
0841
0842 if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^
0843 ipu_rot_mode_is_irt(ctx->rot_mode))
0844 flipped_out_top = resized_height - out_bottom;
0845 else
0846 flipped_out_top = 0;
0847
0848 fill_tile_row(ctx, 0, in, 0, in_bottom,
0849 out, flipped_out_top, out_bottom);
0850
0851 dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__,
0852 in_bottom, flipped_out_top, out_bottom);
0853 }
0854
0855 static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx,
0856 struct ipu_image_convert_image *image)
0857 {
0858 struct ipu_image_convert_chan *chan = ctx->chan;
0859 struct ipu_image_convert_priv *priv = chan->priv;
0860 unsigned int max_width = 1024;
0861 unsigned int max_height = 1024;
0862 unsigned int i;
0863
0864 if (image->type == IMAGE_CONVERT_IN) {
0865
0866 max_width <<= ctx->downsize_coeff_h;
0867 max_height <<= ctx->downsize_coeff_v;
0868 }
0869
0870 for (i = 0; i < ctx->num_tiles; i++) {
0871 struct ipu_image_tile *tile;
0872 const unsigned int row = i / image->num_cols;
0873 const unsigned int col = i % image->num_cols;
0874
0875 if (image->type == IMAGE_CONVERT_OUT)
0876 tile = &image->tile[ctx->out_tile_map[i]];
0877 else
0878 tile = &image->tile[i];
0879
0880 tile->size = ((tile->height * image->fmt->bpp) >> 3) *
0881 tile->width;
0882
0883 if (image->fmt->planar) {
0884 tile->stride = tile->width;
0885 tile->rot_stride = tile->height;
0886 } else {
0887 tile->stride =
0888 (image->fmt->bpp * tile->width) >> 3;
0889 tile->rot_stride =
0890 (image->fmt->bpp * tile->height) >> 3;
0891 }
0892
0893 dev_dbg(priv->ipu->dev,
0894 "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n",
0895 chan->ic_task, ctx,
0896 image->type == IMAGE_CONVERT_IN ? "Input" : "Output",
0897 row, col,
0898 tile->width, tile->height, tile->left, tile->top);
0899
0900 if (!tile->width || tile->width > max_width ||
0901 !tile->height || tile->height > max_height) {
0902 dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n",
0903 image->type == IMAGE_CONVERT_IN ? "input" :
0904 "output", tile->width, tile->height);
0905 return -EINVAL;
0906 }
0907 }
0908
0909 return 0;
0910 }
0911
0912
0913
0914
0915
0916
0917
0918 static int transform_tile_index(struct ipu_image_convert_ctx *ctx,
0919 int src_row, int src_col)
0920 {
0921 struct ipu_image_convert_chan *chan = ctx->chan;
0922 struct ipu_image_convert_priv *priv = chan->priv;
0923 struct ipu_image_convert_image *s_image = &ctx->in;
0924 struct ipu_image_convert_image *d_image = &ctx->out;
0925 int dst_row, dst_col;
0926
0927
0928 if (ctx->rot_mode == IPU_ROTATE_NONE)
0929 return src_row * s_image->num_cols + src_col;
0930
0931
0932
0933
0934
0935 src_row = src_row * 2 - (s_image->num_rows - 1);
0936 src_col = src_col * 2 - (s_image->num_cols - 1);
0937
0938
0939 if (ctx->rot_mode & IPU_ROT_BIT_90) {
0940 dst_col = -src_row;
0941 dst_row = src_col;
0942 } else {
0943 dst_col = src_col;
0944 dst_row = src_row;
0945 }
0946
0947
0948 if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
0949 dst_col = -dst_col;
0950 if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
0951 dst_row = -dst_row;
0952
0953 dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n",
0954 chan->ic_task, ctx, src_col, src_row, dst_col, dst_row);
0955
0956
0957
0958
0959
0960 dst_row += d_image->num_rows - 1;
0961 dst_col += d_image->num_cols - 1;
0962 dst_row /= 2;
0963 dst_col /= 2;
0964
0965 return dst_row * d_image->num_cols + dst_col;
0966 }
0967
0968
0969
0970
0971 static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx)
0972 {
0973 struct ipu_image_convert_image *s_image = &ctx->in;
0974 unsigned int row, col, tile = 0;
0975
0976 for (row = 0; row < s_image->num_rows; row++) {
0977 for (col = 0; col < s_image->num_cols; col++) {
0978 ctx->out_tile_map[tile] =
0979 transform_tile_index(ctx, row, col);
0980 tile++;
0981 }
0982 }
0983 }
0984
0985 static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx,
0986 struct ipu_image_convert_image *image)
0987 {
0988 struct ipu_image_convert_chan *chan = ctx->chan;
0989 struct ipu_image_convert_priv *priv = chan->priv;
0990 const struct ipu_image_pixfmt *fmt = image->fmt;
0991 unsigned int row, col, tile = 0;
0992 u32 H, top, y_stride, uv_stride;
0993 u32 uv_row_off, uv_col_off, uv_off, u_off, v_off;
0994 u32 y_row_off, y_col_off, y_off;
0995 u32 y_size, uv_size;
0996
0997
0998 H = image->base.pix.height;
0999
1000 y_stride = image->stride;
1001 uv_stride = y_stride / fmt->uv_width_dec;
1002 if (fmt->uv_packed)
1003 uv_stride *= 2;
1004
1005 y_size = H * y_stride;
1006 uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
1007
1008 for (row = 0; row < image->num_rows; row++) {
1009 top = image->tile[tile].top;
1010 y_row_off = top * y_stride;
1011 uv_row_off = (top * uv_stride) / fmt->uv_height_dec;
1012
1013 for (col = 0; col < image->num_cols; col++) {
1014 y_col_off = image->tile[tile].left;
1015 uv_col_off = y_col_off / fmt->uv_width_dec;
1016 if (fmt->uv_packed)
1017 uv_col_off *= 2;
1018
1019 y_off = y_row_off + y_col_off;
1020 uv_off = uv_row_off + uv_col_off;
1021
1022 u_off = y_size - y_off + uv_off;
1023 v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
1024 if (fmt->uv_swapped)
1025 swap(u_off, v_off);
1026
1027 image->tile[tile].offset = y_off;
1028 image->tile[tile].u_off = u_off;
1029 image->tile[tile++].v_off = v_off;
1030
1031 if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) {
1032 dev_err(priv->ipu->dev,
1033 "task %u: ctx %p: %s@[%d,%d]: "
1034 "y_off %08x, u_off %08x, v_off %08x\n",
1035 chan->ic_task, ctx,
1036 image->type == IMAGE_CONVERT_IN ?
1037 "Input" : "Output", row, col,
1038 y_off, u_off, v_off);
1039 return -EINVAL;
1040 }
1041 }
1042 }
1043
1044 return 0;
1045 }
1046
1047 static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx,
1048 struct ipu_image_convert_image *image)
1049 {
1050 struct ipu_image_convert_chan *chan = ctx->chan;
1051 struct ipu_image_convert_priv *priv = chan->priv;
1052 const struct ipu_image_pixfmt *fmt = image->fmt;
1053 unsigned int row, col, tile = 0;
1054 u32 bpp, stride, offset;
1055 u32 row_off, col_off;
1056
1057
1058 stride = image->stride;
1059 bpp = fmt->bpp;
1060
1061 for (row = 0; row < image->num_rows; row++) {
1062 row_off = image->tile[tile].top * stride;
1063
1064 for (col = 0; col < image->num_cols; col++) {
1065 col_off = (image->tile[tile].left * bpp) >> 3;
1066
1067 offset = row_off + col_off;
1068
1069 image->tile[tile].offset = offset;
1070 image->tile[tile].u_off = 0;
1071 image->tile[tile++].v_off = 0;
1072
1073 if (offset & 0x7) {
1074 dev_err(priv->ipu->dev,
1075 "task %u: ctx %p: %s@[%d,%d]: "
1076 "phys %08x\n",
1077 chan->ic_task, ctx,
1078 image->type == IMAGE_CONVERT_IN ?
1079 "Input" : "Output", row, col,
1080 row_off + col_off);
1081 return -EINVAL;
1082 }
1083 }
1084 }
1085
1086 return 0;
1087 }
1088
1089 static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx,
1090 struct ipu_image_convert_image *image)
1091 {
1092 if (image->fmt->planar)
1093 return calc_tile_offsets_planar(ctx, image);
1094
1095 return calc_tile_offsets_packed(ctx, image);
1096 }
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107 static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff,
1108 u32 output_size, bool allow_overshoot)
1109 {
1110 u32 downsized = input_size >> downsize_coeff;
1111
1112 if (allow_overshoot)
1113 return DIV_ROUND_CLOSEST(8192 * downsized, output_size);
1114 else
1115 return 8192 * (downsized - 1) / (output_size - 1);
1116 }
1117
1118
1119
1120
1121
1122
1123
1124 static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx)
1125 {
1126 struct ipu_image_convert_chan *chan = ctx->chan;
1127 struct ipu_image_convert_priv *priv = chan->priv;
1128 struct ipu_image_tile *in_tile, *out_tile;
1129 unsigned int col, row, tile_idx;
1130 unsigned int last_output;
1131
1132 for (col = 0; col < ctx->in.num_cols; col++) {
1133 bool closest = (col < ctx->in.num_cols - 1) &&
1134 !(ctx->rot_mode & IPU_ROT_BIT_HFLIP);
1135 u32 resized_width;
1136 u32 resize_coeff_h;
1137 u32 in_width;
1138
1139 tile_idx = col;
1140 in_tile = &ctx->in.tile[tile_idx];
1141 out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];
1142
1143 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1144 resized_width = out_tile->height;
1145 else
1146 resized_width = out_tile->width;
1147
1148 resize_coeff_h = calc_resize_coeff(in_tile->width,
1149 ctx->downsize_coeff_h,
1150 resized_width, closest);
1151
1152 dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n",
1153 __func__, col, resize_coeff_h);
1154
1155
1156
1157
1158
1159 resized_width = round_up(resized_width, 8);
1160
1161
1162
1163
1164
1165
1166 last_output = resized_width - 1;
1167 if (closest && ((last_output * resize_coeff_h) % 8192))
1168 last_output++;
1169 in_width = round_up(
1170 (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1)
1171 << ctx->downsize_coeff_h, 8);
1172
1173 for (row = 0; row < ctx->in.num_rows; row++) {
1174 tile_idx = row * ctx->in.num_cols + col;
1175 in_tile = &ctx->in.tile[tile_idx];
1176 out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];
1177
1178 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1179 out_tile->height = resized_width;
1180 else
1181 out_tile->width = resized_width;
1182
1183 in_tile->width = in_width;
1184 }
1185
1186 ctx->resize_coeffs_h[col] = resize_coeff_h;
1187 }
1188
1189 for (row = 0; row < ctx->in.num_rows; row++) {
1190 bool closest = (row < ctx->in.num_rows - 1) &&
1191 !(ctx->rot_mode & IPU_ROT_BIT_VFLIP);
1192 u32 resized_height;
1193 u32 resize_coeff_v;
1194 u32 in_height;
1195
1196 tile_idx = row * ctx->in.num_cols;
1197 in_tile = &ctx->in.tile[tile_idx];
1198 out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];
1199
1200 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1201 resized_height = out_tile->width;
1202 else
1203 resized_height = out_tile->height;
1204
1205 resize_coeff_v = calc_resize_coeff(in_tile->height,
1206 ctx->downsize_coeff_v,
1207 resized_height, closest);
1208
1209 dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n",
1210 __func__, row, resize_coeff_v);
1211
1212
1213
1214
1215
1216 resized_height = round_up(resized_height, 2);
1217
1218
1219
1220
1221
1222
1223 last_output = resized_height - 1;
1224 if (closest && ((last_output * resize_coeff_v) % 8192))
1225 last_output++;
1226 in_height = round_up(
1227 (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1)
1228 << ctx->downsize_coeff_v, 2);
1229
1230 for (col = 0; col < ctx->in.num_cols; col++) {
1231 tile_idx = row * ctx->in.num_cols + col;
1232 in_tile = &ctx->in.tile[tile_idx];
1233 out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];
1234
1235 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1236 out_tile->width = resized_height;
1237 else
1238 out_tile->height = resized_height;
1239
1240 in_tile->height = in_height;
1241 }
1242
1243 ctx->resize_coeffs_v[row] = resize_coeff_v;
1244 }
1245 }
1246
1247
1248
1249
1250
1251 static int get_run_count(struct ipu_image_convert_ctx *ctx,
1252 struct list_head *q)
1253 {
1254 struct ipu_image_convert_run *run;
1255 int count = 0;
1256
1257 lockdep_assert_held(&ctx->chan->irqlock);
1258
1259 list_for_each_entry(run, q, list) {
1260 if (run->ctx == ctx)
1261 count++;
1262 }
1263
1264 return count;
1265 }
1266
1267 static void convert_stop(struct ipu_image_convert_run *run)
1268 {
1269 struct ipu_image_convert_ctx *ctx = run->ctx;
1270 struct ipu_image_convert_chan *chan = ctx->chan;
1271 struct ipu_image_convert_priv *priv = chan->priv;
1272
1273 dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n",
1274 __func__, chan->ic_task, ctx, run);
1275
1276
1277 ipu_ic_task_disable(chan->ic);
1278 ipu_idmac_disable_channel(chan->in_chan);
1279 ipu_idmac_disable_channel(chan->out_chan);
1280
1281 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
1282 ipu_idmac_disable_channel(chan->rotation_in_chan);
1283 ipu_idmac_disable_channel(chan->rotation_out_chan);
1284 ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan);
1285 }
1286
1287 ipu_ic_disable(chan->ic);
1288 }
1289
1290 static void init_idmac_channel(struct ipu_image_convert_ctx *ctx,
1291 struct ipuv3_channel *channel,
1292 struct ipu_image_convert_image *image,
1293 enum ipu_rotate_mode rot_mode,
1294 bool rot_swap_width_height,
1295 unsigned int tile)
1296 {
1297 struct ipu_image_convert_chan *chan = ctx->chan;
1298 unsigned int burst_size;
1299 u32 width, height, stride;
1300 dma_addr_t addr0, addr1 = 0;
1301 struct ipu_image tile_image;
1302 unsigned int tile_idx[2];
1303
1304 if (image->type == IMAGE_CONVERT_OUT) {
1305 tile_idx[0] = ctx->out_tile_map[tile];
1306 tile_idx[1] = ctx->out_tile_map[1];
1307 } else {
1308 tile_idx[0] = tile;
1309 tile_idx[1] = 1;
1310 }
1311
1312 if (rot_swap_width_height) {
1313 width = image->tile[tile_idx[0]].height;
1314 height = image->tile[tile_idx[0]].width;
1315 stride = image->tile[tile_idx[0]].rot_stride;
1316 addr0 = ctx->rot_intermediate[0].phys;
1317 if (ctx->double_buffering)
1318 addr1 = ctx->rot_intermediate[1].phys;
1319 } else {
1320 width = image->tile[tile_idx[0]].width;
1321 height = image->tile[tile_idx[0]].height;
1322 stride = image->stride;
1323 addr0 = image->base.phys0 +
1324 image->tile[tile_idx[0]].offset;
1325 if (ctx->double_buffering)
1326 addr1 = image->base.phys0 +
1327 image->tile[tile_idx[1]].offset;
1328 }
1329
1330 ipu_cpmem_zero(channel);
1331
1332 memset(&tile_image, 0, sizeof(tile_image));
1333 tile_image.pix.width = tile_image.rect.width = width;
1334 tile_image.pix.height = tile_image.rect.height = height;
1335 tile_image.pix.bytesperline = stride;
1336 tile_image.pix.pixelformat = image->fmt->fourcc;
1337 tile_image.phys0 = addr0;
1338 tile_image.phys1 = addr1;
1339 if (image->fmt->planar && !rot_swap_width_height) {
1340 tile_image.u_offset = image->tile[tile_idx[0]].u_off;
1341 tile_image.v_offset = image->tile[tile_idx[0]].v_off;
1342 }
1343
1344 ipu_cpmem_set_image(channel, &tile_image);
1345
1346 if (rot_mode)
1347 ipu_cpmem_set_rotation(channel, rot_mode);
1348
1349
1350
1351
1352
1353 if ((channel == chan->out_chan ||
1354 channel == chan->rotation_out_chan) &&
1355 image->fmt->planar && image->fmt->uv_height_dec == 2)
1356 ipu_cpmem_skip_odd_chroma_rows(channel);
1357
1358 if (channel == chan->rotation_in_chan ||
1359 channel == chan->rotation_out_chan) {
1360 burst_size = 8;
1361 ipu_cpmem_set_block_mode(channel);
1362 } else
1363 burst_size = (width % 16) ? 8 : 16;
1364
1365 ipu_cpmem_set_burstsize(channel, burst_size);
1366
1367 ipu_ic_task_idma_init(chan->ic, channel, width, height,
1368 burst_size, rot_mode);
1369
1370
1371
1372
1373
1374 if (!channel->ipu->prg_priv)
1375 ipu_cpmem_set_axi_id(channel, 1);
1376
1377 ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
1378 }
1379
1380 static int convert_start(struct ipu_image_convert_run *run, unsigned int tile)
1381 {
1382 struct ipu_image_convert_ctx *ctx = run->ctx;
1383 struct ipu_image_convert_chan *chan = ctx->chan;
1384 struct ipu_image_convert_priv *priv = chan->priv;
1385 struct ipu_image_convert_image *s_image = &ctx->in;
1386 struct ipu_image_convert_image *d_image = &ctx->out;
1387 unsigned int dst_tile = ctx->out_tile_map[tile];
1388 unsigned int dest_width, dest_height;
1389 unsigned int col, row;
1390 u32 rsc;
1391 int ret;
1392
1393 dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n",
1394 __func__, chan->ic_task, ctx, run, tile, dst_tile);
1395
1396
1397 ctx->eof_mask = 0;
1398
1399 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
1400
1401 dest_width = d_image->tile[dst_tile].height;
1402 dest_height = d_image->tile[dst_tile].width;
1403 } else {
1404 dest_width = d_image->tile[dst_tile].width;
1405 dest_height = d_image->tile[dst_tile].height;
1406 }
1407
1408 row = tile / s_image->num_cols;
1409 col = tile % s_image->num_cols;
1410
1411 rsc = (ctx->downsize_coeff_v << 30) |
1412 (ctx->resize_coeffs_v[row] << 16) |
1413 (ctx->downsize_coeff_h << 14) |
1414 (ctx->resize_coeffs_h[col]);
1415
1416 dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n",
1417 __func__, s_image->tile[tile].width,
1418 s_image->tile[tile].height, dest_width, dest_height, rsc);
1419
1420
1421 ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc,
1422 s_image->tile[tile].width,
1423 s_image->tile[tile].height,
1424 dest_width,
1425 dest_height,
1426 rsc);
1427 if (ret) {
1428 dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
1429 return ret;
1430 }
1431
1432
1433 init_idmac_channel(ctx, chan->in_chan, s_image,
1434 IPU_ROTATE_NONE, false, tile);
1435
1436 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
1437
1438 init_idmac_channel(ctx, chan->out_chan, d_image,
1439 IPU_ROTATE_NONE, true, tile);
1440
1441
1442 init_idmac_channel(ctx, chan->rotation_in_chan, d_image,
1443 ctx->rot_mode, true, tile);
1444
1445
1446 init_idmac_channel(ctx, chan->rotation_out_chan, d_image,
1447 IPU_ROTATE_NONE, false, tile);
1448
1449
1450 ipu_idmac_link(chan->out_chan, chan->rotation_in_chan);
1451 } else {
1452
1453 init_idmac_channel(ctx, chan->out_chan, d_image,
1454 ctx->rot_mode, false, tile);
1455 }
1456
1457
1458 ipu_ic_enable(chan->ic);
1459
1460
1461 ipu_idmac_select_buffer(chan->in_chan, 0);
1462 ipu_idmac_select_buffer(chan->out_chan, 0);
1463 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1464 ipu_idmac_select_buffer(chan->rotation_out_chan, 0);
1465 if (ctx->double_buffering) {
1466 ipu_idmac_select_buffer(chan->in_chan, 1);
1467 ipu_idmac_select_buffer(chan->out_chan, 1);
1468 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1469 ipu_idmac_select_buffer(chan->rotation_out_chan, 1);
1470 }
1471
1472
1473 ipu_idmac_enable_channel(chan->in_chan);
1474 ipu_idmac_enable_channel(chan->out_chan);
1475 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
1476 ipu_idmac_enable_channel(chan->rotation_in_chan);
1477 ipu_idmac_enable_channel(chan->rotation_out_chan);
1478 }
1479
1480 ipu_ic_task_enable(chan->ic);
1481
1482 ipu_cpmem_dump(chan->in_chan);
1483 ipu_cpmem_dump(chan->out_chan);
1484 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
1485 ipu_cpmem_dump(chan->rotation_in_chan);
1486 ipu_cpmem_dump(chan->rotation_out_chan);
1487 }
1488
1489 ipu_dump(priv->ipu);
1490
1491 return 0;
1492 }
1493
1494
1495 static int do_run(struct ipu_image_convert_run *run)
1496 {
1497 struct ipu_image_convert_ctx *ctx = run->ctx;
1498 struct ipu_image_convert_chan *chan = ctx->chan;
1499
1500 lockdep_assert_held(&chan->irqlock);
1501
1502 ctx->in.base.phys0 = run->in_phys;
1503 ctx->out.base.phys0 = run->out_phys;
1504
1505 ctx->cur_buf_num = 0;
1506 ctx->next_tile = 1;
1507
1508
1509 list_del(&run->list);
1510 chan->current_run = run;
1511
1512 return convert_start(run, 0);
1513 }
1514
1515
1516 static void run_next(struct ipu_image_convert_chan *chan)
1517 {
1518 struct ipu_image_convert_priv *priv = chan->priv;
1519 struct ipu_image_convert_run *run, *tmp;
1520 int ret;
1521
1522 lockdep_assert_held(&chan->irqlock);
1523
1524 list_for_each_entry_safe(run, tmp, &chan->pending_q, list) {
1525
1526 if (run->ctx->aborting) {
1527 dev_dbg(priv->ipu->dev,
1528 "%s: task %u: skipping aborting ctx %p run %p\n",
1529 __func__, chan->ic_task, run->ctx, run);
1530 continue;
1531 }
1532
1533 ret = do_run(run);
1534 if (!ret)
1535 break;
1536
1537
1538
1539
1540
1541
1542 run->status = ret;
1543 list_add_tail(&run->list, &chan->done_q);
1544 chan->current_run = NULL;
1545 }
1546 }
1547
1548 static void empty_done_q(struct ipu_image_convert_chan *chan)
1549 {
1550 struct ipu_image_convert_priv *priv = chan->priv;
1551 struct ipu_image_convert_run *run;
1552 unsigned long flags;
1553
1554 spin_lock_irqsave(&chan->irqlock, flags);
1555
1556 while (!list_empty(&chan->done_q)) {
1557 run = list_entry(chan->done_q.next,
1558 struct ipu_image_convert_run,
1559 list);
1560
1561 list_del(&run->list);
1562
1563 dev_dbg(priv->ipu->dev,
1564 "%s: task %u: completing ctx %p run %p with %d\n",
1565 __func__, chan->ic_task, run->ctx, run, run->status);
1566
1567
1568 spin_unlock_irqrestore(&chan->irqlock, flags);
1569 run->ctx->complete(run, run->ctx->complete_context);
1570 spin_lock_irqsave(&chan->irqlock, flags);
1571 }
1572
1573 spin_unlock_irqrestore(&chan->irqlock, flags);
1574 }
1575
1576
1577
1578
1579
1580 static irqreturn_t do_bh(int irq, void *dev_id)
1581 {
1582 struct ipu_image_convert_chan *chan = dev_id;
1583 struct ipu_image_convert_priv *priv = chan->priv;
1584 struct ipu_image_convert_ctx *ctx;
1585 unsigned long flags;
1586
1587 dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__,
1588 chan->ic_task);
1589
1590 empty_done_q(chan);
1591
1592 spin_lock_irqsave(&chan->irqlock, flags);
1593
1594
1595
1596
1597
1598 list_for_each_entry(ctx, &chan->ctx_list, list) {
1599 if (ctx->aborting) {
1600 dev_dbg(priv->ipu->dev,
1601 "%s: task %u: signaling abort for ctx %p\n",
1602 __func__, chan->ic_task, ctx);
1603 complete_all(&ctx->aborted);
1604 }
1605 }
1606
1607 spin_unlock_irqrestore(&chan->irqlock, flags);
1608
1609 dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__,
1610 chan->ic_task);
1611
1612 return IRQ_HANDLED;
1613 }
1614
1615 static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx)
1616 {
1617 unsigned int cur_tile = ctx->next_tile - 1;
1618 unsigned int next_tile = ctx->next_tile;
1619
1620 if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] !=
1621 ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] ||
1622 ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] !=
1623 ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] ||
1624 ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width ||
1625 ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height ||
1626 ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width ||
1627 ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height)
1628 return true;
1629
1630 return false;
1631 }
1632
1633
1634 static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run)
1635 {
1636 struct ipu_image_convert_ctx *ctx = run->ctx;
1637 struct ipu_image_convert_chan *chan = ctx->chan;
1638 struct ipu_image_tile *src_tile, *dst_tile;
1639 struct ipu_image_convert_image *s_image = &ctx->in;
1640 struct ipu_image_convert_image *d_image = &ctx->out;
1641 struct ipuv3_channel *outch;
1642 unsigned int dst_idx;
1643
1644 lockdep_assert_held(&chan->irqlock);
1645
1646 outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
1647 chan->rotation_out_chan : chan->out_chan;
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657 if (ctx->aborting && !ctx->double_buffering) {
1658 convert_stop(run);
1659 run->status = -EIO;
1660 goto done;
1661 }
1662
1663 if (ctx->next_tile == ctx->num_tiles) {
1664
1665
1666
1667 convert_stop(run);
1668 run->status = 0;
1669 goto done;
1670 }
1671
1672
1673
1674
1675 if (!ctx->double_buffering) {
1676 if (ic_settings_changed(ctx)) {
1677 convert_stop(run);
1678 convert_start(run, ctx->next_tile);
1679 } else {
1680 src_tile = &s_image->tile[ctx->next_tile];
1681 dst_idx = ctx->out_tile_map[ctx->next_tile];
1682 dst_tile = &d_image->tile[dst_idx];
1683
1684 ipu_cpmem_set_buffer(chan->in_chan, 0,
1685 s_image->base.phys0 +
1686 src_tile->offset);
1687 ipu_cpmem_set_buffer(outch, 0,
1688 d_image->base.phys0 +
1689 dst_tile->offset);
1690 if (s_image->fmt->planar)
1691 ipu_cpmem_set_uv_offset(chan->in_chan,
1692 src_tile->u_off,
1693 src_tile->v_off);
1694 if (d_image->fmt->planar)
1695 ipu_cpmem_set_uv_offset(outch,
1696 dst_tile->u_off,
1697 dst_tile->v_off);
1698
1699 ipu_idmac_select_buffer(chan->in_chan, 0);
1700 ipu_idmac_select_buffer(outch, 0);
1701 }
1702 } else if (ctx->next_tile < ctx->num_tiles - 1) {
1703
1704 src_tile = &s_image->tile[ctx->next_tile + 1];
1705 dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
1706 dst_tile = &d_image->tile[dst_idx];
1707
1708 ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num,
1709 s_image->base.phys0 + src_tile->offset);
1710 ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
1711 d_image->base.phys0 + dst_tile->offset);
1712
1713 ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num);
1714 ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
1715
1716 ctx->cur_buf_num ^= 1;
1717 }
1718
1719 ctx->eof_mask = 0;
1720 ctx->next_tile++;
1721 return IRQ_HANDLED;
1722 done:
1723 list_add_tail(&run->list, &chan->done_q);
1724 chan->current_run = NULL;
1725 run_next(chan);
1726 return IRQ_WAKE_THREAD;
1727 }
1728
1729 static irqreturn_t eof_irq(int irq, void *data)
1730 {
1731 struct ipu_image_convert_chan *chan = data;
1732 struct ipu_image_convert_priv *priv = chan->priv;
1733 struct ipu_image_convert_ctx *ctx;
1734 struct ipu_image_convert_run *run;
1735 irqreturn_t ret = IRQ_HANDLED;
1736 bool tile_complete = false;
1737 unsigned long flags;
1738
1739 spin_lock_irqsave(&chan->irqlock, flags);
1740
1741
1742 run = chan->current_run;
1743 if (!run) {
1744 ret = IRQ_NONE;
1745 goto out;
1746 }
1747
1748 ctx = run->ctx;
1749
1750 if (irq == chan->in_eof_irq) {
1751 ctx->eof_mask |= EOF_IRQ_IN;
1752 } else if (irq == chan->out_eof_irq) {
1753 ctx->eof_mask |= EOF_IRQ_OUT;
1754 } else if (irq == chan->rot_in_eof_irq ||
1755 irq == chan->rot_out_eof_irq) {
1756 if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
1757
1758 dev_err(priv->ipu->dev,
1759 "Unexpected rotation interrupt\n");
1760 goto out;
1761 }
1762 ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ?
1763 EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT;
1764 } else {
1765 dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq);
1766 ret = IRQ_NONE;
1767 goto out;
1768 }
1769
1770 if (ipu_rot_mode_is_irt(ctx->rot_mode))
1771 tile_complete = (ctx->eof_mask == EOF_IRQ_ROT_COMPLETE);
1772 else
1773 tile_complete = (ctx->eof_mask == EOF_IRQ_COMPLETE);
1774
1775 if (tile_complete)
1776 ret = do_tile_complete(run);
1777 out:
1778 spin_unlock_irqrestore(&chan->irqlock, flags);
1779 return ret;
1780 }
1781
1782
1783
1784
1785
1786 static void force_abort(struct ipu_image_convert_ctx *ctx)
1787 {
1788 struct ipu_image_convert_chan *chan = ctx->chan;
1789 struct ipu_image_convert_run *run;
1790 unsigned long flags;
1791
1792 spin_lock_irqsave(&chan->irqlock, flags);
1793
1794 run = chan->current_run;
1795 if (run && run->ctx == ctx) {
1796 convert_stop(run);
1797 run->status = -EIO;
1798 list_add_tail(&run->list, &chan->done_q);
1799 chan->current_run = NULL;
1800 run_next(chan);
1801 }
1802
1803 spin_unlock_irqrestore(&chan->irqlock, flags);
1804
1805 empty_done_q(chan);
1806 }
1807
1808 static void release_ipu_resources(struct ipu_image_convert_chan *chan)
1809 {
1810 if (chan->in_eof_irq >= 0)
1811 free_irq(chan->in_eof_irq, chan);
1812 if (chan->rot_in_eof_irq >= 0)
1813 free_irq(chan->rot_in_eof_irq, chan);
1814 if (chan->out_eof_irq >= 0)
1815 free_irq(chan->out_eof_irq, chan);
1816 if (chan->rot_out_eof_irq >= 0)
1817 free_irq(chan->rot_out_eof_irq, chan);
1818
1819 if (!IS_ERR_OR_NULL(chan->in_chan))
1820 ipu_idmac_put(chan->in_chan);
1821 if (!IS_ERR_OR_NULL(chan->out_chan))
1822 ipu_idmac_put(chan->out_chan);
1823 if (!IS_ERR_OR_NULL(chan->rotation_in_chan))
1824 ipu_idmac_put(chan->rotation_in_chan);
1825 if (!IS_ERR_OR_NULL(chan->rotation_out_chan))
1826 ipu_idmac_put(chan->rotation_out_chan);
1827 if (!IS_ERR_OR_NULL(chan->ic))
1828 ipu_ic_put(chan->ic);
1829
1830 chan->in_chan = chan->out_chan = chan->rotation_in_chan =
1831 chan->rotation_out_chan = NULL;
1832 chan->in_eof_irq = -1;
1833 chan->rot_in_eof_irq = -1;
1834 chan->out_eof_irq = -1;
1835 chan->rot_out_eof_irq = -1;
1836 }
1837
1838 static int get_eof_irq(struct ipu_image_convert_chan *chan,
1839 struct ipuv3_channel *channel)
1840 {
1841 struct ipu_image_convert_priv *priv = chan->priv;
1842 int ret, irq;
1843
1844 irq = ipu_idmac_channel_irq(priv->ipu, channel, IPU_IRQ_EOF);
1845
1846 ret = request_threaded_irq(irq, eof_irq, do_bh, 0, "ipu-ic", chan);
1847 if (ret < 0) {
1848 dev_err(priv->ipu->dev, "could not acquire irq %d\n", irq);
1849 return ret;
1850 }
1851
1852 return irq;
1853 }
1854
1855 static int get_ipu_resources(struct ipu_image_convert_chan *chan)
1856 {
1857 const struct ipu_image_convert_dma_chan *dma = chan->dma_ch;
1858 struct ipu_image_convert_priv *priv = chan->priv;
1859 int ret;
1860
1861
1862 chan->ic = ipu_ic_get(priv->ipu, chan->ic_task);
1863 if (IS_ERR(chan->ic)) {
1864 dev_err(priv->ipu->dev, "could not acquire IC\n");
1865 ret = PTR_ERR(chan->ic);
1866 goto err;
1867 }
1868
1869
1870 chan->in_chan = ipu_idmac_get(priv->ipu, dma->in);
1871 chan->out_chan = ipu_idmac_get(priv->ipu, dma->out);
1872 if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) {
1873 dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
1874 ret = -EBUSY;
1875 goto err;
1876 }
1877
1878 chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in);
1879 chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out);
1880 if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) {
1881 dev_err(priv->ipu->dev,
1882 "could not acquire idmac rotation channels\n");
1883 ret = -EBUSY;
1884 goto err;
1885 }
1886
1887
1888 ret = get_eof_irq(chan, chan->in_chan);
1889 if (ret < 0) {
1890 chan->in_eof_irq = -1;
1891 goto err;
1892 }
1893 chan->in_eof_irq = ret;
1894
1895 ret = get_eof_irq(chan, chan->rotation_in_chan);
1896 if (ret < 0) {
1897 chan->rot_in_eof_irq = -1;
1898 goto err;
1899 }
1900 chan->rot_in_eof_irq = ret;
1901
1902 ret = get_eof_irq(chan, chan->out_chan);
1903 if (ret < 0) {
1904 chan->out_eof_irq = -1;
1905 goto err;
1906 }
1907 chan->out_eof_irq = ret;
1908
1909 ret = get_eof_irq(chan, chan->rotation_out_chan);
1910 if (ret < 0) {
1911 chan->rot_out_eof_irq = -1;
1912 goto err;
1913 }
1914 chan->rot_out_eof_irq = ret;
1915
1916 return 0;
1917 err:
1918 release_ipu_resources(chan);
1919 return ret;
1920 }
1921
1922 static int fill_image(struct ipu_image_convert_ctx *ctx,
1923 struct ipu_image_convert_image *ic_image,
1924 struct ipu_image *image,
1925 enum ipu_image_convert_type type)
1926 {
1927 struct ipu_image_convert_priv *priv = ctx->chan->priv;
1928
1929 ic_image->base = *image;
1930 ic_image->type = type;
1931
1932 ic_image->fmt = get_format(image->pix.pixelformat);
1933 if (!ic_image->fmt) {
1934 dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
1935 type == IMAGE_CONVERT_OUT ? "Output" : "Input");
1936 return -EINVAL;
1937 }
1938
1939 if (ic_image->fmt->planar)
1940 ic_image->stride = ic_image->base.pix.width;
1941 else
1942 ic_image->stride = ic_image->base.pix.bytesperline;
1943
1944 return 0;
1945 }
1946
1947
1948 static unsigned int clamp_align(unsigned int x, unsigned int min,
1949 unsigned int max, unsigned int align)
1950 {
1951
1952 unsigned int mask = ~((1 << align) - 1);
1953
1954
1955 x = clamp(x, (min + ~mask) & mask, max & mask);
1956
1957
1958 if (align)
1959 x = (x + (1 << (align - 1))) & mask;
1960
1961 return x;
1962 }
1963
1964
1965 void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
1966 enum ipu_rotate_mode rot_mode)
1967 {
1968 const struct ipu_image_pixfmt *infmt, *outfmt;
1969 u32 w_align_out, h_align_out;
1970 u32 w_align_in, h_align_in;
1971
1972 infmt = get_format(in->pix.pixelformat);
1973 outfmt = get_format(out->pix.pixelformat);
1974
1975
1976 if (!infmt) {
1977 in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
1978 infmt = get_format(V4L2_PIX_FMT_RGB24);
1979 }
1980 if (!outfmt) {
1981 out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
1982 outfmt = get_format(V4L2_PIX_FMT_RGB24);
1983 }
1984
1985
1986 in->pix.field = out->pix.field = V4L2_FIELD_NONE;
1987
1988
1989 if (ipu_rot_mode_is_irt(rot_mode)) {
1990 out->pix.height = max_t(__u32, out->pix.height,
1991 in->pix.width / 4);
1992 out->pix.width = max_t(__u32, out->pix.width,
1993 in->pix.height / 4);
1994 } else {
1995 out->pix.width = max_t(__u32, out->pix.width,
1996 in->pix.width / 4);
1997 out->pix.height = max_t(__u32, out->pix.height,
1998 in->pix.height / 4);
1999 }
2000
2001
2002 w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt,
2003 rot_mode));
2004 h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt,
2005 rot_mode));
2006 in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W,
2007 w_align_in);
2008 in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H,
2009 h_align_in);
2010
2011
2012 w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt,
2013 rot_mode));
2014 h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt,
2015 rot_mode));
2016 out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W,
2017 w_align_out);
2018 out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H,
2019 h_align_out);
2020
2021
2022 in->pix.bytesperline = infmt->planar ?
2023 clamp_align(in->pix.width, 2 << w_align_in, MAX_W,
2024 w_align_in) :
2025 clamp_align((in->pix.width * infmt->bpp) >> 3,
2026 ((2 << w_align_in) * infmt->bpp) >> 3,
2027 (MAX_W * infmt->bpp) >> 3,
2028 w_align_in);
2029 in->pix.sizeimage = infmt->planar ?
2030 (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 :
2031 in->pix.height * in->pix.bytesperline;
2032 out->pix.bytesperline = outfmt->planar ? out->pix.width :
2033 (out->pix.width * outfmt->bpp) >> 3;
2034 out->pix.sizeimage = outfmt->planar ?
2035 (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 :
2036 out->pix.height * out->pix.bytesperline;
2037 }
2038 EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
2039
2040
2041
2042
2043
2044
2045 int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
2046 enum ipu_rotate_mode rot_mode)
2047 {
2048 struct ipu_image testin, testout;
2049
2050 testin = *in;
2051 testout = *out;
2052
2053 ipu_image_convert_adjust(&testin, &testout, rot_mode);
2054
2055 if (testin.pix.width != in->pix.width ||
2056 testin.pix.height != in->pix.height ||
2057 testout.pix.width != out->pix.width ||
2058 testout.pix.height != out->pix.height)
2059 return -EINVAL;
2060
2061 return 0;
2062 }
2063 EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
2064
2065
2066
2067
2068
2069 struct ipu_image_convert_ctx *
2070 ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
2071 struct ipu_image *in, struct ipu_image *out,
2072 enum ipu_rotate_mode rot_mode,
2073 ipu_image_convert_cb_t complete,
2074 void *complete_context)
2075 {
2076 struct ipu_image_convert_priv *priv = ipu->image_convert_priv;
2077 struct ipu_image_convert_image *s_image, *d_image;
2078 struct ipu_image_convert_chan *chan;
2079 struct ipu_image_convert_ctx *ctx;
2080 unsigned long flags;
2081 unsigned int i;
2082 bool get_res;
2083 int ret;
2084
2085 if (!in || !out || !complete ||
2086 (ic_task != IC_TASK_VIEWFINDER &&
2087 ic_task != IC_TASK_POST_PROCESSOR))
2088 return ERR_PTR(-EINVAL);
2089
2090
2091 ret = ipu_image_convert_verify(in, out, rot_mode);
2092 if (ret) {
2093 dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
2094 __func__);
2095 return ERR_PTR(ret);
2096 }
2097
2098 chan = &priv->chan[ic_task];
2099
2100 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
2101 if (!ctx)
2102 return ERR_PTR(-ENOMEM);
2103
2104 dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__,
2105 chan->ic_task, ctx);
2106
2107 ctx->chan = chan;
2108 init_completion(&ctx->aborted);
2109
2110 ctx->rot_mode = rot_mode;
2111
2112
2113 ret = calc_image_resize_coefficients(ctx, in, out);
2114 if (ret)
2115 goto out_free;
2116
2117 s_image = &ctx->in;
2118 d_image = &ctx->out;
2119
2120
2121 if (ipu_rot_mode_is_irt(rot_mode)) {
2122 d_image->num_rows = s_image->num_cols;
2123 d_image->num_cols = s_image->num_rows;
2124 } else {
2125 d_image->num_rows = s_image->num_rows;
2126 d_image->num_cols = s_image->num_cols;
2127 }
2128
2129 ctx->num_tiles = d_image->num_cols * d_image->num_rows;
2130
2131 ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
2132 if (ret)
2133 goto out_free;
2134 ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
2135 if (ret)
2136 goto out_free;
2137
2138 calc_out_tile_map(ctx);
2139
2140 find_seams(ctx, s_image, d_image);
2141
2142 ret = calc_tile_dimensions(ctx, s_image);
2143 if (ret)
2144 goto out_free;
2145
2146 ret = calc_tile_offsets(ctx, s_image);
2147 if (ret)
2148 goto out_free;
2149
2150 calc_tile_dimensions(ctx, d_image);
2151 ret = calc_tile_offsets(ctx, d_image);
2152 if (ret)
2153 goto out_free;
2154
2155 calc_tile_resize_coefficients(ctx);
2156
2157 ret = ipu_ic_calc_csc(&ctx->csc,
2158 s_image->base.pix.ycbcr_enc,
2159 s_image->base.pix.quantization,
2160 ipu_pixelformat_to_colorspace(s_image->fmt->fourcc),
2161 d_image->base.pix.ycbcr_enc,
2162 d_image->base.pix.quantization,
2163 ipu_pixelformat_to_colorspace(d_image->fmt->fourcc));
2164 if (ret)
2165 goto out_free;
2166
2167 dump_format(ctx, s_image);
2168 dump_format(ctx, d_image);
2169
2170 ctx->complete = complete;
2171 ctx->complete_context = complete_context;
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186 ctx->double_buffering = (ctx->num_tiles > 1 &&
2187 !s_image->fmt->planar &&
2188 !d_image->fmt->planar);
2189 for (i = 1; i < ctx->num_tiles; i++) {
2190 if (ctx->in.tile[i].width != ctx->in.tile[0].width ||
2191 ctx->in.tile[i].height != ctx->in.tile[0].height ||
2192 ctx->out.tile[i].width != ctx->out.tile[0].width ||
2193 ctx->out.tile[i].height != ctx->out.tile[0].height) {
2194 ctx->double_buffering = false;
2195 break;
2196 }
2197 }
2198 for (i = 1; i < ctx->in.num_cols; i++) {
2199 if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) {
2200 ctx->double_buffering = false;
2201 break;
2202 }
2203 }
2204 for (i = 1; i < ctx->in.num_rows; i++) {
2205 if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) {
2206 ctx->double_buffering = false;
2207 break;
2208 }
2209 }
2210
2211 if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
2212 unsigned long intermediate_size = d_image->tile[0].size;
2213
2214 for (i = 1; i < ctx->num_tiles; i++) {
2215 if (d_image->tile[i].size > intermediate_size)
2216 intermediate_size = d_image->tile[i].size;
2217 }
2218
2219 ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0],
2220 intermediate_size);
2221 if (ret)
2222 goto out_free;
2223 if (ctx->double_buffering) {
2224 ret = alloc_dma_buf(priv,
2225 &ctx->rot_intermediate[1],
2226 intermediate_size);
2227 if (ret)
2228 goto out_free_dmabuf0;
2229 }
2230 }
2231
2232 spin_lock_irqsave(&chan->irqlock, flags);
2233
2234 get_res = list_empty(&chan->ctx_list);
2235
2236 list_add_tail(&ctx->list, &chan->ctx_list);
2237
2238 spin_unlock_irqrestore(&chan->irqlock, flags);
2239
2240 if (get_res) {
2241 ret = get_ipu_resources(chan);
2242 if (ret)
2243 goto out_free_dmabuf1;
2244 }
2245
2246 return ctx;
2247
2248 out_free_dmabuf1:
2249 free_dma_buf(priv, &ctx->rot_intermediate[1]);
2250 spin_lock_irqsave(&chan->irqlock, flags);
2251 list_del(&ctx->list);
2252 spin_unlock_irqrestore(&chan->irqlock, flags);
2253 out_free_dmabuf0:
2254 free_dma_buf(priv, &ctx->rot_intermediate[0]);
2255 out_free:
2256 kfree(ctx);
2257 return ERR_PTR(ret);
2258 }
2259 EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
2260
2261
2262
2263
2264
2265
2266 int ipu_image_convert_queue(struct ipu_image_convert_run *run)
2267 {
2268 struct ipu_image_convert_chan *chan;
2269 struct ipu_image_convert_priv *priv;
2270 struct ipu_image_convert_ctx *ctx;
2271 unsigned long flags;
2272 int ret = 0;
2273
2274 if (!run || !run->ctx || !run->in_phys || !run->out_phys)
2275 return -EINVAL;
2276
2277 ctx = run->ctx;
2278 chan = ctx->chan;
2279 priv = chan->priv;
2280
2281 dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__,
2282 chan->ic_task, ctx, run);
2283
2284 INIT_LIST_HEAD(&run->list);
2285
2286 spin_lock_irqsave(&chan->irqlock, flags);
2287
2288 if (ctx->aborting) {
2289 ret = -EIO;
2290 goto unlock;
2291 }
2292
2293 list_add_tail(&run->list, &chan->pending_q);
2294
2295 if (!chan->current_run) {
2296 ret = do_run(run);
2297 if (ret)
2298 chan->current_run = NULL;
2299 }
2300 unlock:
2301 spin_unlock_irqrestore(&chan->irqlock, flags);
2302 return ret;
2303 }
2304 EXPORT_SYMBOL_GPL(ipu_image_convert_queue);
2305
2306
2307 static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx)
2308 {
2309 struct ipu_image_convert_chan *chan = ctx->chan;
2310 struct ipu_image_convert_priv *priv = chan->priv;
2311 struct ipu_image_convert_run *run, *active_run, *tmp;
2312 unsigned long flags;
2313 int run_count, ret;
2314
2315 spin_lock_irqsave(&chan->irqlock, flags);
2316
2317
2318 list_for_each_entry_safe(run, tmp, &chan->pending_q, list) {
2319 if (run->ctx != ctx)
2320 continue;
2321 run->status = -EIO;
2322 list_move_tail(&run->list, &chan->done_q);
2323 }
2324
2325 run_count = get_run_count(ctx, &chan->done_q);
2326 active_run = (chan->current_run && chan->current_run->ctx == ctx) ?
2327 chan->current_run : NULL;
2328
2329 if (active_run)
2330 reinit_completion(&ctx->aborted);
2331
2332 ctx->aborting = true;
2333
2334 spin_unlock_irqrestore(&chan->irqlock, flags);
2335
2336 if (!run_count && !active_run) {
2337 dev_dbg(priv->ipu->dev,
2338 "%s: task %u: no abort needed for ctx %p\n",
2339 __func__, chan->ic_task, ctx);
2340 return;
2341 }
2342
2343 if (!active_run) {
2344 empty_done_q(chan);
2345 return;
2346 }
2347
2348 dev_dbg(priv->ipu->dev,
2349 "%s: task %u: wait for completion: %d runs\n",
2350 __func__, chan->ic_task, run_count);
2351
2352 ret = wait_for_completion_timeout(&ctx->aborted,
2353 msecs_to_jiffies(10000));
2354 if (ret == 0) {
2355 dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
2356 force_abort(ctx);
2357 }
2358 }
2359
2360 void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx)
2361 {
2362 __ipu_image_convert_abort(ctx);
2363 ctx->aborting = false;
2364 }
2365 EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
2366
2367
2368 void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx)
2369 {
2370 struct ipu_image_convert_chan *chan = ctx->chan;
2371 struct ipu_image_convert_priv *priv = chan->priv;
2372 unsigned long flags;
2373 bool put_res;
2374
2375
2376 __ipu_image_convert_abort(ctx);
2377
2378 dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__,
2379 chan->ic_task, ctx);
2380
2381 spin_lock_irqsave(&chan->irqlock, flags);
2382
2383 list_del(&ctx->list);
2384
2385 put_res = list_empty(&chan->ctx_list);
2386
2387 spin_unlock_irqrestore(&chan->irqlock, flags);
2388
2389 if (put_res)
2390 release_ipu_resources(chan);
2391
2392 free_dma_buf(priv, &ctx->rot_intermediate[1]);
2393 free_dma_buf(priv, &ctx->rot_intermediate[0]);
2394
2395 kfree(ctx);
2396 }
2397 EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
2398
2399
2400
2401
2402
2403
2404 struct ipu_image_convert_run *
2405 ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
2406 struct ipu_image *in, struct ipu_image *out,
2407 enum ipu_rotate_mode rot_mode,
2408 ipu_image_convert_cb_t complete,
2409 void *complete_context)
2410 {
2411 struct ipu_image_convert_ctx *ctx;
2412 struct ipu_image_convert_run *run;
2413 int ret;
2414
2415 ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode,
2416 complete, complete_context);
2417 if (IS_ERR(ctx))
2418 return ERR_CAST(ctx);
2419
2420 run = kzalloc(sizeof(*run), GFP_KERNEL);
2421 if (!run) {
2422 ipu_image_convert_unprepare(ctx);
2423 return ERR_PTR(-ENOMEM);
2424 }
2425
2426 run->ctx = ctx;
2427 run->in_phys = in->phys0;
2428 run->out_phys = out->phys0;
2429
2430 ret = ipu_image_convert_queue(run);
2431 if (ret) {
2432 ipu_image_convert_unprepare(ctx);
2433 kfree(run);
2434 return ERR_PTR(ret);
2435 }
2436
2437 return run;
2438 }
2439 EXPORT_SYMBOL_GPL(ipu_image_convert);
2440
2441
2442 static void image_convert_sync_complete(struct ipu_image_convert_run *run,
2443 void *data)
2444 {
2445 struct completion *comp = data;
2446
2447 complete(comp);
2448 }
2449
2450 int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
2451 struct ipu_image *in, struct ipu_image *out,
2452 enum ipu_rotate_mode rot_mode)
2453 {
2454 struct ipu_image_convert_run *run;
2455 struct completion comp;
2456 int ret;
2457
2458 init_completion(&comp);
2459
2460 run = ipu_image_convert(ipu, ic_task, in, out, rot_mode,
2461 image_convert_sync_complete, &comp);
2462 if (IS_ERR(run))
2463 return PTR_ERR(run);
2464
2465 ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
2466 ret = (ret == 0) ? -ETIMEDOUT : 0;
2467
2468 ipu_image_convert_unprepare(run->ctx);
2469 kfree(run);
2470
2471 return ret;
2472 }
2473 EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
2474
2475 int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev)
2476 {
2477 struct ipu_image_convert_priv *priv;
2478 int i;
2479
2480 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2481 if (!priv)
2482 return -ENOMEM;
2483
2484 ipu->image_convert_priv = priv;
2485 priv->ipu = ipu;
2486
2487 for (i = 0; i < IC_NUM_TASKS; i++) {
2488 struct ipu_image_convert_chan *chan = &priv->chan[i];
2489
2490 chan->ic_task = i;
2491 chan->priv = priv;
2492 chan->dma_ch = &image_convert_dma_chan[i];
2493 chan->in_eof_irq = -1;
2494 chan->rot_in_eof_irq = -1;
2495 chan->out_eof_irq = -1;
2496 chan->rot_out_eof_irq = -1;
2497
2498 spin_lock_init(&chan->irqlock);
2499 INIT_LIST_HEAD(&chan->ctx_list);
2500 INIT_LIST_HEAD(&chan->pending_q);
2501 INIT_LIST_HEAD(&chan->done_q);
2502 }
2503
2504 return 0;
2505 }
2506
2507 void ipu_image_convert_exit(struct ipu_soc *ipu)
2508 {
2509 }