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_state *sd_state,
 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, sd_state, code, codes,
 118                                          ARRAY_SIZE(codes));
 119}
 120
 121static int sru_enum_frame_size(struct v4l2_subdev *subdev,
 122                               struct v4l2_subdev_state *sd_state,
 123                               struct v4l2_subdev_frame_size_enum *fse)
 124{
 125        struct vsp1_sru *sru = to_sru(subdev);
 126        struct v4l2_subdev_state *config;
 127        struct v4l2_mbus_framefmt *format;
 128        int ret = 0;
 129
 130        config = vsp1_entity_get_pad_config(&sru->entity, sd_state,
 131                                            fse->which);
 132        if (!config)
 133                return -EINVAL;
 134
 135        format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK);
 136
 137        mutex_lock(&sru->entity.lock);
 138
 139        if (fse->index || fse->code != format->code) {
 140                ret = -EINVAL;
 141                goto done;
 142        }
 143
 144        if (fse->pad == SRU_PAD_SINK) {
 145                fse->min_width = SRU_MIN_SIZE;
 146                fse->max_width = SRU_MAX_SIZE;
 147                fse->min_height = SRU_MIN_SIZE;
 148                fse->max_height = SRU_MAX_SIZE;
 149        } else {
 150                fse->min_width = format->width;
 151                fse->min_height = format->height;
 152                if (format->width <= SRU_MAX_SIZE / 2 &&
 153                    format->height <= SRU_MAX_SIZE / 2) {
 154                        fse->max_width = format->width * 2;
 155                        fse->max_height = format->height * 2;
 156                } else {
 157                        fse->max_width = format->width;
 158                        fse->max_height = format->height;
 159                }
 160        }
 161
 162done:
 163        mutex_unlock(&sru->entity.lock);
 164        return ret;
 165}
 166
 167static void sru_try_format(struct vsp1_sru *sru,
 168                           struct v4l2_subdev_state *sd_state,
 169                           unsigned int pad, struct v4l2_mbus_framefmt *fmt)
 170{
 171        struct v4l2_mbus_framefmt *format;
 172        unsigned int input_area;
 173        unsigned int output_area;
 174
 175        switch (pad) {
 176        case SRU_PAD_SINK:
 177                /* Default to YUV if the requested format is not supported. */
 178                if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
 179                    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
 180                        fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
 181
 182                fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
 183                fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
 184                break;
 185
 186        case SRU_PAD_SOURCE:
 187                /* The SRU can't perform format conversion. */
 188                format = vsp1_entity_get_pad_format(&sru->entity, sd_state,
 189                                                    SRU_PAD_SINK);
 190                fmt->code = format->code;
 191
 192                /*
 193                 * We can upscale by 2 in both direction, but not independently.
 194                 * Compare the input and output rectangles areas (avoiding
 195                 * integer overflows on the output): if the requested output
 196                 * area is larger than 1.5^2 the input area upscale by two,
 197                 * otherwise don't scale.
 198                 */
 199                input_area = format->width * format->height;
 200                output_area = min(fmt->width, SRU_MAX_SIZE)
 201                            * min(fmt->height, SRU_MAX_SIZE);
 202
 203                if (fmt->width <= SRU_MAX_SIZE / 2 &&
 204                    fmt->height <= SRU_MAX_SIZE / 2 &&
 205                    output_area > input_area * 9 / 4) {
 206                        fmt->width = format->width * 2;
 207                        fmt->height = format->height * 2;
 208                } else {
 209                        fmt->width = format->width;
 210                        fmt->height = format->height;
 211                }
 212                break;
 213        }
 214
 215        fmt->field = V4L2_FIELD_NONE;
 216        fmt->colorspace = V4L2_COLORSPACE_SRGB;
 217}
 218
 219static int sru_set_format(struct v4l2_subdev *subdev,
 220                          struct v4l2_subdev_state *sd_state,
 221                          struct v4l2_subdev_format *fmt)
 222{
 223        struct vsp1_sru *sru = to_sru(subdev);
 224        struct v4l2_subdev_state *config;
 225        struct v4l2_mbus_framefmt *format;
 226        int ret = 0;
 227
 228        mutex_lock(&sru->entity.lock);
 229
 230        config = vsp1_entity_get_pad_config(&sru->entity, sd_state,
 231                                            fmt->which);
 232        if (!config) {
 233                ret = -EINVAL;
 234                goto done;
 235        }
 236
 237        sru_try_format(sru, config, fmt->pad, &fmt->format);
 238
 239        format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad);
 240        *format = fmt->format;
 241
 242        if (fmt->pad == SRU_PAD_SINK) {
 243                /* Propagate the format to the source pad. */
 244                format = vsp1_entity_get_pad_format(&sru->entity, config,
 245                                                    SRU_PAD_SOURCE);
 246                *format = fmt->format;
 247
 248                sru_try_format(sru, config, SRU_PAD_SOURCE, format);
 249        }
 250
 251done:
 252        mutex_unlock(&sru->entity.lock);
 253        return ret;
 254}
 255
 256static const struct v4l2_subdev_pad_ops sru_pad_ops = {
 257        .init_cfg = vsp1_entity_init_cfg,
 258        .enum_mbus_code = sru_enum_mbus_code,
 259        .enum_frame_size = sru_enum_frame_size,
 260        .get_fmt = vsp1_subdev_get_pad_format,
 261        .set_fmt = sru_set_format,
 262};
 263
 264static const struct v4l2_subdev_ops sru_ops = {
 265        .pad    = &sru_pad_ops,
 266};
 267
 268/* -----------------------------------------------------------------------------
 269 * VSP1 Entity Operations
 270 */
 271
 272static void sru_configure_stream(struct vsp1_entity *entity,
 273                                 struct vsp1_pipeline *pipe,
 274                                 struct vsp1_dl_list *dl,
 275                                 struct vsp1_dl_body *dlb)
 276{
 277        const struct vsp1_sru_param *param;
 278        struct vsp1_sru *sru = to_sru(&entity->subdev);
 279        struct v4l2_mbus_framefmt *input;
 280        struct v4l2_mbus_framefmt *output;
 281        u32 ctrl0;
 282
 283        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 284                                           SRU_PAD_SINK);
 285        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 286                                            SRU_PAD_SOURCE);
 287
 288        if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
 289                ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
 290                      | VI6_SRU_CTRL0_PARAM4;
 291        else
 292                ctrl0 = VI6_SRU_CTRL0_PARAM3;
 293
 294        if (input->width != output->width)
 295                ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
 296
 297        param = &vsp1_sru_params[sru->intensity - 1];
 298
 299        ctrl0 |= param->ctrl0;
 300
 301        vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0);
 302        vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
 303        vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2);
 304}
 305
 306static unsigned int sru_max_width(struct vsp1_entity *entity,
 307                                  struct vsp1_pipeline *pipe)
 308{
 309        struct vsp1_sru *sru = to_sru(&entity->subdev);
 310        struct v4l2_mbus_framefmt *input;
 311        struct v4l2_mbus_framefmt *output;
 312
 313        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 314                                           SRU_PAD_SINK);
 315        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 316                                            SRU_PAD_SOURCE);
 317
 318        /*
 319         * The maximum input width of the SRU is 288 input pixels, but 32
 320         * pixels are reserved to support overlapping partition windows when
 321         * scaling.
 322         */
 323        if (input->width != output->width)
 324                return 512;
 325        else
 326                return 256;
 327}
 328
 329static void sru_partition(struct vsp1_entity *entity,
 330                          struct vsp1_pipeline *pipe,
 331                          struct vsp1_partition *partition,
 332                          unsigned int partition_idx,
 333                          struct vsp1_partition_window *window)
 334{
 335        struct vsp1_sru *sru = to_sru(&entity->subdev);
 336        struct v4l2_mbus_framefmt *input;
 337        struct v4l2_mbus_framefmt *output;
 338
 339        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 340                                           SRU_PAD_SINK);
 341        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 342                                            SRU_PAD_SOURCE);
 343
 344        /* Adapt if SRUx2 is enabled. */
 345        if (input->width != output->width) {
 346                window->width /= 2;
 347                window->left /= 2;
 348        }
 349
 350        partition->sru = *window;
 351}
 352
 353static const struct vsp1_entity_operations sru_entity_ops = {
 354        .configure_stream = sru_configure_stream,
 355        .max_width = sru_max_width,
 356        .partition = sru_partition,
 357};
 358
 359/* -----------------------------------------------------------------------------
 360 * Initialization and Cleanup
 361 */
 362
 363struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
 364{
 365        struct vsp1_sru *sru;
 366        int ret;
 367
 368        sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
 369        if (sru == NULL)
 370                return ERR_PTR(-ENOMEM);
 371
 372        sru->entity.ops = &sru_entity_ops;
 373        sru->entity.type = VSP1_ENTITY_SRU;
 374
 375        ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
 376                               MEDIA_ENT_F_PROC_VIDEO_SCALER);
 377        if (ret < 0)
 378                return ERR_PTR(ret);
 379
 380        /* Initialize the control handler. */
 381        v4l2_ctrl_handler_init(&sru->ctrls, 1);
 382        v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
 383
 384        sru->intensity = 1;
 385
 386        sru->entity.subdev.ctrl_handler = &sru->ctrls;
 387
 388        if (sru->ctrls.error) {
 389                dev_err(vsp1->dev, "sru: failed to initialize controls\n");
 390                ret = sru->ctrls.error;
 391                vsp1_entity_destroy(&sru->entity);
 392                return ERR_PTR(ret);
 393        }
 394
 395        return sru;
 396}
 397