444 lines · c
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 Corporation6 *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 1U23#define BRX_MAX_SIZE 8190U24 25/* -----------------------------------------------------------------------------26 * Device Access27 */28 29static 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 * Controls37 */38 39static 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 53static const struct v4l2_ctrl_ops brx_ctrl_ops = {54 .s_ctrl = brx_s_ctrl,55};56 57/* -----------------------------------------------------------------------------58 * V4L2 Subdevice Operations59 */60 61/*62 * The BRx can't perform format conversion, all sink and source formats must be63 * identical. We pick the format on the first sink pad (pad 0) and propagate it64 * to all other pads.65 */66 67static 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 80static 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 99static 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 127static 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 170done:171 mutex_unlock(&brx->entity.lock);172 return ret;173}174 175static 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 209static 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 output235 * 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 identical243 * 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 252done:253 mutex_unlock(&brx->entity.lock);254 return ret;255}256 257static 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 266static const struct v4l2_subdev_ops brx_ops = {267 .pad = &brx_pad_ops,268};269 270/* -----------------------------------------------------------------------------271 * VSP1 Entity Operations272 */273 274static 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 to289 * expose all the parameters, nor is it clear whether we would have use290 * cases for all the supported modes. Let's just hardcode the parameters291 * to sane default values for now.292 */293 294 /*295 * Disable dithering and enable color data normalization unless the296 * 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 and305 * 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 ROP317 * unit with a NOP operation to make BRU input 1 available as the318 * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP319 * 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 BRx333 * input for alpha blending. Blend/ROP units corresponding to334 * disabled BRx inputs are used in ROP NOP mode to ignore the335 * SRC input.336 */337 if (brx->inputs[i].rpf) {338 ctrl |= VI6_BRU_CTRL_RBC;339 340 premultiplied = brx->inputs[i].rpf->format.flags341 & 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 to349 * 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 D356 * in that order. In the BRU the Blend/ROP unit B SRC is357 * hardwired to the ROP unit output, the corresponding register358 * bits must be set to 0. The BRS has no ROP unit and doesn't359 * 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 to368 *369 * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa370 * DSTa = DSTa * (1 - SRCa) + SRCa371 *372 * when the SRC input isn't premultiplied, and to373 *374 * DSTc = DSTc * (1 - SRCa) + SRCc375 * DSTa = DSTa * (1 - SRCa) + SRCa376 *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 389static const struct vsp1_entity_operations brx_entity_ops = {390 .configure_stream = brx_configure_stream,391};392 393/* -----------------------------------------------------------------------------394 * Initialization and Cleanup395 */396 397struct 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