1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * vsp1_brx.c -- R-Car VSP1 Blend ROP Unit (BRU and BRS) 4 * 5 * Copyright (C) 2013 Renesas Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <linux/device.h> 11 #include <linux/gfp.h> 12 13 #include <media/v4l2-subdev.h> 14 15 #include "vsp1.h" 16 #include "vsp1_brx.h" 17 #include "vsp1_dl.h" 18 #include "vsp1_entity.h" 19 #include "vsp1_pipe.h" 20 #include "vsp1_rwpf.h" 21 #include "vsp1_video.h" 22 23 #define BRX_MIN_SIZE 1U 24 #define BRX_MAX_SIZE 8190U 25 26 /* ----------------------------------------------------------------------------- 27 * Device Access 28 */ 29 30 static inline void vsp1_brx_write(struct vsp1_brx *brx, 31 struct vsp1_dl_body *dlb, u32 reg, u32 data) 32 { 33 vsp1_dl_body_write(dlb, brx->base + reg, data); 34 } 35 36 /* ----------------------------------------------------------------------------- 37 * Controls 38 */ 39 40 static int brx_s_ctrl(struct v4l2_ctrl *ctrl) 41 { 42 struct vsp1_brx *brx = 43 container_of(ctrl->handler, struct vsp1_brx, ctrls); 44 45 switch (ctrl->id) { 46 case V4L2_CID_BG_COLOR: 47 brx->bgcolor = ctrl->val; 48 break; 49 } 50 51 return 0; 52 } 53 54 static const struct v4l2_ctrl_ops brx_ctrl_ops = { 55 .s_ctrl = brx_s_ctrl, 56 }; 57 58 /* ----------------------------------------------------------------------------- 59 * V4L2 Subdevice Operations 60 */ 61 62 /* 63 * The BRx can't perform format conversion, all sink and source formats must be 64 * identical. We pick the format on the first sink pad (pad 0) and propagate it 65 * to all other pads. 66 */ 67 68 static int brx_enum_mbus_code(struct v4l2_subdev *subdev, 69 struct v4l2_subdev_state *sd_state, 70 struct v4l2_subdev_mbus_code_enum *code) 71 { 72 static const unsigned int codes[] = { 73 MEDIA_BUS_FMT_ARGB8888_1X32, 74 MEDIA_BUS_FMT_AYUV8_1X32, 75 }; 76 77 return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, 78 ARRAY_SIZE(codes)); 79 } 80 81 static int brx_enum_frame_size(struct v4l2_subdev *subdev, 82 struct v4l2_subdev_state *sd_state, 83 struct v4l2_subdev_frame_size_enum *fse) 84 { 85 if (fse->index) 86 return -EINVAL; 87 88 if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 && 89 fse->code != MEDIA_BUS_FMT_AYUV8_1X32) 90 return -EINVAL; 91 92 fse->min_width = BRX_MIN_SIZE; 93 fse->max_width = BRX_MAX_SIZE; 94 fse->min_height = BRX_MIN_SIZE; 95 fse->max_height = BRX_MAX_SIZE; 96 97 return 0; 98 } 99 100 static void brx_try_format(struct vsp1_brx *brx, 101 struct v4l2_subdev_state *sd_state, 102 unsigned int pad, struct v4l2_mbus_framefmt *fmt) 103 { 104 struct v4l2_mbus_framefmt *format; 105 106 switch (pad) { 107 case BRX_PAD_SINK(0): 108 /* Default to YUV if the requested format is not supported. */ 109 if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && 110 fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) 111 fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; 112 113 vsp1_entity_adjust_color_space(fmt); 114 break; 115 116 default: 117 /* The BRx can't perform format conversion. */ 118 format = v4l2_subdev_state_get_format(sd_state, 119 BRX_PAD_SINK(0)); 120 fmt->code = format->code; 121 122 fmt->colorspace = format->colorspace; 123 fmt->xfer_func = format->xfer_func; 124 fmt->ycbcr_enc = format->ycbcr_enc; 125 fmt->quantization = format->quantization; 126 break; 127 } 128 129 fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE); 130 fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE); 131 fmt->field = V4L2_FIELD_NONE; 132 } 133 134 static int brx_set_format(struct v4l2_subdev *subdev, 135 struct v4l2_subdev_state *sd_state, 136 struct v4l2_subdev_format *fmt) 137 { 138 struct vsp1_brx *brx = to_brx(subdev); 139 struct v4l2_subdev_state *state; 140 struct v4l2_mbus_framefmt *format; 141 int ret = 0; 142 143 mutex_lock(&brx->entity.lock); 144 145 state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which); 146 if (!state) { 147 ret = -EINVAL; 148 goto done; 149 } 150 151 brx_try_format(brx, state, fmt->pad, &fmt->format); 152 153 format = v4l2_subdev_state_get_format(state, fmt->pad); 154 *format = fmt->format; 155 156 /* Reset the compose rectangle. */ 157 if (fmt->pad != brx->entity.source_pad) { 158 struct v4l2_rect *compose; 159 160 compose = v4l2_subdev_state_get_compose(state, fmt->pad); 161 compose->left = 0; 162 compose->top = 0; 163 compose->width = format->width; 164 compose->height = format->height; 165 } 166 167 /* Propagate the format code to all pads. */ 168 if (fmt->pad == BRX_PAD_SINK(0)) { 169 unsigned int i; 170 171 for (i = 0; i <= brx->entity.source_pad; ++i) { 172 format = v4l2_subdev_state_get_format(state, i); 173 format->code = fmt->format.code; 174 } 175 } 176 177 done: 178 mutex_unlock(&brx->entity.lock); 179 return ret; 180 } 181 182 static int brx_get_selection(struct v4l2_subdev *subdev, 183 struct v4l2_subdev_state *sd_state, 184 struct v4l2_subdev_selection *sel) 185 { 186 struct vsp1_brx *brx = to_brx(subdev); 187 struct v4l2_subdev_state *state; 188 189 if (sel->pad == brx->entity.source_pad) 190 return -EINVAL; 191 192 switch (sel->target) { 193 case V4L2_SEL_TGT_COMPOSE_BOUNDS: 194 sel->r.left = 0; 195 sel->r.top = 0; 196 sel->r.width = BRX_MAX_SIZE; 197 sel->r.height = BRX_MAX_SIZE; 198 return 0; 199 200 case V4L2_SEL_TGT_COMPOSE: 201 state = vsp1_entity_get_state(&brx->entity, sd_state, 202 sel->which); 203 if (!state) 204 return -EINVAL; 205 206 mutex_lock(&brx->entity.lock); 207 sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); 208 mutex_unlock(&brx->entity.lock); 209 return 0; 210 211 default: 212 return -EINVAL; 213 } 214 } 215 216 static int brx_set_selection(struct v4l2_subdev *subdev, 217 struct v4l2_subdev_state *sd_state, 218 struct v4l2_subdev_selection *sel) 219 { 220 struct vsp1_brx *brx = to_brx(subdev); 221 struct v4l2_subdev_state *state; 222 struct v4l2_mbus_framefmt *format; 223 struct v4l2_rect *compose; 224 int ret = 0; 225 226 if (sel->pad == brx->entity.source_pad) 227 return -EINVAL; 228 229 if (sel->target != V4L2_SEL_TGT_COMPOSE) 230 return -EINVAL; 231 232 mutex_lock(&brx->entity.lock); 233 234 state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); 235 if (!state) { 236 ret = -EINVAL; 237 goto done; 238 } 239 240 /* 241 * The compose rectangle top left corner must be inside the output 242 * frame. 243 */ 244 format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); 245 sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); 246 sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); 247 248 /* 249 * Scaling isn't supported, the compose rectangle size must be identical 250 * to the sink format size. 251 */ 252 format = v4l2_subdev_state_get_format(state, sel->pad); 253 sel->r.width = format->width; 254 sel->r.height = format->height; 255 256 compose = v4l2_subdev_state_get_compose(state, sel->pad); 257 *compose = sel->r; 258 259 done: 260 mutex_unlock(&brx->entity.lock); 261 return ret; 262 } 263 264 static const struct v4l2_subdev_pad_ops brx_pad_ops = { 265 .enum_mbus_code = brx_enum_mbus_code, 266 .enum_frame_size = brx_enum_frame_size, 267 .get_fmt = vsp1_subdev_get_pad_format, 268 .set_fmt = brx_set_format, 269 .get_selection = brx_get_selection, 270 .set_selection = brx_set_selection, 271 }; 272 273 static const struct v4l2_subdev_ops brx_ops = { 274 .pad = &brx_pad_ops, 275 }; 276 277 /* ----------------------------------------------------------------------------- 278 * VSP1 Entity Operations 279 */ 280 281 static void brx_configure_stream(struct vsp1_entity *entity, 282 struct v4l2_subdev_state *state, 283 struct vsp1_pipeline *pipe, 284 struct vsp1_dl_list *dl, 285 struct vsp1_dl_body *dlb) 286 { 287 struct vsp1_brx *brx = to_brx(&entity->subdev); 288 struct v4l2_mbus_framefmt *format; 289 unsigned int flags; 290 unsigned int i; 291 292 format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); 293 294 /* 295 * The hardware is extremely flexible but we have no userspace API to 296 * expose all the parameters, nor is it clear whether we would have use 297 * cases for all the supported modes. Let's just hardcode the parameters 298 * to sane default values for now. 299 */ 300 301 /* 302 * Disable dithering and enable color data normalization unless the 303 * format at the pipeline output is premultiplied. 304 */ 305 flags = pipe->output ? pipe->output->format.flags : 0; 306 vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL, 307 flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 308 0 : VI6_BRU_INCTRL_NRM); 309 310 /* 311 * Set the background position to cover the whole output image and 312 * configure its color. 313 */ 314 vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE, 315 (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | 316 (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); 317 vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0); 318 319 vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor | 320 (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); 321 322 /* 323 * Route BRU input 1 as SRC input to the ROP unit and configure the ROP 324 * unit with a NOP operation to make BRU input 1 available as the 325 * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP 326 * unit. 327 */ 328 if (entity->type == VSP1_ENTITY_BRU) 329 vsp1_brx_write(brx, dlb, VI6_BRU_ROP, 330 VI6_BRU_ROP_DSTSEL_BRUIN(1) | 331 VI6_BRU_ROP_CROP(VI6_ROP_NOP) | 332 VI6_BRU_ROP_AROP(VI6_ROP_NOP)); 333 334 for (i = 0; i < brx->entity.source_pad; ++i) { 335 bool premultiplied = false; 336 u32 ctrl = 0; 337 338 /* 339 * Configure all Blend/ROP units corresponding to an enabled BRx 340 * input for alpha blending. Blend/ROP units corresponding to 341 * disabled BRx inputs are used in ROP NOP mode to ignore the 342 * SRC input. 343 */ 344 if (brx->inputs[i].rpf) { 345 ctrl |= VI6_BRU_CTRL_RBC; 346 347 premultiplied = brx->inputs[i].rpf->format.flags 348 & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; 349 } else { 350 ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) 351 | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); 352 } 353 354 /* 355 * Select the virtual RPF as the Blend/ROP unit A DST input to 356 * serve as a background color. 357 */ 358 if (i == 0) 359 ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; 360 361 /* 362 * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D 363 * in that order. In the BRU the Blend/ROP unit B SRC is 364 * hardwired to the ROP unit output, the corresponding register 365 * bits must be set to 0. The BRS has no ROP unit and doesn't 366 * need any special processing. 367 */ 368 if (!(entity->type == VSP1_ENTITY_BRU && i == 1)) 369 ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); 370 371 vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl); 372 373 /* 374 * Hardcode the blending formula to 375 * 376 * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa 377 * DSTa = DSTa * (1 - SRCa) + SRCa 378 * 379 * when the SRC input isn't premultiplied, and to 380 * 381 * DSTc = DSTc * (1 - SRCa) + SRCc 382 * DSTa = DSTa * (1 - SRCa) + SRCa 383 * 384 * otherwise. 385 */ 386 vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i), 387 VI6_BRU_BLD_CCMDX_255_SRC_A | 388 (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : 389 VI6_BRU_BLD_CCMDY_SRC_A) | 390 VI6_BRU_BLD_ACMDX_255_SRC_A | 391 VI6_BRU_BLD_ACMDY_COEFY | 392 (0xff << VI6_BRU_BLD_COEFY_SHIFT)); 393 } 394 } 395 396 static const struct vsp1_entity_operations brx_entity_ops = { 397 .configure_stream = brx_configure_stream, 398 }; 399 400 /* ----------------------------------------------------------------------------- 401 * Initialization and Cleanup 402 */ 403 404 struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, 405 enum vsp1_entity_type type) 406 { 407 struct vsp1_brx *brx; 408 unsigned int num_pads; 409 const char *name; 410 int ret; 411 412 brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL); 413 if (brx == NULL) 414 return ERR_PTR(-ENOMEM); 415 416 brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; 417 brx->entity.ops = &brx_entity_ops; 418 brx->entity.type = type; 419 420 if (type == VSP1_ENTITY_BRU) { 421 num_pads = vsp1->info->num_bru_inputs + 1; 422 name = "bru"; 423 } else { 424 num_pads = 3; 425 name = "brs"; 426 } 427 428 ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops, 429 MEDIA_ENT_F_PROC_VIDEO_COMPOSER); 430 if (ret < 0) 431 return ERR_PTR(ret); 432 433 /* Initialize the control handler. */ 434 v4l2_ctrl_handler_init(&brx->ctrls, 1); 435 v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR, 436 0, 0xffffff, 1, 0); 437 438 brx->bgcolor = 0; 439 440 brx->entity.subdev.ctrl_handler = &brx->ctrls; 441 442 if (brx->ctrls.error) { 443 dev_err(vsp1->dev, "%s: failed to initialize controls\n", name); 444 ret = brx->ctrls.error; 445 vsp1_entity_destroy(&brx->entity); 446 return ERR_PTR(ret); 447 } 448 449 return brx; 450 } 451