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