linux/drivers/media/platform/vsp1/vsp1_sru.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * vsp1_sru.c  --  R-Car VSP1 Super Resolution Unit
   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_dl.h"
  17#include "vsp1_pipe.h"
  18#include "vsp1_sru.h"
  19
  20#define SRU_MIN_SIZE                            4U
  21#define SRU_MAX_SIZE                            8190U
  22
  23/* -----------------------------------------------------------------------------
  24 * Device Access
  25 */
  26
  27static inline void vsp1_sru_write(struct vsp1_sru *sru,
  28                                  struct vsp1_dl_body *dlb, u32 reg, u32 data)
  29{
  30        vsp1_dl_body_write(dlb, reg, data);
  31}
  32
  33/* -----------------------------------------------------------------------------
  34 * Controls
  35 */
  36
  37#define V4L2_CID_VSP1_SRU_INTENSITY             (V4L2_CID_USER_BASE | 0x1001)
  38
  39struct vsp1_sru_param {
  40        u32 ctrl0;
  41        u32 ctrl2;
  42};
  43
  44#define VI6_SRU_CTRL0_PARAMS(p0, p1)                    \
  45        (((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) |         \
  46         ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
  47
  48#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8)                \
  49        (((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) |         \
  50         ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) |         \
  51         ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
  52
  53static const struct vsp1_sru_param vsp1_sru_params[] = {
  54        {
  55                .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
  56                .ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255),
  57        }, {
  58                .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
  59                .ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255),
  60        }, {
  61                .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
  62                .ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255),
  63        }, {
  64                .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
  65                .ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255),
  66        }, {
  67                .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
  68                .ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255),
  69        }, {
  70                .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
  71                .ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255),
  72        },
  73};
  74
  75static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
  76{
  77        struct vsp1_sru *sru =
  78                container_of(ctrl->handler, struct vsp1_sru, ctrls);
  79
  80        switch (ctrl->id) {
  81        case V4L2_CID_VSP1_SRU_INTENSITY:
  82                sru->intensity = ctrl->val;
  83                break;
  84        }
  85
  86        return 0;
  87}
  88
  89static const struct v4l2_ctrl_ops sru_ctrl_ops = {
  90        .s_ctrl = sru_s_ctrl,
  91};
  92
  93static const struct v4l2_ctrl_config sru_intensity_control = {
  94        .ops = &sru_ctrl_ops,
  95        .id = V4L2_CID_VSP1_SRU_INTENSITY,
  96        .name = "Intensity",
  97        .type = V4L2_CTRL_TYPE_INTEGER,
  98        .min = 1,
  99        .max = 6,
 100        .def = 1,
 101        .step = 1,
 102};
 103
 104/* -----------------------------------------------------------------------------
 105 * V4L2 Subdevice Operations
 106 */
 107
 108static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
 109                              struct v4l2_subdev_pad_config *cfg,
 110                              struct v4l2_subdev_mbus_code_enum *code)
 111{
 112        static const unsigned int codes[] = {
 113                MEDIA_BUS_FMT_ARGB8888_1X32,
 114                MEDIA_BUS_FMT_AYUV8_1X32,
 115        };
 116
 117        return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
 118                                          ARRAY_SIZE(codes));
 119}
 120
 121static int sru_enum_frame_size(struct v4l2_subdev *subdev,
 122                               struct v4l2_subdev_pad_config *cfg,
 123                               struct v4l2_subdev_frame_size_enum *fse)
 124{
 125        struct vsp1_sru *sru = to_sru(subdev);
 126        struct v4l2_subdev_pad_config *config;
 127        struct v4l2_mbus_framefmt *format;
 128        int ret = 0;
 129
 130        config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which);
 131        if (!config)
 132                return -EINVAL;
 133
 134        format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK);
 135
 136        mutex_lock(&sru->entity.lock);
 137
 138        if (fse->index || fse->code != format->code) {
 139                ret = -EINVAL;
 140                goto done;
 141        }
 142
 143        if (fse->pad == SRU_PAD_SINK) {
 144                fse->min_width = SRU_MIN_SIZE;
 145                fse->max_width = SRU_MAX_SIZE;
 146                fse->min_height = SRU_MIN_SIZE;
 147                fse->max_height = SRU_MAX_SIZE;
 148        } else {
 149                fse->min_width = format->width;
 150                fse->min_height = format->height;
 151                if (format->width <= SRU_MAX_SIZE / 2 &&
 152                    format->height <= SRU_MAX_SIZE / 2) {
 153                        fse->max_width = format->width * 2;
 154                        fse->max_height = format->height * 2;
 155                } else {
 156                        fse->max_width = format->width;
 157                        fse->max_height = format->height;
 158                }
 159        }
 160
 161done:
 162        mutex_unlock(&sru->entity.lock);
 163        return ret;
 164}
 165
 166static void sru_try_format(struct vsp1_sru *sru,
 167                           struct v4l2_subdev_pad_config *config,
 168                           unsigned int pad, struct v4l2_mbus_framefmt *fmt)
 169{
 170        struct v4l2_mbus_framefmt *format;
 171        unsigned int input_area;
 172        unsigned int output_area;
 173
 174        switch (pad) {
 175        case SRU_PAD_SINK:
 176                /* Default to YUV if the requested format is not supported. */
 177                if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
 178                    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
 179                        fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
 180
 181                fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
 182                fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
 183                break;
 184
 185        case SRU_PAD_SOURCE:
 186                /* The SRU can't perform format conversion. */
 187                format = vsp1_entity_get_pad_format(&sru->entity, config,
 188                                                    SRU_PAD_SINK);
 189                fmt->code = format->code;
 190
 191                /*
 192                 * We can upscale by 2 in both direction, but not independently.
 193                 * Compare the input and output rectangles areas (avoiding
 194                 * integer overflows on the output): if the requested output
 195                 * area is larger than 1.5^2 the input area upscale by two,
 196                 * otherwise don't scale.
 197                 */
 198                input_area = format->width * format->height;
 199                output_area = min(fmt->width, SRU_MAX_SIZE)
 200                            * min(fmt->height, SRU_MAX_SIZE);
 201
 202                if (fmt->width <= SRU_MAX_SIZE / 2 &&
 203                    fmt->height <= SRU_MAX_SIZE / 2 &&
 204                    output_area > input_area * 9 / 4) {
 205                        fmt->width = format->width * 2;
 206                        fmt->height = format->height * 2;
 207                } else {
 208                        fmt->width = format->width;
 209                        fmt->height = format->height;
 210                }
 211                break;
 212        }
 213
 214        fmt->field = V4L2_FIELD_NONE;
 215        fmt->colorspace = V4L2_COLORSPACE_SRGB;
 216}
 217
 218static int sru_set_format(struct v4l2_subdev *subdev,
 219                          struct v4l2_subdev_pad_config *cfg,
 220                          struct v4l2_subdev_format *fmt)
 221{
 222        struct vsp1_sru *sru = to_sru(subdev);
 223        struct v4l2_subdev_pad_config *config;
 224        struct v4l2_mbus_framefmt *format;
 225        int ret = 0;
 226
 227        mutex_lock(&sru->entity.lock);
 228
 229        config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which);
 230        if (!config) {
 231                ret = -EINVAL;
 232                goto done;
 233        }
 234
 235        sru_try_format(sru, config, fmt->pad, &fmt->format);
 236
 237        format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad);
 238        *format = fmt->format;
 239
 240        if (fmt->pad == SRU_PAD_SINK) {
 241                /* Propagate the format to the source pad. */
 242                format = vsp1_entity_get_pad_format(&sru->entity, config,
 243                                                    SRU_PAD_SOURCE);
 244                *format = fmt->format;
 245
 246                sru_try_format(sru, config, SRU_PAD_SOURCE, format);
 247        }
 248
 249done:
 250        mutex_unlock(&sru->entity.lock);
 251        return ret;
 252}
 253
 254static const struct v4l2_subdev_pad_ops sru_pad_ops = {
 255        .init_cfg = vsp1_entity_init_cfg,
 256        .enum_mbus_code = sru_enum_mbus_code,
 257        .enum_frame_size = sru_enum_frame_size,
 258        .get_fmt = vsp1_subdev_get_pad_format,
 259        .set_fmt = sru_set_format,
 260};
 261
 262static const struct v4l2_subdev_ops sru_ops = {
 263        .pad    = &sru_pad_ops,
 264};
 265
 266/* -----------------------------------------------------------------------------
 267 * VSP1 Entity Operations
 268 */
 269
 270static void sru_configure_stream(struct vsp1_entity *entity,
 271                                 struct vsp1_pipeline *pipe,
 272                                 struct vsp1_dl_body *dlb)
 273{
 274        const struct vsp1_sru_param *param;
 275        struct vsp1_sru *sru = to_sru(&entity->subdev);
 276        struct v4l2_mbus_framefmt *input;
 277        struct v4l2_mbus_framefmt *output;
 278        u32 ctrl0;
 279
 280        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 281                                           SRU_PAD_SINK);
 282        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 283                                            SRU_PAD_SOURCE);
 284
 285        if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
 286                ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
 287                      | VI6_SRU_CTRL0_PARAM4;
 288        else
 289                ctrl0 = VI6_SRU_CTRL0_PARAM3;
 290
 291        if (input->width != output->width)
 292                ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
 293
 294        param = &vsp1_sru_params[sru->intensity - 1];
 295
 296        ctrl0 |= param->ctrl0;
 297
 298        vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0);
 299        vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
 300        vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2);
 301}
 302
 303static unsigned int sru_max_width(struct vsp1_entity *entity,
 304                                  struct vsp1_pipeline *pipe)
 305{
 306        struct vsp1_sru *sru = to_sru(&entity->subdev);
 307        struct v4l2_mbus_framefmt *input;
 308        struct v4l2_mbus_framefmt *output;
 309
 310        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 311                                           SRU_PAD_SINK);
 312        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 313                                            SRU_PAD_SOURCE);
 314
 315        if (input->width != output->width)
 316                return 512;
 317        else
 318                return 256;
 319}
 320
 321static void sru_partition(struct vsp1_entity *entity,
 322                          struct vsp1_pipeline *pipe,
 323                          struct vsp1_partition *partition,
 324                          unsigned int partition_idx,
 325                          struct vsp1_partition_window *window)
 326{
 327        struct vsp1_sru *sru = to_sru(&entity->subdev);
 328        struct v4l2_mbus_framefmt *input;
 329        struct v4l2_mbus_framefmt *output;
 330
 331        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 332                                           SRU_PAD_SINK);
 333        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 334                                            SRU_PAD_SOURCE);
 335
 336        /* Adapt if SRUx2 is enabled */
 337        if (input->width != output->width) {
 338                window->width /= 2;
 339                window->left /= 2;
 340        }
 341
 342        partition->sru = *window;
 343}
 344
 345static const struct vsp1_entity_operations sru_entity_ops = {
 346        .configure_stream = sru_configure_stream,
 347        .max_width = sru_max_width,
 348        .partition = sru_partition,
 349};
 350
 351/* -----------------------------------------------------------------------------
 352 * Initialization and Cleanup
 353 */
 354
 355struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
 356{
 357        struct vsp1_sru *sru;
 358        int ret;
 359
 360        sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
 361        if (sru == NULL)
 362                return ERR_PTR(-ENOMEM);
 363
 364        sru->entity.ops = &sru_entity_ops;
 365        sru->entity.type = VSP1_ENTITY_SRU;
 366
 367        ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
 368                               MEDIA_ENT_F_PROC_VIDEO_SCALER);
 369        if (ret < 0)
 370                return ERR_PTR(ret);
 371
 372        /* Initialize the control handler. */
 373        v4l2_ctrl_handler_init(&sru->ctrls, 1);
 374        v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
 375
 376        sru->intensity = 1;
 377
 378        sru->entity.subdev.ctrl_handler = &sru->ctrls;
 379
 380        if (sru->ctrls.error) {
 381                dev_err(vsp1->dev, "sru: failed to initialize controls\n");
 382                ret = sru->ctrls.error;
 383                vsp1_entity_destroy(&sru->entity);
 384                return ERR_PTR(ret);
 385        }
 386
 387        return sru;
 388}
 389