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_pad_config *config)
  21{
  22        return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config,
  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_pad_config *cfg,
  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_pad_config *cfg,
  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, cfg, fse, RWPF_MIN_WIDTH,
  55                                           RWPF_MIN_HEIGHT, rwpf->max_width,
  56                                           rwpf->max_height);
  57}
  58
  59static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
  60                                struct v4l2_subdev_pad_config *cfg,
  61                                struct v4l2_subdev_format *fmt)
  62{
  63        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
  64        struct v4l2_subdev_pad_config *config;
  65        struct v4l2_mbus_framefmt *format;
  66        int ret = 0;
  67
  68        mutex_lock(&rwpf->entity.lock);
  69
  70        config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which);
  71        if (!config) {
  72                ret = -EINVAL;
  73                goto done;
  74        }
  75
  76        /* Default to YUV if the requested format is not supported. */
  77        if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
  78            fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
  79            fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
  80                fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
  81
  82        format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad);
  83
  84        if (fmt->pad == RWPF_PAD_SOURCE) {
  85                /*
  86                 * The RWPF performs format conversion but can't scale, only the
  87                 * format code can be changed on the source pad.
  88                 */
  89                format->code = fmt->format.code;
  90                fmt->format = *format;
  91                goto done;
  92        }
  93
  94        format->code = fmt->format.code;
  95        format->width = clamp_t(unsigned int, fmt->format.width,
  96                                RWPF_MIN_WIDTH, rwpf->max_width);
  97        format->height = clamp_t(unsigned int, fmt->format.height,
  98                                 RWPF_MIN_HEIGHT, rwpf->max_height);
  99        format->field = V4L2_FIELD_NONE;
 100        format->colorspace = V4L2_COLORSPACE_SRGB;
 101
 102        fmt->format = *format;
 103
 104        if (rwpf->entity.type == VSP1_ENTITY_RPF) {
 105                struct v4l2_rect *crop;
 106
 107                /* Update the sink crop rectangle. */
 108                crop = vsp1_rwpf_get_crop(rwpf, config);
 109                crop->left = 0;
 110                crop->top = 0;
 111                crop->width = fmt->format.width;
 112                crop->height = fmt->format.height;
 113        }
 114
 115        /* Propagate the format to the source pad. */
 116        format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 117                                            RWPF_PAD_SOURCE);
 118        *format = fmt->format;
 119
 120        if (rwpf->flip.rotate) {
 121                format->width = fmt->format.height;
 122                format->height = fmt->format.width;
 123        }
 124
 125done:
 126        mutex_unlock(&rwpf->entity.lock);
 127        return ret;
 128}
 129
 130static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
 131                                   struct v4l2_subdev_pad_config *cfg,
 132                                   struct v4l2_subdev_selection *sel)
 133{
 134        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
 135        struct v4l2_subdev_pad_config *config;
 136        struct v4l2_mbus_framefmt *format;
 137        int ret = 0;
 138
 139        /*
 140         * Cropping is only supported on the RPF and is implemented on the sink
 141         * pad.
 142         */
 143        if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
 144                return -EINVAL;
 145
 146        mutex_lock(&rwpf->entity.lock);
 147
 148        config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
 149        if (!config) {
 150                ret = -EINVAL;
 151                goto done;
 152        }
 153
 154        switch (sel->target) {
 155        case V4L2_SEL_TGT_CROP:
 156                sel->r = *vsp1_rwpf_get_crop(rwpf, config);
 157                break;
 158
 159        case V4L2_SEL_TGT_CROP_BOUNDS:
 160                format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 161                                                    RWPF_PAD_SINK);
 162                sel->r.left = 0;
 163                sel->r.top = 0;
 164                sel->r.width = format->width;
 165                sel->r.height = format->height;
 166                break;
 167
 168        default:
 169                ret = -EINVAL;
 170                break;
 171        }
 172
 173done:
 174        mutex_unlock(&rwpf->entity.lock);
 175        return ret;
 176}
 177
 178static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
 179                                   struct v4l2_subdev_pad_config *cfg,
 180                                   struct v4l2_subdev_selection *sel)
 181{
 182        struct vsp1_rwpf *rwpf = to_rwpf(subdev);
 183        struct v4l2_subdev_pad_config *config;
 184        struct v4l2_mbus_framefmt *format;
 185        struct v4l2_rect *crop;
 186        int ret = 0;
 187
 188        /*
 189         * Cropping is only supported on the RPF and is implemented on the sink
 190         * pad.
 191         */
 192        if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
 193                return -EINVAL;
 194
 195        if (sel->target != V4L2_SEL_TGT_CROP)
 196                return -EINVAL;
 197
 198        mutex_lock(&rwpf->entity.lock);
 199
 200        config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
 201        if (!config) {
 202                ret = -EINVAL;
 203                goto done;
 204        }
 205
 206        /* Make sure the crop rectangle is entirely contained in the image. */
 207        format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 208                                            RWPF_PAD_SINK);
 209
 210        /*
 211         * Restrict the crop rectangle coordinates to multiples of 2 to avoid
 212         * shifting the color plane.
 213         */
 214        if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
 215                sel->r.left = ALIGN(sel->r.left, 2);
 216                sel->r.top = ALIGN(sel->r.top, 2);
 217                sel->r.width = round_down(sel->r.width, 2);
 218                sel->r.height = round_down(sel->r.height, 2);
 219        }
 220
 221        sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
 222        sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
 223        sel->r.width = min_t(unsigned int, sel->r.width,
 224                             format->width - sel->r.left);
 225        sel->r.height = min_t(unsigned int, sel->r.height,
 226                              format->height - sel->r.top);
 227
 228        crop = vsp1_rwpf_get_crop(rwpf, config);
 229        *crop = sel->r;
 230
 231        /* Propagate the format to the source pad. */
 232        format = vsp1_entity_get_pad_format(&rwpf->entity, config,
 233                                            RWPF_PAD_SOURCE);
 234        format->width = crop->width;
 235        format->height = crop->height;
 236
 237done:
 238        mutex_unlock(&rwpf->entity.lock);
 239        return ret;
 240}
 241
 242const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
 243        .init_cfg = vsp1_entity_init_cfg,
 244        .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
 245        .enum_frame_size = vsp1_rwpf_enum_frame_size,
 246        .get_fmt = vsp1_subdev_get_pad_format,
 247        .set_fmt = vsp1_rwpf_set_format,
 248        .get_selection = vsp1_rwpf_get_selection,
 249        .set_selection = vsp1_rwpf_set_selection,
 250};
 251
 252/* -----------------------------------------------------------------------------
 253 * Controls
 254 */
 255
 256static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
 257{
 258        struct vsp1_rwpf *rwpf =
 259                container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
 260
 261        switch (ctrl->id) {
 262        case V4L2_CID_ALPHA_COMPONENT:
 263                rwpf->alpha = ctrl->val;
 264                break;
 265        }
 266
 267        return 0;
 268}
 269
 270static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
 271        .s_ctrl = vsp1_rwpf_s_ctrl,
 272};
 273
 274int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
 275{
 276        v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
 277        v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
 278                          V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
 279
 280        rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
 281
 282        return rwpf->ctrls.error;
 283}
 284