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