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