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 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 const struct v4l2_subdev_pad_ops clu_pad_ops = { 126 .enum_mbus_code = vsp1_subdev_enum_mbus_code, 127 .enum_frame_size = vsp1_subdev_enum_frame_size, 128 .get_fmt = vsp1_subdev_get_pad_format, 129 .set_fmt = vsp1_subdev_set_pad_format, 130 }; 131 132 static const struct v4l2_subdev_ops clu_ops = { 133 .core = &vsp1_entity_core_ops, 134 .pad = &clu_pad_ops, 135 }; 136 137 /* ----------------------------------------------------------------------------- 138 * VSP1 Entity Operations 139 */ 140 141 static void clu_configure_stream(struct vsp1_entity *entity, 142 struct v4l2_subdev_state *state, 143 struct vsp1_pipeline *pipe, 144 struct vsp1_dl_list *dl, 145 struct vsp1_dl_body *dlb) 146 { 147 struct vsp1_clu *clu = to_clu(&entity->subdev); 148 struct v4l2_mbus_framefmt *format; 149 150 /* 151 * The yuv_mode can't be changed during streaming. Cache it internally 152 * for future runtime configuration calls. 153 */ 154 format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK); 155 clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; 156 } 157 158 static void clu_configure_frame(struct vsp1_entity *entity, 159 struct vsp1_pipeline *pipe, 160 struct vsp1_dl_list *dl, 161 struct vsp1_dl_body *dlb) 162 { 163 struct vsp1_clu *clu = to_clu(&entity->subdev); 164 struct vsp1_dl_body *clu_dlb; 165 unsigned long flags; 166 u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; 167 168 /* 2D mode can only be used with the YCbCr pixel encoding. */ 169 if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) 170 ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D 171 | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D 172 | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; 173 174 vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); 175 176 spin_lock_irqsave(&clu->lock, flags); 177 clu_dlb = clu->clu; 178 clu->clu = NULL; 179 spin_unlock_irqrestore(&clu->lock, flags); 180 181 if (clu_dlb) { 182 vsp1_dl_list_add_body(dl, clu_dlb); 183 184 /* Release our local reference. */ 185 vsp1_dl_body_put(clu_dlb); 186 } 187 } 188 189 static void clu_destroy(struct vsp1_entity *entity) 190 { 191 struct vsp1_clu *clu = to_clu(&entity->subdev); 192 193 vsp1_dl_body_pool_destroy(clu->pool); 194 } 195 196 static const struct vsp1_entity_operations clu_entity_ops = { 197 .configure_stream = clu_configure_stream, 198 .configure_frame = clu_configure_frame, 199 .destroy = clu_destroy, 200 }; 201 202 /* ----------------------------------------------------------------------------- 203 * Initialization and Cleanup 204 */ 205 206 struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) 207 { 208 struct vsp1_clu *clu; 209 int ret; 210 211 clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL); 212 if (clu == NULL) 213 return ERR_PTR(-ENOMEM); 214 215 spin_lock_init(&clu->lock); 216 217 clu->entity.ops = &clu_entity_ops; 218 clu->entity.type = VSP1_ENTITY_CLU; 219 clu->entity.codes = clu_codes; 220 clu->entity.num_codes = ARRAY_SIZE(clu_codes); 221 clu->entity.min_width = CLU_MIN_SIZE; 222 clu->entity.min_height = CLU_MIN_SIZE; 223 clu->entity.max_width = CLU_MAX_SIZE; 224 clu->entity.max_height = CLU_MAX_SIZE; 225 226 ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops, 227 MEDIA_ENT_F_PROC_VIDEO_LUT); 228 if (ret < 0) 229 return ERR_PTR(ret); 230 231 /* 232 * Pre-allocate a body pool, with 3 bodies allowing a userspace update 233 * before the hardware has committed a previous set of tables, handling 234 * both the queued and pending dl entries. One extra entry is added to 235 * the CLU_SIZE to allow for the VI6_CLU_ADDR header. 236 */ 237 clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1, 238 0); 239 if (!clu->pool) 240 return ERR_PTR(-ENOMEM); 241 242 /* Initialize the control handler. */ 243 v4l2_ctrl_handler_init(&clu->ctrls, 2); 244 v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL); 245 v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL); 246 247 clu->entity.subdev.ctrl_handler = &clu->ctrls; 248 249 if (clu->ctrls.error) { 250 dev_err(vsp1->dev, "clu: failed to initialize controls\n"); 251 ret = clu->ctrls.error; 252 vsp1_entity_destroy(&clu->entity); 253 return ERR_PTR(ret); 254 } 255 256 v4l2_ctrl_handler_setup(&clu->ctrls); 257 258 return clu; 259 } 260