linux/drivers/media/platform/vsp1/vsp1_lut.c
<<
>>
Prefs
   1/*
   2 * vsp1_lut.c  --  R-Car VSP1 Look-Up Table
   3 *
   4 * Copyright (C) 2013 Renesas Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/device.h>
  15#include <linux/gfp.h>
  16#include <linux/vsp1.h>
  17
  18#include <media/v4l2-subdev.h>
  19
  20#include "vsp1.h"
  21#include "vsp1_lut.h"
  22
  23#define LUT_MIN_SIZE                            4U
  24#define LUT_MAX_SIZE                            8190U
  25
  26/* -----------------------------------------------------------------------------
  27 * Device Access
  28 */
  29
  30static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg)
  31{
  32        return vsp1_read(lut->entity.vsp1, reg);
  33}
  34
  35static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data)
  36{
  37        vsp1_write(lut->entity.vsp1, reg, data);
  38}
  39
  40/* -----------------------------------------------------------------------------
  41 * V4L2 Subdevice Core Operations
  42 */
  43
  44static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config)
  45{
  46        memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut,
  47                    sizeof(config->lut));
  48}
  49
  50static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
  51{
  52        struct vsp1_lut *lut = to_lut(subdev);
  53
  54        switch (cmd) {
  55        case VIDIOC_VSP1_LUT_CONFIG:
  56                lut_configure(lut, arg);
  57                return 0;
  58
  59        default:
  60                return -ENOIOCTLCMD;
  61        }
  62}
  63
  64/* -----------------------------------------------------------------------------
  65 * V4L2 Subdevice Video Operations
  66 */
  67
  68static int lut_s_stream(struct v4l2_subdev *subdev, int enable)
  69{
  70        struct vsp1_lut *lut = to_lut(subdev);
  71
  72        if (!enable)
  73                return 0;
  74
  75        vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
  76
  77        return 0;
  78}
  79
  80/* -----------------------------------------------------------------------------
  81 * V4L2 Subdevice Pad Operations
  82 */
  83
  84static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
  85                              struct v4l2_subdev_pad_config *cfg,
  86                              struct v4l2_subdev_mbus_code_enum *code)
  87{
  88        static const unsigned int codes[] = {
  89                MEDIA_BUS_FMT_ARGB8888_1X32,
  90                MEDIA_BUS_FMT_AHSV8888_1X32,
  91                MEDIA_BUS_FMT_AYUV8_1X32,
  92        };
  93        struct vsp1_lut *lut = to_lut(subdev);
  94        struct v4l2_mbus_framefmt *format;
  95
  96        if (code->pad == LUT_PAD_SINK) {
  97                if (code->index >= ARRAY_SIZE(codes))
  98                        return -EINVAL;
  99
 100                code->code = codes[code->index];
 101        } else {
 102                /* The LUT can't perform format conversion, the sink format is
 103                 * always identical to the source format.
 104                 */
 105                if (code->index)
 106                        return -EINVAL;
 107
 108                format = vsp1_entity_get_pad_format(&lut->entity, cfg,
 109                                                    LUT_PAD_SINK, code->which);
 110                code->code = format->code;
 111        }
 112
 113        return 0;
 114}
 115
 116static int lut_enum_frame_size(struct v4l2_subdev *subdev,
 117                               struct v4l2_subdev_pad_config *cfg,
 118                               struct v4l2_subdev_frame_size_enum *fse)
 119{
 120        struct vsp1_lut *lut = to_lut(subdev);
 121        struct v4l2_mbus_framefmt *format;
 122
 123        format = vsp1_entity_get_pad_format(&lut->entity, cfg,
 124                                            fse->pad, fse->which);
 125
 126        if (fse->index || fse->code != format->code)
 127                return -EINVAL;
 128
 129        if (fse->pad == LUT_PAD_SINK) {
 130                fse->min_width = LUT_MIN_SIZE;
 131                fse->max_width = LUT_MAX_SIZE;
 132                fse->min_height = LUT_MIN_SIZE;
 133                fse->max_height = LUT_MAX_SIZE;
 134        } else {
 135                /* The size on the source pad are fixed and always identical to
 136                 * the size on the sink pad.
 137                 */
 138                fse->min_width = format->width;
 139                fse->max_width = format->width;
 140                fse->min_height = format->height;
 141                fse->max_height = format->height;
 142        }
 143
 144        return 0;
 145}
 146
 147static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 148                          struct v4l2_subdev_format *fmt)
 149{
 150        struct vsp1_lut *lut = to_lut(subdev);
 151
 152        fmt->format = *vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad,
 153                                                  fmt->which);
 154
 155        return 0;
 156}
 157
 158static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
 159                          struct v4l2_subdev_format *fmt)
 160{
 161        struct vsp1_lut *lut = to_lut(subdev);
 162        struct v4l2_mbus_framefmt *format;
 163
 164        /* Default to YUV if the requested format is not supported. */
 165        if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
 166            fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
 167            fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
 168                fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
 169
 170        format = vsp1_entity_get_pad_format(&lut->entity, cfg, fmt->pad,
 171                                            fmt->which);
 172
 173        if (fmt->pad == LUT_PAD_SOURCE) {
 174                /* The LUT output format can't be modified. */
 175                fmt->format = *format;
 176                return 0;
 177        }
 178
 179        format->width = clamp_t(unsigned int, fmt->format.width,
 180                                LUT_MIN_SIZE, LUT_MAX_SIZE);
 181        format->height = clamp_t(unsigned int, fmt->format.height,
 182                                 LUT_MIN_SIZE, LUT_MAX_SIZE);
 183        format->field = V4L2_FIELD_NONE;
 184        format->colorspace = V4L2_COLORSPACE_SRGB;
 185
 186        fmt->format = *format;
 187
 188        /* Propagate the format to the source pad. */
 189        format = vsp1_entity_get_pad_format(&lut->entity, cfg, LUT_PAD_SOURCE,
 190                                            fmt->which);
 191        *format = fmt->format;
 192
 193        return 0;
 194}
 195
 196/* -----------------------------------------------------------------------------
 197 * V4L2 Subdevice Operations
 198 */
 199
 200static struct v4l2_subdev_core_ops lut_core_ops = {
 201        .ioctl = lut_ioctl,
 202};
 203
 204static struct v4l2_subdev_video_ops lut_video_ops = {
 205        .s_stream = lut_s_stream,
 206};
 207
 208static struct v4l2_subdev_pad_ops lut_pad_ops = {
 209        .enum_mbus_code = lut_enum_mbus_code,
 210        .enum_frame_size = lut_enum_frame_size,
 211        .get_fmt = lut_get_format,
 212        .set_fmt = lut_set_format,
 213};
 214
 215static struct v4l2_subdev_ops lut_ops = {
 216        .core   = &lut_core_ops,
 217        .video  = &lut_video_ops,
 218        .pad    = &lut_pad_ops,
 219};
 220
 221/* -----------------------------------------------------------------------------
 222 * Initialization and Cleanup
 223 */
 224
 225struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
 226{
 227        struct v4l2_subdev *subdev;
 228        struct vsp1_lut *lut;
 229        int ret;
 230
 231        lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL);
 232        if (lut == NULL)
 233                return ERR_PTR(-ENOMEM);
 234
 235        lut->entity.type = VSP1_ENTITY_LUT;
 236
 237        ret = vsp1_entity_init(vsp1, &lut->entity, 2);
 238        if (ret < 0)
 239                return ERR_PTR(ret);
 240
 241        /* Initialize the V4L2 subdev. */
 242        subdev = &lut->entity.subdev;
 243        v4l2_subdev_init(subdev, &lut_ops);
 244
 245        subdev->entity.ops = &vsp1_media_ops;
 246        subdev->internal_ops = &vsp1_subdev_internal_ops;
 247        snprintf(subdev->name, sizeof(subdev->name), "%s lut",
 248                 dev_name(vsp1->dev));
 249        v4l2_set_subdevdata(subdev, lut);
 250        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 251
 252        vsp1_entity_init_formats(subdev, NULL);
 253
 254        return lut;
 255}
 256