linux/drivers/media/platform/vsp1/vsp1_clu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * vsp1_clu.c  --  R-Car VSP1 Cubic Look-Up Table
   4 *
   5 * Copyright (C) 2015-2016 Renesas Electronics Corporation
   6 *
   7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   8 */
   9
  10#include <linux/device.h>
  11#include <linux/slab.h>
  12
  13#include <media/v4l2-subdev.h>
  14
  15#include "vsp1.h"
  16#include "vsp1_clu.h"
  17#include "vsp1_dl.h"
  18
  19#define CLU_MIN_SIZE                            4U
  20#define CLU_MAX_SIZE                            8190U
  21
  22#define CLU_SIZE                                (17 * 17 * 17)
  23
  24/* -----------------------------------------------------------------------------
  25 * Device Access
  26 */
  27
  28static inline void vsp1_clu_write(struct vsp1_clu *clu,
  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_CLU_TABLE                 (V4L2_CID_USER_BASE | 0x1001)
  39#define V4L2_CID_VSP1_CLU_MODE                  (V4L2_CID_USER_BASE | 0x1002)
  40#define V4L2_CID_VSP1_CLU_MODE_2D               0
  41#define V4L2_CID_VSP1_CLU_MODE_3D               1
  42
  43static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl)
  44{
  45        struct vsp1_dl_body *dlb;
  46        unsigned int i;
  47
  48        dlb = vsp1_dl_body_get(clu->pool);
  49        if (!dlb)
  50                return -ENOMEM;
  51
  52        vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0);
  53        for (i = 0; i < CLU_SIZE; ++i)
  54                vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
  55
  56        spin_lock_irq(&clu->lock);
  57        swap(clu->clu, dlb);
  58        spin_unlock_irq(&clu->lock);
  59
  60        vsp1_dl_body_put(dlb);
  61        return 0;
  62}
  63
  64static int clu_s_ctrl(struct v4l2_ctrl *ctrl)
  65{
  66        struct vsp1_clu *clu =
  67                container_of(ctrl->handler, struct vsp1_clu, ctrls);
  68
  69        switch (ctrl->id) {
  70        case V4L2_CID_VSP1_CLU_TABLE:
  71                clu_set_table(clu, ctrl);
  72                break;
  73
  74        case V4L2_CID_VSP1_CLU_MODE:
  75                clu->mode = ctrl->val;
  76                break;
  77        }
  78
  79        return 0;
  80}
  81
  82static const struct v4l2_ctrl_ops clu_ctrl_ops = {
  83        .s_ctrl = clu_s_ctrl,
  84};
  85
  86static const struct v4l2_ctrl_config clu_table_control = {
  87        .ops = &clu_ctrl_ops,
  88        .id = V4L2_CID_VSP1_CLU_TABLE,
  89        .name = "Look-Up Table",
  90        .type = V4L2_CTRL_TYPE_U32,
  91        .min = 0x00000000,
  92        .max = 0x00ffffff,
  93        .step = 1,
  94        .def = 0,
  95        .dims = { 17, 17, 17 },
  96};
  97
  98static const char * const clu_mode_menu[] = {
  99        "2D",
 100        "3D",
 101        NULL,
 102};
 103
 104static const struct v4l2_ctrl_config clu_mode_control = {
 105        .ops = &clu_ctrl_ops,
 106        .id = V4L2_CID_VSP1_CLU_MODE,
 107        .name = "Mode",
 108        .type = V4L2_CTRL_TYPE_MENU,
 109        .min = 0,
 110        .max = 1,
 111        .def = 1,
 112        .qmenu = clu_mode_menu,
 113};
 114
 115/* -----------------------------------------------------------------------------
 116 * V4L2 Subdevice Pad Operations
 117 */
 118
 119static const unsigned int clu_codes[] = {
 120        MEDIA_BUS_FMT_ARGB8888_1X32,
 121        MEDIA_BUS_FMT_AHSV8888_1X32,
 122        MEDIA_BUS_FMT_AYUV8_1X32,
 123};
 124
 125static int clu_enum_mbus_code(struct v4l2_subdev *subdev,
 126                              struct v4l2_subdev_state *sd_state,
 127                              struct v4l2_subdev_mbus_code_enum *code)
 128{
 129        return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, clu_codes,
 130                                          ARRAY_SIZE(clu_codes));
 131}
 132
 133static int clu_enum_frame_size(struct v4l2_subdev *subdev,
 134                               struct v4l2_subdev_state *sd_state,
 135                               struct v4l2_subdev_frame_size_enum *fse)
 136{
 137        return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
 138                                           CLU_MIN_SIZE,
 139                                           CLU_MIN_SIZE, CLU_MAX_SIZE,
 140                                           CLU_MAX_SIZE);
 141}
 142
 143static int clu_set_format(struct v4l2_subdev *subdev,
 144                          struct v4l2_subdev_state *sd_state,
 145                          struct v4l2_subdev_format *fmt)
 146{
 147        return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, clu_codes,
 148                                          ARRAY_SIZE(clu_codes),
 149                                          CLU_MIN_SIZE, CLU_MIN_SIZE,
 150                                          CLU_MAX_SIZE, CLU_MAX_SIZE);
 151}
 152
 153/* -----------------------------------------------------------------------------
 154 * V4L2 Subdevice Operations
 155 */
 156
 157static const struct v4l2_subdev_pad_ops clu_pad_ops = {
 158        .init_cfg = vsp1_entity_init_cfg,
 159        .enum_mbus_code = clu_enum_mbus_code,
 160        .enum_frame_size = clu_enum_frame_size,
 161        .get_fmt = vsp1_subdev_get_pad_format,
 162        .set_fmt = clu_set_format,
 163};
 164
 165static const struct v4l2_subdev_ops clu_ops = {
 166        .pad    = &clu_pad_ops,
 167};
 168
 169/* -----------------------------------------------------------------------------
 170 * VSP1 Entity Operations
 171 */
 172
 173static void clu_configure_stream(struct vsp1_entity *entity,
 174                                 struct vsp1_pipeline *pipe,
 175                                 struct vsp1_dl_list *dl,
 176                                 struct vsp1_dl_body *dlb)
 177{
 178        struct vsp1_clu *clu = to_clu(&entity->subdev);
 179        struct v4l2_mbus_framefmt *format;
 180
 181        /*
 182         * The yuv_mode can't be changed during streaming. Cache it internally
 183         * for future runtime configuration calls.
 184         */
 185        format = vsp1_entity_get_pad_format(&clu->entity,
 186                                            clu->entity.config,
 187                                            CLU_PAD_SINK);
 188        clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
 189}
 190
 191static void clu_configure_frame(struct vsp1_entity *entity,
 192                                struct vsp1_pipeline *pipe,
 193                                struct vsp1_dl_list *dl,
 194                                struct vsp1_dl_body *dlb)
 195{
 196        struct vsp1_clu *clu = to_clu(&entity->subdev);
 197        struct vsp1_dl_body *clu_dlb;
 198        unsigned long flags;
 199        u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
 200
 201        /* 2D mode can only be used with the YCbCr pixel encoding. */
 202        if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode)
 203                ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D
 204                     |  VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D
 205                     |  VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D;
 206
 207        vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl);
 208
 209        spin_lock_irqsave(&clu->lock, flags);
 210        clu_dlb = clu->clu;
 211        clu->clu = NULL;
 212        spin_unlock_irqrestore(&clu->lock, flags);
 213
 214        if (clu_dlb) {
 215                vsp1_dl_list_add_body(dl, clu_dlb);
 216
 217                /* Release our local reference. */
 218                vsp1_dl_body_put(clu_dlb);
 219        }
 220}
 221
 222static void clu_destroy(struct vsp1_entity *entity)
 223{
 224        struct vsp1_clu *clu = to_clu(&entity->subdev);
 225
 226        vsp1_dl_body_pool_destroy(clu->pool);
 227}
 228
 229static const struct vsp1_entity_operations clu_entity_ops = {
 230        .configure_stream = clu_configure_stream,
 231        .configure_frame = clu_configure_frame,
 232        .destroy = clu_destroy,
 233};
 234
 235/* -----------------------------------------------------------------------------
 236 * Initialization and Cleanup
 237 */
 238
 239struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1)
 240{
 241        struct vsp1_clu *clu;
 242        int ret;
 243
 244        clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL);
 245        if (clu == NULL)
 246                return ERR_PTR(-ENOMEM);
 247
 248        spin_lock_init(&clu->lock);
 249
 250        clu->entity.ops = &clu_entity_ops;
 251        clu->entity.type = VSP1_ENTITY_CLU;
 252
 253        ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops,
 254                               MEDIA_ENT_F_PROC_VIDEO_LUT);
 255        if (ret < 0)
 256                return ERR_PTR(ret);
 257
 258        /*
 259         * Pre-allocate a body pool, with 3 bodies allowing a userspace update
 260         * before the hardware has committed a previous set of tables, handling
 261         * both the queued and pending dl entries. One extra entry is added to
 262         * the CLU_SIZE to allow for the VI6_CLU_ADDR header.
 263         */
 264        clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1,
 265                                             0);
 266        if (!clu->pool)
 267                return ERR_PTR(-ENOMEM);
 268
 269        /* Initialize the control handler. */
 270        v4l2_ctrl_handler_init(&clu->ctrls, 2);
 271        v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL);
 272        v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL);
 273
 274        clu->entity.subdev.ctrl_handler = &clu->ctrls;
 275
 276        if (clu->ctrls.error) {
 277                dev_err(vsp1->dev, "clu: failed to initialize controls\n");
 278                ret = clu->ctrls.error;
 279                vsp1_entity_destroy(&clu->entity);
 280                return ERR_PTR(ret);
 281        }
 282
 283        v4l2_ctrl_handler_setup(&clu->ctrls);
 284
 285        return clu;
 286}
 287