root/drivers/media/platform/vsp1/vsp1_sru.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vsp1_sru_write
  2. sru_s_ctrl
  3. sru_enum_mbus_code
  4. sru_enum_frame_size
  5. sru_try_format
  6. sru_set_format
  7. sru_configure_stream
  8. sru_max_width
  9. sru_partition
  10. vsp1_sru_create

   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 
  27 static 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 
  39 struct 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 
  53 static 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 
  75 static 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 
  89 static const struct v4l2_ctrl_ops sru_ctrl_ops = {
  90         .s_ctrl = sru_s_ctrl,
  91 };
  92 
  93 static 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 
 108 static 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 
 121 static 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 
 161 done:
 162         mutex_unlock(&sru->entity.lock);
 163         return ret;
 164 }
 165 
 166 static 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 
 218 static 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 
 249 done:
 250         mutex_unlock(&sru->entity.lock);
 251         return ret;
 252 }
 253 
 254 static 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 
 262 static const struct v4l2_subdev_ops sru_ops = {
 263         .pad    = &sru_pad_ops,
 264 };
 265 
 266 /* -----------------------------------------------------------------------------
 267  * VSP1 Entity Operations
 268  */
 269 
 270 static void sru_configure_stream(struct vsp1_entity *entity,
 271                                  struct vsp1_pipeline *pipe,
 272                                  struct vsp1_dl_list *dl,
 273                                  struct vsp1_dl_body *dlb)
 274 {
 275         const struct vsp1_sru_param *param;
 276         struct vsp1_sru *sru = to_sru(&entity->subdev);
 277         struct v4l2_mbus_framefmt *input;
 278         struct v4l2_mbus_framefmt *output;
 279         u32 ctrl0;
 280 
 281         input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 282                                            SRU_PAD_SINK);
 283         output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 284                                             SRU_PAD_SOURCE);
 285 
 286         if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
 287                 ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
 288                       | VI6_SRU_CTRL0_PARAM4;
 289         else
 290                 ctrl0 = VI6_SRU_CTRL0_PARAM3;
 291 
 292         if (input->width != output->width)
 293                 ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
 294 
 295         param = &vsp1_sru_params[sru->intensity - 1];
 296 
 297         ctrl0 |= param->ctrl0;
 298 
 299         vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0);
 300         vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
 301         vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2);
 302 }
 303 
 304 static unsigned int sru_max_width(struct vsp1_entity *entity,
 305                                   struct vsp1_pipeline *pipe)
 306 {
 307         struct vsp1_sru *sru = to_sru(&entity->subdev);
 308         struct v4l2_mbus_framefmt *input;
 309         struct v4l2_mbus_framefmt *output;
 310 
 311         input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 312                                            SRU_PAD_SINK);
 313         output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 314                                             SRU_PAD_SOURCE);
 315 
 316         /*
 317          * The maximum input width of the SRU is 288 input pixels, but 32
 318          * pixels are reserved to support overlapping partition windows when
 319          * scaling.
 320          */
 321         if (input->width != output->width)
 322                 return 512;
 323         else
 324                 return 256;
 325 }
 326 
 327 static void sru_partition(struct vsp1_entity *entity,
 328                           struct vsp1_pipeline *pipe,
 329                           struct vsp1_partition *partition,
 330                           unsigned int partition_idx,
 331                           struct vsp1_partition_window *window)
 332 {
 333         struct vsp1_sru *sru = to_sru(&entity->subdev);
 334         struct v4l2_mbus_framefmt *input;
 335         struct v4l2_mbus_framefmt *output;
 336 
 337         input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 338                                            SRU_PAD_SINK);
 339         output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
 340                                             SRU_PAD_SOURCE);
 341 
 342         /* Adapt if SRUx2 is enabled. */
 343         if (input->width != output->width) {
 344                 window->width /= 2;
 345                 window->left /= 2;
 346         }
 347 
 348         partition->sru = *window;
 349 }
 350 
 351 static const struct vsp1_entity_operations sru_entity_ops = {
 352         .configure_stream = sru_configure_stream,
 353         .max_width = sru_max_width,
 354         .partition = sru_partition,
 355 };
 356 
 357 /* -----------------------------------------------------------------------------
 358  * Initialization and Cleanup
 359  */
 360 
 361 struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
 362 {
 363         struct vsp1_sru *sru;
 364         int ret;
 365 
 366         sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
 367         if (sru == NULL)
 368                 return ERR_PTR(-ENOMEM);
 369 
 370         sru->entity.ops = &sru_entity_ops;
 371         sru->entity.type = VSP1_ENTITY_SRU;
 372 
 373         ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
 374                                MEDIA_ENT_F_PROC_VIDEO_SCALER);
 375         if (ret < 0)
 376                 return ERR_PTR(ret);
 377 
 378         /* Initialize the control handler. */
 379         v4l2_ctrl_handler_init(&sru->ctrls, 1);
 380         v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
 381 
 382         sru->intensity = 1;
 383 
 384         sru->entity.subdev.ctrl_handler = &sru->ctrls;
 385 
 386         if (sru->ctrls.error) {
 387                 dev_err(vsp1->dev, "sru: failed to initialize controls\n");
 388                 ret = sru->ctrls.error;
 389                 vsp1_entity_destroy(&sru->entity);
 390                 return ERR_PTR(ret);
 391         }
 392 
 393         return sru;
 394 }

/* [<][>][^][v][top][bottom][index][help] */