vsp1_clu.c (7271B)
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}