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 28 static 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 43 static 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 64 static 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 82 static const struct v4l2_ctrl_ops clu_ctrl_ops = { 83 .s_ctrl = clu_s_ctrl, 84 }; 85 86 static 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 98 static const char * const clu_mode_menu[] = { 99 "2D", 100 "3D", 101 NULL, 102 }; 103 104 static 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 119 static 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 125 static 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 133 static 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 143 static 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 157 static 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 165 static const struct v4l2_subdev_ops clu_ops = { 166 .pad = &clu_pad_ops, 167 }; 168 169 /* ----------------------------------------------------------------------------- 170 * VSP1 Entity Operations 171 */ 172 173 static 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 191 static 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 222 static 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 229 static 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 239 struct 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