Path: blob/master/drivers/gpu/ipu-v3/ipu-image-convert.c
26428 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2012-2016 Mentor Graphics Inc.3*4* Queued image conversion support, with tiling and rotation.5*/67#include <linux/interrupt.h>8#include <linux/dma-mapping.h>9#include <linux/math.h>1011#include <video/imx-ipu-image-convert.h>1213#include "ipu-prv.h"1415/*16* The IC Resizer has a restriction that the output frame from the17* resizer must be 1024 or less in both width (pixels) and height18* (lines).19*20* The image converter attempts to split up a conversion when21* the desired output (converted) frame resolution exceeds the22* IC resizer limit of 1024 in either dimension.23*24* If either dimension of the output frame exceeds the limit, the25* dimension is split into 1, 2, or 4 equal stripes, for a maximum26* of 4*4 or 16 tiles. A conversion is then carried out for each27* tile (but taking care to pass the full frame stride length to28* the DMA channel's parameter memory!). IDMA double-buffering is used29* to convert each tile back-to-back when possible (see note below30* when double_buffering boolean is set).31*32* Note that the input frame must be split up into the same number33* of tiles as the output frame:34*35* +---------+-----+36* +-----+---+ | A | B |37* | A | B | | | |38* +-----+---+ --> +---------+-----+39* | C | D | | C | D |40* +-----+---+ | | |41* +---------+-----+42*43* Clockwise 90° rotations are handled by first rescaling into a44* reusable temporary tile buffer and then rotating with the 8x845* block rotator, writing to the correct destination:46*47* +-----+-----+48* | | |49* +-----+---+ +---------+ | C | A |50* | A | B | | A,B, | | | | |51* +-----+---+ --> | C,D | | --> | | |52* | C | D | +---------+ +-----+-----+53* +-----+---+ | D | B |54* | | |55* +-----+-----+56*57* If the 8x8 block rotator is used, horizontal or vertical flipping58* is done during the rotation step, otherwise flipping is done59* during the scaling step.60* With rotation or flipping, tile order changes between input and61* output image. Tiles are numbered row major from top left to bottom62* right for both input and output image.63*/6465#define MAX_STRIPES_W 466#define MAX_STRIPES_H 467#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)6869#define MIN_W 1670#define MIN_H 871#define MAX_W 409672#define MAX_H 40967374enum ipu_image_convert_type {75IMAGE_CONVERT_IN = 0,76IMAGE_CONVERT_OUT,77};7879struct ipu_image_convert_dma_buf {80void *virt;81dma_addr_t phys;82unsigned long len;83};8485struct ipu_image_convert_dma_chan {86int in;87int out;88int rot_in;89int rot_out;90int vdi_in_p;91int vdi_in;92int vdi_in_n;93};9495/* dimensions of one tile */96struct ipu_image_tile {97u32 width;98u32 height;99u32 left;100u32 top;101/* size and strides are in bytes */102u32 size;103u32 stride;104u32 rot_stride;105/* start Y or packed offset of this tile */106u32 offset;107/* offset from start to tile in U plane, for planar formats */108u32 u_off;109/* offset from start to tile in V plane, for planar formats */110u32 v_off;111};112113struct ipu_image_convert_image {114struct ipu_image base;115enum ipu_image_convert_type type;116117const struct ipu_image_pixfmt *fmt;118unsigned int stride;119120/* # of rows (horizontal stripes) if dest height is > 1024 */121unsigned int num_rows;122/* # of columns (vertical stripes) if dest width is > 1024 */123unsigned int num_cols;124125struct ipu_image_tile tile[MAX_TILES];126};127128struct ipu_image_pixfmt {129u32 fourcc; /* V4L2 fourcc */130int bpp; /* total bpp */131int uv_width_dec; /* decimation in width for U/V planes */132int uv_height_dec; /* decimation in height for U/V planes */133bool planar; /* planar format */134bool uv_swapped; /* U and V planes are swapped */135bool uv_packed; /* partial planar (U and V in same plane) */136};137138struct ipu_image_convert_ctx;139struct ipu_image_convert_chan;140struct ipu_image_convert_priv;141142enum eof_irq_mask {143EOF_IRQ_IN = BIT(0),144EOF_IRQ_ROT_IN = BIT(1),145EOF_IRQ_OUT = BIT(2),146EOF_IRQ_ROT_OUT = BIT(3),147};148149#define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT)150#define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \151EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT)152153struct ipu_image_convert_ctx {154struct ipu_image_convert_chan *chan;155156ipu_image_convert_cb_t complete;157void *complete_context;158159/* Source/destination image data and rotation mode */160struct ipu_image_convert_image in;161struct ipu_image_convert_image out;162struct ipu_ic_csc csc;163enum ipu_rotate_mode rot_mode;164u32 downsize_coeff_h;165u32 downsize_coeff_v;166u32 image_resize_coeff_h;167u32 image_resize_coeff_v;168u32 resize_coeffs_h[MAX_STRIPES_W];169u32 resize_coeffs_v[MAX_STRIPES_H];170171/* intermediate buffer for rotation */172struct ipu_image_convert_dma_buf rot_intermediate[2];173174/* current buffer number for double buffering */175int cur_buf_num;176177bool aborting;178struct completion aborted;179180/* can we use double-buffering for this conversion operation? */181bool double_buffering;182/* num_rows * num_cols */183unsigned int num_tiles;184/* next tile to process */185unsigned int next_tile;186/* where to place converted tile in dest image */187unsigned int out_tile_map[MAX_TILES];188189/* mask of completed EOF irqs at every tile conversion */190enum eof_irq_mask eof_mask;191192struct list_head list;193};194195struct ipu_image_convert_chan {196struct ipu_image_convert_priv *priv;197198enum ipu_ic_task ic_task;199const struct ipu_image_convert_dma_chan *dma_ch;200201struct ipu_ic *ic;202struct ipuv3_channel *in_chan;203struct ipuv3_channel *out_chan;204struct ipuv3_channel *rotation_in_chan;205struct ipuv3_channel *rotation_out_chan;206207/* the IPU end-of-frame irqs */208int in_eof_irq;209int rot_in_eof_irq;210int out_eof_irq;211int rot_out_eof_irq;212213spinlock_t irqlock;214215/* list of convert contexts */216struct list_head ctx_list;217/* queue of conversion runs */218struct list_head pending_q;219/* queue of completed runs */220struct list_head done_q;221222/* the current conversion run */223struct ipu_image_convert_run *current_run;224};225226struct ipu_image_convert_priv {227struct ipu_image_convert_chan chan[IC_NUM_TASKS];228struct ipu_soc *ipu;229};230231static const struct ipu_image_convert_dma_chan232image_convert_dma_chan[IC_NUM_TASKS] = {233[IC_TASK_VIEWFINDER] = {234.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,235.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,236.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,237.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,238.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV,239.vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR,240.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT,241},242[IC_TASK_POST_PROCESSOR] = {243.in = IPUV3_CHANNEL_MEM_IC_PP,244.out = IPUV3_CHANNEL_IC_PP_MEM,245.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,246.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,247},248};249250static const struct ipu_image_pixfmt image_convert_formats[] = {251{252.fourcc = V4L2_PIX_FMT_RGB565,253.bpp = 16,254}, {255.fourcc = V4L2_PIX_FMT_RGB24,256.bpp = 24,257}, {258.fourcc = V4L2_PIX_FMT_BGR24,259.bpp = 24,260}, {261.fourcc = V4L2_PIX_FMT_RGB32,262.bpp = 32,263}, {264.fourcc = V4L2_PIX_FMT_BGR32,265.bpp = 32,266}, {267.fourcc = V4L2_PIX_FMT_XRGB32,268.bpp = 32,269}, {270.fourcc = V4L2_PIX_FMT_XBGR32,271.bpp = 32,272}, {273.fourcc = V4L2_PIX_FMT_BGRX32,274.bpp = 32,275}, {276.fourcc = V4L2_PIX_FMT_RGBX32,277.bpp = 32,278}, {279.fourcc = V4L2_PIX_FMT_YUYV,280.bpp = 16,281.uv_width_dec = 2,282.uv_height_dec = 1,283}, {284.fourcc = V4L2_PIX_FMT_UYVY,285.bpp = 16,286.uv_width_dec = 2,287.uv_height_dec = 1,288}, {289.fourcc = V4L2_PIX_FMT_YUV420,290.bpp = 12,291.planar = true,292.uv_width_dec = 2,293.uv_height_dec = 2,294}, {295.fourcc = V4L2_PIX_FMT_YVU420,296.bpp = 12,297.planar = true,298.uv_width_dec = 2,299.uv_height_dec = 2,300.uv_swapped = true,301}, {302.fourcc = V4L2_PIX_FMT_NV12,303.bpp = 12,304.planar = true,305.uv_width_dec = 2,306.uv_height_dec = 2,307.uv_packed = true,308}, {309.fourcc = V4L2_PIX_FMT_YUV422P,310.bpp = 16,311.planar = true,312.uv_width_dec = 2,313.uv_height_dec = 1,314}, {315.fourcc = V4L2_PIX_FMT_NV16,316.bpp = 16,317.planar = true,318.uv_width_dec = 2,319.uv_height_dec = 1,320.uv_packed = true,321},322};323324static const struct ipu_image_pixfmt *get_format(u32 fourcc)325{326const struct ipu_image_pixfmt *ret = NULL;327unsigned int i;328329for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) {330if (image_convert_formats[i].fourcc == fourcc) {331ret = &image_convert_formats[i];332break;333}334}335336return ret;337}338339static void dump_format(struct ipu_image_convert_ctx *ctx,340struct ipu_image_convert_image *ic_image)341{342struct ipu_image_convert_chan *chan = ctx->chan;343struct ipu_image_convert_priv *priv = chan->priv;344345dev_dbg(priv->ipu->dev,346"task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n",347chan->ic_task, ctx,348ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",349ic_image->base.pix.width, ic_image->base.pix.height,350ic_image->num_cols, ic_image->num_rows,351ic_image->fmt->fourcc & 0xff,352(ic_image->fmt->fourcc >> 8) & 0xff,353(ic_image->fmt->fourcc >> 16) & 0xff,354(ic_image->fmt->fourcc >> 24) & 0xff);355}356357static void free_dma_buf(struct ipu_image_convert_priv *priv,358struct ipu_image_convert_dma_buf *buf)359{360if (buf->virt)361dma_free_coherent(priv->ipu->dev,362buf->len, buf->virt, buf->phys);363buf->virt = NULL;364buf->phys = 0;365}366367static int alloc_dma_buf(struct ipu_image_convert_priv *priv,368struct ipu_image_convert_dma_buf *buf,369int size)370{371buf->len = PAGE_ALIGN(size);372buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,373GFP_DMA | GFP_KERNEL);374if (!buf->virt) {375dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");376return -ENOMEM;377}378379return 0;380}381382static inline int num_stripes(int dim)383{384return (dim - 1) / 1024 + 1;385}386387/*388* Calculate downsizing coefficients, which are the same for all tiles,389* and initial bilinear resizing coefficients, which are used to find the390* best seam positions.391* Also determine the number of tiles necessary to guarantee that no tile392* is larger than 1024 pixels in either dimension at the output and between393* IC downsizing and main processing sections.394*/395static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx,396struct ipu_image *in,397struct ipu_image *out)398{399u32 downsized_width = in->rect.width;400u32 downsized_height = in->rect.height;401u32 downsize_coeff_v = 0;402u32 downsize_coeff_h = 0;403u32 resized_width = out->rect.width;404u32 resized_height = out->rect.height;405u32 resize_coeff_h;406u32 resize_coeff_v;407u32 cols;408u32 rows;409410if (ipu_rot_mode_is_irt(ctx->rot_mode)) {411resized_width = out->rect.height;412resized_height = out->rect.width;413}414415/* Do not let invalid input lead to an endless loop below */416if (WARN_ON(resized_width == 0 || resized_height == 0))417return -EINVAL;418419while (downsized_width >= resized_width * 2) {420downsized_width >>= 1;421downsize_coeff_h++;422}423424while (downsized_height >= resized_height * 2) {425downsized_height >>= 1;426downsize_coeff_v++;427}428429/*430* Calculate the bilinear resizing coefficients that could be used if431* we were converting with a single tile. The bottom right output pixel432* should sample as close as possible to the bottom right input pixel433* out of the decimator, but not overshoot it:434*/435resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1);436resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1);437438/*439* Both the output of the IC downsizing section before being passed to440* the IC main processing section and the final output of the IC main441* processing section must be <= 1024 pixels in both dimensions.442*/443cols = num_stripes(max_t(u32, downsized_width, resized_width));444rows = num_stripes(max_t(u32, downsized_height, resized_height));445446dev_dbg(ctx->chan->priv->ipu->dev,447"%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n",448__func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v,449resize_coeff_v, cols, rows);450451if (downsize_coeff_h > 2 || downsize_coeff_v > 2 ||452resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff)453return -EINVAL;454455ctx->downsize_coeff_h = downsize_coeff_h;456ctx->downsize_coeff_v = downsize_coeff_v;457ctx->image_resize_coeff_h = resize_coeff_h;458ctx->image_resize_coeff_v = resize_coeff_v;459ctx->in.num_cols = cols;460ctx->in.num_rows = rows;461462return 0;463}464465#define round_closest(x, y) round_down((x) + (y)/2, (y))466467/*468* Find the best aligned seam position for the given column / row index.469* Rotation and image offsets are out of scope.470*471* @index: column / row index, used to calculate valid interval472* @in_edge: input right / bottom edge473* @out_edge: output right / bottom edge474* @in_align: input alignment, either horizontal 8-byte line start address475* alignment, or pixel alignment due to image format476* @out_align: output alignment, either horizontal 8-byte line start address477* alignment, or pixel alignment due to image format or rotator478* block size479* @in_burst: horizontal input burst size in case of horizontal flip480* @out_burst: horizontal output burst size or rotator block size481* @downsize_coeff: downsizing section coefficient482* @resize_coeff: main processing section resizing coefficient483* @_in_seam: aligned input seam position return value484* @_out_seam: aligned output seam position return value485*/486static void find_best_seam(struct ipu_image_convert_ctx *ctx,487unsigned int index,488unsigned int in_edge,489unsigned int out_edge,490unsigned int in_align,491unsigned int out_align,492unsigned int in_burst,493unsigned int out_burst,494unsigned int downsize_coeff,495unsigned int resize_coeff,496u32 *_in_seam,497u32 *_out_seam)498{499struct device *dev = ctx->chan->priv->ipu->dev;500unsigned int out_pos;501/* Input / output seam position candidates */502unsigned int out_seam = 0;503unsigned int in_seam = 0;504unsigned int min_diff = UINT_MAX;505unsigned int out_start;506unsigned int out_end;507unsigned int in_start;508unsigned int in_end;509510/* Start within 1024 pixels of the right / bottom edge */511out_start = max_t(int, index * out_align, out_edge - 1024);512/* End before having to add more columns to the left / rows above */513out_end = min_t(unsigned int, out_edge, index * 1024 + 1);514515/*516* Limit input seam position to make sure that the downsized input tile517* to the right or bottom does not exceed 1024 pixels.518*/519in_start = max_t(int, index * in_align,520in_edge - (1024 << downsize_coeff));521in_end = min_t(unsigned int, in_edge,522index * (1024 << downsize_coeff) + 1);523524/*525* Output tiles must start at a multiple of 8 bytes horizontally and526* possibly at an even line horizontally depending on the pixel format.527* Only consider output aligned positions for the seam.528*/529out_start = round_up(out_start, out_align);530for (out_pos = out_start; out_pos < out_end; out_pos += out_align) {531unsigned int in_pos;532unsigned int in_pos_aligned;533unsigned int in_pos_rounded;534unsigned int diff;535536/*537* Tiles in the right row / bottom column may not be allowed to538* overshoot horizontally / vertically. out_burst may be the539* actual DMA burst size, or the rotator block size.540*/541if ((out_burst > 1) && (out_edge - out_pos) % out_burst)542continue;543544/*545* Input sample position, corresponding to out_pos, 19.13 fixed546* point.547*/548in_pos = (out_pos * resize_coeff) << downsize_coeff;549/*550* The closest input sample position that we could actually551* start the input tile at, 19.13 fixed point.552*/553in_pos_aligned = round_closest(in_pos, 8192U * in_align);554/* Convert 19.13 fixed point to integer */555in_pos_rounded = in_pos_aligned / 8192U;556557if (in_pos_rounded < in_start)558continue;559if (in_pos_rounded >= in_end)560break;561562if ((in_burst > 1) &&563(in_edge - in_pos_rounded) % in_burst)564continue;565566diff = abs_diff(in_pos, in_pos_aligned);567if (diff < min_diff) {568in_seam = in_pos_rounded;569out_seam = out_pos;570min_diff = diff;571}572}573574*_out_seam = out_seam;575*_in_seam = in_seam;576577dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n",578__func__, out_seam, out_align, out_start, out_end,579in_seam, in_align, in_start, in_end, min_diff / 8192,580DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192));581}582583/*584* Tile left edges are required to be aligned to multiples of 8 bytes585* by the IDMAC.586*/587static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt)588{589if (fmt->planar)590return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec;591else592return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8;593}594595/*596* Tile top edge alignment is only limited by chroma subsampling.597*/598static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt)599{600return fmt->uv_height_dec > 1 ? 2 : 1;601}602603static inline u32 tile_width_align(enum ipu_image_convert_type type,604const struct ipu_image_pixfmt *fmt,605enum ipu_rotate_mode rot_mode)606{607if (type == IMAGE_CONVERT_IN) {608/*609* The IC burst reads 8 pixels at a time. Reading beyond the610* end of the line is usually acceptable. Those pixels are611* ignored, unless the IC has to write the scaled line in612* reverse.613*/614return (!ipu_rot_mode_is_irt(rot_mode) &&615(rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2;616}617618/*619* Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled620* formats to guarantee 8-byte aligned line start addresses in the621* chroma planes when IRT is used. Align to 8x8 pixel IRT block size622* for all other formats.623*/624return (ipu_rot_mode_is_irt(rot_mode) &&625fmt->planar && !fmt->uv_packed) ?6268 * fmt->uv_width_dec : 8;627}628629static inline u32 tile_height_align(enum ipu_image_convert_type type,630const struct ipu_image_pixfmt *fmt,631enum ipu_rotate_mode rot_mode)632{633if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode))634return 2;635636/*637* Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled638* formats to guarantee 8-byte aligned line start addresses in the639* chroma planes when IRT is used. Align to 8x8 pixel IRT block size640* for all other formats.641*/642return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8;643}644645/*646* Fill in left position and width and for all tiles in an input column, and647* for all corresponding output tiles. If the 90° rotator is used, the output648* tiles are in a row, and output tile top position and height are set.649*/650static void fill_tile_column(struct ipu_image_convert_ctx *ctx,651unsigned int col,652struct ipu_image_convert_image *in,653unsigned int in_left, unsigned int in_width,654struct ipu_image_convert_image *out,655unsigned int out_left, unsigned int out_width)656{657unsigned int row, tile_idx;658struct ipu_image_tile *in_tile, *out_tile;659660for (row = 0; row < in->num_rows; row++) {661tile_idx = in->num_cols * row + col;662in_tile = &in->tile[tile_idx];663out_tile = &out->tile[ctx->out_tile_map[tile_idx]];664665in_tile->left = in_left;666in_tile->width = in_width;667668if (ipu_rot_mode_is_irt(ctx->rot_mode)) {669out_tile->top = out_left;670out_tile->height = out_width;671} else {672out_tile->left = out_left;673out_tile->width = out_width;674}675}676}677678/*679* Fill in top position and height and for all tiles in an input row, and680* for all corresponding output tiles. If the 90° rotator is used, the output681* tiles are in a column, and output tile left position and width are set.682*/683static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row,684struct ipu_image_convert_image *in,685unsigned int in_top, unsigned int in_height,686struct ipu_image_convert_image *out,687unsigned int out_top, unsigned int out_height)688{689unsigned int col, tile_idx;690struct ipu_image_tile *in_tile, *out_tile;691692for (col = 0; col < in->num_cols; col++) {693tile_idx = in->num_cols * row + col;694in_tile = &in->tile[tile_idx];695out_tile = &out->tile[ctx->out_tile_map[tile_idx]];696697in_tile->top = in_top;698in_tile->height = in_height;699700if (ipu_rot_mode_is_irt(ctx->rot_mode)) {701out_tile->left = out_top;702out_tile->width = out_height;703} else {704out_tile->top = out_top;705out_tile->height = out_height;706}707}708}709710/*711* Find the best horizontal and vertical seam positions to split into tiles.712* Minimize the fractional part of the input sampling position for the713* top / left pixels of each tile.714*/715static void find_seams(struct ipu_image_convert_ctx *ctx,716struct ipu_image_convert_image *in,717struct ipu_image_convert_image *out)718{719struct device *dev = ctx->chan->priv->ipu->dev;720unsigned int resized_width = out->base.rect.width;721unsigned int resized_height = out->base.rect.height;722unsigned int col;723unsigned int row;724unsigned int in_left_align = tile_left_align(in->fmt);725unsigned int in_top_align = tile_top_align(in->fmt);726unsigned int out_left_align = tile_left_align(out->fmt);727unsigned int out_top_align = tile_top_align(out->fmt);728unsigned int out_width_align = tile_width_align(out->type, out->fmt,729ctx->rot_mode);730unsigned int out_height_align = tile_height_align(out->type, out->fmt,731ctx->rot_mode);732unsigned int in_right = in->base.rect.width;733unsigned int in_bottom = in->base.rect.height;734unsigned int out_right = out->base.rect.width;735unsigned int out_bottom = out->base.rect.height;736unsigned int flipped_out_left;737unsigned int flipped_out_top;738739if (ipu_rot_mode_is_irt(ctx->rot_mode)) {740/* Switch width/height and align top left to IRT block size */741resized_width = out->base.rect.height;742resized_height = out->base.rect.width;743out_left_align = out_height_align;744out_top_align = out_width_align;745out_width_align = out_left_align;746out_height_align = out_top_align;747out_right = out->base.rect.height;748out_bottom = out->base.rect.width;749}750751for (col = in->num_cols - 1; col > 0; col--) {752bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) ||753!(ctx->rot_mode & IPU_ROT_BIT_HFLIP);754bool allow_out_overshoot = (col < in->num_cols - 1) &&755!(ctx->rot_mode & IPU_ROT_BIT_HFLIP);756unsigned int in_left;757unsigned int out_left;758759/*760* Align input width to burst length if the scaling step flips761* horizontally.762*/763764find_best_seam(ctx, col,765in_right, out_right,766in_left_align, out_left_align,767allow_in_overshoot ? 1 : 8 /* burst length */,768allow_out_overshoot ? 1 : out_width_align,769ctx->downsize_coeff_h, ctx->image_resize_coeff_h,770&in_left, &out_left);771772if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)773flipped_out_left = resized_width - out_right;774else775flipped_out_left = out_left;776777fill_tile_column(ctx, col, in, in_left, in_right - in_left,778out, flipped_out_left, out_right - out_left);779780dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col,781in_left, in_right - in_left,782flipped_out_left, out_right - out_left);783784in_right = in_left;785out_right = out_left;786}787788flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ?789resized_width - out_right : 0;790791fill_tile_column(ctx, 0, in, 0, in_right,792out, flipped_out_left, out_right);793794dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__,795in_right, flipped_out_left, out_right);796797for (row = in->num_rows - 1; row > 0; row--) {798bool allow_overshoot = row < in->num_rows - 1;799unsigned int in_top;800unsigned int out_top;801802find_best_seam(ctx, row,803in_bottom, out_bottom,804in_top_align, out_top_align,8051, allow_overshoot ? 1 : out_height_align,806ctx->downsize_coeff_v, ctx->image_resize_coeff_v,807&in_top, &out_top);808809if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^810ipu_rot_mode_is_irt(ctx->rot_mode))811flipped_out_top = resized_height - out_bottom;812else813flipped_out_top = out_top;814815fill_tile_row(ctx, row, in, in_top, in_bottom - in_top,816out, flipped_out_top, out_bottom - out_top);817818dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row,819in_top, in_bottom - in_top,820flipped_out_top, out_bottom - out_top);821822in_bottom = in_top;823out_bottom = out_top;824}825826if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^827ipu_rot_mode_is_irt(ctx->rot_mode))828flipped_out_top = resized_height - out_bottom;829else830flipped_out_top = 0;831832fill_tile_row(ctx, 0, in, 0, in_bottom,833out, flipped_out_top, out_bottom);834835dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__,836in_bottom, flipped_out_top, out_bottom);837}838839static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx,840struct ipu_image_convert_image *image)841{842struct ipu_image_convert_chan *chan = ctx->chan;843struct ipu_image_convert_priv *priv = chan->priv;844unsigned int max_width = 1024;845unsigned int max_height = 1024;846unsigned int i;847848if (image->type == IMAGE_CONVERT_IN) {849/* Up to 4096x4096 input tile size */850max_width <<= ctx->downsize_coeff_h;851max_height <<= ctx->downsize_coeff_v;852}853854for (i = 0; i < ctx->num_tiles; i++) {855struct ipu_image_tile *tile;856const unsigned int row = i / image->num_cols;857const unsigned int col = i % image->num_cols;858859if (image->type == IMAGE_CONVERT_OUT)860tile = &image->tile[ctx->out_tile_map[i]];861else862tile = &image->tile[i];863864tile->size = ((tile->height * image->fmt->bpp) >> 3) *865tile->width;866867if (image->fmt->planar) {868tile->stride = tile->width;869tile->rot_stride = tile->height;870} else {871tile->stride =872(image->fmt->bpp * tile->width) >> 3;873tile->rot_stride =874(image->fmt->bpp * tile->height) >> 3;875}876877dev_dbg(priv->ipu->dev,878"task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n",879chan->ic_task, ctx,880image->type == IMAGE_CONVERT_IN ? "Input" : "Output",881row, col,882tile->width, tile->height, tile->left, tile->top);883884if (!tile->width || tile->width > max_width ||885!tile->height || tile->height > max_height) {886dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n",887image->type == IMAGE_CONVERT_IN ? "input" :888"output", tile->width, tile->height);889return -EINVAL;890}891}892893return 0;894}895896/*897* Use the rotation transformation to find the tile coordinates898* (row, col) of a tile in the destination frame that corresponds899* to the given tile coordinates of a source frame. The destination900* coordinate is then converted to a tile index.901*/902static int transform_tile_index(struct ipu_image_convert_ctx *ctx,903int src_row, int src_col)904{905struct ipu_image_convert_chan *chan = ctx->chan;906struct ipu_image_convert_priv *priv = chan->priv;907struct ipu_image_convert_image *s_image = &ctx->in;908struct ipu_image_convert_image *d_image = &ctx->out;909int dst_row, dst_col;910911/* with no rotation it's a 1:1 mapping */912if (ctx->rot_mode == IPU_ROTATE_NONE)913return src_row * s_image->num_cols + src_col;914915/*916* before doing the transform, first we have to translate917* source row,col for an origin in the center of s_image918*/919src_row = src_row * 2 - (s_image->num_rows - 1);920src_col = src_col * 2 - (s_image->num_cols - 1);921922/* do the rotation transform */923if (ctx->rot_mode & IPU_ROT_BIT_90) {924dst_col = -src_row;925dst_row = src_col;926} else {927dst_col = src_col;928dst_row = src_row;929}930931/* apply flip */932if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)933dst_col = -dst_col;934if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)935dst_row = -dst_row;936937dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n",938chan->ic_task, ctx, src_col, src_row, dst_col, dst_row);939940/*941* finally translate dest row,col using an origin in upper942* left of d_image943*/944dst_row += d_image->num_rows - 1;945dst_col += d_image->num_cols - 1;946dst_row /= 2;947dst_col /= 2;948949return dst_row * d_image->num_cols + dst_col;950}951952/*953* Fill the out_tile_map[] with transformed destination tile indeces.954*/955static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx)956{957struct ipu_image_convert_image *s_image = &ctx->in;958unsigned int row, col, tile = 0;959960for (row = 0; row < s_image->num_rows; row++) {961for (col = 0; col < s_image->num_cols; col++) {962ctx->out_tile_map[tile] =963transform_tile_index(ctx, row, col);964tile++;965}966}967}968969static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx,970struct ipu_image_convert_image *image)971{972struct ipu_image_convert_chan *chan = ctx->chan;973struct ipu_image_convert_priv *priv = chan->priv;974const struct ipu_image_pixfmt *fmt = image->fmt;975unsigned int row, col, tile = 0;976u32 H, top, y_stride, uv_stride;977u32 uv_row_off, uv_col_off, uv_off, u_off, v_off;978u32 y_row_off, y_col_off, y_off;979u32 y_size, uv_size;980981/* setup some convenience vars */982H = image->base.pix.height;983984y_stride = image->stride;985uv_stride = y_stride / fmt->uv_width_dec;986if (fmt->uv_packed)987uv_stride *= 2;988989y_size = H * y_stride;990uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);991992for (row = 0; row < image->num_rows; row++) {993top = image->tile[tile].top;994y_row_off = top * y_stride;995uv_row_off = (top * uv_stride) / fmt->uv_height_dec;996997for (col = 0; col < image->num_cols; col++) {998y_col_off = image->tile[tile].left;999uv_col_off = y_col_off / fmt->uv_width_dec;1000if (fmt->uv_packed)1001uv_col_off *= 2;10021003y_off = y_row_off + y_col_off;1004uv_off = uv_row_off + uv_col_off;10051006u_off = y_size - y_off + uv_off;1007v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;1008if (fmt->uv_swapped)1009swap(u_off, v_off);10101011image->tile[tile].offset = y_off;1012image->tile[tile].u_off = u_off;1013image->tile[tile++].v_off = v_off;10141015if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) {1016dev_err(priv->ipu->dev,1017"task %u: ctx %p: %s@[%d,%d]: "1018"y_off %08x, u_off %08x, v_off %08x\n",1019chan->ic_task, ctx,1020image->type == IMAGE_CONVERT_IN ?1021"Input" : "Output", row, col,1022y_off, u_off, v_off);1023return -EINVAL;1024}1025}1026}10271028return 0;1029}10301031static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx,1032struct ipu_image_convert_image *image)1033{1034struct ipu_image_convert_chan *chan = ctx->chan;1035struct ipu_image_convert_priv *priv = chan->priv;1036const struct ipu_image_pixfmt *fmt = image->fmt;1037unsigned int row, col, tile = 0;1038u32 bpp, stride, offset;1039u32 row_off, col_off;10401041/* setup some convenience vars */1042stride = image->stride;1043bpp = fmt->bpp;10441045for (row = 0; row < image->num_rows; row++) {1046row_off = image->tile[tile].top * stride;10471048for (col = 0; col < image->num_cols; col++) {1049col_off = (image->tile[tile].left * bpp) >> 3;10501051offset = row_off + col_off;10521053image->tile[tile].offset = offset;1054image->tile[tile].u_off = 0;1055image->tile[tile++].v_off = 0;10561057if (offset & 0x7) {1058dev_err(priv->ipu->dev,1059"task %u: ctx %p: %s@[%d,%d]: "1060"phys %08x\n",1061chan->ic_task, ctx,1062image->type == IMAGE_CONVERT_IN ?1063"Input" : "Output", row, col,1064row_off + col_off);1065return -EINVAL;1066}1067}1068}10691070return 0;1071}10721073static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx,1074struct ipu_image_convert_image *image)1075{1076if (image->fmt->planar)1077return calc_tile_offsets_planar(ctx, image);10781079return calc_tile_offsets_packed(ctx, image);1080}10811082/*1083* Calculate the resizing ratio for the IC main processing section given input1084* size, fixed downsizing coefficient, and output size.1085* Either round to closest for the next tile's first pixel to minimize seams1086* and distortion (for all but right column / bottom row), or round down to1087* avoid sampling beyond the edges of the input image for this tile's last1088* pixel.1089* Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff.1090*/1091static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff,1092u32 output_size, bool allow_overshoot)1093{1094u32 downsized = input_size >> downsize_coeff;10951096if (allow_overshoot)1097return DIV_ROUND_CLOSEST(8192 * downsized, output_size);1098else1099return 8192 * (downsized - 1) / (output_size - 1);1100}11011102/*1103* Slightly modify resize coefficients per tile to hide the bilinear1104* interpolator reset at tile borders, shifting the right / bottom edge1105* by up to a half input pixel. This removes noticeable seams between1106* tiles at higher upscaling factors.1107*/1108static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx)1109{1110struct ipu_image_convert_chan *chan = ctx->chan;1111struct ipu_image_convert_priv *priv = chan->priv;1112struct ipu_image_tile *in_tile, *out_tile;1113unsigned int col, row, tile_idx;1114unsigned int last_output;11151116for (col = 0; col < ctx->in.num_cols; col++) {1117bool closest = (col < ctx->in.num_cols - 1) &&1118!(ctx->rot_mode & IPU_ROT_BIT_HFLIP);1119u32 resized_width;1120u32 resize_coeff_h;1121u32 in_width;11221123tile_idx = col;1124in_tile = &ctx->in.tile[tile_idx];1125out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];11261127if (ipu_rot_mode_is_irt(ctx->rot_mode))1128resized_width = out_tile->height;1129else1130resized_width = out_tile->width;11311132resize_coeff_h = calc_resize_coeff(in_tile->width,1133ctx->downsize_coeff_h,1134resized_width, closest);11351136dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n",1137__func__, col, resize_coeff_h);11381139/*1140* With the horizontal scaling factor known, round up resized1141* width (output width or height) to burst size.1142*/1143resized_width = round_up(resized_width, 8);11441145/*1146* Calculate input width from the last accessed input pixel1147* given resized width and scaling coefficients. Round up to1148* burst size.1149*/1150last_output = resized_width - 1;1151if (closest && ((last_output * resize_coeff_h) % 8192))1152last_output++;1153in_width = round_up(1154(DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1)1155<< ctx->downsize_coeff_h, 8);11561157for (row = 0; row < ctx->in.num_rows; row++) {1158tile_idx = row * ctx->in.num_cols + col;1159in_tile = &ctx->in.tile[tile_idx];1160out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];11611162if (ipu_rot_mode_is_irt(ctx->rot_mode))1163out_tile->height = resized_width;1164else1165out_tile->width = resized_width;11661167in_tile->width = in_width;1168}11691170ctx->resize_coeffs_h[col] = resize_coeff_h;1171}11721173for (row = 0; row < ctx->in.num_rows; row++) {1174bool closest = (row < ctx->in.num_rows - 1) &&1175!(ctx->rot_mode & IPU_ROT_BIT_VFLIP);1176u32 resized_height;1177u32 resize_coeff_v;1178u32 in_height;11791180tile_idx = row * ctx->in.num_cols;1181in_tile = &ctx->in.tile[tile_idx];1182out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];11831184if (ipu_rot_mode_is_irt(ctx->rot_mode))1185resized_height = out_tile->width;1186else1187resized_height = out_tile->height;11881189resize_coeff_v = calc_resize_coeff(in_tile->height,1190ctx->downsize_coeff_v,1191resized_height, closest);11921193dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n",1194__func__, row, resize_coeff_v);11951196/*1197* With the vertical scaling factor known, round up resized1198* height (output width or height) to IDMAC limitations.1199*/1200resized_height = round_up(resized_height, 2);12011202/*1203* Calculate input width from the last accessed input pixel1204* given resized height and scaling coefficients. Align to1205* IDMAC restrictions.1206*/1207last_output = resized_height - 1;1208if (closest && ((last_output * resize_coeff_v) % 8192))1209last_output++;1210in_height = round_up(1211(DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1)1212<< ctx->downsize_coeff_v, 2);12131214for (col = 0; col < ctx->in.num_cols; col++) {1215tile_idx = row * ctx->in.num_cols + col;1216in_tile = &ctx->in.tile[tile_idx];1217out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]];12181219if (ipu_rot_mode_is_irt(ctx->rot_mode))1220out_tile->width = resized_height;1221else1222out_tile->height = resized_height;12231224in_tile->height = in_height;1225}12261227ctx->resize_coeffs_v[row] = resize_coeff_v;1228}1229}12301231/*1232* return the number of runs in given queue (pending_q or done_q)1233* for this context. hold irqlock when calling.1234*/1235static int get_run_count(struct ipu_image_convert_ctx *ctx,1236struct list_head *q)1237{1238struct ipu_image_convert_run *run;1239int count = 0;12401241lockdep_assert_held(&ctx->chan->irqlock);12421243list_for_each_entry(run, q, list) {1244if (run->ctx == ctx)1245count++;1246}12471248return count;1249}12501251static void convert_stop(struct ipu_image_convert_run *run)1252{1253struct ipu_image_convert_ctx *ctx = run->ctx;1254struct ipu_image_convert_chan *chan = ctx->chan;1255struct ipu_image_convert_priv *priv = chan->priv;12561257dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n",1258__func__, chan->ic_task, ctx, run);12591260/* disable IC tasks and the channels */1261ipu_ic_task_disable(chan->ic);1262ipu_idmac_disable_channel(chan->in_chan);1263ipu_idmac_disable_channel(chan->out_chan);12641265if (ipu_rot_mode_is_irt(ctx->rot_mode)) {1266ipu_idmac_disable_channel(chan->rotation_in_chan);1267ipu_idmac_disable_channel(chan->rotation_out_chan);1268ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan);1269}12701271ipu_ic_disable(chan->ic);1272}12731274static void init_idmac_channel(struct ipu_image_convert_ctx *ctx,1275struct ipuv3_channel *channel,1276struct ipu_image_convert_image *image,1277enum ipu_rotate_mode rot_mode,1278bool rot_swap_width_height,1279unsigned int tile)1280{1281struct ipu_image_convert_chan *chan = ctx->chan;1282unsigned int burst_size;1283u32 width, height, stride;1284dma_addr_t addr0, addr1 = 0;1285struct ipu_image tile_image;1286unsigned int tile_idx[2];12871288if (image->type == IMAGE_CONVERT_OUT) {1289tile_idx[0] = ctx->out_tile_map[tile];1290tile_idx[1] = ctx->out_tile_map[1];1291} else {1292tile_idx[0] = tile;1293tile_idx[1] = 1;1294}12951296if (rot_swap_width_height) {1297width = image->tile[tile_idx[0]].height;1298height = image->tile[tile_idx[0]].width;1299stride = image->tile[tile_idx[0]].rot_stride;1300addr0 = ctx->rot_intermediate[0].phys;1301if (ctx->double_buffering)1302addr1 = ctx->rot_intermediate[1].phys;1303} else {1304width = image->tile[tile_idx[0]].width;1305height = image->tile[tile_idx[0]].height;1306stride = image->stride;1307addr0 = image->base.phys0 +1308image->tile[tile_idx[0]].offset;1309if (ctx->double_buffering)1310addr1 = image->base.phys0 +1311image->tile[tile_idx[1]].offset;1312}13131314ipu_cpmem_zero(channel);13151316memset(&tile_image, 0, sizeof(tile_image));1317tile_image.pix.width = tile_image.rect.width = width;1318tile_image.pix.height = tile_image.rect.height = height;1319tile_image.pix.bytesperline = stride;1320tile_image.pix.pixelformat = image->fmt->fourcc;1321tile_image.phys0 = addr0;1322tile_image.phys1 = addr1;1323if (image->fmt->planar && !rot_swap_width_height) {1324tile_image.u_offset = image->tile[tile_idx[0]].u_off;1325tile_image.v_offset = image->tile[tile_idx[0]].v_off;1326}13271328ipu_cpmem_set_image(channel, &tile_image);13291330if (rot_mode)1331ipu_cpmem_set_rotation(channel, rot_mode);13321333/*1334* Skip writing U and V components to odd rows in the output1335* channels for planar 4:2:0.1336*/1337if ((channel == chan->out_chan ||1338channel == chan->rotation_out_chan) &&1339image->fmt->planar && image->fmt->uv_height_dec == 2)1340ipu_cpmem_skip_odd_chroma_rows(channel);13411342if (channel == chan->rotation_in_chan ||1343channel == chan->rotation_out_chan) {1344burst_size = 8;1345ipu_cpmem_set_block_mode(channel);1346} else1347burst_size = (width % 16) ? 8 : 16;13481349ipu_cpmem_set_burstsize(channel, burst_size);13501351ipu_ic_task_idma_init(chan->ic, channel, width, height,1352burst_size, rot_mode);13531354/*1355* Setting a non-zero AXI ID collides with the PRG AXI snooping, so1356* only do this when there is no PRG present.1357*/1358if (!channel->ipu->prg_priv)1359ipu_cpmem_set_axi_id(channel, 1);13601361ipu_idmac_set_double_buffer(channel, ctx->double_buffering);1362}13631364static int convert_start(struct ipu_image_convert_run *run, unsigned int tile)1365{1366struct ipu_image_convert_ctx *ctx = run->ctx;1367struct ipu_image_convert_chan *chan = ctx->chan;1368struct ipu_image_convert_priv *priv = chan->priv;1369struct ipu_image_convert_image *s_image = &ctx->in;1370struct ipu_image_convert_image *d_image = &ctx->out;1371unsigned int dst_tile = ctx->out_tile_map[tile];1372unsigned int dest_width, dest_height;1373unsigned int col, row;1374u32 rsc;1375int ret;13761377dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n",1378__func__, chan->ic_task, ctx, run, tile, dst_tile);13791380/* clear EOF irq mask */1381ctx->eof_mask = 0;13821383if (ipu_rot_mode_is_irt(ctx->rot_mode)) {1384/* swap width/height for resizer */1385dest_width = d_image->tile[dst_tile].height;1386dest_height = d_image->tile[dst_tile].width;1387} else {1388dest_width = d_image->tile[dst_tile].width;1389dest_height = d_image->tile[dst_tile].height;1390}13911392row = tile / s_image->num_cols;1393col = tile % s_image->num_cols;13941395rsc = (ctx->downsize_coeff_v << 30) |1396(ctx->resize_coeffs_v[row] << 16) |1397(ctx->downsize_coeff_h << 14) |1398(ctx->resize_coeffs_h[col]);13991400dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n",1401__func__, s_image->tile[tile].width,1402s_image->tile[tile].height, dest_width, dest_height, rsc);14031404/* setup the IC resizer and CSC */1405ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc,1406s_image->tile[tile].width,1407s_image->tile[tile].height,1408dest_width,1409dest_height,1410rsc);1411if (ret) {1412dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);1413return ret;1414}14151416/* init the source MEM-->IC PP IDMAC channel */1417init_idmac_channel(ctx, chan->in_chan, s_image,1418IPU_ROTATE_NONE, false, tile);14191420if (ipu_rot_mode_is_irt(ctx->rot_mode)) {1421/* init the IC PP-->MEM IDMAC channel */1422init_idmac_channel(ctx, chan->out_chan, d_image,1423IPU_ROTATE_NONE, true, tile);14241425/* init the MEM-->IC PP ROT IDMAC channel */1426init_idmac_channel(ctx, chan->rotation_in_chan, d_image,1427ctx->rot_mode, true, tile);14281429/* init the destination IC PP ROT-->MEM IDMAC channel */1430init_idmac_channel(ctx, chan->rotation_out_chan, d_image,1431IPU_ROTATE_NONE, false, tile);14321433/* now link IC PP-->MEM to MEM-->IC PP ROT */1434ipu_idmac_link(chan->out_chan, chan->rotation_in_chan);1435} else {1436/* init the destination IC PP-->MEM IDMAC channel */1437init_idmac_channel(ctx, chan->out_chan, d_image,1438ctx->rot_mode, false, tile);1439}14401441/* enable the IC */1442ipu_ic_enable(chan->ic);14431444/* set buffers ready */1445ipu_idmac_select_buffer(chan->in_chan, 0);1446ipu_idmac_select_buffer(chan->out_chan, 0);1447if (ipu_rot_mode_is_irt(ctx->rot_mode))1448ipu_idmac_select_buffer(chan->rotation_out_chan, 0);1449if (ctx->double_buffering) {1450ipu_idmac_select_buffer(chan->in_chan, 1);1451ipu_idmac_select_buffer(chan->out_chan, 1);1452if (ipu_rot_mode_is_irt(ctx->rot_mode))1453ipu_idmac_select_buffer(chan->rotation_out_chan, 1);1454}14551456/* enable the channels! */1457ipu_idmac_enable_channel(chan->in_chan);1458ipu_idmac_enable_channel(chan->out_chan);1459if (ipu_rot_mode_is_irt(ctx->rot_mode)) {1460ipu_idmac_enable_channel(chan->rotation_in_chan);1461ipu_idmac_enable_channel(chan->rotation_out_chan);1462}14631464ipu_ic_task_enable(chan->ic);14651466ipu_cpmem_dump(chan->in_chan);1467ipu_cpmem_dump(chan->out_chan);1468if (ipu_rot_mode_is_irt(ctx->rot_mode)) {1469ipu_cpmem_dump(chan->rotation_in_chan);1470ipu_cpmem_dump(chan->rotation_out_chan);1471}14721473ipu_dump(priv->ipu);14741475return 0;1476}14771478/* hold irqlock when calling */1479static int do_run(struct ipu_image_convert_run *run)1480{1481struct ipu_image_convert_ctx *ctx = run->ctx;1482struct ipu_image_convert_chan *chan = ctx->chan;14831484lockdep_assert_held(&chan->irqlock);14851486ctx->in.base.phys0 = run->in_phys;1487ctx->out.base.phys0 = run->out_phys;14881489ctx->cur_buf_num = 0;1490ctx->next_tile = 1;14911492/* remove run from pending_q and set as current */1493list_del(&run->list);1494chan->current_run = run;14951496return convert_start(run, 0);1497}14981499/* hold irqlock when calling */1500static void run_next(struct ipu_image_convert_chan *chan)1501{1502struct ipu_image_convert_priv *priv = chan->priv;1503struct ipu_image_convert_run *run, *tmp;1504int ret;15051506lockdep_assert_held(&chan->irqlock);15071508list_for_each_entry_safe(run, tmp, &chan->pending_q, list) {1509/* skip contexts that are aborting */1510if (run->ctx->aborting) {1511dev_dbg(priv->ipu->dev,1512"%s: task %u: skipping aborting ctx %p run %p\n",1513__func__, chan->ic_task, run->ctx, run);1514continue;1515}15161517ret = do_run(run);1518if (!ret)1519break;15201521/*1522* something went wrong with start, add the run1523* to done q and continue to the next run in the1524* pending q.1525*/1526run->status = ret;1527list_add_tail(&run->list, &chan->done_q);1528chan->current_run = NULL;1529}1530}15311532static void empty_done_q(struct ipu_image_convert_chan *chan)1533{1534struct ipu_image_convert_priv *priv = chan->priv;1535struct ipu_image_convert_run *run;1536unsigned long flags;15371538spin_lock_irqsave(&chan->irqlock, flags);15391540while (!list_empty(&chan->done_q)) {1541run = list_entry(chan->done_q.next,1542struct ipu_image_convert_run,1543list);15441545list_del(&run->list);15461547dev_dbg(priv->ipu->dev,1548"%s: task %u: completing ctx %p run %p with %d\n",1549__func__, chan->ic_task, run->ctx, run, run->status);15501551/* call the completion callback and free the run */1552spin_unlock_irqrestore(&chan->irqlock, flags);1553run->ctx->complete(run, run->ctx->complete_context);1554spin_lock_irqsave(&chan->irqlock, flags);1555}15561557spin_unlock_irqrestore(&chan->irqlock, flags);1558}15591560/*1561* the bottom half thread clears out the done_q, calling the1562* completion handler for each.1563*/1564static irqreturn_t do_bh(int irq, void *dev_id)1565{1566struct ipu_image_convert_chan *chan = dev_id;1567struct ipu_image_convert_priv *priv = chan->priv;1568struct ipu_image_convert_ctx *ctx;1569unsigned long flags;15701571dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__,1572chan->ic_task);15731574empty_done_q(chan);15751576spin_lock_irqsave(&chan->irqlock, flags);15771578/*1579* the done_q is cleared out, signal any contexts1580* that are aborting that abort can complete.1581*/1582list_for_each_entry(ctx, &chan->ctx_list, list) {1583if (ctx->aborting) {1584dev_dbg(priv->ipu->dev,1585"%s: task %u: signaling abort for ctx %p\n",1586__func__, chan->ic_task, ctx);1587complete_all(&ctx->aborted);1588}1589}15901591spin_unlock_irqrestore(&chan->irqlock, flags);15921593dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__,1594chan->ic_task);15951596return IRQ_HANDLED;1597}15981599static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx)1600{1601unsigned int cur_tile = ctx->next_tile - 1;1602unsigned int next_tile = ctx->next_tile;16031604if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] !=1605ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] ||1606ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] !=1607ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] ||1608ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width ||1609ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height ||1610ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width ||1611ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height)1612return true;16131614return false;1615}16161617/* hold irqlock when calling */1618static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run)1619{1620struct ipu_image_convert_ctx *ctx = run->ctx;1621struct ipu_image_convert_chan *chan = ctx->chan;1622struct ipu_image_tile *src_tile, *dst_tile;1623struct ipu_image_convert_image *s_image = &ctx->in;1624struct ipu_image_convert_image *d_image = &ctx->out;1625struct ipuv3_channel *outch;1626unsigned int dst_idx;16271628lockdep_assert_held(&chan->irqlock);16291630outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?1631chan->rotation_out_chan : chan->out_chan;16321633/*1634* It is difficult to stop the channel DMA before the channels1635* enter the paused state. Without double-buffering the channels1636* are always in a paused state when the EOF irq occurs, so it1637* is safe to stop the channels now. For double-buffering we1638* just ignore the abort until the operation completes, when it1639* is safe to shut down.1640*/1641if (ctx->aborting && !ctx->double_buffering) {1642convert_stop(run);1643run->status = -EIO;1644goto done;1645}16461647if (ctx->next_tile == ctx->num_tiles) {1648/*1649* the conversion is complete1650*/1651convert_stop(run);1652run->status = 0;1653goto done;1654}16551656/*1657* not done, place the next tile buffers.1658*/1659if (!ctx->double_buffering) {1660if (ic_settings_changed(ctx)) {1661convert_stop(run);1662convert_start(run, ctx->next_tile);1663} else {1664src_tile = &s_image->tile[ctx->next_tile];1665dst_idx = ctx->out_tile_map[ctx->next_tile];1666dst_tile = &d_image->tile[dst_idx];16671668ipu_cpmem_set_buffer(chan->in_chan, 0,1669s_image->base.phys0 +1670src_tile->offset);1671ipu_cpmem_set_buffer(outch, 0,1672d_image->base.phys0 +1673dst_tile->offset);1674if (s_image->fmt->planar)1675ipu_cpmem_set_uv_offset(chan->in_chan,1676src_tile->u_off,1677src_tile->v_off);1678if (d_image->fmt->planar)1679ipu_cpmem_set_uv_offset(outch,1680dst_tile->u_off,1681dst_tile->v_off);16821683ipu_idmac_select_buffer(chan->in_chan, 0);1684ipu_idmac_select_buffer(outch, 0);1685}1686} else if (ctx->next_tile < ctx->num_tiles - 1) {16871688src_tile = &s_image->tile[ctx->next_tile + 1];1689dst_idx = ctx->out_tile_map[ctx->next_tile + 1];1690dst_tile = &d_image->tile[dst_idx];16911692ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num,1693s_image->base.phys0 + src_tile->offset);1694ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,1695d_image->base.phys0 + dst_tile->offset);16961697ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num);1698ipu_idmac_select_buffer(outch, ctx->cur_buf_num);16991700ctx->cur_buf_num ^= 1;1701}17021703ctx->eof_mask = 0; /* clear EOF irq mask for next tile */1704ctx->next_tile++;1705return IRQ_HANDLED;1706done:1707list_add_tail(&run->list, &chan->done_q);1708chan->current_run = NULL;1709run_next(chan);1710return IRQ_WAKE_THREAD;1711}17121713static irqreturn_t eof_irq(int irq, void *data)1714{1715struct ipu_image_convert_chan *chan = data;1716struct ipu_image_convert_priv *priv = chan->priv;1717struct ipu_image_convert_ctx *ctx;1718struct ipu_image_convert_run *run;1719irqreturn_t ret = IRQ_HANDLED;1720bool tile_complete = false;1721unsigned long flags;17221723spin_lock_irqsave(&chan->irqlock, flags);17241725/* get current run and its context */1726run = chan->current_run;1727if (!run) {1728ret = IRQ_NONE;1729goto out;1730}17311732ctx = run->ctx;17331734if (irq == chan->in_eof_irq) {1735ctx->eof_mask |= EOF_IRQ_IN;1736} else if (irq == chan->out_eof_irq) {1737ctx->eof_mask |= EOF_IRQ_OUT;1738} else if (irq == chan->rot_in_eof_irq ||1739irq == chan->rot_out_eof_irq) {1740if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {1741/* this was NOT a rotation op, shouldn't happen */1742dev_err(priv->ipu->dev,1743"Unexpected rotation interrupt\n");1744goto out;1745}1746ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ?1747EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT;1748} else {1749dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq);1750ret = IRQ_NONE;1751goto out;1752}17531754if (ipu_rot_mode_is_irt(ctx->rot_mode))1755tile_complete = (ctx->eof_mask == EOF_IRQ_ROT_COMPLETE);1756else1757tile_complete = (ctx->eof_mask == EOF_IRQ_COMPLETE);17581759if (tile_complete)1760ret = do_tile_complete(run);1761out:1762spin_unlock_irqrestore(&chan->irqlock, flags);1763return ret;1764}17651766/*1767* try to force the completion of runs for this ctx. Called when1768* abort wait times out in ipu_image_convert_abort().1769*/1770static void force_abort(struct ipu_image_convert_ctx *ctx)1771{1772struct ipu_image_convert_chan *chan = ctx->chan;1773struct ipu_image_convert_run *run;1774unsigned long flags;17751776spin_lock_irqsave(&chan->irqlock, flags);17771778run = chan->current_run;1779if (run && run->ctx == ctx) {1780convert_stop(run);1781run->status = -EIO;1782list_add_tail(&run->list, &chan->done_q);1783chan->current_run = NULL;1784run_next(chan);1785}17861787spin_unlock_irqrestore(&chan->irqlock, flags);17881789empty_done_q(chan);1790}17911792static void release_ipu_resources(struct ipu_image_convert_chan *chan)1793{1794if (chan->in_eof_irq >= 0)1795free_irq(chan->in_eof_irq, chan);1796if (chan->rot_in_eof_irq >= 0)1797free_irq(chan->rot_in_eof_irq, chan);1798if (chan->out_eof_irq >= 0)1799free_irq(chan->out_eof_irq, chan);1800if (chan->rot_out_eof_irq >= 0)1801free_irq(chan->rot_out_eof_irq, chan);18021803if (!IS_ERR_OR_NULL(chan->in_chan))1804ipu_idmac_put(chan->in_chan);1805if (!IS_ERR_OR_NULL(chan->out_chan))1806ipu_idmac_put(chan->out_chan);1807if (!IS_ERR_OR_NULL(chan->rotation_in_chan))1808ipu_idmac_put(chan->rotation_in_chan);1809if (!IS_ERR_OR_NULL(chan->rotation_out_chan))1810ipu_idmac_put(chan->rotation_out_chan);1811if (!IS_ERR_OR_NULL(chan->ic))1812ipu_ic_put(chan->ic);18131814chan->in_chan = chan->out_chan = chan->rotation_in_chan =1815chan->rotation_out_chan = NULL;1816chan->in_eof_irq = -1;1817chan->rot_in_eof_irq = -1;1818chan->out_eof_irq = -1;1819chan->rot_out_eof_irq = -1;1820}18211822static int get_eof_irq(struct ipu_image_convert_chan *chan,1823struct ipuv3_channel *channel)1824{1825struct ipu_image_convert_priv *priv = chan->priv;1826int ret, irq;18271828irq = ipu_idmac_channel_irq(priv->ipu, channel, IPU_IRQ_EOF);18291830ret = request_threaded_irq(irq, eof_irq, do_bh, 0, "ipu-ic", chan);1831if (ret < 0) {1832dev_err(priv->ipu->dev, "could not acquire irq %d\n", irq);1833return ret;1834}18351836return irq;1837}18381839static int get_ipu_resources(struct ipu_image_convert_chan *chan)1840{1841const struct ipu_image_convert_dma_chan *dma = chan->dma_ch;1842struct ipu_image_convert_priv *priv = chan->priv;1843int ret;18441845/* get IC */1846chan->ic = ipu_ic_get(priv->ipu, chan->ic_task);1847if (IS_ERR(chan->ic)) {1848dev_err(priv->ipu->dev, "could not acquire IC\n");1849ret = PTR_ERR(chan->ic);1850goto err;1851}18521853/* get IDMAC channels */1854chan->in_chan = ipu_idmac_get(priv->ipu, dma->in);1855chan->out_chan = ipu_idmac_get(priv->ipu, dma->out);1856if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) {1857dev_err(priv->ipu->dev, "could not acquire idmac channels\n");1858ret = -EBUSY;1859goto err;1860}18611862chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in);1863chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out);1864if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) {1865dev_err(priv->ipu->dev,1866"could not acquire idmac rotation channels\n");1867ret = -EBUSY;1868goto err;1869}18701871/* acquire the EOF interrupts */1872ret = get_eof_irq(chan, chan->in_chan);1873if (ret < 0) {1874chan->in_eof_irq = -1;1875goto err;1876}1877chan->in_eof_irq = ret;18781879ret = get_eof_irq(chan, chan->rotation_in_chan);1880if (ret < 0) {1881chan->rot_in_eof_irq = -1;1882goto err;1883}1884chan->rot_in_eof_irq = ret;18851886ret = get_eof_irq(chan, chan->out_chan);1887if (ret < 0) {1888chan->out_eof_irq = -1;1889goto err;1890}1891chan->out_eof_irq = ret;18921893ret = get_eof_irq(chan, chan->rotation_out_chan);1894if (ret < 0) {1895chan->rot_out_eof_irq = -1;1896goto err;1897}1898chan->rot_out_eof_irq = ret;18991900return 0;1901err:1902release_ipu_resources(chan);1903return ret;1904}19051906static int fill_image(struct ipu_image_convert_ctx *ctx,1907struct ipu_image_convert_image *ic_image,1908struct ipu_image *image,1909enum ipu_image_convert_type type)1910{1911struct ipu_image_convert_priv *priv = ctx->chan->priv;19121913ic_image->base = *image;1914ic_image->type = type;19151916ic_image->fmt = get_format(image->pix.pixelformat);1917if (!ic_image->fmt) {1918dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",1919type == IMAGE_CONVERT_OUT ? "Output" : "Input");1920return -EINVAL;1921}19221923if (ic_image->fmt->planar)1924ic_image->stride = ic_image->base.pix.width;1925else1926ic_image->stride = ic_image->base.pix.bytesperline;19271928return 0;1929}19301931/* borrowed from drivers/media/v4l2-core/v4l2-common.c */1932static unsigned int clamp_align(unsigned int x, unsigned int min,1933unsigned int max, unsigned int align)1934{1935/* Bits that must be zero to be aligned */1936unsigned int mask = ~((1 << align) - 1);19371938/* Clamp to aligned min and max */1939x = clamp(x, (min + ~mask) & mask, max & mask);19401941/* Round to nearest aligned value */1942if (align)1943x = (x + (1 << (align - 1))) & mask;19441945return x;1946}19471948/* Adjusts input/output images to IPU restrictions */1949void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,1950enum ipu_rotate_mode rot_mode)1951{1952const struct ipu_image_pixfmt *infmt, *outfmt;1953u32 w_align_out, h_align_out;1954u32 w_align_in, h_align_in;19551956infmt = get_format(in->pix.pixelformat);1957outfmt = get_format(out->pix.pixelformat);19581959/* set some default pixel formats if needed */1960if (!infmt) {1961in->pix.pixelformat = V4L2_PIX_FMT_RGB24;1962infmt = get_format(V4L2_PIX_FMT_RGB24);1963}1964if (!outfmt) {1965out->pix.pixelformat = V4L2_PIX_FMT_RGB24;1966outfmt = get_format(V4L2_PIX_FMT_RGB24);1967}19681969/* image converter does not handle fields */1970in->pix.field = out->pix.field = V4L2_FIELD_NONE;19711972/* resizer cannot downsize more than 4:1 */1973if (ipu_rot_mode_is_irt(rot_mode)) {1974out->pix.height = max_t(__u32, out->pix.height,1975in->pix.width / 4);1976out->pix.width = max_t(__u32, out->pix.width,1977in->pix.height / 4);1978} else {1979out->pix.width = max_t(__u32, out->pix.width,1980in->pix.width / 4);1981out->pix.height = max_t(__u32, out->pix.height,1982in->pix.height / 4);1983}19841985/* align input width/height */1986w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt,1987rot_mode));1988h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt,1989rot_mode));1990in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W,1991w_align_in);1992in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H,1993h_align_in);19941995/* align output width/height */1996w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt,1997rot_mode));1998h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt,1999rot_mode));2000out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W,2001w_align_out);2002out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H,2003h_align_out);20042005/* set input/output strides and image sizes */2006in->pix.bytesperline = infmt->planar ?2007clamp_align(in->pix.width, 2 << w_align_in, MAX_W,2008w_align_in) :2009clamp_align((in->pix.width * infmt->bpp) >> 3,2010((2 << w_align_in) * infmt->bpp) >> 3,2011(MAX_W * infmt->bpp) >> 3,2012w_align_in);2013in->pix.sizeimage = infmt->planar ?2014(in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 :2015in->pix.height * in->pix.bytesperline;2016out->pix.bytesperline = outfmt->planar ? out->pix.width :2017(out->pix.width * outfmt->bpp) >> 3;2018out->pix.sizeimage = outfmt->planar ?2019(out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 :2020out->pix.height * out->pix.bytesperline;2021}2022EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);20232024/*2025* this is used by ipu_image_convert_prepare() to verify set input and2026* output images are valid before starting the conversion. Clients can2027* also call it before calling ipu_image_convert_prepare().2028*/2029int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,2030enum ipu_rotate_mode rot_mode)2031{2032struct ipu_image testin, testout;20332034testin = *in;2035testout = *out;20362037ipu_image_convert_adjust(&testin, &testout, rot_mode);20382039if (testin.pix.width != in->pix.width ||2040testin.pix.height != in->pix.height ||2041testout.pix.width != out->pix.width ||2042testout.pix.height != out->pix.height)2043return -EINVAL;20442045return 0;2046}2047EXPORT_SYMBOL_GPL(ipu_image_convert_verify);20482049/*2050* Call ipu_image_convert_prepare() to prepare for the conversion of2051* given images and rotation mode. Returns a new conversion context.2052*/2053struct ipu_image_convert_ctx *2054ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task,2055struct ipu_image *in, struct ipu_image *out,2056enum ipu_rotate_mode rot_mode,2057ipu_image_convert_cb_t complete,2058void *complete_context)2059{2060struct ipu_image_convert_priv *priv = ipu->image_convert_priv;2061struct ipu_image_convert_image *s_image, *d_image;2062struct ipu_image_convert_chan *chan;2063struct ipu_image_convert_ctx *ctx;2064unsigned long flags;2065unsigned int i;2066bool get_res;2067int ret;20682069if (!in || !out || !complete ||2070(ic_task != IC_TASK_VIEWFINDER &&2071ic_task != IC_TASK_POST_PROCESSOR))2072return ERR_PTR(-EINVAL);20732074/* verify the in/out images before continuing */2075ret = ipu_image_convert_verify(in, out, rot_mode);2076if (ret) {2077dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",2078__func__);2079return ERR_PTR(ret);2080}20812082chan = &priv->chan[ic_task];20832084ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);2085if (!ctx)2086return ERR_PTR(-ENOMEM);20872088dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__,2089chan->ic_task, ctx);20902091ctx->chan = chan;2092init_completion(&ctx->aborted);20932094ctx->rot_mode = rot_mode;20952096/* Sets ctx->in.num_rows/cols as well */2097ret = calc_image_resize_coefficients(ctx, in, out);2098if (ret)2099goto out_free;21002101s_image = &ctx->in;2102d_image = &ctx->out;21032104/* set tiling and rotation */2105if (ipu_rot_mode_is_irt(rot_mode)) {2106d_image->num_rows = s_image->num_cols;2107d_image->num_cols = s_image->num_rows;2108} else {2109d_image->num_rows = s_image->num_rows;2110d_image->num_cols = s_image->num_cols;2111}21122113ctx->num_tiles = d_image->num_cols * d_image->num_rows;21142115ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);2116if (ret)2117goto out_free;2118ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);2119if (ret)2120goto out_free;21212122calc_out_tile_map(ctx);21232124find_seams(ctx, s_image, d_image);21252126ret = calc_tile_dimensions(ctx, s_image);2127if (ret)2128goto out_free;21292130ret = calc_tile_offsets(ctx, s_image);2131if (ret)2132goto out_free;21332134calc_tile_dimensions(ctx, d_image);2135ret = calc_tile_offsets(ctx, d_image);2136if (ret)2137goto out_free;21382139calc_tile_resize_coefficients(ctx);21402141ret = ipu_ic_calc_csc(&ctx->csc,2142s_image->base.pix.ycbcr_enc,2143s_image->base.pix.quantization,2144ipu_pixelformat_to_colorspace(s_image->fmt->fourcc),2145d_image->base.pix.ycbcr_enc,2146d_image->base.pix.quantization,2147ipu_pixelformat_to_colorspace(d_image->fmt->fourcc));2148if (ret)2149goto out_free;21502151dump_format(ctx, s_image);2152dump_format(ctx, d_image);21532154ctx->complete = complete;2155ctx->complete_context = complete_context;21562157/*2158* Can we use double-buffering for this operation? If there is2159* only one tile (the whole image can be converted in a single2160* operation) there's no point in using double-buffering. Also,2161* the IPU's IDMAC channels allow only a single U and V plane2162* offset shared between both buffers, but these offsets change2163* for every tile, and therefore would have to be updated for2164* each buffer which is not possible. So double-buffering is2165* impossible when either the source or destination images are2166* a planar format (YUV420, YUV422P, etc.). Further, differently2167* sized tiles or different resizing coefficients per tile2168* prevent double-buffering as well.2169*/2170ctx->double_buffering = (ctx->num_tiles > 1 &&2171!s_image->fmt->planar &&2172!d_image->fmt->planar);2173for (i = 1; i < ctx->num_tiles; i++) {2174if (ctx->in.tile[i].width != ctx->in.tile[0].width ||2175ctx->in.tile[i].height != ctx->in.tile[0].height ||2176ctx->out.tile[i].width != ctx->out.tile[0].width ||2177ctx->out.tile[i].height != ctx->out.tile[0].height) {2178ctx->double_buffering = false;2179break;2180}2181}2182for (i = 1; i < ctx->in.num_cols; i++) {2183if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) {2184ctx->double_buffering = false;2185break;2186}2187}2188for (i = 1; i < ctx->in.num_rows; i++) {2189if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) {2190ctx->double_buffering = false;2191break;2192}2193}21942195if (ipu_rot_mode_is_irt(ctx->rot_mode)) {2196unsigned long intermediate_size = d_image->tile[0].size;21972198for (i = 1; i < ctx->num_tiles; i++) {2199if (d_image->tile[i].size > intermediate_size)2200intermediate_size = d_image->tile[i].size;2201}22022203ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0],2204intermediate_size);2205if (ret)2206goto out_free;2207if (ctx->double_buffering) {2208ret = alloc_dma_buf(priv,2209&ctx->rot_intermediate[1],2210intermediate_size);2211if (ret)2212goto out_free_dmabuf0;2213}2214}22152216spin_lock_irqsave(&chan->irqlock, flags);22172218get_res = list_empty(&chan->ctx_list);22192220list_add_tail(&ctx->list, &chan->ctx_list);22212222spin_unlock_irqrestore(&chan->irqlock, flags);22232224if (get_res) {2225ret = get_ipu_resources(chan);2226if (ret)2227goto out_free_dmabuf1;2228}22292230return ctx;22312232out_free_dmabuf1:2233free_dma_buf(priv, &ctx->rot_intermediate[1]);2234spin_lock_irqsave(&chan->irqlock, flags);2235list_del(&ctx->list);2236spin_unlock_irqrestore(&chan->irqlock, flags);2237out_free_dmabuf0:2238free_dma_buf(priv, &ctx->rot_intermediate[0]);2239out_free:2240kfree(ctx);2241return ERR_PTR(ret);2242}2243EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);22442245/*2246* Carry out a single image conversion run. Only the physaddr's of the input2247* and output image buffers are needed. The conversion context must have2248* been created previously with ipu_image_convert_prepare().2249*/2250int ipu_image_convert_queue(struct ipu_image_convert_run *run)2251{2252struct ipu_image_convert_chan *chan;2253struct ipu_image_convert_priv *priv;2254struct ipu_image_convert_ctx *ctx;2255unsigned long flags;2256int ret = 0;22572258if (!run || !run->ctx || !run->in_phys || !run->out_phys)2259return -EINVAL;22602261ctx = run->ctx;2262chan = ctx->chan;2263priv = chan->priv;22642265dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__,2266chan->ic_task, ctx, run);22672268INIT_LIST_HEAD(&run->list);22692270spin_lock_irqsave(&chan->irqlock, flags);22712272if (ctx->aborting) {2273ret = -EIO;2274goto unlock;2275}22762277list_add_tail(&run->list, &chan->pending_q);22782279if (!chan->current_run) {2280ret = do_run(run);2281if (ret)2282chan->current_run = NULL;2283}2284unlock:2285spin_unlock_irqrestore(&chan->irqlock, flags);2286return ret;2287}2288EXPORT_SYMBOL_GPL(ipu_image_convert_queue);22892290/* Abort any active or pending conversions for this context */2291static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx)2292{2293struct ipu_image_convert_chan *chan = ctx->chan;2294struct ipu_image_convert_priv *priv = chan->priv;2295struct ipu_image_convert_run *run, *active_run, *tmp;2296unsigned long flags;2297int run_count, ret;22982299spin_lock_irqsave(&chan->irqlock, flags);23002301/* move all remaining pending runs in this context to done_q */2302list_for_each_entry_safe(run, tmp, &chan->pending_q, list) {2303if (run->ctx != ctx)2304continue;2305run->status = -EIO;2306list_move_tail(&run->list, &chan->done_q);2307}23082309run_count = get_run_count(ctx, &chan->done_q);2310active_run = (chan->current_run && chan->current_run->ctx == ctx) ?2311chan->current_run : NULL;23122313if (active_run)2314reinit_completion(&ctx->aborted);23152316ctx->aborting = true;23172318spin_unlock_irqrestore(&chan->irqlock, flags);23192320if (!run_count && !active_run) {2321dev_dbg(priv->ipu->dev,2322"%s: task %u: no abort needed for ctx %p\n",2323__func__, chan->ic_task, ctx);2324return;2325}23262327if (!active_run) {2328empty_done_q(chan);2329return;2330}23312332dev_dbg(priv->ipu->dev,2333"%s: task %u: wait for completion: %d runs\n",2334__func__, chan->ic_task, run_count);23352336ret = wait_for_completion_timeout(&ctx->aborted,2337msecs_to_jiffies(10000));2338if (ret == 0) {2339dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);2340force_abort(ctx);2341}2342}23432344void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx)2345{2346__ipu_image_convert_abort(ctx);2347ctx->aborting = false;2348}2349EXPORT_SYMBOL_GPL(ipu_image_convert_abort);23502351/* Unprepare image conversion context */2352void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx)2353{2354struct ipu_image_convert_chan *chan = ctx->chan;2355struct ipu_image_convert_priv *priv = chan->priv;2356unsigned long flags;2357bool put_res;23582359/* make sure no runs are hanging around */2360__ipu_image_convert_abort(ctx);23612362dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__,2363chan->ic_task, ctx);23642365spin_lock_irqsave(&chan->irqlock, flags);23662367list_del(&ctx->list);23682369put_res = list_empty(&chan->ctx_list);23702371spin_unlock_irqrestore(&chan->irqlock, flags);23722373if (put_res)2374release_ipu_resources(chan);23752376free_dma_buf(priv, &ctx->rot_intermediate[1]);2377free_dma_buf(priv, &ctx->rot_intermediate[0]);23782379kfree(ctx);2380}2381EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);23822383/*2384* "Canned" asynchronous single image conversion. Allocates and returns2385* a new conversion run. On successful return the caller must free the2386* run and call ipu_image_convert_unprepare() after conversion completes.2387*/2388struct ipu_image_convert_run *2389ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task,2390struct ipu_image *in, struct ipu_image *out,2391enum ipu_rotate_mode rot_mode,2392ipu_image_convert_cb_t complete,2393void *complete_context)2394{2395struct ipu_image_convert_ctx *ctx;2396struct ipu_image_convert_run *run;2397int ret;23982399ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode,2400complete, complete_context);2401if (IS_ERR(ctx))2402return ERR_CAST(ctx);24032404run = kzalloc(sizeof(*run), GFP_KERNEL);2405if (!run) {2406ipu_image_convert_unprepare(ctx);2407return ERR_PTR(-ENOMEM);2408}24092410run->ctx = ctx;2411run->in_phys = in->phys0;2412run->out_phys = out->phys0;24132414ret = ipu_image_convert_queue(run);2415if (ret) {2416ipu_image_convert_unprepare(ctx);2417kfree(run);2418return ERR_PTR(ret);2419}24202421return run;2422}2423EXPORT_SYMBOL_GPL(ipu_image_convert);24242425int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev)2426{2427struct ipu_image_convert_priv *priv;2428int i;24292430priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);2431if (!priv)2432return -ENOMEM;24332434ipu->image_convert_priv = priv;2435priv->ipu = ipu;24362437for (i = 0; i < IC_NUM_TASKS; i++) {2438struct ipu_image_convert_chan *chan = &priv->chan[i];24392440chan->ic_task = i;2441chan->priv = priv;2442chan->dma_ch = &image_convert_dma_chan[i];2443chan->in_eof_irq = -1;2444chan->rot_in_eof_irq = -1;2445chan->out_eof_irq = -1;2446chan->rot_out_eof_irq = -1;24472448spin_lock_init(&chan->irqlock);2449INIT_LIST_HEAD(&chan->ctx_list);2450INIT_LIST_HEAD(&chan->pending_q);2451INIT_LIST_HEAD(&chan->done_q);2452}24532454return 0;2455}24562457void ipu_image_convert_exit(struct ipu_soc *ipu)2458{2459}246024612462