linux/drivers/media/platform/vsp1/vsp1_brx.c
<<
>>
Prefs
   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
  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 * Controls
  37 */
  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 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
  67static 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
  80static 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
  99static 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
 106static 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
 134static 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
 178done:
 179        mutex_unlock(&brx->entity.lock);
 180        return ret;
 181}
 182
 183static 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
 217static 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
 261done:
 262        mutex_unlock(&brx->entity.lock);
 263        return ret;
 264}
 265
 266static 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
 276static const struct v4l2_subdev_ops brx_ops = {
 277        .pad    = &brx_pad_ops,
 278};
 279
 280/* -----------------------------------------------------------------------------
 281 * VSP1 Entity Operations
 282 */
 283
 284static 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
 399static const struct vsp1_entity_operations brx_entity_ops = {
 400        .configure_stream = brx_configure_stream,
 401};
 402
 403/* -----------------------------------------------------------------------------
 404 * Initialization and Cleanup
 405 */
 406
 407struct 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}
 454