linux/drivers/media/platform/vsp1/vsp1_lut.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * vsp1_lut.c  --  R-Car VSP1 Look-Up Table
   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_lut.h"
  18
  19#define LUT_MIN_SIZE                            4U
  20#define LUT_MAX_SIZE                            8190U
  21
  22#define LUT_SIZE                                256
  23
  24/* -----------------------------------------------------------------------------
  25 * Device Access
  26 */
  27
  28static inline void vsp1_lut_write(struct vsp1_lut *lut,
  29                                  struct vsp1_dl_body *dlb, u32 reg, u32 data)
  30{
  31        vsp1_dl_body_write(dlb, reg, data);
  32}
  33
  34/* -----------------------------------------------------------------------------
  35 * Controls
  36 */
  37
  38#define V4L2_CID_VSP1_LUT_TABLE                 (V4L2_CID_USER_BASE | 0x1001)
  39
  40static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl)
  41{
  42        struct vsp1_dl_body *dlb;
  43        unsigned int i;
  44
  45        dlb = vsp1_dl_body_get(lut->pool);
  46        if (!dlb)
  47                return -ENOMEM;
  48
  49        for (i = 0; i < LUT_SIZE; ++i)
  50                vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i,
  51                                       ctrl->p_new.p_u32[i]);
  52
  53        spin_lock_irq(&lut->lock);
  54        swap(lut->lut, dlb);
  55        spin_unlock_irq(&lut->lock);
  56
  57        vsp1_dl_body_put(dlb);
  58        return 0;
  59}
  60
  61static int lut_s_ctrl(struct v4l2_ctrl *ctrl)
  62{
  63        struct vsp1_lut *lut =
  64                container_of(ctrl->handler, struct vsp1_lut, ctrls);
  65
  66        switch (ctrl->id) {
  67        case V4L2_CID_VSP1_LUT_TABLE:
  68                lut_set_table(lut, ctrl);
  69                break;
  70        }
  71
  72        return 0;
  73}
  74
  75static const struct v4l2_ctrl_ops lut_ctrl_ops = {
  76        .s_ctrl = lut_s_ctrl,
  77};
  78
  79static const struct v4l2_ctrl_config lut_table_control = {
  80        .ops = &lut_ctrl_ops,
  81        .id = V4L2_CID_VSP1_LUT_TABLE,
  82        .name = "Look-Up Table",
  83        .type = V4L2_CTRL_TYPE_U32,
  84        .min = 0x00000000,
  85        .max = 0x00ffffff,
  86        .step = 1,
  87        .def = 0,
  88        .dims = { LUT_SIZE },
  89};
  90
  91/* -----------------------------------------------------------------------------
  92 * V4L2 Subdevice Pad Operations
  93 */
  94
  95static const unsigned int lut_codes[] = {
  96        MEDIA_BUS_FMT_ARGB8888_1X32,
  97        MEDIA_BUS_FMT_AHSV8888_1X32,
  98        MEDIA_BUS_FMT_AYUV8_1X32,
  99};
 100
 101static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
 102                              struct v4l2_subdev_state *sd_state,
 103                              struct v4l2_subdev_mbus_code_enum *code)
 104{
 105        return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lut_codes,
 106                                          ARRAY_SIZE(lut_codes));
 107}
 108
 109static int lut_enum_frame_size(struct v4l2_subdev *subdev,
 110                               struct v4l2_subdev_state *sd_state,
 111                               struct v4l2_subdev_frame_size_enum *fse)
 112{
 113        return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
 114                                           LUT_MIN_SIZE,
 115                                           LUT_MIN_SIZE, LUT_MAX_SIZE,
 116                                           LUT_MAX_SIZE);
 117}
 118
 119static int lut_set_format(struct v4l2_subdev *subdev,
 120                          struct v4l2_subdev_state *sd_state,
 121                          struct v4l2_subdev_format *fmt)
 122{
 123        return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lut_codes,
 124                                          ARRAY_SIZE(lut_codes),
 125                                          LUT_MIN_SIZE, LUT_MIN_SIZE,
 126                                          LUT_MAX_SIZE, LUT_MAX_SIZE);
 127}
 128
 129/* -----------------------------------------------------------------------------
 130 * V4L2 Subdevice Operations
 131 */
 132
 133static const struct v4l2_subdev_pad_ops lut_pad_ops = {
 134        .init_cfg = vsp1_entity_init_cfg,
 135        .enum_mbus_code = lut_enum_mbus_code,
 136        .enum_frame_size = lut_enum_frame_size,
 137        .get_fmt = vsp1_subdev_get_pad_format,
 138        .set_fmt = lut_set_format,
 139};
 140
 141static const struct v4l2_subdev_ops lut_ops = {
 142        .pad    = &lut_pad_ops,
 143};
 144
 145/* -----------------------------------------------------------------------------
 146 * VSP1 Entity Operations
 147 */
 148
 149static void lut_configure_stream(struct vsp1_entity *entity,
 150                                 struct vsp1_pipeline *pipe,
 151                                 struct vsp1_dl_list *dl,
 152                                 struct vsp1_dl_body *dlb)
 153{
 154        struct vsp1_lut *lut = to_lut(&entity->subdev);
 155
 156        vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
 157}
 158
 159static void lut_configure_frame(struct vsp1_entity *entity,
 160                                struct vsp1_pipeline *pipe,
 161                                struct vsp1_dl_list *dl,
 162                                struct vsp1_dl_body *dlb)
 163{
 164        struct vsp1_lut *lut = to_lut(&entity->subdev);
 165        struct vsp1_dl_body *lut_dlb;
 166        unsigned long flags;
 167
 168        spin_lock_irqsave(&lut->lock, flags);
 169        lut_dlb = lut->lut;
 170        lut->lut = NULL;
 171        spin_unlock_irqrestore(&lut->lock, flags);
 172
 173        if (lut_dlb) {
 174                vsp1_dl_list_add_body(dl, lut_dlb);
 175
 176                /* Release our local reference. */
 177                vsp1_dl_body_put(lut_dlb);
 178        }
 179}
 180
 181static void lut_destroy(struct vsp1_entity *entity)
 182{
 183        struct vsp1_lut *lut = to_lut(&entity->subdev);
 184
 185        vsp1_dl_body_pool_destroy(lut->pool);
 186}
 187
 188static const struct vsp1_entity_operations lut_entity_ops = {
 189        .configure_stream = lut_configure_stream,
 190        .configure_frame = lut_configure_frame,
 191        .destroy = lut_destroy,
 192};
 193
 194/* -----------------------------------------------------------------------------
 195 * Initialization and Cleanup
 196 */
 197
 198struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
 199{
 200        struct vsp1_lut *lut;
 201        int ret;
 202
 203        lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL);
 204        if (lut == NULL)
 205                return ERR_PTR(-ENOMEM);
 206
 207        spin_lock_init(&lut->lock);
 208
 209        lut->entity.ops = &lut_entity_ops;
 210        lut->entity.type = VSP1_ENTITY_LUT;
 211
 212        ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops,
 213                               MEDIA_ENT_F_PROC_VIDEO_LUT);
 214        if (ret < 0)
 215                return ERR_PTR(ret);
 216
 217        /*
 218         * Pre-allocate a body pool, with 3 bodies allowing a userspace update
 219         * before the hardware has committed a previous set of tables, handling
 220         * both the queued and pending dl entries.
 221         */
 222        lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0);
 223        if (!lut->pool)
 224                return ERR_PTR(-ENOMEM);
 225
 226        /* Initialize the control handler. */
 227        v4l2_ctrl_handler_init(&lut->ctrls, 1);
 228        v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL);
 229
 230        lut->entity.subdev.ctrl_handler = &lut->ctrls;
 231
 232        if (lut->ctrls.error) {
 233                dev_err(vsp1->dev, "lut: failed to initialize controls\n");
 234                ret = lut->ctrls.error;
 235                vsp1_entity_destroy(&lut->entity);
 236                return ERR_PTR(ret);
 237        }
 238
 239        v4l2_ctrl_handler_setup(&lut->ctrls);
 240
 241        return lut;
 242}
 243