Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / media / platform / vsp1 / vsp1_lut.c
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_pad_config *cfg,
103                               struct v4l2_subdev_mbus_code_enum *code)
104 {
105         return vsp1_subdev_enum_mbus_code(subdev, cfg, 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_pad_config *cfg,
111                                struct v4l2_subdev_frame_size_enum *fse)
112 {
113         return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LUT_MIN_SIZE,
114                                            LUT_MIN_SIZE, LUT_MAX_SIZE,
115                                            LUT_MAX_SIZE);
116 }
117
118 static int lut_set_format(struct v4l2_subdev *subdev,
119                           struct v4l2_subdev_pad_config *cfg,
120                           struct v4l2_subdev_format *fmt)
121 {
122         return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lut_codes,
123                                           ARRAY_SIZE(lut_codes),
124                                           LUT_MIN_SIZE, LUT_MIN_SIZE,
125                                           LUT_MAX_SIZE, LUT_MAX_SIZE);
126 }
127
128 /* -----------------------------------------------------------------------------
129  * V4L2 Subdevice Operations
130  */
131
132 static const struct v4l2_subdev_pad_ops lut_pad_ops = {
133         .init_cfg = vsp1_entity_init_cfg,
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 }