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 .enum_mbus_code = clu_enum_mbus_code, 159 .enum_frame_size = clu_enum_frame_size, 160 .get_fmt = vsp1_subdev_get_pad_format, 161 .set_fmt = clu_set_format, 162 }; 163 164 static const struct v4l2_subdev_ops clu_ops = { 165 .pad = &clu_pad_ops, 166 }; 167 168 /* ----------------------------------------------------------------------------- 169 * VSP1 Entity Operations 170 */ 171 172 static void clu_configure_stream(struct vsp1_entity *entity, 173 struct vsp1_pipeline *pipe, 174 struct vsp1_dl_list *dl, 175 struct vsp1_dl_body *dlb) 176 { 177 struct vsp1_clu *clu = to_clu(&entity->subdev); 178 struct v4l2_mbus_framefmt *format; 179 180 /* 181 * The yuv_mode can't be changed during streaming. Cache it internally 182 * for future runtime configuration calls. 183 */ 184 format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, 185 CLU_PAD_SINK); 186 clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; 187 } 188 189 static void clu_configure_frame(struct vsp1_entity *entity, 190 struct vsp1_pipeline *pipe, 191 struct vsp1_dl_list *dl, 192 struct vsp1_dl_body *dlb) 193 { 194 struct vsp1_clu *clu = to_clu(&entity->subdev); 195 struct vsp1_dl_body *clu_dlb; 196 unsigned long flags; 197 u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; 198 199 /* 2D mode can only be used with the YCbCr pixel encoding. */ 200 if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) 201 ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D 202 | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D 203 | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; 204 205 vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); 206 207 spin_lock_irqsave(&clu->lock, flags); 208 clu_dlb = clu->clu; 209 clu->clu = NULL; 210 spin_unlock_irqrestore(&clu->lock, flags); 211 212 if (clu_dlb) { 213 vsp1_dl_list_add_body(dl, clu_dlb); 214 215 /* Release our local reference. */ 216 vsp1_dl_body_put(clu_dlb); 217 } 218 } 219 220 static void clu_destroy(struct vsp1_entity *entity) 221 { 222 struct vsp1_clu *clu = to_clu(&entity->subdev); 223 224 vsp1_dl_body_pool_destroy(clu->pool); 225 } 226 227 static const struct vsp1_entity_operations clu_entity_ops = { 228 .configure_stream = clu_configure_stream, 229 .configure_frame = clu_configure_frame, 230 .destroy = clu_destroy, 231 }; 232 233 /* ----------------------------------------------------------------------------- 234 * Initialization and Cleanup 235 */ 236 237 struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) 238 { 239 struct vsp1_clu *clu; 240 int ret; 241 242 clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL); 243 if (clu == NULL) 244 return ERR_PTR(-ENOMEM); 245 246 spin_lock_init(&clu->lock); 247 248 clu->entity.ops = &clu_entity_ops; 249 clu->entity.type = VSP1_ENTITY_CLU; 250 251 ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops, 252 MEDIA_ENT_F_PROC_VIDEO_LUT); 253 if (ret < 0) 254 return ERR_PTR(ret); 255 256 /* 257 * Pre-allocate a body pool, with 3 bodies allowing a userspace update 258 * before the hardware has committed a previous set of tables, handling 259 * both the queued and pending dl entries. One extra entry is added to 260 * the CLU_SIZE to allow for the VI6_CLU_ADDR header. 261 */ 262 clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1, 263 0); 264 if (!clu->pool) 265 return ERR_PTR(-ENOMEM); 266 267 /* Initialize the control handler. */ 268 v4l2_ctrl_handler_init(&clu->ctrls, 2); 269 v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL); 270 v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL); 271 272 clu->entity.subdev.ctrl_handler = &clu->ctrls; 273 274 if (clu->ctrls.error) { 275 dev_err(vsp1->dev, "clu: failed to initialize controls\n"); 276 ret = clu->ctrls.error; 277 vsp1_entity_destroy(&clu->entity); 278 return ERR_PTR(ret); 279 } 280 281 v4l2_ctrl_handler_setup(&clu->ctrls); 282 283 return clu; 284 } 285