linux/drivers/media/platform/vsp1/vsp1_bru.c
<<
>>
Prefs
   1/*
   2 * vsp1_bru.c  --  R-Car VSP1 Blend ROP Unit
   3 *
   4 * Copyright (C) 2013 Renesas Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/device.h>
  15#include <linux/gfp.h>
  16
  17#include <media/v4l2-subdev.h>
  18
  19#include "vsp1.h"
  20#include "vsp1_bru.h"
  21#include "vsp1_dl.h"
  22#include "vsp1_pipe.h"
  23#include "vsp1_rwpf.h"
  24#include "vsp1_video.h"
  25
  26#define BRU_MIN_SIZE                            1U
  27#define BRU_MAX_SIZE                            8190U
  28
  29/* -----------------------------------------------------------------------------
  30 * Device Access
  31 */
  32
  33static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl,
  34                                  u32 reg, u32 data)
  35{
  36        vsp1_dl_list_write(dl, reg, data);
  37}
  38
  39/* -----------------------------------------------------------------------------
  40 * Controls
  41 */
  42
  43static int bru_s_ctrl(struct v4l2_ctrl *ctrl)
  44{
  45        struct vsp1_bru *bru =
  46                container_of(ctrl->handler, struct vsp1_bru, ctrls);
  47
  48        switch (ctrl->id) {
  49        case V4L2_CID_BG_COLOR:
  50                bru->bgcolor = ctrl->val;
  51                break;
  52        }
  53
  54        return 0;
  55}
  56
  57static const struct v4l2_ctrl_ops bru_ctrl_ops = {
  58        .s_ctrl = bru_s_ctrl,
  59};
  60
  61/* -----------------------------------------------------------------------------
  62 * V4L2 Subdevice Operations
  63 */
  64
  65/*
  66 * The BRU can't perform format conversion, all sink and source formats must be
  67 * identical. We pick the format on the first sink pad (pad 0) and propagate it
  68 * to all other pads.
  69 */
  70
  71static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
  72                              struct v4l2_subdev_pad_config *cfg,
  73                              struct v4l2_subdev_mbus_code_enum *code)
  74{
  75        static const unsigned int codes[] = {
  76                MEDIA_BUS_FMT_ARGB8888_1X32,
  77                MEDIA_BUS_FMT_AYUV8_1X32,
  78        };
  79
  80        return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
  81                                          ARRAY_SIZE(codes));
  82}
  83
  84static int bru_enum_frame_size(struct v4l2_subdev *subdev,
  85                               struct v4l2_subdev_pad_config *cfg,
  86                               struct v4l2_subdev_frame_size_enum *fse)
  87{
  88        if (fse->index)
  89                return -EINVAL;
  90
  91        if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
  92            fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
  93                return -EINVAL;
  94
  95        fse->min_width = BRU_MIN_SIZE;
  96        fse->max_width = BRU_MAX_SIZE;
  97        fse->min_height = BRU_MIN_SIZE;
  98        fse->max_height = BRU_MAX_SIZE;
  99
 100        return 0;
 101}
 102
 103static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
 104                                         struct v4l2_subdev_pad_config *cfg,
 105                                         unsigned int pad)
 106{
 107        return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad);
 108}
 109
 110static void bru_try_format(struct vsp1_bru *bru,
 111                           struct v4l2_subdev_pad_config *config,
 112                           unsigned int pad, struct v4l2_mbus_framefmt *fmt)
 113{
 114        struct v4l2_mbus_framefmt *format;
 115
 116        switch (pad) {
 117        case BRU_PAD_SINK(0):
 118                /* Default to YUV if the requested format is not supported. */
 119                if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
 120                    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
 121                        fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
 122                break;
 123
 124        default:
 125                /* The BRU can't perform format conversion. */
 126                format = vsp1_entity_get_pad_format(&bru->entity, config,
 127                                                    BRU_PAD_SINK(0));
 128                fmt->code = format->code;
 129                break;
 130        }
 131
 132        fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
 133        fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
 134        fmt->field = V4L2_FIELD_NONE;
 135        fmt->colorspace = V4L2_COLORSPACE_SRGB;
 136}
 137
 138static int bru_set_format(struct v4l2_subdev *subdev,
 139                          struct v4l2_subdev_pad_config *cfg,
 140                          struct v4l2_subdev_format *fmt)
 141{
 142        struct vsp1_bru *bru = to_bru(subdev);
 143        struct v4l2_subdev_pad_config *config;
 144        struct v4l2_mbus_framefmt *format;
 145
 146        config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which);
 147        if (!config)
 148                return -EINVAL;
 149
 150        bru_try_format(bru, config, fmt->pad, &fmt->format);
 151
 152        format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad);
 153        *format = fmt->format;
 154
 155        /* Reset the compose rectangle */
 156        if (fmt->pad != bru->entity.source_pad) {
 157                struct v4l2_rect *compose;
 158
 159                compose = bru_get_compose(bru, config, fmt->pad);
 160                compose->left = 0;
 161                compose->top = 0;
 162                compose->width = format->width;
 163                compose->height = format->height;
 164        }
 165
 166        /* Propagate the format code to all pads */
 167        if (fmt->pad == BRU_PAD_SINK(0)) {
 168                unsigned int i;
 169
 170                for (i = 0; i <= bru->entity.source_pad; ++i) {
 171                        format = vsp1_entity_get_pad_format(&bru->entity,
 172                                                            config, i);
 173                        format->code = fmt->format.code;
 174                }
 175        }
 176
 177        return 0;
 178}
 179
 180static int bru_get_selection(struct v4l2_subdev *subdev,
 181                             struct v4l2_subdev_pad_config *cfg,
 182                             struct v4l2_subdev_selection *sel)
 183{
 184        struct vsp1_bru *bru = to_bru(subdev);
 185        struct v4l2_subdev_pad_config *config;
 186
 187        if (sel->pad == bru->entity.source_pad)
 188                return -EINVAL;
 189
 190        switch (sel->target) {
 191        case V4L2_SEL_TGT_COMPOSE_BOUNDS:
 192                sel->r.left = 0;
 193                sel->r.top = 0;
 194                sel->r.width = BRU_MAX_SIZE;
 195                sel->r.height = BRU_MAX_SIZE;
 196                return 0;
 197
 198        case V4L2_SEL_TGT_COMPOSE:
 199                config = vsp1_entity_get_pad_config(&bru->entity, cfg,
 200                                                    sel->which);
 201                if (!config)
 202                        return -EINVAL;
 203
 204                sel->r = *bru_get_compose(bru, config, sel->pad);
 205                return 0;
 206
 207        default:
 208                return -EINVAL;
 209        }
 210}
 211
 212static int bru_set_selection(struct v4l2_subdev *subdev,
 213                             struct v4l2_subdev_pad_config *cfg,
 214                             struct v4l2_subdev_selection *sel)
 215{
 216        struct vsp1_bru *bru = to_bru(subdev);
 217        struct v4l2_subdev_pad_config *config;
 218        struct v4l2_mbus_framefmt *format;
 219        struct v4l2_rect *compose;
 220
 221        if (sel->pad == bru->entity.source_pad)
 222                return -EINVAL;
 223
 224        if (sel->target != V4L2_SEL_TGT_COMPOSE)
 225                return -EINVAL;
 226
 227        config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which);
 228        if (!config)
 229                return -EINVAL;
 230
 231        /* The compose rectangle top left corner must be inside the output
 232         * frame.
 233         */
 234        format = vsp1_entity_get_pad_format(&bru->entity, config,
 235                                            bru->entity.source_pad);
 236        sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
 237        sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
 238
 239        /* Scaling isn't supported, the compose rectangle size must be identical
 240         * to the sink format size.
 241         */
 242        format = vsp1_entity_get_pad_format(&bru->entity, config, sel->pad);
 243        sel->r.width = format->width;
 244        sel->r.height = format->height;
 245
 246        compose = bru_get_compose(bru, config, sel->pad);
 247        *compose = sel->r;
 248
 249        return 0;
 250}
 251
 252static struct v4l2_subdev_pad_ops bru_pad_ops = {
 253        .init_cfg = vsp1_entity_init_cfg,
 254        .enum_mbus_code = bru_enum_mbus_code,
 255        .enum_frame_size = bru_enum_frame_size,
 256        .get_fmt = vsp1_subdev_get_pad_format,
 257        .set_fmt = bru_set_format,
 258        .get_selection = bru_get_selection,
 259        .set_selection = bru_set_selection,
 260};
 261
 262static struct v4l2_subdev_ops bru_ops = {
 263        .pad    = &bru_pad_ops,
 264};
 265
 266/* -----------------------------------------------------------------------------
 267 * VSP1 Entity Operations
 268 */
 269
 270static void bru_configure(struct vsp1_entity *entity,
 271                          struct vsp1_pipeline *pipe,
 272                          struct vsp1_dl_list *dl)
 273{
 274        struct vsp1_bru *bru = to_bru(&entity->subdev);
 275        struct v4l2_mbus_framefmt *format;
 276        unsigned int flags;
 277        unsigned int i;
 278
 279        format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
 280                                            bru->entity.source_pad);
 281
 282        /* The hardware is extremely flexible but we have no userspace API to
 283         * expose all the parameters, nor is it clear whether we would have use
 284         * cases for all the supported modes. Let's just harcode the parameters
 285         * to sane default values for now.
 286         */
 287
 288        /* Disable dithering and enable color data normalization unless the
 289         * format at the pipeline output is premultiplied.
 290         */
 291        flags = pipe->output ? pipe->output->format.flags : 0;
 292        vsp1_bru_write(bru, dl, VI6_BRU_INCTRL,
 293                       flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
 294                       0 : VI6_BRU_INCTRL_NRM);
 295
 296        /* Set the background position to cover the whole output image and
 297         * configure its color.
 298         */
 299        vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE,
 300                       (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
 301                       (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
 302        vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0);
 303
 304        vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor |
 305                       (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
 306
 307        /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
 308         * unit with a NOP operation to make BRU input 1 available as the
 309         * Blend/ROP unit B SRC input.
 310         */
 311        vsp1_bru_write(bru, dl, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
 312                       VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
 313                       VI6_BRU_ROP_AROP(VI6_ROP_NOP));
 314
 315        for (i = 0; i < bru->entity.source_pad; ++i) {
 316                bool premultiplied = false;
 317                u32 ctrl = 0;
 318
 319                /* Configure all Blend/ROP units corresponding to an enabled BRU
 320                 * input for alpha blending. Blend/ROP units corresponding to
 321                 * disabled BRU inputs are used in ROP NOP mode to ignore the
 322                 * SRC input.
 323                 */
 324                if (bru->inputs[i].rpf) {
 325                        ctrl |= VI6_BRU_CTRL_RBC;
 326
 327                        premultiplied = bru->inputs[i].rpf->format.flags
 328                                      & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
 329                } else {
 330                        ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
 331                             |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
 332                }
 333
 334                /* Select the virtual RPF as the Blend/ROP unit A DST input to
 335                 * serve as a background color.
 336                 */
 337                if (i == 0)
 338                        ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
 339
 340                /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to
 341                 * D in that order. The Blend/ROP unit B SRC is hardwired to the
 342                 * ROP unit output, the corresponding register bits must be set
 343                 * to 0.
 344                 */
 345                if (i != 1)
 346                        ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
 347
 348                vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl);
 349
 350                /* Harcode the blending formula to
 351                 *
 352                 *      DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
 353                 *      DSTa = DSTa * (1 - SRCa) + SRCa
 354                 *
 355                 * when the SRC input isn't premultiplied, and to
 356                 *
 357                 *      DSTc = DSTc * (1 - SRCa) + SRCc
 358                 *      DSTa = DSTa * (1 - SRCa) + SRCa
 359                 *
 360                 * otherwise.
 361                 */
 362                vsp1_bru_write(bru, dl, VI6_BRU_BLD(i),
 363                               VI6_BRU_BLD_CCMDX_255_SRC_A |
 364                               (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
 365                                                VI6_BRU_BLD_CCMDY_SRC_A) |
 366                               VI6_BRU_BLD_ACMDX_255_SRC_A |
 367                               VI6_BRU_BLD_ACMDY_COEFY |
 368                               (0xff << VI6_BRU_BLD_COEFY_SHIFT));
 369        }
 370}
 371
 372static const struct vsp1_entity_operations bru_entity_ops = {
 373        .configure = bru_configure,
 374};
 375
 376/* -----------------------------------------------------------------------------
 377 * Initialization and Cleanup
 378 */
 379
 380struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
 381{
 382        struct vsp1_bru *bru;
 383        int ret;
 384
 385        bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
 386        if (bru == NULL)
 387                return ERR_PTR(-ENOMEM);
 388
 389        bru->entity.ops = &bru_entity_ops;
 390        bru->entity.type = VSP1_ENTITY_BRU;
 391
 392        ret = vsp1_entity_init(vsp1, &bru->entity, "bru",
 393                               vsp1->info->num_bru_inputs + 1, &bru_ops);
 394        if (ret < 0)
 395                return ERR_PTR(ret);
 396
 397        /* Initialize the control handler. */
 398        v4l2_ctrl_handler_init(&bru->ctrls, 1);
 399        v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR,
 400                          0, 0xffffff, 1, 0);
 401
 402        bru->bgcolor = 0;
 403
 404        bru->entity.subdev.ctrl_handler = &bru->ctrls;
 405
 406        if (bru->ctrls.error) {
 407                dev_err(vsp1->dev, "bru: failed to initialize controls\n");
 408                ret = bru->ctrls.error;
 409                vsp1_entity_destroy(&bru->entity);
 410                return ERR_PTR(ret);
 411        }
 412
 413        return bru;
 414}
 415