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 vsp1_pipeline *pipe, 150 struct vsp1_dl_list *dl, 151 struct vsp1_dl_body *dlb) 152 { 153 struct vsp1_lut *lut = to_lut(&entity->subdev); 154 155 vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); 156 } 157 158 static void lut_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_lut *lut = to_lut(&entity->subdev); 164 struct vsp1_dl_body *lut_dlb; 165 unsigned long flags; 166 167 spin_lock_irqsave(&lut->lock, flags); 168 lut_dlb = lut->lut; 169 lut->lut = NULL; 170 spin_unlock_irqrestore(&lut->lock, flags); 171 172 if (lut_dlb) { 173 vsp1_dl_list_add_body(dl, lut_dlb); 174 175 /* Release our local reference. */ 176 vsp1_dl_body_put(lut_dlb); 177 } 178 } 179 180 static void lut_destroy(struct vsp1_entity *entity) 181 { 182 struct vsp1_lut *lut = to_lut(&entity->subdev); 183 184 vsp1_dl_body_pool_destroy(lut->pool); 185 } 186 187 static const struct vsp1_entity_operations lut_entity_ops = { 188 .configure_stream = lut_configure_stream, 189 .configure_frame = lut_configure_frame, 190 .destroy = lut_destroy, 191 }; 192 193 /* ----------------------------------------------------------------------------- 194 * Initialization and Cleanup 195 */ 196 197 struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) 198 { 199 struct vsp1_lut *lut; 200 int ret; 201 202 lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL); 203 if (lut == NULL) 204 return ERR_PTR(-ENOMEM); 205 206 spin_lock_init(&lut->lock); 207 208 lut->entity.ops = &lut_entity_ops; 209 lut->entity.type = VSP1_ENTITY_LUT; 210 211 ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops, 212 MEDIA_ENT_F_PROC_VIDEO_LUT); 213 if (ret < 0) 214 return ERR_PTR(ret); 215 216 /* 217 * Pre-allocate a body pool, with 3 bodies allowing a userspace update 218 * before the hardware has committed a previous set of tables, handling 219 * both the queued and pending dl entries. 220 */ 221 lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0); 222 if (!lut->pool) 223 return ERR_PTR(-ENOMEM); 224 225 /* Initialize the control handler. */ 226 v4l2_ctrl_handler_init(&lut->ctrls, 1); 227 v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL); 228 229 lut->entity.subdev.ctrl_handler = &lut->ctrls; 230 231 if (lut->ctrls.error) { 232 dev_err(vsp1->dev, "lut: failed to initialize controls\n"); 233 ret = lut->ctrls.error; 234 vsp1_entity_destroy(&lut->entity); 235 return ERR_PTR(ret); 236 } 237 238 v4l2_ctrl_handler_setup(&lut->ctrls); 239 240 return lut; 241 } 242