1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * vsp1_lut.c -- R-Car VSP1 Look-Up Table 4 * 5 * Copyright (C) 2013 Renesas Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <linux/device.h> 11 #include <linux/gfp.h> 12 13 #include <media/v4l2-subdev.h> 14 15 #include "vsp1.h" 16 #include "vsp1_dl.h" 17 #include "vsp1_lut.h" 18 19 #define LUT_MIN_SIZE 4U 20 #define LUT_MAX_SIZE 8190U 21 22 #define LUT_SIZE 256 23 24 /* ----------------------------------------------------------------------------- 25 * Device Access 26 */ 27 28 static inline void vsp1_lut_write(struct vsp1_lut *lut, 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_LUT_TABLE (V4L2_CID_USER_BASE | 0x1001) 39 40 static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) 41 { 42 struct vsp1_dl_body *dlb; 43 unsigned int i; 44 45 dlb = vsp1_dl_body_get(lut->pool); 46 if (!dlb) 47 return -ENOMEM; 48 49 for (i = 0; i < LUT_SIZE; ++i) 50 vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, 51 ctrl->p_new.p_u32[i]); 52 53 spin_lock_irq(&lut->lock); 54 swap(lut->lut, dlb); 55 spin_unlock_irq(&lut->lock); 56 57 vsp1_dl_body_put(dlb); 58 return 0; 59 } 60 61 static int lut_s_ctrl(struct v4l2_ctrl *ctrl) 62 { 63 struct vsp1_lut *lut = 64 container_of(ctrl->handler, struct vsp1_lut, ctrls); 65 66 switch (ctrl->id) { 67 case V4L2_CID_VSP1_LUT_TABLE: 68 lut_set_table(lut, ctrl); 69 break; 70 } 71 72 return 0; 73 } 74 75 static const struct v4l2_ctrl_ops lut_ctrl_ops = { 76 .s_ctrl = lut_s_ctrl, 77 }; 78 79 static const struct v4l2_ctrl_config lut_table_control = { 80 .ops = &lut_ctrl_ops, 81 .id = V4L2_CID_VSP1_LUT_TABLE, 82 .name = "Look-Up Table", 83 .type = V4L2_CTRL_TYPE_U32, 84 .min = 0x00000000, 85 .max = 0x00ffffff, 86 .step = 1, 87 .def = 0, 88 .dims = { LUT_SIZE }, 89 }; 90 91 /* ----------------------------------------------------------------------------- 92 * V4L2 Subdevice Pad Operations 93 */ 94 95 static const unsigned int lut_codes[] = { 96 MEDIA_BUS_FMT_ARGB8888_1X32, 97 MEDIA_BUS_FMT_AHSV8888_1X32, 98 MEDIA_BUS_FMT_AYUV8_1X32, 99 }; 100 101 static int lut_enum_mbus_code(struct v4l2_subdev *subdev, 102 struct v4l2_subdev_state *sd_state, 103 struct v4l2_subdev_mbus_code_enum *code) 104 { 105 return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, lut_codes, 106 ARRAY_SIZE(lut_codes)); 107 } 108 109 static int lut_enum_frame_size(struct v4l2_subdev *subdev, 110 struct v4l2_subdev_state *sd_state, 111 struct v4l2_subdev_frame_size_enum *fse) 112 { 113 return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, 114 LUT_MIN_SIZE, 115 LUT_MIN_SIZE, LUT_MAX_SIZE, 116 LUT_MAX_SIZE); 117 } 118 119 static int lut_set_format(struct v4l2_subdev *subdev, 120 struct v4l2_subdev_state *sd_state, 121 struct v4l2_subdev_format *fmt) 122 { 123 return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, lut_codes, 124 ARRAY_SIZE(lut_codes), 125 LUT_MIN_SIZE, LUT_MIN_SIZE, 126 LUT_MAX_SIZE, LUT_MAX_SIZE); 127 } 128 129 /* ----------------------------------------------------------------------------- 130 * V4L2 Subdevice Operations 131 */ 132 133 static const struct v4l2_subdev_pad_ops lut_pad_ops = { 134 .enum_mbus_code = lut_enum_mbus_code, 135 .enum_frame_size = lut_enum_frame_size, 136 .get_fmt = vsp1_subdev_get_pad_format, 137 .set_fmt = lut_set_format, 138 }; 139 140 static const struct v4l2_subdev_ops lut_ops = { 141 .pad = &lut_pad_ops, 142 }; 143 144 /* ----------------------------------------------------------------------------- 145 * VSP1 Entity Operations 146 */ 147 148 static void lut_configure_stream(struct vsp1_entity *entity, 149 struct v4l2_subdev_state *state, 150 struct vsp1_pipeline *pipe, 151 struct vsp1_dl_list *dl, 152 struct vsp1_dl_body *dlb) 153 { 154 struct vsp1_lut *lut = to_lut(&entity->subdev); 155 156 vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); 157 } 158 159 static void lut_configure_frame(struct vsp1_entity *entity, 160 struct vsp1_pipeline *pipe, 161 struct vsp1_dl_list *dl, 162 struct vsp1_dl_body *dlb) 163 { 164 struct vsp1_lut *lut = to_lut(&entity->subdev); 165 struct vsp1_dl_body *lut_dlb; 166 unsigned long flags; 167 168 spin_lock_irqsave(&lut->lock, flags); 169 lut_dlb = lut->lut; 170 lut->lut = NULL; 171 spin_unlock_irqrestore(&lut->lock, flags); 172 173 if (lut_dlb) { 174 vsp1_dl_list_add_body(dl, lut_dlb); 175 176 /* Release our local reference. */ 177 vsp1_dl_body_put(lut_dlb); 178 } 179 } 180 181 static void lut_destroy(struct vsp1_entity *entity) 182 { 183 struct vsp1_lut *lut = to_lut(&entity->subdev); 184 185 vsp1_dl_body_pool_destroy(lut->pool); 186 } 187 188 static const struct vsp1_entity_operations lut_entity_ops = { 189 .configure_stream = lut_configure_stream, 190 .configure_frame = lut_configure_frame, 191 .destroy = lut_destroy, 192 }; 193 194 /* ----------------------------------------------------------------------------- 195 * Initialization and Cleanup 196 */ 197 198 struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) 199 { 200 struct vsp1_lut *lut; 201 int ret; 202 203 lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL); 204 if (lut == NULL) 205 return ERR_PTR(-ENOMEM); 206 207 spin_lock_init(&lut->lock); 208 209 lut->entity.ops = &lut_entity_ops; 210 lut->entity.type = VSP1_ENTITY_LUT; 211 212 ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops, 213 MEDIA_ENT_F_PROC_VIDEO_LUT); 214 if (ret < 0) 215 return ERR_PTR(ret); 216 217 /* 218 * Pre-allocate a body pool, with 3 bodies allowing a userspace update 219 * before the hardware has committed a previous set of tables, handling 220 * both the queued and pending dl entries. 221 */ 222 lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0); 223 if (!lut->pool) 224 return ERR_PTR(-ENOMEM); 225 226 /* Initialize the control handler. */ 227 v4l2_ctrl_handler_init(&lut->ctrls, 1); 228 v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL); 229 230 lut->entity.subdev.ctrl_handler = &lut->ctrls; 231 232 if (lut->ctrls.error) { 233 dev_err(vsp1->dev, "lut: failed to initialize controls\n"); 234 ret = lut->ctrls.error; 235 vsp1_entity_destroy(&lut->entity); 236 return ERR_PTR(ret); 237 } 238 239 v4l2_ctrl_handler_setup(&lut->ctrls); 240 241 return lut; 242 } 243