linux/drivers/media/platform/vsp1/vsp1_rwpf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * vsp1_rwpf.c  --  R-Car VSP1 Read and Write Pixel Formatters
   4 *
   5 * Copyright (C) 2013-2014 Renesas Electronics Corporation
   6 *
   7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   8 */
   9
  10#include <media/v4l2-subdev.h>
  11
  12#include "vsp1.h"
  13#include "vsp1_rwpf.h"
  14#include "vsp1_video.h"
  15
  16#define RWPF_MIN_WIDTH                          1
  17#define RWPF_MIN_HEIGHT                         1
  18
  19struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
  20                                     struct v4l2_subdev_state *sd_state)
  21{
  22        return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state,
  23                                        RWPF_PAD_SINK);
  24}
  25
  26/* -----------------------------------------------------------------------------
  27 * V4L2 Subdevice Pad Operations
  28 */
  29
  30static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
  31                                    struct v4l2_subdev_state *sd_state,
  32                                    struct v4l2_subdev_mbus_code_enum *code)
  33{
  34        static const unsigned int codes[] = {
  35                MEDIA_BUS_FMT_ARGB8888_1X32,
  36                MEDIA_BUS_FMT_AHSV8888_1X32,
  37                MEDIA_BUS_FMT_AYUV8_1X32,
  38        };
  39
  40        if (code->index >= ARRAY_SIZE(codes))
  41                return -EINVAL;
  42
  43        code->code = codes[code->index];
  44
  45        return 0;
  46}
  47
  48static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
  49                                     struct v4l2_subdev_state *sd_state,
  50                                     struct v4l2_subdev_frame_size_enum *fse)
  51{
  52        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
  53
  54        return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
  55                                           RWPF_MIN_WIDTH,
  56                                           RWPF_MIN_HEIGHT, rwpf->max_width,
  57                                           rwpf->max_height);
  58}
  59
  60static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
  61                                struct v4l2_subdev_state *sd_state,
  62                                struct v4l2_subdev_format *fmt)
  63{
  64        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
  65        struct v4l2_subdev_state *config;
  66        struct v4l2_mbus_framefmt *format;
  67        int ret = 0;
  68
  69        mutex_lock(&rwpf->entity.lock);
  70
  71        config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state,
  72                                            fmt->which);
  73        if (!config) {
  74                ret = -EINVAL;
  75                goto done;
  76        }
  77
  78        /* Default to YUV if the requested format is not supported. */
  79        if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
  80            fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
  81            fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
  82                fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
  83
  84        format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad);
  85
  86        if (fmt->pad == RWPF_PAD_SOURCE) {
  87                /*
  88                 * The RWPF performs format conversion but can't scale, only the
  89                 * format code can be changed on the source pad.
  90                 */
  91                format->code = fmt->format.code;
  92                fmt->format = *format;
  93                goto done;
  94        }
  95
  96        format->code = fmt->format.code;
  97        format->width = clamp_t(unsigned int, fmt->format.width,
  98                                RWPF_MIN_WIDTH, rwpf->max_width);
  99        format->height = clamp_t(unsigned int, fmt->format.height,
 100                                 RWPF_MIN_HEIGHT, rwpf->max_height);
 101        format->field = V4L2_FIELD_NONE;
 102        format->colorspace = V4L2_COLORSPACE_SRGB;
 103
 104        fmt->format = *format;
 105
 106        if (rwpf->entity.type == VSP1_ENTITY_RPF) {
 107                struct v4l2_rect *crop;
 108
 109                /* Update the sink crop rectangle. */
 110                crop = vsp1_rwpf_get_crop(rwpf, config);
 111                crop->left = 0;
 112                crop->top = 0;
 113                crop->width = fmt->format.width;
 114                crop->height = fmt->format.height;
 115        }
 116
 117        /* Propagate the format to the source pad. */
 118        format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 119                                            RWPF_PAD_SOURCE);
 120        *format = fmt->format;
 121
 122        if (rwpf->flip.rotate) {
 123                format->width = fmt->format.height;
 124                format->height = fmt->format.width;
 125        }
 126
 127done:
 128        mutex_unlock(&rwpf->entity.lock);
 129        return ret;
 130}
 131
 132static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
 133                                   struct v4l2_subdev_state *sd_state,
 134                                   struct v4l2_subdev_selection *sel)
 135{
 136        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
 137        struct v4l2_subdev_state *config;
 138        struct v4l2_mbus_framefmt *format;
 139        int ret = 0;
 140
 141        /*
 142         * Cropping is only supported on the RPF and is implemented on the sink
 143         * pad.
 144         */
 145        if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
 146                return -EINVAL;
 147
 148        mutex_lock(&rwpf->entity.lock);
 149
 150        config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state,
 151                                            sel->which);
 152        if (!config) {
 153                ret = -EINVAL;
 154                goto done;
 155        }
 156
 157        switch (sel->target) {
 158        case V4L2_SEL_TGT_CROP:
 159                sel->r = *vsp1_rwpf_get_crop(rwpf, config);
 160                break;
 161
 162        case V4L2_SEL_TGT_CROP_BOUNDS:
 163                format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 164                                                    RWPF_PAD_SINK);
 165                sel->r.left = 0;
 166                sel->r.top = 0;
 167                sel->r.width = format->width;
 168                sel->r.height = format->height;
 169                break;
 170
 171        default:
 172                ret = -EINVAL;
 173                break;
 174        }
 175
 176done:
 177        mutex_unlock(&rwpf->entity.lock);
 178        return ret;
 179}
 180
 181static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
 182                                   struct v4l2_subdev_state *sd_state,
 183                                   struct v4l2_subdev_selection *sel)
 184{
 185        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
 186        struct v4l2_subdev_state *config;
 187        struct v4l2_mbus_framefmt *format;
 188        struct v4l2_rect *crop;
 189        int ret = 0;
 190
 191        /*
 192         * Cropping is only supported on the RPF and is implemented on the sink
 193         * pad.
 194         */
 195        if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
 196                return -EINVAL;
 197
 198        if (sel->target != V4L2_SEL_TGT_CROP)
 199                return -EINVAL;
 200
 201        mutex_lock(&rwpf->entity.lock);
 202
 203        config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state,
 204                                            sel->which);
 205        if (!config) {
 206                ret = -EINVAL;
 207                goto done;
 208        }
 209
 210        /* Make sure the crop rectangle is entirely contained in the image. */
 211        format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 212                                            RWPF_PAD_SINK);
 213
 214        /*
 215         * Restrict the crop rectangle coordinates to multiples of 2 to avoid
 216         * shifting the color plane.
 217         */
 218        if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
 219                sel->r.left = ALIGN(sel->r.left, 2);
 220                sel->r.top = ALIGN(sel->r.top, 2);
 221                sel->r.width = round_down(sel->r.width, 2);
 222                sel->r.height = round_down(sel->r.height, 2);
 223        }
 224
 225        sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
 226        sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
 227        sel->r.width = min_t(unsigned int, sel->r.width,
 228                             format->width - sel->r.left);
 229        sel->r.height = min_t(unsigned int, sel->r.height,
 230                              format->height - sel->r.top);
 231
 232        crop = vsp1_rwpf_get_crop(rwpf, config);
 233        *crop = sel->r;
 234
 235        /* Propagate the format to the source pad. */
 236        format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 237                                            RWPF_PAD_SOURCE);
 238        format->width = crop->width;
 239        format->height = crop->height;
 240
 241done:
 242        mutex_unlock(&rwpf->entity.lock);
 243        return ret;
 244}
 245
 246const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
 247        .init_cfg = vsp1_entity_init_cfg,
 248        .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
 249        .enum_frame_size = vsp1_rwpf_enum_frame_size,
 250        .get_fmt = vsp1_subdev_get_pad_format,
 251        .set_fmt = vsp1_rwpf_set_format,
 252        .get_selection = vsp1_rwpf_get_selection,
 253        .set_selection = vsp1_rwpf_set_selection,
 254};
 255
 256/* -----------------------------------------------------------------------------
 257 * Controls
 258 */
 259
 260static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
 261{
 262        struct vsp1_rwpf *rwpf =
 263                container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
 264
 265        switch (ctrl->id) {
 266        case V4L2_CID_ALPHA_COMPONENT:
 267                rwpf->alpha = ctrl->val;
 268                break;
 269        }
 270
 271        return 0;
 272}
 273
 274static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
 275        .s_ctrl = vsp1_rwpf_s_ctrl,
 276};
 277
 278int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
 279{
 280        v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
 281        v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
 282                          V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
 283
 284        rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
 285
 286        return rwpf->ctrls.error;
 287}
 288