vsp1_rpf.c (11127B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter 4 * 5 * Copyright (C) 2013-2014 Renesas Electronics Corporation 6 * 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 8190 21#define RPF_MAX_HEIGHT 8190 22 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 Access 37 */ 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 * V4L2 Subdevice Operations 48 */ 49 50static const struct v4l2_subdev_ops rpf_ops = { 51 .pad = &vsp1_rwpf_pad_ops, 52}; 53 54/* ----------------------------------------------------------------------------- 55 * VSP1 Entity Operations 56 */ 57 58static void rpf_configure_stream(struct vsp1_entity *entity, 59 struct vsp1_pipeline *pipe, 60 struct vsp1_dl_list *dl, 61 struct vsp1_dl_body *dlb) 62{ 63 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); 64 const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; 65 const struct v4l2_pix_format_mplane *format = &rpf->format; 66 const struct v4l2_mbus_framefmt *source_format; 67 const struct v4l2_mbus_framefmt *sink_format; 68 unsigned int left = 0; 69 unsigned int top = 0; 70 u32 pstride; 71 u32 infmt; 72 73 /* Stride */ 74 pstride = format->plane_fmt[0].bytesperline 75 << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; 76 if (format->num_planes > 1) 77 pstride |= format->plane_fmt[1].bytesperline 78 << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; 79 80 /* 81 * pstride has both STRIDE_Y and STRIDE_C, but multiplying the whole 82 * of pstride by 2 is conveniently OK here as we are multiplying both 83 * values. 84 */ 85 if (pipe->interlaced) 86 pstride *= 2; 87 88 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); 89 90 /* Format */ 91 sink_format = vsp1_entity_get_pad_format(&rpf->entity, 92 rpf->entity.config, 93 RWPF_PAD_SINK); 94 source_format = vsp1_entity_get_pad_format(&rpf->entity, 95 rpf->entity.config, 96 RWPF_PAD_SOURCE); 97 98 infmt = VI6_RPF_INFMT_CIPM 99 | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); 100 101 if (fmtinfo->swap_yc) 102 infmt |= VI6_RPF_INFMT_SPYCS; 103 if (fmtinfo->swap_uv) 104 infmt |= VI6_RPF_INFMT_SPUVS; 105 106 if (sink_format->code != source_format->code) 107 infmt |= VI6_RPF_INFMT_CSC; 108 109 vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt); 110 vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap); 111 112 /* Output location. */ 113 if (pipe->brx) { 114 const struct v4l2_rect *compose; 115 116 compose = vsp1_entity_get_pad_selection(pipe->brx, 117 pipe->brx->config, 118 rpf->brx_input, 119 V4L2_SEL_TGT_COMPOSE); 120 left = compose->left; 121 top = compose->top; 122 } 123 124 if (pipe->interlaced) 125 top /= 2; 126 127 vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC, 128 (left << VI6_RPF_LOC_HCOORD_SHIFT) | 129 (top << VI6_RPF_LOC_VCOORD_SHIFT)); 130 131 /* 132 * On Gen2 use the alpha channel (extended to 8 bits) when available or 133 * a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control 134 * otherwise. 135 * 136 * The Gen3 RPF has extended alpha capability and can both multiply the 137 * alpha channel by a fixed global alpha value, and multiply the pixel 138 * components to convert the input to premultiplied alpha. 139 * 140 * As alpha premultiplication is available in the BRx for both Gen2 and 141 * Gen3 we handle it there and use the Gen3 alpha multiplier for global 142 * alpha multiplication only. This however prevents conversion to 143 * premultiplied alpha if no BRx is present in the pipeline. If that use 144 * case turns out to be useful we will revisit the implementation (for 145 * Gen3 only). 146 * 147 * We enable alpha multiplication on Gen3 using the fixed alpha value 148 * set through the V4L2_CID_ALPHA_COMPONENT control when the input 149 * contains an alpha channel. On Gen2 the global alpha is ignored in 150 * that case. 151 * 152 * In all cases, disable color keying. 153 */ 154 vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | 155 (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED 156 : VI6_RPF_ALPH_SEL_ASEL_FIXED)); 157 158 if (entity->vsp1->info->gen == 3) { 159 u32 mult; 160 161 if (fmtinfo->alpha) { 162 /* 163 * When the input contains an alpha channel enable the 164 * alpha multiplier. If the input is premultiplied we 165 * need to multiply both the alpha channel and the pixel 166 * components by the global alpha value to keep them 167 * premultiplied. Otherwise multiply the alpha channel 168 * only. 169 */ 170 bool premultiplied = format->flags 171 & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; 172 173 mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO 174 | (premultiplied ? 175 VI6_RPF_MULT_ALPHA_P_MMD_RATIO : 176 VI6_RPF_MULT_ALPHA_P_MMD_NONE); 177 } else { 178 /* 179 * When the input doesn't contain an alpha channel the 180 * global alpha value is applied in the unpacking unit, 181 * the alpha multiplier isn't needed and must be 182 * disabled. 183 */ 184 mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE 185 | VI6_RPF_MULT_ALPHA_P_MMD_NONE; 186 } 187 188 rpf->mult_alpha = mult; 189 } 190 191 vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0); 192 vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0); 193 194} 195 196static void vsp1_rpf_configure_autofld(struct vsp1_rwpf *rpf, 197 struct vsp1_dl_list *dl) 198{ 199 const struct v4l2_pix_format_mplane *format = &rpf->format; 200 struct vsp1_dl_ext_cmd *cmd; 201 struct vsp1_extcmd_auto_fld_body *auto_fld; 202 u32 offset_y, offset_c; 203 204 cmd = vsp1_dl_get_pre_cmd(dl); 205 if (WARN_ONCE(!cmd, "Failed to obtain an autofld cmd")) 206 return; 207 208 /* Re-index our auto_fld to match the current RPF. */ 209 auto_fld = cmd->data; 210 auto_fld = &auto_fld[rpf->entity.index]; 211 212 auto_fld->top_y0 = rpf->mem.addr[0]; 213 auto_fld->top_c0 = rpf->mem.addr[1]; 214 auto_fld->top_c1 = rpf->mem.addr[2]; 215 216 offset_y = format->plane_fmt[0].bytesperline; 217 offset_c = format->plane_fmt[1].bytesperline; 218 219 auto_fld->bottom_y0 = rpf->mem.addr[0] + offset_y; 220 auto_fld->bottom_c0 = rpf->mem.addr[1] + offset_c; 221 auto_fld->bottom_c1 = rpf->mem.addr[2] + offset_c; 222 223 cmd->flags |= VI6_DL_EXT_AUTOFLD_INT | BIT(16 + rpf->entity.index); 224} 225 226static void rpf_configure_frame(struct vsp1_entity *entity, 227 struct vsp1_pipeline *pipe, 228 struct vsp1_dl_list *dl, 229 struct vsp1_dl_body *dlb) 230{ 231 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); 232 233 vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET, 234 rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); 235 vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | 236 (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); 237 238 vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha); 239} 240 241static void rpf_configure_partition(struct vsp1_entity *entity, 242 struct vsp1_pipeline *pipe, 243 struct vsp1_dl_list *dl, 244 struct vsp1_dl_body *dlb) 245{ 246 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); 247 struct vsp1_rwpf_memory mem = rpf->mem; 248 struct vsp1_device *vsp1 = rpf->entity.vsp1; 249 const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; 250 const struct v4l2_pix_format_mplane *format = &rpf->format; 251 struct v4l2_rect crop; 252 253 /* 254 * Source size and crop offsets. 255 * 256 * The crop offsets correspond to the location of the crop 257 * rectangle top left corner in the plane buffer. Only two 258 * offsets are needed, as planes 2 and 3 always have identical 259 * strides. 260 */ 261 crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); 262 263 /* 264 * Partition Algorithm Control 265 * 266 * The partition algorithm can split this frame into multiple 267 * slices. We must scale our partition window based on the pipe 268 * configuration to match the destination partition window. 269 * To achieve this, we adjust our crop to provide a 'sub-crop' 270 * matching the expected partition window. Only 'left' and 271 * 'width' need to be adjusted. 272 */ 273 if (pipe->partitions > 1) { 274 crop.width = pipe->partition->rpf.width; 275 crop.left += pipe->partition->rpf.left; 276 } 277 278 if (pipe->interlaced) { 279 crop.height = round_down(crop.height / 2, fmtinfo->vsub); 280 crop.top = round_down(crop.top / 2, fmtinfo->vsub); 281 } 282 283 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE, 284 (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | 285 (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); 286 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE, 287 (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | 288 (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); 289 290 mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline 291 + crop.left * fmtinfo->bpp[0] / 8; 292 293 if (format->num_planes > 1) { 294 unsigned int bpl = format->plane_fmt[1].bytesperline; 295 unsigned int offset; 296 297 offset = crop.top / fmtinfo->vsub * bpl 298 + crop.left / fmtinfo->hsub * fmtinfo->bpp[1] / 8; 299 mem.addr[1] += offset; 300 mem.addr[2] += offset; 301 } 302 303 /* 304 * On Gen3 hardware the SPUVS bit has no effect on 3-planar 305 * formats. Swap the U and V planes manually in that case. 306 */ 307 if (vsp1->info->gen == 3 && format->num_planes == 3 && 308 fmtinfo->swap_uv) 309 swap(mem.addr[1], mem.addr[2]); 310 311 /* 312 * Interlaced pipelines will use the extended pre-cmd to process 313 * SRCM_ADDR_{Y,C0,C1}. 314 */ 315 if (pipe->interlaced) { 316 vsp1_rpf_configure_autofld(rpf, dl); 317 } else { 318 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); 319 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); 320 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); 321 } 322} 323 324static void rpf_partition(struct vsp1_entity *entity, 325 struct vsp1_pipeline *pipe, 326 struct vsp1_partition *partition, 327 unsigned int partition_idx, 328 struct vsp1_partition_window *window) 329{ 330 partition->rpf = *window; 331} 332 333static const struct vsp1_entity_operations rpf_entity_ops = { 334 .configure_stream = rpf_configure_stream, 335 .configure_frame = rpf_configure_frame, 336 .configure_partition = rpf_configure_partition, 337 .partition = rpf_partition, 338}; 339 340/* ----------------------------------------------------------------------------- 341 * Initialization and Cleanup 342 */ 343 344struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) 345{ 346 struct vsp1_rwpf *rpf; 347 char name[6]; 348 int ret; 349 350 rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); 351 if (rpf == NULL) 352 return ERR_PTR(-ENOMEM); 353 354 rpf->max_width = RPF_MAX_WIDTH; 355 rpf->max_height = RPF_MAX_HEIGHT; 356 357 rpf->entity.ops = &rpf_entity_ops; 358 rpf->entity.type = VSP1_ENTITY_RPF; 359 rpf->entity.index = index; 360 361 sprintf(name, "rpf.%u", index); 362 ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops, 363 MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); 364 if (ret < 0) 365 return ERR_PTR(ret); 366 367 /* Initialize the control handler. */ 368 ret = vsp1_rwpf_init_ctrls(rpf, 0); 369 if (ret < 0) { 370 dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", 371 index); 372 goto error; 373 } 374 375 v4l2_ctrl_handler_setup(&rpf->ctrls); 376 377 return rpf; 378 379error: 380 vsp1_entity_destroy(&rpf->entity); 381 return ERR_PTR(ret); 382}