425 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter4 *5 * Copyright (C) 2013-2014 Renesas Electronics Corporation6 *7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <linux/device.h>11 12#include <media/v4l2-subdev.h>13 14#include "vsp1.h"15#include "vsp1_dl.h"16#include "vsp1_pipe.h"17#include "vsp1_rwpf.h"18#include "vsp1_video.h"19 20#define RPF_MAX_WIDTH 819021#define RPF_MAX_HEIGHT 819022 23/* Pre extended display list command data structure. */24struct vsp1_extcmd_auto_fld_body {25 u32 top_y0;26 u32 bottom_y0;27 u32 top_c0;28 u32 bottom_c0;29 u32 top_c1;30 u32 bottom_c1;31 u32 reserved0;32 u32 reserved1;33} __packed;34 35/* -----------------------------------------------------------------------------36 * Device Access37 */38 39static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,40 struct vsp1_dl_body *dlb, u32 reg, u32 data)41{42 vsp1_dl_body_write(dlb, reg + rpf->entity.index * VI6_RPF_OFFSET,43 data);44}45 46/* -----------------------------------------------------------------------------47 * VSP1 Entity Operations48 */49 50static void rpf_configure_stream(struct vsp1_entity *entity,51 struct v4l2_subdev_state *state,52 struct vsp1_pipeline *pipe,53 struct vsp1_dl_list *dl,54 struct vsp1_dl_body *dlb)55{56 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);57 const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;58 const struct v4l2_pix_format_mplane *format = &rpf->format;59 const struct v4l2_mbus_framefmt *source_format;60 const struct v4l2_mbus_framefmt *sink_format;61 unsigned int left = 0;62 unsigned int top = 0;63 u32 pstride;64 u32 infmt;65 66 /* Stride */67 pstride = format->plane_fmt[0].bytesperline68 << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;69 if (format->num_planes > 1)70 pstride |= format->plane_fmt[1].bytesperline71 << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;72 73 /*74 * pstride has both STRIDE_Y and STRIDE_C, but multiplying the whole75 * of pstride by 2 is conveniently OK here as we are multiplying both76 * values.77 */78 if (pipe->interlaced)79 pstride *= 2;80 81 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);82 83 /* Format */84 sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);85 source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);86 87 infmt = VI6_RPF_INFMT_CIPM88 | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);89 90 if (fmtinfo->swap_yc)91 infmt |= VI6_RPF_INFMT_SPYCS;92 if (fmtinfo->swap_uv)93 infmt |= VI6_RPF_INFMT_SPUVS;94 95 if (sink_format->code != source_format->code)96 infmt |= VI6_RPF_INFMT_CSC;97 98 vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt);99 vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap);100 101 if (entity->vsp1->info->gen == 4) {102 u32 ext_infmt0;103 u32 ext_infmt1;104 u32 ext_infmt2;105 106 switch (fmtinfo->fourcc) {107 case V4L2_PIX_FMT_RGBX1010102:108 ext_infmt0 = VI6_RPF_EXT_INFMT0_BYPP_M1_RGB10;109 ext_infmt1 = VI6_RPF_EXT_INFMT1_PACK_CPOS(0, 10, 20, 0);110 ext_infmt2 = VI6_RPF_EXT_INFMT2_PACK_CLEN(10, 10, 10, 0);111 break;112 113 case V4L2_PIX_FMT_RGBA1010102:114 ext_infmt0 = VI6_RPF_EXT_INFMT0_BYPP_M1_RGB10;115 ext_infmt1 = VI6_RPF_EXT_INFMT1_PACK_CPOS(0, 10, 20, 30);116 ext_infmt2 = VI6_RPF_EXT_INFMT2_PACK_CLEN(10, 10, 10, 2);117 break;118 119 case V4L2_PIX_FMT_ARGB2101010:120 ext_infmt0 = VI6_RPF_EXT_INFMT0_BYPP_M1_RGB10;121 ext_infmt1 = VI6_RPF_EXT_INFMT1_PACK_CPOS(2, 12, 22, 0);122 ext_infmt2 = VI6_RPF_EXT_INFMT2_PACK_CLEN(10, 10, 10, 2);123 break;124 125 case V4L2_PIX_FMT_Y210:126 ext_infmt0 = VI6_RPF_EXT_INFMT0_F2B |127 VI6_RPF_EXT_INFMT0_IPBD_Y_10 |128 VI6_RPF_EXT_INFMT0_IPBD_C_10;129 ext_infmt1 = 0x0;130 ext_infmt2 = 0x0;131 break;132 133 case V4L2_PIX_FMT_Y212:134 ext_infmt0 = VI6_RPF_EXT_INFMT0_F2B |135 VI6_RPF_EXT_INFMT0_IPBD_Y_12 |136 VI6_RPF_EXT_INFMT0_IPBD_C_12;137 ext_infmt1 = 0x0;138 ext_infmt2 = 0x0;139 break;140 141 default:142 ext_infmt0 = 0;143 ext_infmt1 = 0;144 ext_infmt2 = 0;145 break;146 }147 148 vsp1_rpf_write(rpf, dlb, VI6_RPF_EXT_INFMT0, ext_infmt0);149 vsp1_rpf_write(rpf, dlb, VI6_RPF_EXT_INFMT1, ext_infmt1);150 vsp1_rpf_write(rpf, dlb, VI6_RPF_EXT_INFMT2, ext_infmt2);151 }152 153 /* Output location. */154 if (pipe->brx) {155 const struct v4l2_rect *compose;156 157 compose = v4l2_subdev_state_get_compose(pipe->brx->state,158 rpf->brx_input);159 left = compose->left;160 top = compose->top;161 }162 163 if (pipe->interlaced)164 top /= 2;165 166 vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,167 (left << VI6_RPF_LOC_HCOORD_SHIFT) |168 (top << VI6_RPF_LOC_VCOORD_SHIFT));169 170 /*171 * On Gen2 use the alpha channel (extended to 8 bits) when available or172 * a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control173 * otherwise.174 *175 * The Gen3+ RPF has extended alpha capability and can both multiply the176 * alpha channel by a fixed global alpha value, and multiply the pixel177 * components to convert the input to premultiplied alpha.178 *179 * As alpha premultiplication is available in the BRx for both Gen2 and180 * Gen3+ we handle it there and use the Gen3 alpha multiplier for global181 * alpha multiplication only. This however prevents conversion to182 * premultiplied alpha if no BRx is present in the pipeline. If that use183 * case turns out to be useful we will revisit the implementation (for184 * Gen3 only).185 *186 * We enable alpha multiplication on Gen3+ using the fixed alpha value187 * set through the V4L2_CID_ALPHA_COMPONENT control when the input188 * contains an alpha channel. On Gen2 the global alpha is ignored in189 * that case.190 *191 * In all cases, disable color keying.192 */193 vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |194 (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED195 : VI6_RPF_ALPH_SEL_ASEL_FIXED));196 197 if (entity->vsp1->info->gen >= 3) {198 u32 mult;199 200 if (fmtinfo->alpha) {201 /*202 * When the input contains an alpha channel enable the203 * alpha multiplier. If the input is premultiplied we204 * need to multiply both the alpha channel and the pixel205 * components by the global alpha value to keep them206 * premultiplied. Otherwise multiply the alpha channel207 * only.208 */209 bool premultiplied = format->flags210 & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;211 212 mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO213 | (premultiplied ?214 VI6_RPF_MULT_ALPHA_P_MMD_RATIO :215 VI6_RPF_MULT_ALPHA_P_MMD_NONE);216 } else {217 /*218 * When the input doesn't contain an alpha channel the219 * global alpha value is applied in the unpacking unit,220 * the alpha multiplier isn't needed and must be221 * disabled.222 */223 mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE224 | VI6_RPF_MULT_ALPHA_P_MMD_NONE;225 }226 227 rpf->mult_alpha = mult;228 }229 230 vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0);231 vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0);232 233}234 235static void vsp1_rpf_configure_autofld(struct vsp1_rwpf *rpf,236 struct vsp1_dl_list *dl)237{238 const struct v4l2_pix_format_mplane *format = &rpf->format;239 struct vsp1_dl_ext_cmd *cmd;240 struct vsp1_extcmd_auto_fld_body *auto_fld;241 u32 offset_y, offset_c;242 243 cmd = vsp1_dl_get_pre_cmd(dl);244 if (WARN_ONCE(!cmd, "Failed to obtain an autofld cmd"))245 return;246 247 /* Re-index our auto_fld to match the current RPF. */248 auto_fld = cmd->data;249 auto_fld = &auto_fld[rpf->entity.index];250 251 auto_fld->top_y0 = rpf->mem.addr[0];252 auto_fld->top_c0 = rpf->mem.addr[1];253 auto_fld->top_c1 = rpf->mem.addr[2];254 255 offset_y = format->plane_fmt[0].bytesperline;256 offset_c = format->plane_fmt[1].bytesperline;257 258 auto_fld->bottom_y0 = rpf->mem.addr[0] + offset_y;259 auto_fld->bottom_c0 = rpf->mem.addr[1] + offset_c;260 auto_fld->bottom_c1 = rpf->mem.addr[2] + offset_c;261 262 cmd->flags |= VI6_DL_EXT_AUTOFLD_INT | BIT(16 + rpf->entity.index);263}264 265static void rpf_configure_frame(struct vsp1_entity *entity,266 struct vsp1_pipeline *pipe,267 struct vsp1_dl_list *dl,268 struct vsp1_dl_body *dlb)269{270 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);271 272 vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET,273 rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);274 vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |275 (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));276 277 vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha);278}279 280static void rpf_configure_partition(struct vsp1_entity *entity,281 struct vsp1_pipeline *pipe,282 const struct vsp1_partition *partition,283 struct vsp1_dl_list *dl,284 struct vsp1_dl_body *dlb)285{286 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);287 struct vsp1_rwpf_memory mem = rpf->mem;288 struct vsp1_device *vsp1 = rpf->entity.vsp1;289 const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;290 const struct v4l2_pix_format_mplane *format = &rpf->format;291 struct v4l2_rect crop = partition->rpf[rpf->entity.index];292 293 /*294 * Source size and crop offsets.295 *296 * The crop offsets correspond to the location of the crop297 * rectangle top left corner in the plane buffer. Only two298 * offsets are needed, as planes 2 and 3 always have identical299 * strides.300 */301 302 if (pipe->interlaced) {303 crop.height = round_down(crop.height / 2, fmtinfo->vsub);304 crop.top = round_down(crop.top / 2, fmtinfo->vsub);305 }306 307 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE,308 (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |309 (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));310 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE,311 (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |312 (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));313 314 mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline315 + crop.left * fmtinfo->bpp[0] / 8;316 317 if (format->num_planes > 1) {318 unsigned int bpl = format->plane_fmt[1].bytesperline;319 unsigned int offset;320 321 offset = crop.top / fmtinfo->vsub * bpl322 + crop.left / fmtinfo->hsub * fmtinfo->bpp[1] / 8;323 mem.addr[1] += offset;324 mem.addr[2] += offset;325 }326 327 /*328 * On Gen3+ hardware the SPUVS bit has no effect on 3-planar329 * formats. Swap the U and V planes manually in that case.330 */331 if (vsp1->info->gen >= 3 && format->num_planes == 3 &&332 fmtinfo->swap_uv)333 swap(mem.addr[1], mem.addr[2]);334 335 /*336 * Interlaced pipelines will use the extended pre-cmd to process337 * SRCM_ADDR_{Y,C0,C1}.338 */339 if (pipe->interlaced) {340 vsp1_rpf_configure_autofld(rpf, dl);341 } else {342 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);343 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);344 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);345 }346}347 348static void rpf_partition(struct vsp1_entity *entity,349 struct v4l2_subdev_state *state,350 struct vsp1_pipeline *pipe,351 struct vsp1_partition *partition,352 unsigned int partition_idx,353 struct v4l2_rect *window)354{355 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);356 struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index];357 358 /*359 * Partition Algorithm Control360 *361 * The partition algorithm can split this frame into multiple slices. We362 * must adjust our partition window based on the pipe configuration to363 * match the destination partition window. To achieve this, we adjust364 * our crop to provide a 'sub-crop' matching the expected partition365 * window.366 */367 *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);368 369 if (pipe->partitions > 1) {370 rpf_rect->width = window->width;371 rpf_rect->left += window->left;372 }373}374 375static const struct vsp1_entity_operations rpf_entity_ops = {376 .configure_stream = rpf_configure_stream,377 .configure_frame = rpf_configure_frame,378 .configure_partition = rpf_configure_partition,379 .partition = rpf_partition,380};381 382/* -----------------------------------------------------------------------------383 * Initialization and Cleanup384 */385 386struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)387{388 struct vsp1_rwpf *rpf;389 char name[6];390 int ret;391 392 rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);393 if (rpf == NULL)394 return ERR_PTR(-ENOMEM);395 396 rpf->max_width = RPF_MAX_WIDTH;397 rpf->max_height = RPF_MAX_HEIGHT;398 399 rpf->entity.ops = &rpf_entity_ops;400 rpf->entity.type = VSP1_ENTITY_RPF;401 rpf->entity.index = index;402 403 sprintf(name, "rpf.%u", index);404 ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &vsp1_rwpf_subdev_ops,405 MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);406 if (ret < 0)407 return ERR_PTR(ret);408 409 /* Initialize the control handler. */410 ret = vsp1_rwpf_init_ctrls(rpf, 0);411 if (ret < 0) {412 dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",413 index);414 goto error;415 }416 417 v4l2_ctrl_handler_setup(&rpf->ctrls);418 419 return rpf;420 421error:422 vsp1_entity_destroy(&rpf->entity);423 return ERR_PTR(ret);424}425