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_state_get_compose(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 *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 = vsp1_entity_get_pad_format(&brx->entity, 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 = brx_get_compose(brx, 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 = vsp1_entity_get_pad_format(&brx->entity, 173 state, i); 174 format->code = fmt->format.code; 175 } 176 } 177 178 done: 179 mutex_unlock(&brx->entity.lock); 180 return ret; 181 } 182 183 static int brx_get_selection(struct v4l2_subdev *subdev, 184 struct v4l2_subdev_state *sd_state, 185 struct v4l2_subdev_selection *sel) 186 { 187 struct vsp1_brx *brx = to_brx(subdev); 188 struct v4l2_subdev_state *state; 189 190 if (sel->pad == brx->entity.source_pad) 191 return -EINVAL; 192 193 switch (sel->target) { 194 case V4L2_SEL_TGT_COMPOSE_BOUNDS: 195 sel->r.left = 0; 196 sel->r.top = 0; 197 sel->r.width = BRX_MAX_SIZE; 198 sel->r.height = BRX_MAX_SIZE; 199 return 0; 200 201 case V4L2_SEL_TGT_COMPOSE: 202 state = vsp1_entity_get_state(&brx->entity, sd_state, 203 sel->which); 204 if (!state) 205 return -EINVAL; 206 207 mutex_lock(&brx->entity.lock); 208 sel->r = *brx_get_compose(brx, state, sel->pad); 209 mutex_unlock(&brx->entity.lock); 210 return 0; 211 212 default: 213 return -EINVAL; 214 } 215 } 216 217 static int brx_set_selection(struct v4l2_subdev *subdev, 218 struct v4l2_subdev_state *sd_state, 219 struct v4l2_subdev_selection *sel) 220 { 221 struct vsp1_brx *brx = to_brx(subdev); 222 struct v4l2_subdev_state *state; 223 struct v4l2_mbus_framefmt *format; 224 struct v4l2_rect *compose; 225 int ret = 0; 226 227 if (sel->pad == brx->entity.source_pad) 228 return -EINVAL; 229 230 if (sel->target != V4L2_SEL_TGT_COMPOSE) 231 return -EINVAL; 232 233 mutex_lock(&brx->entity.lock); 234 235 state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); 236 if (!state) { 237 ret = -EINVAL; 238 goto done; 239 } 240 241 /* 242 * The compose rectangle top left corner must be inside the output 243 * frame. 244 */ 245 format = vsp1_entity_get_pad_format(&brx->entity, state, 246 brx->entity.source_pad); 247 sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); 248 sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); 249 250 /* 251 * Scaling isn't supported, the compose rectangle size must be identical 252 * to the sink format size. 253 */ 254 format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); 255 sel->r.width = format->width; 256 sel->r.height = format->height; 257 258 compose = brx_get_compose(brx, state, sel->pad); 259 *compose = sel->r; 260 261 done: 262 mutex_unlock(&brx->entity.lock); 263 return ret; 264 } 265 266 static const struct v4l2_subdev_pad_ops brx_pad_ops = { 267 .enum_mbus_code = brx_enum_mbus_code, 268 .enum_frame_size = brx_enum_frame_size, 269 .get_fmt = vsp1_subdev_get_pad_format, 270 .set_fmt = brx_set_format, 271 .get_selection = brx_get_selection, 272 .set_selection = brx_set_selection, 273 }; 274 275 static const struct v4l2_subdev_ops brx_ops = { 276 .pad = &brx_pad_ops, 277 }; 278 279 /* ----------------------------------------------------------------------------- 280 * VSP1 Entity Operations 281 */ 282 283 static void brx_configure_stream(struct vsp1_entity *entity, 284 struct vsp1_pipeline *pipe, 285 struct vsp1_dl_list *dl, 286 struct vsp1_dl_body *dlb) 287 { 288 struct vsp1_brx *brx = to_brx(&entity->subdev); 289 struct v4l2_mbus_framefmt *format; 290 unsigned int flags; 291 unsigned int i; 292 293 format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, 294 brx->entity.source_pad); 295 296 /* 297 * The hardware is extremely flexible but we have no userspace API to 298 * expose all the parameters, nor is it clear whether we would have use 299 * cases for all the supported modes. Let's just hardcode the parameters 300 * to sane default values for now. 301 */ 302 303 /* 304 * Disable dithering and enable color data normalization unless the 305 * format at the pipeline output is premultiplied. 306 */ 307 flags = pipe->output ? pipe->output->format.flags : 0; 308 vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL, 309 flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 310 0 : VI6_BRU_INCTRL_NRM); 311 312 /* 313 * Set the background position to cover the whole output image and 314 * configure its color. 315 */ 316 vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE, 317 (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | 318 (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); 319 vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0); 320 321 vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor | 322 (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); 323 324 /* 325 * Route BRU input 1 as SRC input to the ROP unit and configure the ROP 326 * unit with a NOP operation to make BRU input 1 available as the 327 * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP 328 * unit. 329 */ 330 if (entity->type == VSP1_ENTITY_BRU) 331 vsp1_brx_write(brx, dlb, VI6_BRU_ROP, 332 VI6_BRU_ROP_DSTSEL_BRUIN(1) | 333 VI6_BRU_ROP_CROP(VI6_ROP_NOP) | 334 VI6_BRU_ROP_AROP(VI6_ROP_NOP)); 335 336 for (i = 0; i < brx->entity.source_pad; ++i) { 337 bool premultiplied = false; 338 u32 ctrl = 0; 339 340 /* 341 * Configure all Blend/ROP units corresponding to an enabled BRx 342 * input for alpha blending. Blend/ROP units corresponding to 343 * disabled BRx inputs are used in ROP NOP mode to ignore the 344 * SRC input. 345 */ 346 if (brx->inputs[i].rpf) { 347 ctrl |= VI6_BRU_CTRL_RBC; 348 349 premultiplied = brx->inputs[i].rpf->format.flags 350 & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; 351 } else { 352 ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) 353 | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); 354 } 355 356 /* 357 * Select the virtual RPF as the Blend/ROP unit A DST input to 358 * serve as a background color. 359 */ 360 if (i == 0) 361 ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; 362 363 /* 364 * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D 365 * in that order. In the BRU the Blend/ROP unit B SRC is 366 * hardwired to the ROP unit output, the corresponding register 367 * bits must be set to 0. The BRS has no ROP unit and doesn't 368 * need any special processing. 369 */ 370 if (!(entity->type == VSP1_ENTITY_BRU && i == 1)) 371 ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); 372 373 vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl); 374 375 /* 376 * Hardcode the blending formula to 377 * 378 * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa 379 * DSTa = DSTa * (1 - SRCa) + SRCa 380 * 381 * when the SRC input isn't premultiplied, and to 382 * 383 * DSTc = DSTc * (1 - SRCa) + SRCc 384 * DSTa = DSTa * (1 - SRCa) + SRCa 385 * 386 * otherwise. 387 */ 388 vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i), 389 VI6_BRU_BLD_CCMDX_255_SRC_A | 390 (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : 391 VI6_BRU_BLD_CCMDY_SRC_A) | 392 VI6_BRU_BLD_ACMDX_255_SRC_A | 393 VI6_BRU_BLD_ACMDY_COEFY | 394 (0xff << VI6_BRU_BLD_COEFY_SHIFT)); 395 } 396 } 397 398 static const struct vsp1_entity_operations brx_entity_ops = { 399 .configure_stream = brx_configure_stream, 400 }; 401 402 /* ----------------------------------------------------------------------------- 403 * Initialization and Cleanup 404 */ 405 406 struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, 407 enum vsp1_entity_type type) 408 { 409 struct vsp1_brx *brx; 410 unsigned int num_pads; 411 const char *name; 412 int ret; 413 414 brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL); 415 if (brx == NULL) 416 return ERR_PTR(-ENOMEM); 417 418 brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; 419 brx->entity.ops = &brx_entity_ops; 420 brx->entity.type = type; 421 422 if (type == VSP1_ENTITY_BRU) { 423 num_pads = vsp1->info->num_bru_inputs + 1; 424 name = "bru"; 425 } else { 426 num_pads = 3; 427 name = "brs"; 428 } 429 430 ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops, 431 MEDIA_ENT_F_PROC_VIDEO_COMPOSER); 432 if (ret < 0) 433 return ERR_PTR(ret); 434 435 /* Initialize the control handler. */ 436 v4l2_ctrl_handler_init(&brx->ctrls, 1); 437 v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR, 438 0, 0xffffff, 1, 0); 439 440 brx->bgcolor = 0; 441 442 brx->entity.subdev.ctrl_handler = &brx->ctrls; 443 444 if (brx->ctrls.error) { 445 dev_err(vsp1->dev, "%s: failed to initialize controls\n", name); 446 ret = brx->ctrls.error; 447 vsp1_entity_destroy(&brx->entity); 448 return ERR_PTR(ret); 449 } 450 451 return brx; 452 } 453