GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / media / platform / vsp1 / vsp1_sru.c
1 /*
2  * vsp1_sru.c  --  R-Car VSP1 Super Resolution Unit
3  *
4  * Copyright (C) 2013 Renesas Corporation
5  *
6  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13
14 #include <linux/device.h>
15 #include <linux/gfp.h>
16
17 #include <media/v4l2-subdev.h>
18
19 #include "vsp1.h"
20 #include "vsp1_dl.h"
21 #include "vsp1_pipe.h"
22 #include "vsp1_sru.h"
23
24 #define SRU_MIN_SIZE                            4U
25 #define SRU_MAX_SIZE                            8190U
26
27 /* -----------------------------------------------------------------------------
28  * Device Access
29  */
30
31 static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl,
32                                   u32 reg, u32 data)
33 {
34         vsp1_dl_list_write(dl, reg, data);
35 }
36
37 /* -----------------------------------------------------------------------------
38  * Controls
39  */
40
41 #define V4L2_CID_VSP1_SRU_INTENSITY             (V4L2_CID_USER_BASE | 0x1001)
42
43 struct vsp1_sru_param {
44         u32 ctrl0;
45         u32 ctrl2;
46 };
47
48 #define VI6_SRU_CTRL0_PARAMS(p0, p1)                    \
49         (((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) |         \
50          ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
51
52 #define VI6_SRU_CTRL2_PARAMS(p6, p7, p8)                \
53         (((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) |         \
54          ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) |         \
55          ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
56
57 static const struct vsp1_sru_param vsp1_sru_params[] = {
58         {
59                 .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
60                 .ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255),
61         }, {
62                 .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
63                 .ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255),
64         }, {
65                 .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
66                 .ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255),
67         }, {
68                 .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
69                 .ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255),
70         }, {
71                 .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
72                 .ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255),
73         }, {
74                 .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
75                 .ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255),
76         },
77 };
78
79 static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
80 {
81         struct vsp1_sru *sru =
82                 container_of(ctrl->handler, struct vsp1_sru, ctrls);
83
84         switch (ctrl->id) {
85         case V4L2_CID_VSP1_SRU_INTENSITY:
86                 sru->intensity = ctrl->val;
87                 break;
88         }
89
90         return 0;
91 }
92
93 static const struct v4l2_ctrl_ops sru_ctrl_ops = {
94         .s_ctrl = sru_s_ctrl,
95 };
96
97 static const struct v4l2_ctrl_config sru_intensity_control = {
98         .ops = &sru_ctrl_ops,
99         .id = V4L2_CID_VSP1_SRU_INTENSITY,
100         .name = "Intensity",
101         .type = V4L2_CTRL_TYPE_INTEGER,
102         .min = 1,
103         .max = 6,
104         .def = 1,
105         .step = 1,
106 };
107
108 /* -----------------------------------------------------------------------------
109  * V4L2 Subdevice Operations
110  */
111
112 static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
113                               struct v4l2_subdev_pad_config *cfg,
114                               struct v4l2_subdev_mbus_code_enum *code)
115 {
116         static const unsigned int codes[] = {
117                 MEDIA_BUS_FMT_ARGB8888_1X32,
118                 MEDIA_BUS_FMT_AYUV8_1X32,
119         };
120
121         return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
122                                           ARRAY_SIZE(codes));
123 }
124
125 static int sru_enum_frame_size(struct v4l2_subdev *subdev,
126                                struct v4l2_subdev_pad_config *cfg,
127                                struct v4l2_subdev_frame_size_enum *fse)
128 {
129         struct vsp1_sru *sru = to_sru(subdev);
130         struct v4l2_subdev_pad_config *config;
131         struct v4l2_mbus_framefmt *format;
132         int ret = 0;
133
134         config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which);
135         if (!config)
136                 return -EINVAL;
137
138         format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK);
139
140         mutex_lock(&sru->entity.lock);
141
142         if (fse->index || fse->code != format->code) {
143                 ret = -EINVAL;
144                 goto done;
145         }
146
147         if (fse->pad == SRU_PAD_SINK) {
148                 fse->min_width = SRU_MIN_SIZE;
149                 fse->max_width = SRU_MAX_SIZE;
150                 fse->min_height = SRU_MIN_SIZE;
151                 fse->max_height = SRU_MAX_SIZE;
152         } else {
153                 fse->min_width = format->width;
154                 fse->min_height = format->height;
155                 if (format->width <= SRU_MAX_SIZE / 2 &&
156                     format->height <= SRU_MAX_SIZE / 2) {
157                         fse->max_width = format->width * 2;
158                         fse->max_height = format->height * 2;
159                 } else {
160                         fse->max_width = format->width;
161                         fse->max_height = format->height;
162                 }
163         }
164
165 done:
166         mutex_unlock(&sru->entity.lock);
167         return ret;
168 }
169
170 static void sru_try_format(struct vsp1_sru *sru,
171                            struct v4l2_subdev_pad_config *config,
172                            unsigned int pad, struct v4l2_mbus_framefmt *fmt)
173 {
174         struct v4l2_mbus_framefmt *format;
175         unsigned int input_area;
176         unsigned int output_area;
177
178         switch (pad) {
179         case SRU_PAD_SINK:
180                 /* Default to YUV if the requested format is not supported. */
181                 if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
182                     fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
183                         fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
184
185                 fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
186                 fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
187                 break;
188
189         case SRU_PAD_SOURCE:
190                 /* The SRU can't perform format conversion. */
191                 format = vsp1_entity_get_pad_format(&sru->entity, config,
192                                                     SRU_PAD_SINK);
193                 fmt->code = format->code;
194
195                 /*
196                  * We can upscale by 2 in both direction, but not independently.
197                  * Compare the input and output rectangles areas (avoiding
198                  * integer overflows on the output): if the requested output
199                  * area is larger than 1.5^2 the input area upscale by two,
200                  * otherwise don't scale.
201                  */
202                 input_area = format->width * format->height;
203                 output_area = min(fmt->width, SRU_MAX_SIZE)
204                             * min(fmt->height, SRU_MAX_SIZE);
205
206                 if (fmt->width <= SRU_MAX_SIZE / 2 &&
207                     fmt->height <= SRU_MAX_SIZE / 2 &&
208                     output_area > input_area * 9 / 4) {
209                         fmt->width = format->width * 2;
210                         fmt->height = format->height * 2;
211                 } else {
212                         fmt->width = format->width;
213                         fmt->height = format->height;
214                 }
215                 break;
216         }
217
218         fmt->field = V4L2_FIELD_NONE;
219         fmt->colorspace = V4L2_COLORSPACE_SRGB;
220 }
221
222 static int sru_set_format(struct v4l2_subdev *subdev,
223                           struct v4l2_subdev_pad_config *cfg,
224                           struct v4l2_subdev_format *fmt)
225 {
226         struct vsp1_sru *sru = to_sru(subdev);
227         struct v4l2_subdev_pad_config *config;
228         struct v4l2_mbus_framefmt *format;
229         int ret = 0;
230
231         mutex_lock(&sru->entity.lock);
232
233         config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which);
234         if (!config) {
235                 ret = -EINVAL;
236                 goto done;
237         }
238
239         sru_try_format(sru, config, fmt->pad, &fmt->format);
240
241         format = vsp1_entity_get_pad_format(&sru->entity, config, fmt->pad);
242         *format = fmt->format;
243
244         if (fmt->pad == SRU_PAD_SINK) {
245                 /* Propagate the format to the source pad. */
246                 format = vsp1_entity_get_pad_format(&sru->entity, config,
247                                                     SRU_PAD_SOURCE);
248                 *format = fmt->format;
249
250                 sru_try_format(sru, config, SRU_PAD_SOURCE, format);
251         }
252
253 done:
254         mutex_unlock(&sru->entity.lock);
255         return ret;
256 }
257
258 static const struct v4l2_subdev_pad_ops sru_pad_ops = {
259         .init_cfg = vsp1_entity_init_cfg,
260         .enum_mbus_code = sru_enum_mbus_code,
261         .enum_frame_size = sru_enum_frame_size,
262         .get_fmt = vsp1_subdev_get_pad_format,
263         .set_fmt = sru_set_format,
264 };
265
266 static const struct v4l2_subdev_ops sru_ops = {
267         .pad    = &sru_pad_ops,
268 };
269
270 /* -----------------------------------------------------------------------------
271  * VSP1 Entity Operations
272  */
273
274 static void sru_configure(struct vsp1_entity *entity,
275                           struct vsp1_pipeline *pipe,
276                           struct vsp1_dl_list *dl,
277                           enum vsp1_entity_params params)
278 {
279         const struct vsp1_sru_param *param;
280         struct vsp1_sru *sru = to_sru(&entity->subdev);
281         struct v4l2_mbus_framefmt *input;
282         struct v4l2_mbus_framefmt *output;
283         u32 ctrl0;
284
285         if (params != VSP1_ENTITY_PARAMS_INIT)
286                 return;
287
288         input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
289                                            SRU_PAD_SINK);
290         output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
291                                             SRU_PAD_SOURCE);
292
293         if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
294                 ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
295                       | VI6_SRU_CTRL0_PARAM4;
296         else
297                 ctrl0 = VI6_SRU_CTRL0_PARAM3;
298
299         if (input->width != output->width)
300                 ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
301
302         param = &vsp1_sru_params[sru->intensity - 1];
303
304         ctrl0 |= param->ctrl0;
305
306         vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0);
307         vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
308         vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2);
309 }
310
311 static unsigned int sru_max_width(struct vsp1_entity *entity,
312                                   struct vsp1_pipeline *pipe)
313 {
314         struct vsp1_sru *sru = to_sru(&entity->subdev);
315         struct v4l2_mbus_framefmt *input;
316         struct v4l2_mbus_framefmt *output;
317
318         input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
319                                            SRU_PAD_SINK);
320         output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
321                                             SRU_PAD_SOURCE);
322
323         if (input->width != output->width)
324                 return 512;
325         else
326                 return 256;
327 }
328
329 static void sru_partition(struct vsp1_entity *entity,
330                           struct vsp1_pipeline *pipe,
331                           struct vsp1_partition *partition,
332                           unsigned int partition_idx,
333                           struct vsp1_partition_window *window)
334 {
335         struct vsp1_sru *sru = to_sru(&entity->subdev);
336         struct v4l2_mbus_framefmt *input;
337         struct v4l2_mbus_framefmt *output;
338
339         input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
340                                            SRU_PAD_SINK);
341         output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
342                                             SRU_PAD_SOURCE);
343
344         /* Adapt if SRUx2 is enabled */
345         if (input->width != output->width) {
346                 window->width /= 2;
347                 window->left /= 2;
348         }
349
350         partition->sru = *window;
351 }
352
353 static const struct vsp1_entity_operations sru_entity_ops = {
354         .configure = sru_configure,
355         .max_width = sru_max_width,
356         .partition = sru_partition,
357 };
358
359 /* -----------------------------------------------------------------------------
360  * Initialization and Cleanup
361  */
362
363 struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
364 {
365         struct vsp1_sru *sru;
366         int ret;
367
368         sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
369         if (sru == NULL)
370                 return ERR_PTR(-ENOMEM);
371
372         sru->entity.ops = &sru_entity_ops;
373         sru->entity.type = VSP1_ENTITY_SRU;
374
375         ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
376                                MEDIA_ENT_F_PROC_VIDEO_SCALER);
377         if (ret < 0)
378                 return ERR_PTR(ret);
379
380         /* Initialize the control handler. */
381         v4l2_ctrl_handler_init(&sru->ctrls, 1);
382         v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
383
384         sru->intensity = 1;
385
386         sru->entity.subdev.ctrl_handler = &sru->ctrls;
387
388         if (sru->ctrls.error) {
389                 dev_err(vsp1->dev, "sru: failed to initialize controls\n");
390                 ret = sru->ctrls.error;
391                 vsp1_entity_destroy(&sru->entity);
392                 return ERR_PTR(ret);
393         }
394
395         return sru;
396 }