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