root/drivers/media/platform/vsp1/vsp1_brx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vsp1_brx_write
  2. brx_s_ctrl
  3. brx_enum_mbus_code
  4. brx_enum_frame_size
  5. brx_get_compose
  6. brx_try_format
  7. brx_set_format
  8. brx_get_selection
  9. brx_set_selection
  10. brx_configure_stream
  11. vsp1_brx_create

   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_pad_config *cfg,
  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, cfg, code, codes,
  77                                           ARRAY_SIZE(codes));
  78 }
  79 
  80 static int brx_enum_frame_size(struct v4l2_subdev *subdev,
  81                                struct v4l2_subdev_pad_config *cfg,
  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_pad_config *cfg,
 101                                          unsigned int pad)
 102 {
 103         return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad);
 104 }
 105 
 106 static void brx_try_format(struct vsp1_brx *brx,
 107                            struct v4l2_subdev_pad_config *config,
 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, config,
 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_pad_config *cfg,
 136                           struct v4l2_subdev_format *fmt)
 137 {
 138         struct vsp1_brx *brx = to_brx(subdev);
 139         struct v4l2_subdev_pad_config *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, cfg, fmt->which);
 146         if (!config) {
 147                 ret = -EINVAL;
 148                 goto done;
 149         }
 150 
 151         brx_try_format(brx, config, fmt->pad, &fmt->format);
 152 
 153         format = vsp1_entity_get_pad_format(&brx->entity, config, 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, config, 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                                                             config, 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_pad_config *cfg,
 185                              struct v4l2_subdev_selection *sel)
 186 {
 187         struct vsp1_brx *brx = to_brx(subdev);
 188         struct v4l2_subdev_pad_config *config;
 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                 config = vsp1_entity_get_pad_config(&brx->entity, cfg,
 203                                                     sel->which);
 204                 if (!config)
 205                         return -EINVAL;
 206 
 207                 mutex_lock(&brx->entity.lock);
 208                 sel->r = *brx_get_compose(brx, config, 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_pad_config *cfg,
 219                              struct v4l2_subdev_selection *sel)
 220 {
 221         struct vsp1_brx *brx = to_brx(subdev);
 222         struct v4l2_subdev_pad_config *config;
 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         config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which);
 236         if (!config) {
 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, config,
 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, config, sel->pad);
 255         sel->r.width = format->width;
 256         sel->r.height = format->height;
 257 
 258         compose = brx_get_compose(brx, config, 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         .init_cfg = vsp1_entity_init_cfg,
 268         .enum_mbus_code = brx_enum_mbus_code,
 269         .enum_frame_size = brx_enum_frame_size,
 270         .get_fmt = vsp1_subdev_get_pad_format,
 271         .set_fmt = brx_set_format,
 272         .get_selection = brx_get_selection,
 273         .set_selection = brx_set_selection,
 274 };
 275 
 276 static const struct v4l2_subdev_ops brx_ops = {
 277         .pad    = &brx_pad_ops,
 278 };
 279 
 280 /* -----------------------------------------------------------------------------
 281  * VSP1 Entity Operations
 282  */
 283 
 284 static void brx_configure_stream(struct vsp1_entity *entity,
 285                                  struct vsp1_pipeline *pipe,
 286                                  struct vsp1_dl_list *dl,
 287                                  struct vsp1_dl_body *dlb)
 288 {
 289         struct vsp1_brx *brx = to_brx(&entity->subdev);
 290         struct v4l2_mbus_framefmt *format;
 291         unsigned int flags;
 292         unsigned int i;
 293 
 294         format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config,
 295                                             brx->entity.source_pad);
 296 
 297         /*
 298          * The hardware is extremely flexible but we have no userspace API to
 299          * expose all the parameters, nor is it clear whether we would have use
 300          * cases for all the supported modes. Let's just hardcode the parameters
 301          * to sane default values for now.
 302          */
 303 
 304         /*
 305          * Disable dithering and enable color data normalization unless the
 306          * format at the pipeline output is premultiplied.
 307          */
 308         flags = pipe->output ? pipe->output->format.flags : 0;
 309         vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,
 310                        flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
 311                        0 : VI6_BRU_INCTRL_NRM);
 312 
 313         /*
 314          * Set the background position to cover the whole output image and
 315          * configure its color.
 316          */
 317         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,
 318                        (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
 319                        (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
 320         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);
 321 
 322         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |
 323                        (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
 324 
 325         /*
 326          * Route BRU input 1 as SRC input to the ROP unit and configure the ROP
 327          * unit with a NOP operation to make BRU input 1 available as the
 328          * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP
 329          * unit.
 330          */
 331         if (entity->type == VSP1_ENTITY_BRU)
 332                 vsp1_brx_write(brx, dlb, VI6_BRU_ROP,
 333                                VI6_BRU_ROP_DSTSEL_BRUIN(1) |
 334                                VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
 335                                VI6_BRU_ROP_AROP(VI6_ROP_NOP));
 336 
 337         for (i = 0; i < brx->entity.source_pad; ++i) {
 338                 bool premultiplied = false;
 339                 u32 ctrl = 0;
 340 
 341                 /*
 342                  * Configure all Blend/ROP units corresponding to an enabled BRx
 343                  * input for alpha blending. Blend/ROP units corresponding to
 344                  * disabled BRx inputs are used in ROP NOP mode to ignore the
 345                  * SRC input.
 346                  */
 347                 if (brx->inputs[i].rpf) {
 348                         ctrl |= VI6_BRU_CTRL_RBC;
 349 
 350                         premultiplied = brx->inputs[i].rpf->format.flags
 351                                       & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
 352                 } else {
 353                         ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
 354                              |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
 355                 }
 356 
 357                 /*
 358                  * Select the virtual RPF as the Blend/ROP unit A DST input to
 359                  * serve as a background color.
 360                  */
 361                 if (i == 0)
 362                         ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
 363 
 364                 /*
 365                  * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D
 366                  * in that order. In the BRU the Blend/ROP unit B SRC is
 367                  * hardwired to the ROP unit output, the corresponding register
 368                  * bits must be set to 0. The BRS has no ROP unit and doesn't
 369                  * need any special processing.
 370                  */
 371                 if (!(entity->type == VSP1_ENTITY_BRU && i == 1))
 372                         ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
 373 
 374                 vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
 375 
 376                 /*
 377                  * Hardcode the blending formula to
 378                  *
 379                  *      DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
 380                  *      DSTa = DSTa * (1 - SRCa) + SRCa
 381                  *
 382                  * when the SRC input isn't premultiplied, and to
 383                  *
 384                  *      DSTc = DSTc * (1 - SRCa) + SRCc
 385                  *      DSTa = DSTa * (1 - SRCa) + SRCa
 386                  *
 387                  * otherwise.
 388                  */
 389                 vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),
 390                                VI6_BRU_BLD_CCMDX_255_SRC_A |
 391                                (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
 392                                                 VI6_BRU_BLD_CCMDY_SRC_A) |
 393                                VI6_BRU_BLD_ACMDX_255_SRC_A |
 394                                VI6_BRU_BLD_ACMDY_COEFY |
 395                                (0xff << VI6_BRU_BLD_COEFY_SHIFT));
 396         }
 397 }
 398 
 399 static const struct vsp1_entity_operations brx_entity_ops = {
 400         .configure_stream = brx_configure_stream,
 401 };
 402 
 403 /* -----------------------------------------------------------------------------
 404  * Initialization and Cleanup
 405  */
 406 
 407 struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
 408                                  enum vsp1_entity_type type)
 409 {
 410         struct vsp1_brx *brx;
 411         unsigned int num_pads;
 412         const char *name;
 413         int ret;
 414 
 415         brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);
 416         if (brx == NULL)
 417                 return ERR_PTR(-ENOMEM);
 418 
 419         brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
 420         brx->entity.ops = &brx_entity_ops;
 421         brx->entity.type = type;
 422 
 423         if (type == VSP1_ENTITY_BRU) {
 424                 num_pads = vsp1->info->num_bru_inputs + 1;
 425                 name = "bru";
 426         } else {
 427                 num_pads = 3;
 428                 name = "brs";
 429         }
 430 
 431         ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,
 432                                MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
 433         if (ret < 0)
 434                 return ERR_PTR(ret);
 435 
 436         /* Initialize the control handler. */
 437         v4l2_ctrl_handler_init(&brx->ctrls, 1);
 438         v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,
 439                           0, 0xffffff, 1, 0);
 440 
 441         brx->bgcolor = 0;
 442 
 443         brx->entity.subdev.ctrl_handler = &brx->ctrls;
 444 
 445         if (brx->ctrls.error) {
 446                 dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
 447                 ret = brx->ctrls.error;
 448                 vsp1_entity_destroy(&brx->entity);
 449                 return ERR_PTR(ret);
 450         }
 451 
 452         return brx;
 453 }

/* [<][>][^][v][top][bottom][index][help] */