18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012-2016 Mentor Graphics Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Queued image conversion support, with tiling and rotation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <video/imx-ipu-image-convert.h> 118c2ecf20Sopenharmony_ci#include "ipu-prv.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * The IC Resizer has a restriction that the output frame from the 158c2ecf20Sopenharmony_ci * resizer must be 1024 or less in both width (pixels) and height 168c2ecf20Sopenharmony_ci * (lines). 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * The image converter attempts to split up a conversion when 198c2ecf20Sopenharmony_ci * the desired output (converted) frame resolution exceeds the 208c2ecf20Sopenharmony_ci * IC resizer limit of 1024 in either dimension. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * If either dimension of the output frame exceeds the limit, the 238c2ecf20Sopenharmony_ci * dimension is split into 1, 2, or 4 equal stripes, for a maximum 248c2ecf20Sopenharmony_ci * of 4*4 or 16 tiles. A conversion is then carried out for each 258c2ecf20Sopenharmony_ci * tile (but taking care to pass the full frame stride length to 268c2ecf20Sopenharmony_ci * the DMA channel's parameter memory!). IDMA double-buffering is used 278c2ecf20Sopenharmony_ci * to convert each tile back-to-back when possible (see note below 288c2ecf20Sopenharmony_ci * when double_buffering boolean is set). 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Note that the input frame must be split up into the same number 318c2ecf20Sopenharmony_ci * of tiles as the output frame: 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * +---------+-----+ 348c2ecf20Sopenharmony_ci * +-----+---+ | A | B | 358c2ecf20Sopenharmony_ci * | A | B | | | | 368c2ecf20Sopenharmony_ci * +-----+---+ --> +---------+-----+ 378c2ecf20Sopenharmony_ci * | C | D | | C | D | 388c2ecf20Sopenharmony_ci * +-----+---+ | | | 398c2ecf20Sopenharmony_ci * +---------+-----+ 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Clockwise 90° rotations are handled by first rescaling into a 428c2ecf20Sopenharmony_ci * reusable temporary tile buffer and then rotating with the 8x8 438c2ecf20Sopenharmony_ci * block rotator, writing to the correct destination: 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * +-----+-----+ 468c2ecf20Sopenharmony_ci * | | | 478c2ecf20Sopenharmony_ci * +-----+---+ +---------+ | C | A | 488c2ecf20Sopenharmony_ci * | A | B | | A,B, | | | | | 498c2ecf20Sopenharmony_ci * +-----+---+ --> | C,D | | --> | | | 508c2ecf20Sopenharmony_ci * | C | D | +---------+ +-----+-----+ 518c2ecf20Sopenharmony_ci * +-----+---+ | D | B | 528c2ecf20Sopenharmony_ci * | | | 538c2ecf20Sopenharmony_ci * +-----+-----+ 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * If the 8x8 block rotator is used, horizontal or vertical flipping 568c2ecf20Sopenharmony_ci * is done during the rotation step, otherwise flipping is done 578c2ecf20Sopenharmony_ci * during the scaling step. 588c2ecf20Sopenharmony_ci * With rotation or flipping, tile order changes between input and 598c2ecf20Sopenharmony_ci * output image. Tiles are numbered row major from top left to bottom 608c2ecf20Sopenharmony_ci * right for both input and output image. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define MAX_STRIPES_W 4 648c2ecf20Sopenharmony_ci#define MAX_STRIPES_H 4 658c2ecf20Sopenharmony_ci#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define MIN_W 16 688c2ecf20Sopenharmony_ci#define MIN_H 8 698c2ecf20Sopenharmony_ci#define MAX_W 4096 708c2ecf20Sopenharmony_ci#define MAX_H 4096 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cienum ipu_image_convert_type { 738c2ecf20Sopenharmony_ci IMAGE_CONVERT_IN = 0, 748c2ecf20Sopenharmony_ci IMAGE_CONVERT_OUT, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct ipu_image_convert_dma_buf { 788c2ecf20Sopenharmony_ci void *virt; 798c2ecf20Sopenharmony_ci dma_addr_t phys; 808c2ecf20Sopenharmony_ci unsigned long len; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct ipu_image_convert_dma_chan { 848c2ecf20Sopenharmony_ci int in; 858c2ecf20Sopenharmony_ci int out; 868c2ecf20Sopenharmony_ci int rot_in; 878c2ecf20Sopenharmony_ci int rot_out; 888c2ecf20Sopenharmony_ci int vdi_in_p; 898c2ecf20Sopenharmony_ci int vdi_in; 908c2ecf20Sopenharmony_ci int vdi_in_n; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* dimensions of one tile */ 948c2ecf20Sopenharmony_cistruct ipu_image_tile { 958c2ecf20Sopenharmony_ci u32 width; 968c2ecf20Sopenharmony_ci u32 height; 978c2ecf20Sopenharmony_ci u32 left; 988c2ecf20Sopenharmony_ci u32 top; 998c2ecf20Sopenharmony_ci /* size and strides are in bytes */ 1008c2ecf20Sopenharmony_ci u32 size; 1018c2ecf20Sopenharmony_ci u32 stride; 1028c2ecf20Sopenharmony_ci u32 rot_stride; 1038c2ecf20Sopenharmony_ci /* start Y or packed offset of this tile */ 1048c2ecf20Sopenharmony_ci u32 offset; 1058c2ecf20Sopenharmony_ci /* offset from start to tile in U plane, for planar formats */ 1068c2ecf20Sopenharmony_ci u32 u_off; 1078c2ecf20Sopenharmony_ci /* offset from start to tile in V plane, for planar formats */ 1088c2ecf20Sopenharmony_ci u32 v_off; 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct ipu_image_convert_image { 1128c2ecf20Sopenharmony_ci struct ipu_image base; 1138c2ecf20Sopenharmony_ci enum ipu_image_convert_type type; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *fmt; 1168c2ecf20Sopenharmony_ci unsigned int stride; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* # of rows (horizontal stripes) if dest height is > 1024 */ 1198c2ecf20Sopenharmony_ci unsigned int num_rows; 1208c2ecf20Sopenharmony_ci /* # of columns (vertical stripes) if dest width is > 1024 */ 1218c2ecf20Sopenharmony_ci unsigned int num_cols; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci struct ipu_image_tile tile[MAX_TILES]; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct ipu_image_pixfmt { 1278c2ecf20Sopenharmony_ci u32 fourcc; /* V4L2 fourcc */ 1288c2ecf20Sopenharmony_ci int bpp; /* total bpp */ 1298c2ecf20Sopenharmony_ci int uv_width_dec; /* decimation in width for U/V planes */ 1308c2ecf20Sopenharmony_ci int uv_height_dec; /* decimation in height for U/V planes */ 1318c2ecf20Sopenharmony_ci bool planar; /* planar format */ 1328c2ecf20Sopenharmony_ci bool uv_swapped; /* U and V planes are swapped */ 1338c2ecf20Sopenharmony_ci bool uv_packed; /* partial planar (U and V in same plane) */ 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistruct ipu_image_convert_ctx; 1378c2ecf20Sopenharmony_cistruct ipu_image_convert_chan; 1388c2ecf20Sopenharmony_cistruct ipu_image_convert_priv; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cienum eof_irq_mask { 1418c2ecf20Sopenharmony_ci EOF_IRQ_IN = BIT(0), 1428c2ecf20Sopenharmony_ci EOF_IRQ_ROT_IN = BIT(1), 1438c2ecf20Sopenharmony_ci EOF_IRQ_OUT = BIT(2), 1448c2ecf20Sopenharmony_ci EOF_IRQ_ROT_OUT = BIT(3), 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT) 1488c2ecf20Sopenharmony_ci#define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \ 1498c2ecf20Sopenharmony_ci EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT) 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct ipu_image_convert_ctx { 1528c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ipu_image_convert_cb_t complete; 1558c2ecf20Sopenharmony_ci void *complete_context; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Source/destination image data and rotation mode */ 1588c2ecf20Sopenharmony_ci struct ipu_image_convert_image in; 1598c2ecf20Sopenharmony_ci struct ipu_image_convert_image out; 1608c2ecf20Sopenharmony_ci struct ipu_ic_csc csc; 1618c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode; 1628c2ecf20Sopenharmony_ci u32 downsize_coeff_h; 1638c2ecf20Sopenharmony_ci u32 downsize_coeff_v; 1648c2ecf20Sopenharmony_ci u32 image_resize_coeff_h; 1658c2ecf20Sopenharmony_ci u32 image_resize_coeff_v; 1668c2ecf20Sopenharmony_ci u32 resize_coeffs_h[MAX_STRIPES_W]; 1678c2ecf20Sopenharmony_ci u32 resize_coeffs_v[MAX_STRIPES_H]; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* intermediate buffer for rotation */ 1708c2ecf20Sopenharmony_ci struct ipu_image_convert_dma_buf rot_intermediate[2]; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* current buffer number for double buffering */ 1738c2ecf20Sopenharmony_ci int cur_buf_num; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci bool aborting; 1768c2ecf20Sopenharmony_ci struct completion aborted; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* can we use double-buffering for this conversion operation? */ 1798c2ecf20Sopenharmony_ci bool double_buffering; 1808c2ecf20Sopenharmony_ci /* num_rows * num_cols */ 1818c2ecf20Sopenharmony_ci unsigned int num_tiles; 1828c2ecf20Sopenharmony_ci /* next tile to process */ 1838c2ecf20Sopenharmony_ci unsigned int next_tile; 1848c2ecf20Sopenharmony_ci /* where to place converted tile in dest image */ 1858c2ecf20Sopenharmony_ci unsigned int out_tile_map[MAX_TILES]; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* mask of completed EOF irqs at every tile conversion */ 1888c2ecf20Sopenharmony_ci enum eof_irq_mask eof_mask; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci struct list_head list; 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistruct ipu_image_convert_chan { 1948c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci enum ipu_ic_task ic_task; 1978c2ecf20Sopenharmony_ci const struct ipu_image_convert_dma_chan *dma_ch; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci struct ipu_ic *ic; 2008c2ecf20Sopenharmony_ci struct ipuv3_channel *in_chan; 2018c2ecf20Sopenharmony_ci struct ipuv3_channel *out_chan; 2028c2ecf20Sopenharmony_ci struct ipuv3_channel *rotation_in_chan; 2038c2ecf20Sopenharmony_ci struct ipuv3_channel *rotation_out_chan; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* the IPU end-of-frame irqs */ 2068c2ecf20Sopenharmony_ci int in_eof_irq; 2078c2ecf20Sopenharmony_ci int rot_in_eof_irq; 2088c2ecf20Sopenharmony_ci int out_eof_irq; 2098c2ecf20Sopenharmony_ci int rot_out_eof_irq; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci spinlock_t irqlock; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* list of convert contexts */ 2148c2ecf20Sopenharmony_ci struct list_head ctx_list; 2158c2ecf20Sopenharmony_ci /* queue of conversion runs */ 2168c2ecf20Sopenharmony_ci struct list_head pending_q; 2178c2ecf20Sopenharmony_ci /* queue of completed runs */ 2188c2ecf20Sopenharmony_ci struct list_head done_q; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* the current conversion run */ 2218c2ecf20Sopenharmony_ci struct ipu_image_convert_run *current_run; 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistruct ipu_image_convert_priv { 2258c2ecf20Sopenharmony_ci struct ipu_image_convert_chan chan[IC_NUM_TASKS]; 2268c2ecf20Sopenharmony_ci struct ipu_soc *ipu; 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic const struct ipu_image_convert_dma_chan 2308c2ecf20Sopenharmony_ciimage_convert_dma_chan[IC_NUM_TASKS] = { 2318c2ecf20Sopenharmony_ci [IC_TASK_VIEWFINDER] = { 2328c2ecf20Sopenharmony_ci .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, 2338c2ecf20Sopenharmony_ci .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, 2348c2ecf20Sopenharmony_ci .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, 2358c2ecf20Sopenharmony_ci .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, 2368c2ecf20Sopenharmony_ci .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, 2378c2ecf20Sopenharmony_ci .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, 2388c2ecf20Sopenharmony_ci .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci [IC_TASK_POST_PROCESSOR] = { 2418c2ecf20Sopenharmony_ci .in = IPUV3_CHANNEL_MEM_IC_PP, 2428c2ecf20Sopenharmony_ci .out = IPUV3_CHANNEL_IC_PP_MEM, 2438c2ecf20Sopenharmony_ci .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, 2448c2ecf20Sopenharmony_ci .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, 2458c2ecf20Sopenharmony_ci }, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const struct ipu_image_pixfmt image_convert_formats[] = { 2498c2ecf20Sopenharmony_ci { 2508c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 2518c2ecf20Sopenharmony_ci .bpp = 16, 2528c2ecf20Sopenharmony_ci }, { 2538c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB24, 2548c2ecf20Sopenharmony_ci .bpp = 24, 2558c2ecf20Sopenharmony_ci }, { 2568c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR24, 2578c2ecf20Sopenharmony_ci .bpp = 24, 2588c2ecf20Sopenharmony_ci }, { 2598c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB32, 2608c2ecf20Sopenharmony_ci .bpp = 32, 2618c2ecf20Sopenharmony_ci }, { 2628c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR32, 2638c2ecf20Sopenharmony_ci .bpp = 32, 2648c2ecf20Sopenharmony_ci }, { 2658c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_XRGB32, 2668c2ecf20Sopenharmony_ci .bpp = 32, 2678c2ecf20Sopenharmony_ci }, { 2688c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_XBGR32, 2698c2ecf20Sopenharmony_ci .bpp = 32, 2708c2ecf20Sopenharmony_ci }, { 2718c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGRX32, 2728c2ecf20Sopenharmony_ci .bpp = 32, 2738c2ecf20Sopenharmony_ci }, { 2748c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGBX32, 2758c2ecf20Sopenharmony_ci .bpp = 32, 2768c2ecf20Sopenharmony_ci }, { 2778c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 2788c2ecf20Sopenharmony_ci .bpp = 16, 2798c2ecf20Sopenharmony_ci .uv_width_dec = 2, 2808c2ecf20Sopenharmony_ci .uv_height_dec = 1, 2818c2ecf20Sopenharmony_ci }, { 2828c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 2838c2ecf20Sopenharmony_ci .bpp = 16, 2848c2ecf20Sopenharmony_ci .uv_width_dec = 2, 2858c2ecf20Sopenharmony_ci .uv_height_dec = 1, 2868c2ecf20Sopenharmony_ci }, { 2878c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUV420, 2888c2ecf20Sopenharmony_ci .bpp = 12, 2898c2ecf20Sopenharmony_ci .planar = true, 2908c2ecf20Sopenharmony_ci .uv_width_dec = 2, 2918c2ecf20Sopenharmony_ci .uv_height_dec = 2, 2928c2ecf20Sopenharmony_ci }, { 2938c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YVU420, 2948c2ecf20Sopenharmony_ci .bpp = 12, 2958c2ecf20Sopenharmony_ci .planar = true, 2968c2ecf20Sopenharmony_ci .uv_width_dec = 2, 2978c2ecf20Sopenharmony_ci .uv_height_dec = 2, 2988c2ecf20Sopenharmony_ci .uv_swapped = true, 2998c2ecf20Sopenharmony_ci }, { 3008c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV12, 3018c2ecf20Sopenharmony_ci .bpp = 12, 3028c2ecf20Sopenharmony_ci .planar = true, 3038c2ecf20Sopenharmony_ci .uv_width_dec = 2, 3048c2ecf20Sopenharmony_ci .uv_height_dec = 2, 3058c2ecf20Sopenharmony_ci .uv_packed = true, 3068c2ecf20Sopenharmony_ci }, { 3078c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUV422P, 3088c2ecf20Sopenharmony_ci .bpp = 16, 3098c2ecf20Sopenharmony_ci .planar = true, 3108c2ecf20Sopenharmony_ci .uv_width_dec = 2, 3118c2ecf20Sopenharmony_ci .uv_height_dec = 1, 3128c2ecf20Sopenharmony_ci }, { 3138c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV16, 3148c2ecf20Sopenharmony_ci .bpp = 16, 3158c2ecf20Sopenharmony_ci .planar = true, 3168c2ecf20Sopenharmony_ci .uv_width_dec = 2, 3178c2ecf20Sopenharmony_ci .uv_height_dec = 1, 3188c2ecf20Sopenharmony_ci .uv_packed = true, 3198c2ecf20Sopenharmony_ci }, 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct ipu_image_pixfmt *get_format(u32 fourcc) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *ret = NULL; 3258c2ecf20Sopenharmony_ci unsigned int i; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { 3288c2ecf20Sopenharmony_ci if (image_convert_formats[i].fourcc == fourcc) { 3298c2ecf20Sopenharmony_ci ret = &image_convert_formats[i]; 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic void dump_format(struct ipu_image_convert_ctx *ctx, 3388c2ecf20Sopenharmony_ci struct ipu_image_convert_image *ic_image) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 3418c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 3448c2ecf20Sopenharmony_ci "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", 3458c2ecf20Sopenharmony_ci chan->ic_task, ctx, 3468c2ecf20Sopenharmony_ci ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", 3478c2ecf20Sopenharmony_ci ic_image->base.pix.width, ic_image->base.pix.height, 3488c2ecf20Sopenharmony_ci ic_image->num_cols, ic_image->num_rows, 3498c2ecf20Sopenharmony_ci ic_image->fmt->fourcc & 0xff, 3508c2ecf20Sopenharmony_ci (ic_image->fmt->fourcc >> 8) & 0xff, 3518c2ecf20Sopenharmony_ci (ic_image->fmt->fourcc >> 16) & 0xff, 3528c2ecf20Sopenharmony_ci (ic_image->fmt->fourcc >> 24) & 0xff); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciint ipu_image_convert_enum_format(int index, u32 *fourcc) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *fmt; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (index >= (int)ARRAY_SIZE(image_convert_formats)) 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Format found */ 3638c2ecf20Sopenharmony_ci fmt = &image_convert_formats[index]; 3648c2ecf20Sopenharmony_ci *fourcc = fmt->fourcc; 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void free_dma_buf(struct ipu_image_convert_priv *priv, 3708c2ecf20Sopenharmony_ci struct ipu_image_convert_dma_buf *buf) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci if (buf->virt) 3738c2ecf20Sopenharmony_ci dma_free_coherent(priv->ipu->dev, 3748c2ecf20Sopenharmony_ci buf->len, buf->virt, buf->phys); 3758c2ecf20Sopenharmony_ci buf->virt = NULL; 3768c2ecf20Sopenharmony_ci buf->phys = 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int alloc_dma_buf(struct ipu_image_convert_priv *priv, 3808c2ecf20Sopenharmony_ci struct ipu_image_convert_dma_buf *buf, 3818c2ecf20Sopenharmony_ci int size) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci buf->len = PAGE_ALIGN(size); 3848c2ecf20Sopenharmony_ci buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, 3858c2ecf20Sopenharmony_ci GFP_DMA | GFP_KERNEL); 3868c2ecf20Sopenharmony_ci if (!buf->virt) { 3878c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); 3888c2ecf20Sopenharmony_ci return -ENOMEM; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic inline int num_stripes(int dim) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci return (dim - 1) / 1024 + 1; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* 4008c2ecf20Sopenharmony_ci * Calculate downsizing coefficients, which are the same for all tiles, 4018c2ecf20Sopenharmony_ci * and initial bilinear resizing coefficients, which are used to find the 4028c2ecf20Sopenharmony_ci * best seam positions. 4038c2ecf20Sopenharmony_ci * Also determine the number of tiles necessary to guarantee that no tile 4048c2ecf20Sopenharmony_ci * is larger than 1024 pixels in either dimension at the output and between 4058c2ecf20Sopenharmony_ci * IC downsizing and main processing sections. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, 4088c2ecf20Sopenharmony_ci struct ipu_image *in, 4098c2ecf20Sopenharmony_ci struct ipu_image *out) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci u32 downsized_width = in->rect.width; 4128c2ecf20Sopenharmony_ci u32 downsized_height = in->rect.height; 4138c2ecf20Sopenharmony_ci u32 downsize_coeff_v = 0; 4148c2ecf20Sopenharmony_ci u32 downsize_coeff_h = 0; 4158c2ecf20Sopenharmony_ci u32 resized_width = out->rect.width; 4168c2ecf20Sopenharmony_ci u32 resized_height = out->rect.height; 4178c2ecf20Sopenharmony_ci u32 resize_coeff_h; 4188c2ecf20Sopenharmony_ci u32 resize_coeff_v; 4198c2ecf20Sopenharmony_ci u32 cols; 4208c2ecf20Sopenharmony_ci u32 rows; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 4238c2ecf20Sopenharmony_ci resized_width = out->rect.height; 4248c2ecf20Sopenharmony_ci resized_height = out->rect.width; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Do not let invalid input lead to an endless loop below */ 4288c2ecf20Sopenharmony_ci if (WARN_ON(resized_width == 0 || resized_height == 0)) 4298c2ecf20Sopenharmony_ci return -EINVAL; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci while (downsized_width >= resized_width * 2) { 4328c2ecf20Sopenharmony_ci downsized_width >>= 1; 4338c2ecf20Sopenharmony_ci downsize_coeff_h++; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci while (downsized_height >= resized_height * 2) { 4378c2ecf20Sopenharmony_ci downsized_height >>= 1; 4388c2ecf20Sopenharmony_ci downsize_coeff_v++; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* 4428c2ecf20Sopenharmony_ci * Calculate the bilinear resizing coefficients that could be used if 4438c2ecf20Sopenharmony_ci * we were converting with a single tile. The bottom right output pixel 4448c2ecf20Sopenharmony_ci * should sample as close as possible to the bottom right input pixel 4458c2ecf20Sopenharmony_ci * out of the decimator, but not overshoot it: 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); 4488c2ecf20Sopenharmony_ci resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * Both the output of the IC downsizing section before being passed to 4528c2ecf20Sopenharmony_ci * the IC main processing section and the final output of the IC main 4538c2ecf20Sopenharmony_ci * processing section must be <= 1024 pixels in both dimensions. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci cols = num_stripes(max_t(u32, downsized_width, resized_width)); 4568c2ecf20Sopenharmony_ci rows = num_stripes(max_t(u32, downsized_height, resized_height)); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci dev_dbg(ctx->chan->priv->ipu->dev, 4598c2ecf20Sopenharmony_ci "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", 4608c2ecf20Sopenharmony_ci __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, 4618c2ecf20Sopenharmony_ci resize_coeff_v, cols, rows); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || 4648c2ecf20Sopenharmony_ci resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) 4658c2ecf20Sopenharmony_ci return -EINVAL; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ctx->downsize_coeff_h = downsize_coeff_h; 4688c2ecf20Sopenharmony_ci ctx->downsize_coeff_v = downsize_coeff_v; 4698c2ecf20Sopenharmony_ci ctx->image_resize_coeff_h = resize_coeff_h; 4708c2ecf20Sopenharmony_ci ctx->image_resize_coeff_v = resize_coeff_v; 4718c2ecf20Sopenharmony_ci ctx->in.num_cols = cols; 4728c2ecf20Sopenharmony_ci ctx->in.num_rows = rows; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci#define round_closest(x, y) round_down((x) + (y)/2, (y)) 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* 4808c2ecf20Sopenharmony_ci * Find the best aligned seam position for the given column / row index. 4818c2ecf20Sopenharmony_ci * Rotation and image offsets are out of scope. 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * @index: column / row index, used to calculate valid interval 4848c2ecf20Sopenharmony_ci * @in_edge: input right / bottom edge 4858c2ecf20Sopenharmony_ci * @out_edge: output right / bottom edge 4868c2ecf20Sopenharmony_ci * @in_align: input alignment, either horizontal 8-byte line start address 4878c2ecf20Sopenharmony_ci * alignment, or pixel alignment due to image format 4888c2ecf20Sopenharmony_ci * @out_align: output alignment, either horizontal 8-byte line start address 4898c2ecf20Sopenharmony_ci * alignment, or pixel alignment due to image format or rotator 4908c2ecf20Sopenharmony_ci * block size 4918c2ecf20Sopenharmony_ci * @in_burst: horizontal input burst size in case of horizontal flip 4928c2ecf20Sopenharmony_ci * @out_burst: horizontal output burst size or rotator block size 4938c2ecf20Sopenharmony_ci * @downsize_coeff: downsizing section coefficient 4948c2ecf20Sopenharmony_ci * @resize_coeff: main processing section resizing coefficient 4958c2ecf20Sopenharmony_ci * @_in_seam: aligned input seam position return value 4968c2ecf20Sopenharmony_ci * @_out_seam: aligned output seam position return value 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_cistatic void find_best_seam(struct ipu_image_convert_ctx *ctx, 4998c2ecf20Sopenharmony_ci unsigned int index, 5008c2ecf20Sopenharmony_ci unsigned int in_edge, 5018c2ecf20Sopenharmony_ci unsigned int out_edge, 5028c2ecf20Sopenharmony_ci unsigned int in_align, 5038c2ecf20Sopenharmony_ci unsigned int out_align, 5048c2ecf20Sopenharmony_ci unsigned int in_burst, 5058c2ecf20Sopenharmony_ci unsigned int out_burst, 5068c2ecf20Sopenharmony_ci unsigned int downsize_coeff, 5078c2ecf20Sopenharmony_ci unsigned int resize_coeff, 5088c2ecf20Sopenharmony_ci u32 *_in_seam, 5098c2ecf20Sopenharmony_ci u32 *_out_seam) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct device *dev = ctx->chan->priv->ipu->dev; 5128c2ecf20Sopenharmony_ci unsigned int out_pos; 5138c2ecf20Sopenharmony_ci /* Input / output seam position candidates */ 5148c2ecf20Sopenharmony_ci unsigned int out_seam = 0; 5158c2ecf20Sopenharmony_ci unsigned int in_seam = 0; 5168c2ecf20Sopenharmony_ci unsigned int min_diff = UINT_MAX; 5178c2ecf20Sopenharmony_ci unsigned int out_start; 5188c2ecf20Sopenharmony_ci unsigned int out_end; 5198c2ecf20Sopenharmony_ci unsigned int in_start; 5208c2ecf20Sopenharmony_ci unsigned int in_end; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Start within 1024 pixels of the right / bottom edge */ 5238c2ecf20Sopenharmony_ci out_start = max_t(int, index * out_align, out_edge - 1024); 5248c2ecf20Sopenharmony_ci /* End before having to add more columns to the left / rows above */ 5258c2ecf20Sopenharmony_ci out_end = min_t(unsigned int, out_edge, index * 1024 + 1); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* 5288c2ecf20Sopenharmony_ci * Limit input seam position to make sure that the downsized input tile 5298c2ecf20Sopenharmony_ci * to the right or bottom does not exceed 1024 pixels. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci in_start = max_t(int, index * in_align, 5328c2ecf20Sopenharmony_ci in_edge - (1024 << downsize_coeff)); 5338c2ecf20Sopenharmony_ci in_end = min_t(unsigned int, in_edge, 5348c2ecf20Sopenharmony_ci index * (1024 << downsize_coeff) + 1); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * Output tiles must start at a multiple of 8 bytes horizontally and 5388c2ecf20Sopenharmony_ci * possibly at an even line horizontally depending on the pixel format. 5398c2ecf20Sopenharmony_ci * Only consider output aligned positions for the seam. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci out_start = round_up(out_start, out_align); 5428c2ecf20Sopenharmony_ci for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { 5438c2ecf20Sopenharmony_ci unsigned int in_pos; 5448c2ecf20Sopenharmony_ci unsigned int in_pos_aligned; 5458c2ecf20Sopenharmony_ci unsigned int in_pos_rounded; 5468c2ecf20Sopenharmony_ci unsigned int abs_diff; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * Tiles in the right row / bottom column may not be allowed to 5508c2ecf20Sopenharmony_ci * overshoot horizontally / vertically. out_burst may be the 5518c2ecf20Sopenharmony_ci * actual DMA burst size, or the rotator block size. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci if ((out_burst > 1) && (out_edge - out_pos) % out_burst) 5548c2ecf20Sopenharmony_ci continue; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* 5578c2ecf20Sopenharmony_ci * Input sample position, corresponding to out_pos, 19.13 fixed 5588c2ecf20Sopenharmony_ci * point. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci in_pos = (out_pos * resize_coeff) << downsize_coeff; 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * The closest input sample position that we could actually 5638c2ecf20Sopenharmony_ci * start the input tile at, 19.13 fixed point. 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci in_pos_aligned = round_closest(in_pos, 8192U * in_align); 5668c2ecf20Sopenharmony_ci /* Convert 19.13 fixed point to integer */ 5678c2ecf20Sopenharmony_ci in_pos_rounded = in_pos_aligned / 8192U; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (in_pos_rounded < in_start) 5708c2ecf20Sopenharmony_ci continue; 5718c2ecf20Sopenharmony_ci if (in_pos_rounded >= in_end) 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if ((in_burst > 1) && 5758c2ecf20Sopenharmony_ci (in_edge - in_pos_rounded) % in_burst) 5768c2ecf20Sopenharmony_ci continue; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (in_pos < in_pos_aligned) 5798c2ecf20Sopenharmony_ci abs_diff = in_pos_aligned - in_pos; 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci abs_diff = in_pos - in_pos_aligned; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (abs_diff < min_diff) { 5848c2ecf20Sopenharmony_ci in_seam = in_pos_rounded; 5858c2ecf20Sopenharmony_ci out_seam = out_pos; 5868c2ecf20Sopenharmony_ci min_diff = abs_diff; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci *_out_seam = out_seam; 5918c2ecf20Sopenharmony_ci *_in_seam = in_seam; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", 5948c2ecf20Sopenharmony_ci __func__, out_seam, out_align, out_start, out_end, 5958c2ecf20Sopenharmony_ci in_seam, in_align, in_start, in_end, min_diff / 8192, 5968c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* 6008c2ecf20Sopenharmony_ci * Tile left edges are required to be aligned to multiples of 8 bytes 6018c2ecf20Sopenharmony_ci * by the IDMAC. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cistatic inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci if (fmt->planar) 6068c2ecf20Sopenharmony_ci return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; 6078c2ecf20Sopenharmony_ci else 6088c2ecf20Sopenharmony_ci return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/* 6128c2ecf20Sopenharmony_ci * Tile top edge alignment is only limited by chroma subsampling. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_cistatic inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci return fmt->uv_height_dec > 1 ? 2 : 1; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic inline u32 tile_width_align(enum ipu_image_convert_type type, 6208c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *fmt, 6218c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci if (type == IMAGE_CONVERT_IN) { 6248c2ecf20Sopenharmony_ci /* 6258c2ecf20Sopenharmony_ci * The IC burst reads 8 pixels at a time. Reading beyond the 6268c2ecf20Sopenharmony_ci * end of the line is usually acceptable. Those pixels are 6278c2ecf20Sopenharmony_ci * ignored, unless the IC has to write the scaled line in 6288c2ecf20Sopenharmony_ci * reverse. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci return (!ipu_rot_mode_is_irt(rot_mode) && 6318c2ecf20Sopenharmony_ci (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled 6368c2ecf20Sopenharmony_ci * formats to guarantee 8-byte aligned line start addresses in the 6378c2ecf20Sopenharmony_ci * chroma planes when IRT is used. Align to 8x8 pixel IRT block size 6388c2ecf20Sopenharmony_ci * for all other formats. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci return (ipu_rot_mode_is_irt(rot_mode) && 6418c2ecf20Sopenharmony_ci fmt->planar && !fmt->uv_packed) ? 6428c2ecf20Sopenharmony_ci 8 * fmt->uv_width_dec : 8; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic inline u32 tile_height_align(enum ipu_image_convert_type type, 6468c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *fmt, 6478c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) 6508c2ecf20Sopenharmony_ci return 2; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* 6538c2ecf20Sopenharmony_ci * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled 6548c2ecf20Sopenharmony_ci * formats to guarantee 8-byte aligned line start addresses in the 6558c2ecf20Sopenharmony_ci * chroma planes when IRT is used. Align to 8x8 pixel IRT block size 6568c2ecf20Sopenharmony_ci * for all other formats. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/* 6628c2ecf20Sopenharmony_ci * Fill in left position and width and for all tiles in an input column, and 6638c2ecf20Sopenharmony_ci * for all corresponding output tiles. If the 90° rotator is used, the output 6648c2ecf20Sopenharmony_ci * tiles are in a row, and output tile top position and height are set. 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_cistatic void fill_tile_column(struct ipu_image_convert_ctx *ctx, 6678c2ecf20Sopenharmony_ci unsigned int col, 6688c2ecf20Sopenharmony_ci struct ipu_image_convert_image *in, 6698c2ecf20Sopenharmony_ci unsigned int in_left, unsigned int in_width, 6708c2ecf20Sopenharmony_ci struct ipu_image_convert_image *out, 6718c2ecf20Sopenharmony_ci unsigned int out_left, unsigned int out_width) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci unsigned int row, tile_idx; 6748c2ecf20Sopenharmony_ci struct ipu_image_tile *in_tile, *out_tile; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci for (row = 0; row < in->num_rows; row++) { 6778c2ecf20Sopenharmony_ci tile_idx = in->num_cols * row + col; 6788c2ecf20Sopenharmony_ci in_tile = &in->tile[tile_idx]; 6798c2ecf20Sopenharmony_ci out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci in_tile->left = in_left; 6828c2ecf20Sopenharmony_ci in_tile->width = in_width; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 6858c2ecf20Sopenharmony_ci out_tile->top = out_left; 6868c2ecf20Sopenharmony_ci out_tile->height = out_width; 6878c2ecf20Sopenharmony_ci } else { 6888c2ecf20Sopenharmony_ci out_tile->left = out_left; 6898c2ecf20Sopenharmony_ci out_tile->width = out_width; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci/* 6958c2ecf20Sopenharmony_ci * Fill in top position and height and for all tiles in an input row, and 6968c2ecf20Sopenharmony_ci * for all corresponding output tiles. If the 90° rotator is used, the output 6978c2ecf20Sopenharmony_ci * tiles are in a column, and output tile left position and width are set. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_cistatic void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, 7008c2ecf20Sopenharmony_ci struct ipu_image_convert_image *in, 7018c2ecf20Sopenharmony_ci unsigned int in_top, unsigned int in_height, 7028c2ecf20Sopenharmony_ci struct ipu_image_convert_image *out, 7038c2ecf20Sopenharmony_ci unsigned int out_top, unsigned int out_height) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci unsigned int col, tile_idx; 7068c2ecf20Sopenharmony_ci struct ipu_image_tile *in_tile, *out_tile; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci for (col = 0; col < in->num_cols; col++) { 7098c2ecf20Sopenharmony_ci tile_idx = in->num_cols * row + col; 7108c2ecf20Sopenharmony_ci in_tile = &in->tile[tile_idx]; 7118c2ecf20Sopenharmony_ci out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci in_tile->top = in_top; 7148c2ecf20Sopenharmony_ci in_tile->height = in_height; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 7178c2ecf20Sopenharmony_ci out_tile->left = out_top; 7188c2ecf20Sopenharmony_ci out_tile->width = out_height; 7198c2ecf20Sopenharmony_ci } else { 7208c2ecf20Sopenharmony_ci out_tile->top = out_top; 7218c2ecf20Sopenharmony_ci out_tile->height = out_height; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/* 7278c2ecf20Sopenharmony_ci * Find the best horizontal and vertical seam positions to split into tiles. 7288c2ecf20Sopenharmony_ci * Minimize the fractional part of the input sampling position for the 7298c2ecf20Sopenharmony_ci * top / left pixels of each tile. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_cistatic void find_seams(struct ipu_image_convert_ctx *ctx, 7328c2ecf20Sopenharmony_ci struct ipu_image_convert_image *in, 7338c2ecf20Sopenharmony_ci struct ipu_image_convert_image *out) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct device *dev = ctx->chan->priv->ipu->dev; 7368c2ecf20Sopenharmony_ci unsigned int resized_width = out->base.rect.width; 7378c2ecf20Sopenharmony_ci unsigned int resized_height = out->base.rect.height; 7388c2ecf20Sopenharmony_ci unsigned int col; 7398c2ecf20Sopenharmony_ci unsigned int row; 7408c2ecf20Sopenharmony_ci unsigned int in_left_align = tile_left_align(in->fmt); 7418c2ecf20Sopenharmony_ci unsigned int in_top_align = tile_top_align(in->fmt); 7428c2ecf20Sopenharmony_ci unsigned int out_left_align = tile_left_align(out->fmt); 7438c2ecf20Sopenharmony_ci unsigned int out_top_align = tile_top_align(out->fmt); 7448c2ecf20Sopenharmony_ci unsigned int out_width_align = tile_width_align(out->type, out->fmt, 7458c2ecf20Sopenharmony_ci ctx->rot_mode); 7468c2ecf20Sopenharmony_ci unsigned int out_height_align = tile_height_align(out->type, out->fmt, 7478c2ecf20Sopenharmony_ci ctx->rot_mode); 7488c2ecf20Sopenharmony_ci unsigned int in_right = in->base.rect.width; 7498c2ecf20Sopenharmony_ci unsigned int in_bottom = in->base.rect.height; 7508c2ecf20Sopenharmony_ci unsigned int out_right = out->base.rect.width; 7518c2ecf20Sopenharmony_ci unsigned int out_bottom = out->base.rect.height; 7528c2ecf20Sopenharmony_ci unsigned int flipped_out_left; 7538c2ecf20Sopenharmony_ci unsigned int flipped_out_top; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 7568c2ecf20Sopenharmony_ci /* Switch width/height and align top left to IRT block size */ 7578c2ecf20Sopenharmony_ci resized_width = out->base.rect.height; 7588c2ecf20Sopenharmony_ci resized_height = out->base.rect.width; 7598c2ecf20Sopenharmony_ci out_left_align = out_height_align; 7608c2ecf20Sopenharmony_ci out_top_align = out_width_align; 7618c2ecf20Sopenharmony_ci out_width_align = out_left_align; 7628c2ecf20Sopenharmony_ci out_height_align = out_top_align; 7638c2ecf20Sopenharmony_ci out_right = out->base.rect.height; 7648c2ecf20Sopenharmony_ci out_bottom = out->base.rect.width; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci for (col = in->num_cols - 1; col > 0; col--) { 7688c2ecf20Sopenharmony_ci bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || 7698c2ecf20Sopenharmony_ci !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); 7708c2ecf20Sopenharmony_ci bool allow_out_overshoot = (col < in->num_cols - 1) && 7718c2ecf20Sopenharmony_ci !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); 7728c2ecf20Sopenharmony_ci unsigned int in_left; 7738c2ecf20Sopenharmony_ci unsigned int out_left; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* 7768c2ecf20Sopenharmony_ci * Align input width to burst length if the scaling step flips 7778c2ecf20Sopenharmony_ci * horizontally. 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci find_best_seam(ctx, col, 7818c2ecf20Sopenharmony_ci in_right, out_right, 7828c2ecf20Sopenharmony_ci in_left_align, out_left_align, 7838c2ecf20Sopenharmony_ci allow_in_overshoot ? 1 : 8 /* burst length */, 7848c2ecf20Sopenharmony_ci allow_out_overshoot ? 1 : out_width_align, 7858c2ecf20Sopenharmony_ci ctx->downsize_coeff_h, ctx->image_resize_coeff_h, 7868c2ecf20Sopenharmony_ci &in_left, &out_left); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) 7898c2ecf20Sopenharmony_ci flipped_out_left = resized_width - out_right; 7908c2ecf20Sopenharmony_ci else 7918c2ecf20Sopenharmony_ci flipped_out_left = out_left; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci fill_tile_column(ctx, col, in, in_left, in_right - in_left, 7948c2ecf20Sopenharmony_ci out, flipped_out_left, out_right - out_left); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, 7978c2ecf20Sopenharmony_ci in_left, in_right - in_left, 7988c2ecf20Sopenharmony_ci flipped_out_left, out_right - out_left); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci in_right = in_left; 8018c2ecf20Sopenharmony_ci out_right = out_left; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? 8058c2ecf20Sopenharmony_ci resized_width - out_right : 0; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci fill_tile_column(ctx, 0, in, 0, in_right, 8088c2ecf20Sopenharmony_ci out, flipped_out_left, out_right); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, 8118c2ecf20Sopenharmony_ci in_right, flipped_out_left, out_right); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci for (row = in->num_rows - 1; row > 0; row--) { 8148c2ecf20Sopenharmony_ci bool allow_overshoot = row < in->num_rows - 1; 8158c2ecf20Sopenharmony_ci unsigned int in_top; 8168c2ecf20Sopenharmony_ci unsigned int out_top; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci find_best_seam(ctx, row, 8198c2ecf20Sopenharmony_ci in_bottom, out_bottom, 8208c2ecf20Sopenharmony_ci in_top_align, out_top_align, 8218c2ecf20Sopenharmony_ci 1, allow_overshoot ? 1 : out_height_align, 8228c2ecf20Sopenharmony_ci ctx->downsize_coeff_v, ctx->image_resize_coeff_v, 8238c2ecf20Sopenharmony_ci &in_top, &out_top); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ 8268c2ecf20Sopenharmony_ci ipu_rot_mode_is_irt(ctx->rot_mode)) 8278c2ecf20Sopenharmony_ci flipped_out_top = resized_height - out_bottom; 8288c2ecf20Sopenharmony_ci else 8298c2ecf20Sopenharmony_ci flipped_out_top = out_top; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, 8328c2ecf20Sopenharmony_ci out, flipped_out_top, out_bottom - out_top); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, 8358c2ecf20Sopenharmony_ci in_top, in_bottom - in_top, 8368c2ecf20Sopenharmony_ci flipped_out_top, out_bottom - out_top); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci in_bottom = in_top; 8398c2ecf20Sopenharmony_ci out_bottom = out_top; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ 8438c2ecf20Sopenharmony_ci ipu_rot_mode_is_irt(ctx->rot_mode)) 8448c2ecf20Sopenharmony_ci flipped_out_top = resized_height - out_bottom; 8458c2ecf20Sopenharmony_ci else 8468c2ecf20Sopenharmony_ci flipped_out_top = 0; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci fill_tile_row(ctx, 0, in, 0, in_bottom, 8498c2ecf20Sopenharmony_ci out, flipped_out_top, out_bottom); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, 8528c2ecf20Sopenharmony_ci in_bottom, flipped_out_top, out_bottom); 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, 8568c2ecf20Sopenharmony_ci struct ipu_image_convert_image *image) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 8598c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 8608c2ecf20Sopenharmony_ci unsigned int max_width = 1024; 8618c2ecf20Sopenharmony_ci unsigned int max_height = 1024; 8628c2ecf20Sopenharmony_ci unsigned int i; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (image->type == IMAGE_CONVERT_IN) { 8658c2ecf20Sopenharmony_ci /* Up to 4096x4096 input tile size */ 8668c2ecf20Sopenharmony_ci max_width <<= ctx->downsize_coeff_h; 8678c2ecf20Sopenharmony_ci max_height <<= ctx->downsize_coeff_v; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci for (i = 0; i < ctx->num_tiles; i++) { 8718c2ecf20Sopenharmony_ci struct ipu_image_tile *tile; 8728c2ecf20Sopenharmony_ci const unsigned int row = i / image->num_cols; 8738c2ecf20Sopenharmony_ci const unsigned int col = i % image->num_cols; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (image->type == IMAGE_CONVERT_OUT) 8768c2ecf20Sopenharmony_ci tile = &image->tile[ctx->out_tile_map[i]]; 8778c2ecf20Sopenharmony_ci else 8788c2ecf20Sopenharmony_ci tile = &image->tile[i]; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci tile->size = ((tile->height * image->fmt->bpp) >> 3) * 8818c2ecf20Sopenharmony_ci tile->width; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (image->fmt->planar) { 8848c2ecf20Sopenharmony_ci tile->stride = tile->width; 8858c2ecf20Sopenharmony_ci tile->rot_stride = tile->height; 8868c2ecf20Sopenharmony_ci } else { 8878c2ecf20Sopenharmony_ci tile->stride = 8888c2ecf20Sopenharmony_ci (image->fmt->bpp * tile->width) >> 3; 8898c2ecf20Sopenharmony_ci tile->rot_stride = 8908c2ecf20Sopenharmony_ci (image->fmt->bpp * tile->height) >> 3; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 8948c2ecf20Sopenharmony_ci "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", 8958c2ecf20Sopenharmony_ci chan->ic_task, ctx, 8968c2ecf20Sopenharmony_ci image->type == IMAGE_CONVERT_IN ? "Input" : "Output", 8978c2ecf20Sopenharmony_ci row, col, 8988c2ecf20Sopenharmony_ci tile->width, tile->height, tile->left, tile->top); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (!tile->width || tile->width > max_width || 9018c2ecf20Sopenharmony_ci !tile->height || tile->height > max_height) { 9028c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", 9038c2ecf20Sopenharmony_ci image->type == IMAGE_CONVERT_IN ? "input" : 9048c2ecf20Sopenharmony_ci "output", tile->width, tile->height); 9058c2ecf20Sopenharmony_ci return -EINVAL; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci/* 9138c2ecf20Sopenharmony_ci * Use the rotation transformation to find the tile coordinates 9148c2ecf20Sopenharmony_ci * (row, col) of a tile in the destination frame that corresponds 9158c2ecf20Sopenharmony_ci * to the given tile coordinates of a source frame. The destination 9168c2ecf20Sopenharmony_ci * coordinate is then converted to a tile index. 9178c2ecf20Sopenharmony_ci */ 9188c2ecf20Sopenharmony_cistatic int transform_tile_index(struct ipu_image_convert_ctx *ctx, 9198c2ecf20Sopenharmony_ci int src_row, int src_col) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 9228c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 9238c2ecf20Sopenharmony_ci struct ipu_image_convert_image *s_image = &ctx->in; 9248c2ecf20Sopenharmony_ci struct ipu_image_convert_image *d_image = &ctx->out; 9258c2ecf20Sopenharmony_ci int dst_row, dst_col; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* with no rotation it's a 1:1 mapping */ 9288c2ecf20Sopenharmony_ci if (ctx->rot_mode == IPU_ROTATE_NONE) 9298c2ecf20Sopenharmony_ci return src_row * s_image->num_cols + src_col; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * before doing the transform, first we have to translate 9338c2ecf20Sopenharmony_ci * source row,col for an origin in the center of s_image 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_ci src_row = src_row * 2 - (s_image->num_rows - 1); 9368c2ecf20Sopenharmony_ci src_col = src_col * 2 - (s_image->num_cols - 1); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* do the rotation transform */ 9398c2ecf20Sopenharmony_ci if (ctx->rot_mode & IPU_ROT_BIT_90) { 9408c2ecf20Sopenharmony_ci dst_col = -src_row; 9418c2ecf20Sopenharmony_ci dst_row = src_col; 9428c2ecf20Sopenharmony_ci } else { 9438c2ecf20Sopenharmony_ci dst_col = src_col; 9448c2ecf20Sopenharmony_ci dst_row = src_row; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* apply flip */ 9488c2ecf20Sopenharmony_ci if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) 9498c2ecf20Sopenharmony_ci dst_col = -dst_col; 9508c2ecf20Sopenharmony_ci if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) 9518c2ecf20Sopenharmony_ci dst_row = -dst_row; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", 9548c2ecf20Sopenharmony_ci chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* 9578c2ecf20Sopenharmony_ci * finally translate dest row,col using an origin in upper 9588c2ecf20Sopenharmony_ci * left of d_image 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci dst_row += d_image->num_rows - 1; 9618c2ecf20Sopenharmony_ci dst_col += d_image->num_cols - 1; 9628c2ecf20Sopenharmony_ci dst_row /= 2; 9638c2ecf20Sopenharmony_ci dst_col /= 2; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci return dst_row * d_image->num_cols + dst_col; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci/* 9698c2ecf20Sopenharmony_ci * Fill the out_tile_map[] with transformed destination tile indeces. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_cistatic void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct ipu_image_convert_image *s_image = &ctx->in; 9748c2ecf20Sopenharmony_ci unsigned int row, col, tile = 0; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci for (row = 0; row < s_image->num_rows; row++) { 9778c2ecf20Sopenharmony_ci for (col = 0; col < s_image->num_cols; col++) { 9788c2ecf20Sopenharmony_ci ctx->out_tile_map[tile] = 9798c2ecf20Sopenharmony_ci transform_tile_index(ctx, row, col); 9808c2ecf20Sopenharmony_ci tile++; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, 9868c2ecf20Sopenharmony_ci struct ipu_image_convert_image *image) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 9898c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 9908c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *fmt = image->fmt; 9918c2ecf20Sopenharmony_ci unsigned int row, col, tile = 0; 9928c2ecf20Sopenharmony_ci u32 H, top, y_stride, uv_stride; 9938c2ecf20Sopenharmony_ci u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; 9948c2ecf20Sopenharmony_ci u32 y_row_off, y_col_off, y_off; 9958c2ecf20Sopenharmony_ci u32 y_size, uv_size; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* setup some convenience vars */ 9988c2ecf20Sopenharmony_ci H = image->base.pix.height; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci y_stride = image->stride; 10018c2ecf20Sopenharmony_ci uv_stride = y_stride / fmt->uv_width_dec; 10028c2ecf20Sopenharmony_ci if (fmt->uv_packed) 10038c2ecf20Sopenharmony_ci uv_stride *= 2; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci y_size = H * y_stride; 10068c2ecf20Sopenharmony_ci uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci for (row = 0; row < image->num_rows; row++) { 10098c2ecf20Sopenharmony_ci top = image->tile[tile].top; 10108c2ecf20Sopenharmony_ci y_row_off = top * y_stride; 10118c2ecf20Sopenharmony_ci uv_row_off = (top * uv_stride) / fmt->uv_height_dec; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci for (col = 0; col < image->num_cols; col++) { 10148c2ecf20Sopenharmony_ci y_col_off = image->tile[tile].left; 10158c2ecf20Sopenharmony_ci uv_col_off = y_col_off / fmt->uv_width_dec; 10168c2ecf20Sopenharmony_ci if (fmt->uv_packed) 10178c2ecf20Sopenharmony_ci uv_col_off *= 2; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci y_off = y_row_off + y_col_off; 10208c2ecf20Sopenharmony_ci uv_off = uv_row_off + uv_col_off; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci u_off = y_size - y_off + uv_off; 10238c2ecf20Sopenharmony_ci v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; 10248c2ecf20Sopenharmony_ci if (fmt->uv_swapped) { 10258c2ecf20Sopenharmony_ci tmp = u_off; 10268c2ecf20Sopenharmony_ci u_off = v_off; 10278c2ecf20Sopenharmony_ci v_off = tmp; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci image->tile[tile].offset = y_off; 10318c2ecf20Sopenharmony_ci image->tile[tile].u_off = u_off; 10328c2ecf20Sopenharmony_ci image->tile[tile++].v_off = v_off; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { 10358c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, 10368c2ecf20Sopenharmony_ci "task %u: ctx %p: %s@[%d,%d]: " 10378c2ecf20Sopenharmony_ci "y_off %08x, u_off %08x, v_off %08x\n", 10388c2ecf20Sopenharmony_ci chan->ic_task, ctx, 10398c2ecf20Sopenharmony_ci image->type == IMAGE_CONVERT_IN ? 10408c2ecf20Sopenharmony_ci "Input" : "Output", row, col, 10418c2ecf20Sopenharmony_ci y_off, u_off, v_off); 10428c2ecf20Sopenharmony_ci return -EINVAL; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return 0; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, 10518c2ecf20Sopenharmony_ci struct ipu_image_convert_image *image) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 10548c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 10558c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *fmt = image->fmt; 10568c2ecf20Sopenharmony_ci unsigned int row, col, tile = 0; 10578c2ecf20Sopenharmony_ci u32 bpp, stride, offset; 10588c2ecf20Sopenharmony_ci u32 row_off, col_off; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* setup some convenience vars */ 10618c2ecf20Sopenharmony_ci stride = image->stride; 10628c2ecf20Sopenharmony_ci bpp = fmt->bpp; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci for (row = 0; row < image->num_rows; row++) { 10658c2ecf20Sopenharmony_ci row_off = image->tile[tile].top * stride; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci for (col = 0; col < image->num_cols; col++) { 10688c2ecf20Sopenharmony_ci col_off = (image->tile[tile].left * bpp) >> 3; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci offset = row_off + col_off; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci image->tile[tile].offset = offset; 10738c2ecf20Sopenharmony_ci image->tile[tile].u_off = 0; 10748c2ecf20Sopenharmony_ci image->tile[tile++].v_off = 0; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (offset & 0x7) { 10778c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, 10788c2ecf20Sopenharmony_ci "task %u: ctx %p: %s@[%d,%d]: " 10798c2ecf20Sopenharmony_ci "phys %08x\n", 10808c2ecf20Sopenharmony_ci chan->ic_task, ctx, 10818c2ecf20Sopenharmony_ci image->type == IMAGE_CONVERT_IN ? 10828c2ecf20Sopenharmony_ci "Input" : "Output", row, col, 10838c2ecf20Sopenharmony_ci row_off + col_off); 10848c2ecf20Sopenharmony_ci return -EINVAL; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, 10938c2ecf20Sopenharmony_ci struct ipu_image_convert_image *image) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci if (image->fmt->planar) 10968c2ecf20Sopenharmony_ci return calc_tile_offsets_planar(ctx, image); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return calc_tile_offsets_packed(ctx, image); 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* 11028c2ecf20Sopenharmony_ci * Calculate the resizing ratio for the IC main processing section given input 11038c2ecf20Sopenharmony_ci * size, fixed downsizing coefficient, and output size. 11048c2ecf20Sopenharmony_ci * Either round to closest for the next tile's first pixel to minimize seams 11058c2ecf20Sopenharmony_ci * and distortion (for all but right column / bottom row), or round down to 11068c2ecf20Sopenharmony_ci * avoid sampling beyond the edges of the input image for this tile's last 11078c2ecf20Sopenharmony_ci * pixel. 11088c2ecf20Sopenharmony_ci * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_cistatic u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, 11118c2ecf20Sopenharmony_ci u32 output_size, bool allow_overshoot) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci u32 downsized = input_size >> downsize_coeff; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (allow_overshoot) 11168c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(8192 * downsized, output_size); 11178c2ecf20Sopenharmony_ci else 11188c2ecf20Sopenharmony_ci return 8192 * (downsized - 1) / (output_size - 1); 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci/* 11228c2ecf20Sopenharmony_ci * Slightly modify resize coefficients per tile to hide the bilinear 11238c2ecf20Sopenharmony_ci * interpolator reset at tile borders, shifting the right / bottom edge 11248c2ecf20Sopenharmony_ci * by up to a half input pixel. This removes noticeable seams between 11258c2ecf20Sopenharmony_ci * tiles at higher upscaling factors. 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_cistatic void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 11308c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 11318c2ecf20Sopenharmony_ci struct ipu_image_tile *in_tile, *out_tile; 11328c2ecf20Sopenharmony_ci unsigned int col, row, tile_idx; 11338c2ecf20Sopenharmony_ci unsigned int last_output; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci for (col = 0; col < ctx->in.num_cols; col++) { 11368c2ecf20Sopenharmony_ci bool closest = (col < ctx->in.num_cols - 1) && 11378c2ecf20Sopenharmony_ci !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); 11388c2ecf20Sopenharmony_ci u32 resized_width; 11398c2ecf20Sopenharmony_ci u32 resize_coeff_h; 11408c2ecf20Sopenharmony_ci u32 in_width; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci tile_idx = col; 11438c2ecf20Sopenharmony_ci in_tile = &ctx->in.tile[tile_idx]; 11448c2ecf20Sopenharmony_ci out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 11478c2ecf20Sopenharmony_ci resized_width = out_tile->height; 11488c2ecf20Sopenharmony_ci else 11498c2ecf20Sopenharmony_ci resized_width = out_tile->width; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci resize_coeff_h = calc_resize_coeff(in_tile->width, 11528c2ecf20Sopenharmony_ci ctx->downsize_coeff_h, 11538c2ecf20Sopenharmony_ci resized_width, closest); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", 11568c2ecf20Sopenharmony_ci __func__, col, resize_coeff_h); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * With the horizontal scaling factor known, round up resized 11608c2ecf20Sopenharmony_ci * width (output width or height) to burst size. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci resized_width = round_up(resized_width, 8); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* 11658c2ecf20Sopenharmony_ci * Calculate input width from the last accessed input pixel 11668c2ecf20Sopenharmony_ci * given resized width and scaling coefficients. Round up to 11678c2ecf20Sopenharmony_ci * burst size. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_ci last_output = resized_width - 1; 11708c2ecf20Sopenharmony_ci if (closest && ((last_output * resize_coeff_h) % 8192)) 11718c2ecf20Sopenharmony_ci last_output++; 11728c2ecf20Sopenharmony_ci in_width = round_up( 11738c2ecf20Sopenharmony_ci (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) 11748c2ecf20Sopenharmony_ci << ctx->downsize_coeff_h, 8); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci for (row = 0; row < ctx->in.num_rows; row++) { 11778c2ecf20Sopenharmony_ci tile_idx = row * ctx->in.num_cols + col; 11788c2ecf20Sopenharmony_ci in_tile = &ctx->in.tile[tile_idx]; 11798c2ecf20Sopenharmony_ci out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 11828c2ecf20Sopenharmony_ci out_tile->height = resized_width; 11838c2ecf20Sopenharmony_ci else 11848c2ecf20Sopenharmony_ci out_tile->width = resized_width; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci in_tile->width = in_width; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci ctx->resize_coeffs_h[col] = resize_coeff_h; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci for (row = 0; row < ctx->in.num_rows; row++) { 11938c2ecf20Sopenharmony_ci bool closest = (row < ctx->in.num_rows - 1) && 11948c2ecf20Sopenharmony_ci !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); 11958c2ecf20Sopenharmony_ci u32 resized_height; 11968c2ecf20Sopenharmony_ci u32 resize_coeff_v; 11978c2ecf20Sopenharmony_ci u32 in_height; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci tile_idx = row * ctx->in.num_cols; 12008c2ecf20Sopenharmony_ci in_tile = &ctx->in.tile[tile_idx]; 12018c2ecf20Sopenharmony_ci out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 12048c2ecf20Sopenharmony_ci resized_height = out_tile->width; 12058c2ecf20Sopenharmony_ci else 12068c2ecf20Sopenharmony_ci resized_height = out_tile->height; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci resize_coeff_v = calc_resize_coeff(in_tile->height, 12098c2ecf20Sopenharmony_ci ctx->downsize_coeff_v, 12108c2ecf20Sopenharmony_ci resized_height, closest); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", 12138c2ecf20Sopenharmony_ci __func__, row, resize_coeff_v); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* 12168c2ecf20Sopenharmony_ci * With the vertical scaling factor known, round up resized 12178c2ecf20Sopenharmony_ci * height (output width or height) to IDMAC limitations. 12188c2ecf20Sopenharmony_ci */ 12198c2ecf20Sopenharmony_ci resized_height = round_up(resized_height, 2); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* 12228c2ecf20Sopenharmony_ci * Calculate input width from the last accessed input pixel 12238c2ecf20Sopenharmony_ci * given resized height and scaling coefficients. Align to 12248c2ecf20Sopenharmony_ci * IDMAC restrictions. 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_ci last_output = resized_height - 1; 12278c2ecf20Sopenharmony_ci if (closest && ((last_output * resize_coeff_v) % 8192)) 12288c2ecf20Sopenharmony_ci last_output++; 12298c2ecf20Sopenharmony_ci in_height = round_up( 12308c2ecf20Sopenharmony_ci (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) 12318c2ecf20Sopenharmony_ci << ctx->downsize_coeff_v, 2); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci for (col = 0; col < ctx->in.num_cols; col++) { 12348c2ecf20Sopenharmony_ci tile_idx = row * ctx->in.num_cols + col; 12358c2ecf20Sopenharmony_ci in_tile = &ctx->in.tile[tile_idx]; 12368c2ecf20Sopenharmony_ci out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 12398c2ecf20Sopenharmony_ci out_tile->width = resized_height; 12408c2ecf20Sopenharmony_ci else 12418c2ecf20Sopenharmony_ci out_tile->height = resized_height; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci in_tile->height = in_height; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci ctx->resize_coeffs_v[row] = resize_coeff_v; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci/* 12518c2ecf20Sopenharmony_ci * return the number of runs in given queue (pending_q or done_q) 12528c2ecf20Sopenharmony_ci * for this context. hold irqlock when calling. 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_cistatic int get_run_count(struct ipu_image_convert_ctx *ctx, 12558c2ecf20Sopenharmony_ci struct list_head *q) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run; 12588c2ecf20Sopenharmony_ci int count = 0; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci lockdep_assert_held(&ctx->chan->irqlock); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci list_for_each_entry(run, q, list) { 12638c2ecf20Sopenharmony_ci if (run->ctx == ctx) 12648c2ecf20Sopenharmony_ci count++; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return count; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic void convert_stop(struct ipu_image_convert_run *run) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx = run->ctx; 12738c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 12748c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", 12778c2ecf20Sopenharmony_ci __func__, chan->ic_task, ctx, run); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* disable IC tasks and the channels */ 12808c2ecf20Sopenharmony_ci ipu_ic_task_disable(chan->ic); 12818c2ecf20Sopenharmony_ci ipu_idmac_disable_channel(chan->in_chan); 12828c2ecf20Sopenharmony_ci ipu_idmac_disable_channel(chan->out_chan); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 12858c2ecf20Sopenharmony_ci ipu_idmac_disable_channel(chan->rotation_in_chan); 12868c2ecf20Sopenharmony_ci ipu_idmac_disable_channel(chan->rotation_out_chan); 12878c2ecf20Sopenharmony_ci ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci ipu_ic_disable(chan->ic); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic void init_idmac_channel(struct ipu_image_convert_ctx *ctx, 12948c2ecf20Sopenharmony_ci struct ipuv3_channel *channel, 12958c2ecf20Sopenharmony_ci struct ipu_image_convert_image *image, 12968c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode, 12978c2ecf20Sopenharmony_ci bool rot_swap_width_height, 12988c2ecf20Sopenharmony_ci unsigned int tile) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 13018c2ecf20Sopenharmony_ci unsigned int burst_size; 13028c2ecf20Sopenharmony_ci u32 width, height, stride; 13038c2ecf20Sopenharmony_ci dma_addr_t addr0, addr1 = 0; 13048c2ecf20Sopenharmony_ci struct ipu_image tile_image; 13058c2ecf20Sopenharmony_ci unsigned int tile_idx[2]; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (image->type == IMAGE_CONVERT_OUT) { 13088c2ecf20Sopenharmony_ci tile_idx[0] = ctx->out_tile_map[tile]; 13098c2ecf20Sopenharmony_ci tile_idx[1] = ctx->out_tile_map[1]; 13108c2ecf20Sopenharmony_ci } else { 13118c2ecf20Sopenharmony_ci tile_idx[0] = tile; 13128c2ecf20Sopenharmony_ci tile_idx[1] = 1; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (rot_swap_width_height) { 13168c2ecf20Sopenharmony_ci width = image->tile[tile_idx[0]].height; 13178c2ecf20Sopenharmony_ci height = image->tile[tile_idx[0]].width; 13188c2ecf20Sopenharmony_ci stride = image->tile[tile_idx[0]].rot_stride; 13198c2ecf20Sopenharmony_ci addr0 = ctx->rot_intermediate[0].phys; 13208c2ecf20Sopenharmony_ci if (ctx->double_buffering) 13218c2ecf20Sopenharmony_ci addr1 = ctx->rot_intermediate[1].phys; 13228c2ecf20Sopenharmony_ci } else { 13238c2ecf20Sopenharmony_ci width = image->tile[tile_idx[0]].width; 13248c2ecf20Sopenharmony_ci height = image->tile[tile_idx[0]].height; 13258c2ecf20Sopenharmony_ci stride = image->stride; 13268c2ecf20Sopenharmony_ci addr0 = image->base.phys0 + 13278c2ecf20Sopenharmony_ci image->tile[tile_idx[0]].offset; 13288c2ecf20Sopenharmony_ci if (ctx->double_buffering) 13298c2ecf20Sopenharmony_ci addr1 = image->base.phys0 + 13308c2ecf20Sopenharmony_ci image->tile[tile_idx[1]].offset; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci ipu_cpmem_zero(channel); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci memset(&tile_image, 0, sizeof(tile_image)); 13368c2ecf20Sopenharmony_ci tile_image.pix.width = tile_image.rect.width = width; 13378c2ecf20Sopenharmony_ci tile_image.pix.height = tile_image.rect.height = height; 13388c2ecf20Sopenharmony_ci tile_image.pix.bytesperline = stride; 13398c2ecf20Sopenharmony_ci tile_image.pix.pixelformat = image->fmt->fourcc; 13408c2ecf20Sopenharmony_ci tile_image.phys0 = addr0; 13418c2ecf20Sopenharmony_ci tile_image.phys1 = addr1; 13428c2ecf20Sopenharmony_ci if (image->fmt->planar && !rot_swap_width_height) { 13438c2ecf20Sopenharmony_ci tile_image.u_offset = image->tile[tile_idx[0]].u_off; 13448c2ecf20Sopenharmony_ci tile_image.v_offset = image->tile[tile_idx[0]].v_off; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci ipu_cpmem_set_image(channel, &tile_image); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (rot_mode) 13508c2ecf20Sopenharmony_ci ipu_cpmem_set_rotation(channel, rot_mode); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* 13538c2ecf20Sopenharmony_ci * Skip writing U and V components to odd rows in the output 13548c2ecf20Sopenharmony_ci * channels for planar 4:2:0. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_ci if ((channel == chan->out_chan || 13578c2ecf20Sopenharmony_ci channel == chan->rotation_out_chan) && 13588c2ecf20Sopenharmony_ci image->fmt->planar && image->fmt->uv_height_dec == 2) 13598c2ecf20Sopenharmony_ci ipu_cpmem_skip_odd_chroma_rows(channel); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (channel == chan->rotation_in_chan || 13628c2ecf20Sopenharmony_ci channel == chan->rotation_out_chan) { 13638c2ecf20Sopenharmony_ci burst_size = 8; 13648c2ecf20Sopenharmony_ci ipu_cpmem_set_block_mode(channel); 13658c2ecf20Sopenharmony_ci } else 13668c2ecf20Sopenharmony_ci burst_size = (width % 16) ? 8 : 16; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci ipu_cpmem_set_burstsize(channel, burst_size); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci ipu_ic_task_idma_init(chan->ic, channel, width, height, 13718c2ecf20Sopenharmony_ci burst_size, rot_mode); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* 13748c2ecf20Sopenharmony_ci * Setting a non-zero AXI ID collides with the PRG AXI snooping, so 13758c2ecf20Sopenharmony_ci * only do this when there is no PRG present. 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci if (!channel->ipu->prg_priv) 13788c2ecf20Sopenharmony_ci ipu_cpmem_set_axi_id(channel, 1); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci ipu_idmac_set_double_buffer(channel, ctx->double_buffering); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic int convert_start(struct ipu_image_convert_run *run, unsigned int tile) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx = run->ctx; 13868c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 13878c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 13888c2ecf20Sopenharmony_ci struct ipu_image_convert_image *s_image = &ctx->in; 13898c2ecf20Sopenharmony_ci struct ipu_image_convert_image *d_image = &ctx->out; 13908c2ecf20Sopenharmony_ci unsigned int dst_tile = ctx->out_tile_map[tile]; 13918c2ecf20Sopenharmony_ci unsigned int dest_width, dest_height; 13928c2ecf20Sopenharmony_ci unsigned int col, row; 13938c2ecf20Sopenharmony_ci u32 rsc; 13948c2ecf20Sopenharmony_ci int ret; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", 13978c2ecf20Sopenharmony_ci __func__, chan->ic_task, ctx, run, tile, dst_tile); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* clear EOF irq mask */ 14008c2ecf20Sopenharmony_ci ctx->eof_mask = 0; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 14038c2ecf20Sopenharmony_ci /* swap width/height for resizer */ 14048c2ecf20Sopenharmony_ci dest_width = d_image->tile[dst_tile].height; 14058c2ecf20Sopenharmony_ci dest_height = d_image->tile[dst_tile].width; 14068c2ecf20Sopenharmony_ci } else { 14078c2ecf20Sopenharmony_ci dest_width = d_image->tile[dst_tile].width; 14088c2ecf20Sopenharmony_ci dest_height = d_image->tile[dst_tile].height; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci row = tile / s_image->num_cols; 14128c2ecf20Sopenharmony_ci col = tile % s_image->num_cols; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci rsc = (ctx->downsize_coeff_v << 30) | 14158c2ecf20Sopenharmony_ci (ctx->resize_coeffs_v[row] << 16) | 14168c2ecf20Sopenharmony_ci (ctx->downsize_coeff_h << 14) | 14178c2ecf20Sopenharmony_ci (ctx->resize_coeffs_h[col]); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", 14208c2ecf20Sopenharmony_ci __func__, s_image->tile[tile].width, 14218c2ecf20Sopenharmony_ci s_image->tile[tile].height, dest_width, dest_height, rsc); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* setup the IC resizer and CSC */ 14248c2ecf20Sopenharmony_ci ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, 14258c2ecf20Sopenharmony_ci s_image->tile[tile].width, 14268c2ecf20Sopenharmony_ci s_image->tile[tile].height, 14278c2ecf20Sopenharmony_ci dest_width, 14288c2ecf20Sopenharmony_ci dest_height, 14298c2ecf20Sopenharmony_ci rsc); 14308c2ecf20Sopenharmony_ci if (ret) { 14318c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); 14328c2ecf20Sopenharmony_ci return ret; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci /* init the source MEM-->IC PP IDMAC channel */ 14368c2ecf20Sopenharmony_ci init_idmac_channel(ctx, chan->in_chan, s_image, 14378c2ecf20Sopenharmony_ci IPU_ROTATE_NONE, false, tile); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 14408c2ecf20Sopenharmony_ci /* init the IC PP-->MEM IDMAC channel */ 14418c2ecf20Sopenharmony_ci init_idmac_channel(ctx, chan->out_chan, d_image, 14428c2ecf20Sopenharmony_ci IPU_ROTATE_NONE, true, tile); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* init the MEM-->IC PP ROT IDMAC channel */ 14458c2ecf20Sopenharmony_ci init_idmac_channel(ctx, chan->rotation_in_chan, d_image, 14468c2ecf20Sopenharmony_ci ctx->rot_mode, true, tile); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* init the destination IC PP ROT-->MEM IDMAC channel */ 14498c2ecf20Sopenharmony_ci init_idmac_channel(ctx, chan->rotation_out_chan, d_image, 14508c2ecf20Sopenharmony_ci IPU_ROTATE_NONE, false, tile); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* now link IC PP-->MEM to MEM-->IC PP ROT */ 14538c2ecf20Sopenharmony_ci ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); 14548c2ecf20Sopenharmony_ci } else { 14558c2ecf20Sopenharmony_ci /* init the destination IC PP-->MEM IDMAC channel */ 14568c2ecf20Sopenharmony_ci init_idmac_channel(ctx, chan->out_chan, d_image, 14578c2ecf20Sopenharmony_ci ctx->rot_mode, false, tile); 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* enable the IC */ 14618c2ecf20Sopenharmony_ci ipu_ic_enable(chan->ic); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* set buffers ready */ 14648c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->in_chan, 0); 14658c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->out_chan, 0); 14668c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 14678c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->rotation_out_chan, 0); 14688c2ecf20Sopenharmony_ci if (ctx->double_buffering) { 14698c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->in_chan, 1); 14708c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->out_chan, 1); 14718c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 14728c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->rotation_out_chan, 1); 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* enable the channels! */ 14768c2ecf20Sopenharmony_ci ipu_idmac_enable_channel(chan->in_chan); 14778c2ecf20Sopenharmony_ci ipu_idmac_enable_channel(chan->out_chan); 14788c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 14798c2ecf20Sopenharmony_ci ipu_idmac_enable_channel(chan->rotation_in_chan); 14808c2ecf20Sopenharmony_ci ipu_idmac_enable_channel(chan->rotation_out_chan); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci ipu_ic_task_enable(chan->ic); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci ipu_cpmem_dump(chan->in_chan); 14868c2ecf20Sopenharmony_ci ipu_cpmem_dump(chan->out_chan); 14878c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 14888c2ecf20Sopenharmony_ci ipu_cpmem_dump(chan->rotation_in_chan); 14898c2ecf20Sopenharmony_ci ipu_cpmem_dump(chan->rotation_out_chan); 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci ipu_dump(priv->ipu); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci return 0; 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci/* hold irqlock when calling */ 14988c2ecf20Sopenharmony_cistatic int do_run(struct ipu_image_convert_run *run) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx = run->ctx; 15018c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci lockdep_assert_held(&chan->irqlock); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci ctx->in.base.phys0 = run->in_phys; 15068c2ecf20Sopenharmony_ci ctx->out.base.phys0 = run->out_phys; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci ctx->cur_buf_num = 0; 15098c2ecf20Sopenharmony_ci ctx->next_tile = 1; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci /* remove run from pending_q and set as current */ 15128c2ecf20Sopenharmony_ci list_del(&run->list); 15138c2ecf20Sopenharmony_ci chan->current_run = run; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci return convert_start(run, 0); 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci/* hold irqlock when calling */ 15198c2ecf20Sopenharmony_cistatic void run_next(struct ipu_image_convert_chan *chan) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 15228c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run, *tmp; 15238c2ecf20Sopenharmony_ci int ret; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci lockdep_assert_held(&chan->irqlock); 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { 15288c2ecf20Sopenharmony_ci /* skip contexts that are aborting */ 15298c2ecf20Sopenharmony_ci if (run->ctx->aborting) { 15308c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 15318c2ecf20Sopenharmony_ci "%s: task %u: skipping aborting ctx %p run %p\n", 15328c2ecf20Sopenharmony_ci __func__, chan->ic_task, run->ctx, run); 15338c2ecf20Sopenharmony_ci continue; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci ret = do_run(run); 15378c2ecf20Sopenharmony_ci if (!ret) 15388c2ecf20Sopenharmony_ci break; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci /* 15418c2ecf20Sopenharmony_ci * something went wrong with start, add the run 15428c2ecf20Sopenharmony_ci * to done q and continue to the next run in the 15438c2ecf20Sopenharmony_ci * pending q. 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_ci run->status = ret; 15468c2ecf20Sopenharmony_ci list_add_tail(&run->list, &chan->done_q); 15478c2ecf20Sopenharmony_ci chan->current_run = NULL; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic void empty_done_q(struct ipu_image_convert_chan *chan) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 15548c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run; 15558c2ecf20Sopenharmony_ci unsigned long flags; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci while (!list_empty(&chan->done_q)) { 15608c2ecf20Sopenharmony_ci run = list_entry(chan->done_q.next, 15618c2ecf20Sopenharmony_ci struct ipu_image_convert_run, 15628c2ecf20Sopenharmony_ci list); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci list_del(&run->list); 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 15678c2ecf20Sopenharmony_ci "%s: task %u: completing ctx %p run %p with %d\n", 15688c2ecf20Sopenharmony_ci __func__, chan->ic_task, run->ctx, run, run->status); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci /* call the completion callback and free the run */ 15718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 15728c2ecf20Sopenharmony_ci run->ctx->complete(run, run->ctx->complete_context); 15738c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci/* 15808c2ecf20Sopenharmony_ci * the bottom half thread clears out the done_q, calling the 15818c2ecf20Sopenharmony_ci * completion handler for each. 15828c2ecf20Sopenharmony_ci */ 15838c2ecf20Sopenharmony_cistatic irqreturn_t do_bh(int irq, void *dev_id) 15848c2ecf20Sopenharmony_ci{ 15858c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = dev_id; 15868c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 15878c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx; 15888c2ecf20Sopenharmony_ci unsigned long flags; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, 15918c2ecf20Sopenharmony_ci chan->ic_task); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci empty_done_q(chan); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci /* 15988c2ecf20Sopenharmony_ci * the done_q is cleared out, signal any contexts 15998c2ecf20Sopenharmony_ci * that are aborting that abort can complete. 16008c2ecf20Sopenharmony_ci */ 16018c2ecf20Sopenharmony_ci list_for_each_entry(ctx, &chan->ctx_list, list) { 16028c2ecf20Sopenharmony_ci if (ctx->aborting) { 16038c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 16048c2ecf20Sopenharmony_ci "%s: task %u: signaling abort for ctx %p\n", 16058c2ecf20Sopenharmony_ci __func__, chan->ic_task, ctx); 16068c2ecf20Sopenharmony_ci complete_all(&ctx->aborted); 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, 16138c2ecf20Sopenharmony_ci chan->ic_task); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return IRQ_HANDLED; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci unsigned int cur_tile = ctx->next_tile - 1; 16218c2ecf20Sopenharmony_ci unsigned int next_tile = ctx->next_tile; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != 16248c2ecf20Sopenharmony_ci ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || 16258c2ecf20Sopenharmony_ci ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != 16268c2ecf20Sopenharmony_ci ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || 16278c2ecf20Sopenharmony_ci ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || 16288c2ecf20Sopenharmony_ci ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || 16298c2ecf20Sopenharmony_ci ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || 16308c2ecf20Sopenharmony_ci ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) 16318c2ecf20Sopenharmony_ci return true; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci return false; 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci/* hold irqlock when calling */ 16378c2ecf20Sopenharmony_cistatic irqreturn_t do_tile_complete(struct ipu_image_convert_run *run) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx = run->ctx; 16408c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 16418c2ecf20Sopenharmony_ci struct ipu_image_tile *src_tile, *dst_tile; 16428c2ecf20Sopenharmony_ci struct ipu_image_convert_image *s_image = &ctx->in; 16438c2ecf20Sopenharmony_ci struct ipu_image_convert_image *d_image = &ctx->out; 16448c2ecf20Sopenharmony_ci struct ipuv3_channel *outch; 16458c2ecf20Sopenharmony_ci unsigned int dst_idx; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci lockdep_assert_held(&chan->irqlock); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? 16508c2ecf20Sopenharmony_ci chan->rotation_out_chan : chan->out_chan; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci /* 16538c2ecf20Sopenharmony_ci * It is difficult to stop the channel DMA before the channels 16548c2ecf20Sopenharmony_ci * enter the paused state. Without double-buffering the channels 16558c2ecf20Sopenharmony_ci * are always in a paused state when the EOF irq occurs, so it 16568c2ecf20Sopenharmony_ci * is safe to stop the channels now. For double-buffering we 16578c2ecf20Sopenharmony_ci * just ignore the abort until the operation completes, when it 16588c2ecf20Sopenharmony_ci * is safe to shut down. 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_ci if (ctx->aborting && !ctx->double_buffering) { 16618c2ecf20Sopenharmony_ci convert_stop(run); 16628c2ecf20Sopenharmony_ci run->status = -EIO; 16638c2ecf20Sopenharmony_ci goto done; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (ctx->next_tile == ctx->num_tiles) { 16678c2ecf20Sopenharmony_ci /* 16688c2ecf20Sopenharmony_ci * the conversion is complete 16698c2ecf20Sopenharmony_ci */ 16708c2ecf20Sopenharmony_ci convert_stop(run); 16718c2ecf20Sopenharmony_ci run->status = 0; 16728c2ecf20Sopenharmony_ci goto done; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* 16768c2ecf20Sopenharmony_ci * not done, place the next tile buffers. 16778c2ecf20Sopenharmony_ci */ 16788c2ecf20Sopenharmony_ci if (!ctx->double_buffering) { 16798c2ecf20Sopenharmony_ci if (ic_settings_changed(ctx)) { 16808c2ecf20Sopenharmony_ci convert_stop(run); 16818c2ecf20Sopenharmony_ci convert_start(run, ctx->next_tile); 16828c2ecf20Sopenharmony_ci } else { 16838c2ecf20Sopenharmony_ci src_tile = &s_image->tile[ctx->next_tile]; 16848c2ecf20Sopenharmony_ci dst_idx = ctx->out_tile_map[ctx->next_tile]; 16858c2ecf20Sopenharmony_ci dst_tile = &d_image->tile[dst_idx]; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci ipu_cpmem_set_buffer(chan->in_chan, 0, 16888c2ecf20Sopenharmony_ci s_image->base.phys0 + 16898c2ecf20Sopenharmony_ci src_tile->offset); 16908c2ecf20Sopenharmony_ci ipu_cpmem_set_buffer(outch, 0, 16918c2ecf20Sopenharmony_ci d_image->base.phys0 + 16928c2ecf20Sopenharmony_ci dst_tile->offset); 16938c2ecf20Sopenharmony_ci if (s_image->fmt->planar) 16948c2ecf20Sopenharmony_ci ipu_cpmem_set_uv_offset(chan->in_chan, 16958c2ecf20Sopenharmony_ci src_tile->u_off, 16968c2ecf20Sopenharmony_ci src_tile->v_off); 16978c2ecf20Sopenharmony_ci if (d_image->fmt->planar) 16988c2ecf20Sopenharmony_ci ipu_cpmem_set_uv_offset(outch, 16998c2ecf20Sopenharmony_ci dst_tile->u_off, 17008c2ecf20Sopenharmony_ci dst_tile->v_off); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->in_chan, 0); 17038c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(outch, 0); 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci } else if (ctx->next_tile < ctx->num_tiles - 1) { 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci src_tile = &s_image->tile[ctx->next_tile + 1]; 17088c2ecf20Sopenharmony_ci dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; 17098c2ecf20Sopenharmony_ci dst_tile = &d_image->tile[dst_idx]; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, 17128c2ecf20Sopenharmony_ci s_image->base.phys0 + src_tile->offset); 17138c2ecf20Sopenharmony_ci ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, 17148c2ecf20Sopenharmony_ci d_image->base.phys0 + dst_tile->offset); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); 17178c2ecf20Sopenharmony_ci ipu_idmac_select_buffer(outch, ctx->cur_buf_num); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci ctx->cur_buf_num ^= 1; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci ctx->eof_mask = 0; /* clear EOF irq mask for next tile */ 17238c2ecf20Sopenharmony_ci ctx->next_tile++; 17248c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17258c2ecf20Sopenharmony_cidone: 17268c2ecf20Sopenharmony_ci list_add_tail(&run->list, &chan->done_q); 17278c2ecf20Sopenharmony_ci chan->current_run = NULL; 17288c2ecf20Sopenharmony_ci run_next(chan); 17298c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cistatic irqreturn_t eof_irq(int irq, void *data) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = data; 17358c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 17368c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx; 17378c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run; 17388c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_HANDLED; 17398c2ecf20Sopenharmony_ci bool tile_complete = false; 17408c2ecf20Sopenharmony_ci unsigned long flags; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci /* get current run and its context */ 17458c2ecf20Sopenharmony_ci run = chan->current_run; 17468c2ecf20Sopenharmony_ci if (!run) { 17478c2ecf20Sopenharmony_ci ret = IRQ_NONE; 17488c2ecf20Sopenharmony_ci goto out; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci ctx = run->ctx; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci if (irq == chan->in_eof_irq) { 17548c2ecf20Sopenharmony_ci ctx->eof_mask |= EOF_IRQ_IN; 17558c2ecf20Sopenharmony_ci } else if (irq == chan->out_eof_irq) { 17568c2ecf20Sopenharmony_ci ctx->eof_mask |= EOF_IRQ_OUT; 17578c2ecf20Sopenharmony_ci } else if (irq == chan->rot_in_eof_irq || 17588c2ecf20Sopenharmony_ci irq == chan->rot_out_eof_irq) { 17598c2ecf20Sopenharmony_ci if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { 17608c2ecf20Sopenharmony_ci /* this was NOT a rotation op, shouldn't happen */ 17618c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, 17628c2ecf20Sopenharmony_ci "Unexpected rotation interrupt\n"); 17638c2ecf20Sopenharmony_ci goto out; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ? 17668c2ecf20Sopenharmony_ci EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT; 17678c2ecf20Sopenharmony_ci } else { 17688c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq); 17698c2ecf20Sopenharmony_ci ret = IRQ_NONE; 17708c2ecf20Sopenharmony_ci goto out; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) 17748c2ecf20Sopenharmony_ci tile_complete = (ctx->eof_mask == EOF_IRQ_ROT_COMPLETE); 17758c2ecf20Sopenharmony_ci else 17768c2ecf20Sopenharmony_ci tile_complete = (ctx->eof_mask == EOF_IRQ_COMPLETE); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (tile_complete) 17798c2ecf20Sopenharmony_ci ret = do_tile_complete(run); 17808c2ecf20Sopenharmony_ciout: 17818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 17828c2ecf20Sopenharmony_ci return ret; 17838c2ecf20Sopenharmony_ci} 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci/* 17868c2ecf20Sopenharmony_ci * try to force the completion of runs for this ctx. Called when 17878c2ecf20Sopenharmony_ci * abort wait times out in ipu_image_convert_abort(). 17888c2ecf20Sopenharmony_ci */ 17898c2ecf20Sopenharmony_cistatic void force_abort(struct ipu_image_convert_ctx *ctx) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 17928c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run; 17938c2ecf20Sopenharmony_ci unsigned long flags; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci run = chan->current_run; 17988c2ecf20Sopenharmony_ci if (run && run->ctx == ctx) { 17998c2ecf20Sopenharmony_ci convert_stop(run); 18008c2ecf20Sopenharmony_ci run->status = -EIO; 18018c2ecf20Sopenharmony_ci list_add_tail(&run->list, &chan->done_q); 18028c2ecf20Sopenharmony_ci chan->current_run = NULL; 18038c2ecf20Sopenharmony_ci run_next(chan); 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci empty_done_q(chan); 18098c2ecf20Sopenharmony_ci} 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_cistatic void release_ipu_resources(struct ipu_image_convert_chan *chan) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci if (chan->in_eof_irq >= 0) 18148c2ecf20Sopenharmony_ci free_irq(chan->in_eof_irq, chan); 18158c2ecf20Sopenharmony_ci if (chan->rot_in_eof_irq >= 0) 18168c2ecf20Sopenharmony_ci free_irq(chan->rot_in_eof_irq, chan); 18178c2ecf20Sopenharmony_ci if (chan->out_eof_irq >= 0) 18188c2ecf20Sopenharmony_ci free_irq(chan->out_eof_irq, chan); 18198c2ecf20Sopenharmony_ci if (chan->rot_out_eof_irq >= 0) 18208c2ecf20Sopenharmony_ci free_irq(chan->rot_out_eof_irq, chan); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(chan->in_chan)) 18238c2ecf20Sopenharmony_ci ipu_idmac_put(chan->in_chan); 18248c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(chan->out_chan)) 18258c2ecf20Sopenharmony_ci ipu_idmac_put(chan->out_chan); 18268c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) 18278c2ecf20Sopenharmony_ci ipu_idmac_put(chan->rotation_in_chan); 18288c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) 18298c2ecf20Sopenharmony_ci ipu_idmac_put(chan->rotation_out_chan); 18308c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(chan->ic)) 18318c2ecf20Sopenharmony_ci ipu_ic_put(chan->ic); 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci chan->in_chan = chan->out_chan = chan->rotation_in_chan = 18348c2ecf20Sopenharmony_ci chan->rotation_out_chan = NULL; 18358c2ecf20Sopenharmony_ci chan->in_eof_irq = -1; 18368c2ecf20Sopenharmony_ci chan->rot_in_eof_irq = -1; 18378c2ecf20Sopenharmony_ci chan->out_eof_irq = -1; 18388c2ecf20Sopenharmony_ci chan->rot_out_eof_irq = -1; 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic int get_eof_irq(struct ipu_image_convert_chan *chan, 18428c2ecf20Sopenharmony_ci struct ipuv3_channel *channel) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 18458c2ecf20Sopenharmony_ci int ret, irq; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci irq = ipu_idmac_channel_irq(priv->ipu, channel, IPU_IRQ_EOF); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci ret = request_threaded_irq(irq, eof_irq, do_bh, 0, "ipu-ic", chan); 18508c2ecf20Sopenharmony_ci if (ret < 0) { 18518c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "could not acquire irq %d\n", irq); 18528c2ecf20Sopenharmony_ci return ret; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci return irq; 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_cistatic int get_ipu_resources(struct ipu_image_convert_chan *chan) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; 18618c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 18628c2ecf20Sopenharmony_ci int ret; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci /* get IC */ 18658c2ecf20Sopenharmony_ci chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); 18668c2ecf20Sopenharmony_ci if (IS_ERR(chan->ic)) { 18678c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "could not acquire IC\n"); 18688c2ecf20Sopenharmony_ci ret = PTR_ERR(chan->ic); 18698c2ecf20Sopenharmony_ci goto err; 18708c2ecf20Sopenharmony_ci } 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci /* get IDMAC channels */ 18738c2ecf20Sopenharmony_ci chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); 18748c2ecf20Sopenharmony_ci chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); 18758c2ecf20Sopenharmony_ci if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { 18768c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); 18778c2ecf20Sopenharmony_ci ret = -EBUSY; 18788c2ecf20Sopenharmony_ci goto err; 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); 18828c2ecf20Sopenharmony_ci chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); 18838c2ecf20Sopenharmony_ci if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { 18848c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, 18858c2ecf20Sopenharmony_ci "could not acquire idmac rotation channels\n"); 18868c2ecf20Sopenharmony_ci ret = -EBUSY; 18878c2ecf20Sopenharmony_ci goto err; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci /* acquire the EOF interrupts */ 18918c2ecf20Sopenharmony_ci ret = get_eof_irq(chan, chan->in_chan); 18928c2ecf20Sopenharmony_ci if (ret < 0) { 18938c2ecf20Sopenharmony_ci chan->in_eof_irq = -1; 18948c2ecf20Sopenharmony_ci goto err; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci chan->in_eof_irq = ret; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci ret = get_eof_irq(chan, chan->rotation_in_chan); 18998c2ecf20Sopenharmony_ci if (ret < 0) { 19008c2ecf20Sopenharmony_ci chan->rot_in_eof_irq = -1; 19018c2ecf20Sopenharmony_ci goto err; 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci chan->rot_in_eof_irq = ret; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci ret = get_eof_irq(chan, chan->out_chan); 19068c2ecf20Sopenharmony_ci if (ret < 0) { 19078c2ecf20Sopenharmony_ci chan->out_eof_irq = -1; 19088c2ecf20Sopenharmony_ci goto err; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci chan->out_eof_irq = ret; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci ret = get_eof_irq(chan, chan->rotation_out_chan); 19138c2ecf20Sopenharmony_ci if (ret < 0) { 19148c2ecf20Sopenharmony_ci chan->rot_out_eof_irq = -1; 19158c2ecf20Sopenharmony_ci goto err; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci chan->rot_out_eof_irq = ret; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci return 0; 19208c2ecf20Sopenharmony_cierr: 19218c2ecf20Sopenharmony_ci release_ipu_resources(chan); 19228c2ecf20Sopenharmony_ci return ret; 19238c2ecf20Sopenharmony_ci} 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_cistatic int fill_image(struct ipu_image_convert_ctx *ctx, 19268c2ecf20Sopenharmony_ci struct ipu_image_convert_image *ic_image, 19278c2ecf20Sopenharmony_ci struct ipu_image *image, 19288c2ecf20Sopenharmony_ci enum ipu_image_convert_type type) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = ctx->chan->priv; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci ic_image->base = *image; 19338c2ecf20Sopenharmony_ci ic_image->type = type; 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci ic_image->fmt = get_format(image->pix.pixelformat); 19368c2ecf20Sopenharmony_ci if (!ic_image->fmt) { 19378c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", 19388c2ecf20Sopenharmony_ci type == IMAGE_CONVERT_OUT ? "Output" : "Input"); 19398c2ecf20Sopenharmony_ci return -EINVAL; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci if (ic_image->fmt->planar) 19438c2ecf20Sopenharmony_ci ic_image->stride = ic_image->base.pix.width; 19448c2ecf20Sopenharmony_ci else 19458c2ecf20Sopenharmony_ci ic_image->stride = ic_image->base.pix.bytesperline; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci return 0; 19488c2ecf20Sopenharmony_ci} 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ 19518c2ecf20Sopenharmony_cistatic unsigned int clamp_align(unsigned int x, unsigned int min, 19528c2ecf20Sopenharmony_ci unsigned int max, unsigned int align) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci /* Bits that must be zero to be aligned */ 19558c2ecf20Sopenharmony_ci unsigned int mask = ~((1 << align) - 1); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* Clamp to aligned min and max */ 19588c2ecf20Sopenharmony_ci x = clamp(x, (min + ~mask) & mask, max & mask); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* Round to nearest aligned value */ 19618c2ecf20Sopenharmony_ci if (align) 19628c2ecf20Sopenharmony_ci x = (x + (1 << (align - 1))) & mask; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci return x; 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci/* Adjusts input/output images to IPU restrictions */ 19688c2ecf20Sopenharmony_civoid ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, 19698c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode) 19708c2ecf20Sopenharmony_ci{ 19718c2ecf20Sopenharmony_ci const struct ipu_image_pixfmt *infmt, *outfmt; 19728c2ecf20Sopenharmony_ci u32 w_align_out, h_align_out; 19738c2ecf20Sopenharmony_ci u32 w_align_in, h_align_in; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci infmt = get_format(in->pix.pixelformat); 19768c2ecf20Sopenharmony_ci outfmt = get_format(out->pix.pixelformat); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* set some default pixel formats if needed */ 19798c2ecf20Sopenharmony_ci if (!infmt) { 19808c2ecf20Sopenharmony_ci in->pix.pixelformat = V4L2_PIX_FMT_RGB24; 19818c2ecf20Sopenharmony_ci infmt = get_format(V4L2_PIX_FMT_RGB24); 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci if (!outfmt) { 19848c2ecf20Sopenharmony_ci out->pix.pixelformat = V4L2_PIX_FMT_RGB24; 19858c2ecf20Sopenharmony_ci outfmt = get_format(V4L2_PIX_FMT_RGB24); 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci /* image converter does not handle fields */ 19898c2ecf20Sopenharmony_ci in->pix.field = out->pix.field = V4L2_FIELD_NONE; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci /* resizer cannot downsize more than 4:1 */ 19928c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(rot_mode)) { 19938c2ecf20Sopenharmony_ci out->pix.height = max_t(__u32, out->pix.height, 19948c2ecf20Sopenharmony_ci in->pix.width / 4); 19958c2ecf20Sopenharmony_ci out->pix.width = max_t(__u32, out->pix.width, 19968c2ecf20Sopenharmony_ci in->pix.height / 4); 19978c2ecf20Sopenharmony_ci } else { 19988c2ecf20Sopenharmony_ci out->pix.width = max_t(__u32, out->pix.width, 19998c2ecf20Sopenharmony_ci in->pix.width / 4); 20008c2ecf20Sopenharmony_ci out->pix.height = max_t(__u32, out->pix.height, 20018c2ecf20Sopenharmony_ci in->pix.height / 4); 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* align input width/height */ 20058c2ecf20Sopenharmony_ci w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, 20068c2ecf20Sopenharmony_ci rot_mode)); 20078c2ecf20Sopenharmony_ci h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, 20088c2ecf20Sopenharmony_ci rot_mode)); 20098c2ecf20Sopenharmony_ci in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, 20108c2ecf20Sopenharmony_ci w_align_in); 20118c2ecf20Sopenharmony_ci in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, 20128c2ecf20Sopenharmony_ci h_align_in); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci /* align output width/height */ 20158c2ecf20Sopenharmony_ci w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, 20168c2ecf20Sopenharmony_ci rot_mode)); 20178c2ecf20Sopenharmony_ci h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, 20188c2ecf20Sopenharmony_ci rot_mode)); 20198c2ecf20Sopenharmony_ci out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, 20208c2ecf20Sopenharmony_ci w_align_out); 20218c2ecf20Sopenharmony_ci out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, 20228c2ecf20Sopenharmony_ci h_align_out); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci /* set input/output strides and image sizes */ 20258c2ecf20Sopenharmony_ci in->pix.bytesperline = infmt->planar ? 20268c2ecf20Sopenharmony_ci clamp_align(in->pix.width, 2 << w_align_in, MAX_W, 20278c2ecf20Sopenharmony_ci w_align_in) : 20288c2ecf20Sopenharmony_ci clamp_align((in->pix.width * infmt->bpp) >> 3, 20298c2ecf20Sopenharmony_ci ((2 << w_align_in) * infmt->bpp) >> 3, 20308c2ecf20Sopenharmony_ci (MAX_W * infmt->bpp) >> 3, 20318c2ecf20Sopenharmony_ci w_align_in); 20328c2ecf20Sopenharmony_ci in->pix.sizeimage = infmt->planar ? 20338c2ecf20Sopenharmony_ci (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : 20348c2ecf20Sopenharmony_ci in->pix.height * in->pix.bytesperline; 20358c2ecf20Sopenharmony_ci out->pix.bytesperline = outfmt->planar ? out->pix.width : 20368c2ecf20Sopenharmony_ci (out->pix.width * outfmt->bpp) >> 3; 20378c2ecf20Sopenharmony_ci out->pix.sizeimage = outfmt->planar ? 20388c2ecf20Sopenharmony_ci (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : 20398c2ecf20Sopenharmony_ci out->pix.height * out->pix.bytesperline; 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_adjust); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci/* 20448c2ecf20Sopenharmony_ci * this is used by ipu_image_convert_prepare() to verify set input and 20458c2ecf20Sopenharmony_ci * output images are valid before starting the conversion. Clients can 20468c2ecf20Sopenharmony_ci * also call it before calling ipu_image_convert_prepare(). 20478c2ecf20Sopenharmony_ci */ 20488c2ecf20Sopenharmony_ciint ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, 20498c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode) 20508c2ecf20Sopenharmony_ci{ 20518c2ecf20Sopenharmony_ci struct ipu_image testin, testout; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci testin = *in; 20548c2ecf20Sopenharmony_ci testout = *out; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci ipu_image_convert_adjust(&testin, &testout, rot_mode); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci if (testin.pix.width != in->pix.width || 20598c2ecf20Sopenharmony_ci testin.pix.height != in->pix.height || 20608c2ecf20Sopenharmony_ci testout.pix.width != out->pix.width || 20618c2ecf20Sopenharmony_ci testout.pix.height != out->pix.height) 20628c2ecf20Sopenharmony_ci return -EINVAL; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci return 0; 20658c2ecf20Sopenharmony_ci} 20668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_verify); 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci/* 20698c2ecf20Sopenharmony_ci * Call ipu_image_convert_prepare() to prepare for the conversion of 20708c2ecf20Sopenharmony_ci * given images and rotation mode. Returns a new conversion context. 20718c2ecf20Sopenharmony_ci */ 20728c2ecf20Sopenharmony_cistruct ipu_image_convert_ctx * 20738c2ecf20Sopenharmony_ciipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, 20748c2ecf20Sopenharmony_ci struct ipu_image *in, struct ipu_image *out, 20758c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode, 20768c2ecf20Sopenharmony_ci ipu_image_convert_cb_t complete, 20778c2ecf20Sopenharmony_ci void *complete_context) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = ipu->image_convert_priv; 20808c2ecf20Sopenharmony_ci struct ipu_image_convert_image *s_image, *d_image; 20818c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan; 20828c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx; 20838c2ecf20Sopenharmony_ci unsigned long flags; 20848c2ecf20Sopenharmony_ci unsigned int i; 20858c2ecf20Sopenharmony_ci bool get_res; 20868c2ecf20Sopenharmony_ci int ret; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci if (!in || !out || !complete || 20898c2ecf20Sopenharmony_ci (ic_task != IC_TASK_VIEWFINDER && 20908c2ecf20Sopenharmony_ci ic_task != IC_TASK_POST_PROCESSOR)) 20918c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci /* verify the in/out images before continuing */ 20948c2ecf20Sopenharmony_ci ret = ipu_image_convert_verify(in, out, rot_mode); 20958c2ecf20Sopenharmony_ci if (ret) { 20968c2ecf20Sopenharmony_ci dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", 20978c2ecf20Sopenharmony_ci __func__); 20988c2ecf20Sopenharmony_ci return ERR_PTR(ret); 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci chan = &priv->chan[ic_task]; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 21048c2ecf20Sopenharmony_ci if (!ctx) 21058c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, 21088c2ecf20Sopenharmony_ci chan->ic_task, ctx); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci ctx->chan = chan; 21118c2ecf20Sopenharmony_ci init_completion(&ctx->aborted); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci ctx->rot_mode = rot_mode; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci /* Sets ctx->in.num_rows/cols as well */ 21168c2ecf20Sopenharmony_ci ret = calc_image_resize_coefficients(ctx, in, out); 21178c2ecf20Sopenharmony_ci if (ret) 21188c2ecf20Sopenharmony_ci goto out_free; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci s_image = &ctx->in; 21218c2ecf20Sopenharmony_ci d_image = &ctx->out; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci /* set tiling and rotation */ 21248c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(rot_mode)) { 21258c2ecf20Sopenharmony_ci d_image->num_rows = s_image->num_cols; 21268c2ecf20Sopenharmony_ci d_image->num_cols = s_image->num_rows; 21278c2ecf20Sopenharmony_ci } else { 21288c2ecf20Sopenharmony_ci d_image->num_rows = s_image->num_rows; 21298c2ecf20Sopenharmony_ci d_image->num_cols = s_image->num_cols; 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci ctx->num_tiles = d_image->num_cols * d_image->num_rows; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); 21358c2ecf20Sopenharmony_ci if (ret) 21368c2ecf20Sopenharmony_ci goto out_free; 21378c2ecf20Sopenharmony_ci ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); 21388c2ecf20Sopenharmony_ci if (ret) 21398c2ecf20Sopenharmony_ci goto out_free; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci calc_out_tile_map(ctx); 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci find_seams(ctx, s_image, d_image); 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci ret = calc_tile_dimensions(ctx, s_image); 21468c2ecf20Sopenharmony_ci if (ret) 21478c2ecf20Sopenharmony_ci goto out_free; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci ret = calc_tile_offsets(ctx, s_image); 21508c2ecf20Sopenharmony_ci if (ret) 21518c2ecf20Sopenharmony_ci goto out_free; 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci calc_tile_dimensions(ctx, d_image); 21548c2ecf20Sopenharmony_ci ret = calc_tile_offsets(ctx, d_image); 21558c2ecf20Sopenharmony_ci if (ret) 21568c2ecf20Sopenharmony_ci goto out_free; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci calc_tile_resize_coefficients(ctx); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci ret = ipu_ic_calc_csc(&ctx->csc, 21618c2ecf20Sopenharmony_ci s_image->base.pix.ycbcr_enc, 21628c2ecf20Sopenharmony_ci s_image->base.pix.quantization, 21638c2ecf20Sopenharmony_ci ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), 21648c2ecf20Sopenharmony_ci d_image->base.pix.ycbcr_enc, 21658c2ecf20Sopenharmony_ci d_image->base.pix.quantization, 21668c2ecf20Sopenharmony_ci ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); 21678c2ecf20Sopenharmony_ci if (ret) 21688c2ecf20Sopenharmony_ci goto out_free; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci dump_format(ctx, s_image); 21718c2ecf20Sopenharmony_ci dump_format(ctx, d_image); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci ctx->complete = complete; 21748c2ecf20Sopenharmony_ci ctx->complete_context = complete_context; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci /* 21778c2ecf20Sopenharmony_ci * Can we use double-buffering for this operation? If there is 21788c2ecf20Sopenharmony_ci * only one tile (the whole image can be converted in a single 21798c2ecf20Sopenharmony_ci * operation) there's no point in using double-buffering. Also, 21808c2ecf20Sopenharmony_ci * the IPU's IDMAC channels allow only a single U and V plane 21818c2ecf20Sopenharmony_ci * offset shared between both buffers, but these offsets change 21828c2ecf20Sopenharmony_ci * for every tile, and therefore would have to be updated for 21838c2ecf20Sopenharmony_ci * each buffer which is not possible. So double-buffering is 21848c2ecf20Sopenharmony_ci * impossible when either the source or destination images are 21858c2ecf20Sopenharmony_ci * a planar format (YUV420, YUV422P, etc.). Further, differently 21868c2ecf20Sopenharmony_ci * sized tiles or different resizing coefficients per tile 21878c2ecf20Sopenharmony_ci * prevent double-buffering as well. 21888c2ecf20Sopenharmony_ci */ 21898c2ecf20Sopenharmony_ci ctx->double_buffering = (ctx->num_tiles > 1 && 21908c2ecf20Sopenharmony_ci !s_image->fmt->planar && 21918c2ecf20Sopenharmony_ci !d_image->fmt->planar); 21928c2ecf20Sopenharmony_ci for (i = 1; i < ctx->num_tiles; i++) { 21938c2ecf20Sopenharmony_ci if (ctx->in.tile[i].width != ctx->in.tile[0].width || 21948c2ecf20Sopenharmony_ci ctx->in.tile[i].height != ctx->in.tile[0].height || 21958c2ecf20Sopenharmony_ci ctx->out.tile[i].width != ctx->out.tile[0].width || 21968c2ecf20Sopenharmony_ci ctx->out.tile[i].height != ctx->out.tile[0].height) { 21978c2ecf20Sopenharmony_ci ctx->double_buffering = false; 21988c2ecf20Sopenharmony_ci break; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci for (i = 1; i < ctx->in.num_cols; i++) { 22028c2ecf20Sopenharmony_ci if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { 22038c2ecf20Sopenharmony_ci ctx->double_buffering = false; 22048c2ecf20Sopenharmony_ci break; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci } 22078c2ecf20Sopenharmony_ci for (i = 1; i < ctx->in.num_rows; i++) { 22088c2ecf20Sopenharmony_ci if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { 22098c2ecf20Sopenharmony_ci ctx->double_buffering = false; 22108c2ecf20Sopenharmony_ci break; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci } 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci if (ipu_rot_mode_is_irt(ctx->rot_mode)) { 22158c2ecf20Sopenharmony_ci unsigned long intermediate_size = d_image->tile[0].size; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci for (i = 1; i < ctx->num_tiles; i++) { 22188c2ecf20Sopenharmony_ci if (d_image->tile[i].size > intermediate_size) 22198c2ecf20Sopenharmony_ci intermediate_size = d_image->tile[i].size; 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], 22238c2ecf20Sopenharmony_ci intermediate_size); 22248c2ecf20Sopenharmony_ci if (ret) 22258c2ecf20Sopenharmony_ci goto out_free; 22268c2ecf20Sopenharmony_ci if (ctx->double_buffering) { 22278c2ecf20Sopenharmony_ci ret = alloc_dma_buf(priv, 22288c2ecf20Sopenharmony_ci &ctx->rot_intermediate[1], 22298c2ecf20Sopenharmony_ci intermediate_size); 22308c2ecf20Sopenharmony_ci if (ret) 22318c2ecf20Sopenharmony_ci goto out_free_dmabuf0; 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci get_res = list_empty(&chan->ctx_list); 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci list_add_tail(&ctx->list, &chan->ctx_list); 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci if (get_res) { 22448c2ecf20Sopenharmony_ci ret = get_ipu_resources(chan); 22458c2ecf20Sopenharmony_ci if (ret) 22468c2ecf20Sopenharmony_ci goto out_free_dmabuf1; 22478c2ecf20Sopenharmony_ci } 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci return ctx; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ciout_free_dmabuf1: 22528c2ecf20Sopenharmony_ci free_dma_buf(priv, &ctx->rot_intermediate[1]); 22538c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 22548c2ecf20Sopenharmony_ci list_del(&ctx->list); 22558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 22568c2ecf20Sopenharmony_ciout_free_dmabuf0: 22578c2ecf20Sopenharmony_ci free_dma_buf(priv, &ctx->rot_intermediate[0]); 22588c2ecf20Sopenharmony_ciout_free: 22598c2ecf20Sopenharmony_ci kfree(ctx); 22608c2ecf20Sopenharmony_ci return ERR_PTR(ret); 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_prepare); 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci/* 22658c2ecf20Sopenharmony_ci * Carry out a single image conversion run. Only the physaddr's of the input 22668c2ecf20Sopenharmony_ci * and output image buffers are needed. The conversion context must have 22678c2ecf20Sopenharmony_ci * been created previously with ipu_image_convert_prepare(). 22688c2ecf20Sopenharmony_ci */ 22698c2ecf20Sopenharmony_ciint ipu_image_convert_queue(struct ipu_image_convert_run *run) 22708c2ecf20Sopenharmony_ci{ 22718c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan; 22728c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv; 22738c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx; 22748c2ecf20Sopenharmony_ci unsigned long flags; 22758c2ecf20Sopenharmony_ci int ret = 0; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (!run || !run->ctx || !run->in_phys || !run->out_phys) 22788c2ecf20Sopenharmony_ci return -EINVAL; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci ctx = run->ctx; 22818c2ecf20Sopenharmony_ci chan = ctx->chan; 22828c2ecf20Sopenharmony_ci priv = chan->priv; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, 22858c2ecf20Sopenharmony_ci chan->ic_task, ctx, run); 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&run->list); 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci if (ctx->aborting) { 22928c2ecf20Sopenharmony_ci ret = -EIO; 22938c2ecf20Sopenharmony_ci goto unlock; 22948c2ecf20Sopenharmony_ci } 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci list_add_tail(&run->list, &chan->pending_q); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci if (!chan->current_run) { 22998c2ecf20Sopenharmony_ci ret = do_run(run); 23008c2ecf20Sopenharmony_ci if (ret) 23018c2ecf20Sopenharmony_ci chan->current_run = NULL; 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ciunlock: 23048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 23058c2ecf20Sopenharmony_ci return ret; 23068c2ecf20Sopenharmony_ci} 23078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_queue); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci/* Abort any active or pending conversions for this context */ 23108c2ecf20Sopenharmony_cistatic void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) 23118c2ecf20Sopenharmony_ci{ 23128c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 23138c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 23148c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run, *active_run, *tmp; 23158c2ecf20Sopenharmony_ci unsigned long flags; 23168c2ecf20Sopenharmony_ci int run_count, ret; 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci /* move all remaining pending runs in this context to done_q */ 23218c2ecf20Sopenharmony_ci list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { 23228c2ecf20Sopenharmony_ci if (run->ctx != ctx) 23238c2ecf20Sopenharmony_ci continue; 23248c2ecf20Sopenharmony_ci run->status = -EIO; 23258c2ecf20Sopenharmony_ci list_move_tail(&run->list, &chan->done_q); 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci run_count = get_run_count(ctx, &chan->done_q); 23298c2ecf20Sopenharmony_ci active_run = (chan->current_run && chan->current_run->ctx == ctx) ? 23308c2ecf20Sopenharmony_ci chan->current_run : NULL; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci if (active_run) 23338c2ecf20Sopenharmony_ci reinit_completion(&ctx->aborted); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci ctx->aborting = true; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci if (!run_count && !active_run) { 23408c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 23418c2ecf20Sopenharmony_ci "%s: task %u: no abort needed for ctx %p\n", 23428c2ecf20Sopenharmony_ci __func__, chan->ic_task, ctx); 23438c2ecf20Sopenharmony_ci return; 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (!active_run) { 23478c2ecf20Sopenharmony_ci empty_done_q(chan); 23488c2ecf20Sopenharmony_ci return; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, 23528c2ecf20Sopenharmony_ci "%s: task %u: wait for completion: %d runs\n", 23538c2ecf20Sopenharmony_ci __func__, chan->ic_task, run_count); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ctx->aborted, 23568c2ecf20Sopenharmony_ci msecs_to_jiffies(10000)); 23578c2ecf20Sopenharmony_ci if (ret == 0) { 23588c2ecf20Sopenharmony_ci dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); 23598c2ecf20Sopenharmony_ci force_abort(ctx); 23608c2ecf20Sopenharmony_ci } 23618c2ecf20Sopenharmony_ci} 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_civoid ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) 23648c2ecf20Sopenharmony_ci{ 23658c2ecf20Sopenharmony_ci __ipu_image_convert_abort(ctx); 23668c2ecf20Sopenharmony_ci ctx->aborting = false; 23678c2ecf20Sopenharmony_ci} 23688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_abort); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci/* Unprepare image conversion context */ 23718c2ecf20Sopenharmony_civoid ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) 23728c2ecf20Sopenharmony_ci{ 23738c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = ctx->chan; 23748c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv = chan->priv; 23758c2ecf20Sopenharmony_ci unsigned long flags; 23768c2ecf20Sopenharmony_ci bool put_res; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci /* make sure no runs are hanging around */ 23798c2ecf20Sopenharmony_ci __ipu_image_convert_abort(ctx); 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, 23828c2ecf20Sopenharmony_ci chan->ic_task, ctx); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->irqlock, flags); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci list_del(&ctx->list); 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci put_res = list_empty(&chan->ctx_list); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->irqlock, flags); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (put_res) 23938c2ecf20Sopenharmony_ci release_ipu_resources(chan); 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci free_dma_buf(priv, &ctx->rot_intermediate[1]); 23968c2ecf20Sopenharmony_ci free_dma_buf(priv, &ctx->rot_intermediate[0]); 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci kfree(ctx); 23998c2ecf20Sopenharmony_ci} 24008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci/* 24038c2ecf20Sopenharmony_ci * "Canned" asynchronous single image conversion. Allocates and returns 24048c2ecf20Sopenharmony_ci * a new conversion run. On successful return the caller must free the 24058c2ecf20Sopenharmony_ci * run and call ipu_image_convert_unprepare() after conversion completes. 24068c2ecf20Sopenharmony_ci */ 24078c2ecf20Sopenharmony_cistruct ipu_image_convert_run * 24088c2ecf20Sopenharmony_ciipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, 24098c2ecf20Sopenharmony_ci struct ipu_image *in, struct ipu_image *out, 24108c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode, 24118c2ecf20Sopenharmony_ci ipu_image_convert_cb_t complete, 24128c2ecf20Sopenharmony_ci void *complete_context) 24138c2ecf20Sopenharmony_ci{ 24148c2ecf20Sopenharmony_ci struct ipu_image_convert_ctx *ctx; 24158c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run; 24168c2ecf20Sopenharmony_ci int ret; 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, 24198c2ecf20Sopenharmony_ci complete, complete_context); 24208c2ecf20Sopenharmony_ci if (IS_ERR(ctx)) 24218c2ecf20Sopenharmony_ci return ERR_CAST(ctx); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci run = kzalloc(sizeof(*run), GFP_KERNEL); 24248c2ecf20Sopenharmony_ci if (!run) { 24258c2ecf20Sopenharmony_ci ipu_image_convert_unprepare(ctx); 24268c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci run->ctx = ctx; 24308c2ecf20Sopenharmony_ci run->in_phys = in->phys0; 24318c2ecf20Sopenharmony_ci run->out_phys = out->phys0; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci ret = ipu_image_convert_queue(run); 24348c2ecf20Sopenharmony_ci if (ret) { 24358c2ecf20Sopenharmony_ci ipu_image_convert_unprepare(ctx); 24368c2ecf20Sopenharmony_ci kfree(run); 24378c2ecf20Sopenharmony_ci return ERR_PTR(ret); 24388c2ecf20Sopenharmony_ci } 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci return run; 24418c2ecf20Sopenharmony_ci} 24428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci/* "Canned" synchronous single image conversion */ 24458c2ecf20Sopenharmony_cistatic void image_convert_sync_complete(struct ipu_image_convert_run *run, 24468c2ecf20Sopenharmony_ci void *data) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci struct completion *comp = data; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci complete(comp); 24518c2ecf20Sopenharmony_ci} 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ciint ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, 24548c2ecf20Sopenharmony_ci struct ipu_image *in, struct ipu_image *out, 24558c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode) 24568c2ecf20Sopenharmony_ci{ 24578c2ecf20Sopenharmony_ci struct ipu_image_convert_run *run; 24588c2ecf20Sopenharmony_ci struct completion comp; 24598c2ecf20Sopenharmony_ci int ret; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci init_completion(&comp); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, 24648c2ecf20Sopenharmony_ci image_convert_sync_complete, &comp); 24658c2ecf20Sopenharmony_ci if (IS_ERR(run)) 24668c2ecf20Sopenharmony_ci return PTR_ERR(run); 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); 24698c2ecf20Sopenharmony_ci ret = (ret == 0) ? -ETIMEDOUT : 0; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci ipu_image_convert_unprepare(run->ctx); 24728c2ecf20Sopenharmony_ci kfree(run); 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci return ret; 24758c2ecf20Sopenharmony_ci} 24768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_image_convert_sync); 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ciint ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci struct ipu_image_convert_priv *priv; 24818c2ecf20Sopenharmony_ci int i; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 24848c2ecf20Sopenharmony_ci if (!priv) 24858c2ecf20Sopenharmony_ci return -ENOMEM; 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci ipu->image_convert_priv = priv; 24888c2ecf20Sopenharmony_ci priv->ipu = ipu; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci for (i = 0; i < IC_NUM_TASKS; i++) { 24918c2ecf20Sopenharmony_ci struct ipu_image_convert_chan *chan = &priv->chan[i]; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci chan->ic_task = i; 24948c2ecf20Sopenharmony_ci chan->priv = priv; 24958c2ecf20Sopenharmony_ci chan->dma_ch = &image_convert_dma_chan[i]; 24968c2ecf20Sopenharmony_ci chan->in_eof_irq = -1; 24978c2ecf20Sopenharmony_ci chan->rot_in_eof_irq = -1; 24988c2ecf20Sopenharmony_ci chan->out_eof_irq = -1; 24998c2ecf20Sopenharmony_ci chan->rot_out_eof_irq = -1; 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci spin_lock_init(&chan->irqlock); 25028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chan->ctx_list); 25038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chan->pending_q); 25048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chan->done_q); 25058c2ecf20Sopenharmony_ci } 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci return 0; 25088c2ecf20Sopenharmony_ci} 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_civoid ipu_image_convert_exit(struct ipu_soc *ipu) 25118c2ecf20Sopenharmony_ci{ 25128c2ecf20Sopenharmony_ci} 2513