GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / media / platform / vsp1 / vsp1_brx.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * vsp1_brx.c  --  R-Car VSP1 Blend ROP Unit (BRU and BRS)
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_brx.h"
17 #include "vsp1_dl.h"
18 #include "vsp1_pipe.h"
19 #include "vsp1_rwpf.h"
20 #include "vsp1_video.h"
21
22 #define BRX_MIN_SIZE                            1U
23 #define BRX_MAX_SIZE                            8190U
24
25 /* -----------------------------------------------------------------------------
26  * Device Access
27  */
28
29 static inline void vsp1_brx_write(struct vsp1_brx *brx,
30                                   struct vsp1_dl_body *dlb, u32 reg, u32 data)
31 {
32         vsp1_dl_body_write(dlb, brx->base + reg, data);
33 }
34
35 /* -----------------------------------------------------------------------------
36  * Controls
37  */
38
39 static int brx_s_ctrl(struct v4l2_ctrl *ctrl)
40 {
41         struct vsp1_brx *brx =
42                 container_of(ctrl->handler, struct vsp1_brx, ctrls);
43
44         switch (ctrl->id) {
45         case V4L2_CID_BG_COLOR:
46                 brx->bgcolor = ctrl->val;
47                 break;
48         }
49
50         return 0;
51 }
52
53 static const struct v4l2_ctrl_ops brx_ctrl_ops = {
54         .s_ctrl = brx_s_ctrl,
55 };
56
57 /* -----------------------------------------------------------------------------
58  * V4L2 Subdevice Operations
59  */
60
61 /*
62  * The BRx can't perform format conversion, all sink and source formats must be
63  * identical. We pick the format on the first sink pad (pad 0) and propagate it
64  * to all other pads.
65  */
66
67 static int brx_enum_mbus_code(struct v4l2_subdev *subdev,
68                               struct v4l2_subdev_pad_config *cfg,
69                               struct v4l2_subdev_mbus_code_enum *code)
70 {
71         static const unsigned int codes[] = {
72                 MEDIA_BUS_FMT_ARGB8888_1X32,
73                 MEDIA_BUS_FMT_AYUV8_1X32,
74         };
75
76         return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
77                                           ARRAY_SIZE(codes));
78 }
79
80 static int brx_enum_frame_size(struct v4l2_subdev *subdev,
81                                struct v4l2_subdev_pad_config *cfg,
82                                struct v4l2_subdev_frame_size_enum *fse)
83 {
84         if (fse->index)
85                 return -EINVAL;
86
87         if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
88             fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
89                 return -EINVAL;
90
91         fse->min_width = BRX_MIN_SIZE;
92         fse->max_width = BRX_MAX_SIZE;
93         fse->min_height = BRX_MIN_SIZE;
94         fse->max_height = BRX_MAX_SIZE;
95
96         return 0;
97 }
98
99 static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx,
100                                          struct v4l2_subdev_pad_config *cfg,
101                                          unsigned int pad)
102 {
103         return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad);
104 }
105
106 static void brx_try_format(struct vsp1_brx *brx,
107                            struct v4l2_subdev_pad_config *config,
108                            unsigned int pad, struct v4l2_mbus_framefmt *fmt)
109 {
110         struct v4l2_mbus_framefmt *format;
111
112         switch (pad) {
113         case BRX_PAD_SINK(0):
114                 /* Default to YUV if the requested format is not supported. */
115                 if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
116                     fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
117                         fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
118                 break;
119
120         default:
121                 /* The BRx can't perform format conversion. */
122                 format = vsp1_entity_get_pad_format(&brx->entity, config,
123                                                     BRX_PAD_SINK(0));
124                 fmt->code = format->code;
125                 break;
126         }
127
128         fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE);
129         fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE);
130         fmt->field = V4L2_FIELD_NONE;
131         fmt->colorspace = V4L2_COLORSPACE_SRGB;
132 }
133
134 static int brx_set_format(struct v4l2_subdev *subdev,
135                           struct v4l2_subdev_pad_config *cfg,
136                           struct v4l2_subdev_format *fmt)
137 {
138         struct vsp1_brx *brx = to_brx(subdev);
139         struct v4l2_subdev_pad_config *config;
140         struct v4l2_mbus_framefmt *format;
141         int ret = 0;
142
143         mutex_lock(&brx->entity.lock);
144
145         config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which);
146         if (!config) {
147                 ret = -EINVAL;
148                 goto done;
149         }
150
151         brx_try_format(brx, config, fmt->pad, &fmt->format);
152
153         format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad);
154         *format = fmt->format;
155
156         /* Reset the compose rectangle */
157         if (fmt->pad != brx->entity.source_pad) {
158                 struct v4l2_rect *compose;
159
160                 compose = brx_get_compose(brx, config, fmt->pad);
161                 compose->left = 0;
162                 compose->top = 0;
163                 compose->width = format->width;
164                 compose->height = format->height;
165         }
166
167         /* Propagate the format code to all pads */
168         if (fmt->pad == BRX_PAD_SINK(0)) {
169                 unsigned int i;
170
171                 for (i = 0; i <= brx->entity.source_pad; ++i) {
172                         format = vsp1_entity_get_pad_format(&brx->entity,
173                                                             config, i);
174                         format->code = fmt->format.code;
175                 }
176         }
177
178 done:
179         mutex_unlock(&brx->entity.lock);
180         return ret;
181 }
182
183 static int brx_get_selection(struct v4l2_subdev *subdev,
184                              struct v4l2_subdev_pad_config *cfg,
185                              struct v4l2_subdev_selection *sel)
186 {
187         struct vsp1_brx *brx = to_brx(subdev);
188         struct v4l2_subdev_pad_config *config;
189
190         if (sel->pad == brx->entity.source_pad)
191                 return -EINVAL;
192
193         switch (sel->target) {
194         case V4L2_SEL_TGT_COMPOSE_BOUNDS:
195                 sel->r.left = 0;
196                 sel->r.top = 0;
197                 sel->r.width = BRX_MAX_SIZE;
198                 sel->r.height = BRX_MAX_SIZE;
199                 return 0;
200
201         case V4L2_SEL_TGT_COMPOSE:
202                 config = vsp1_entity_get_pad_config(&brx->entity, cfg,
203                                                     sel->which);
204                 if (!config)
205                         return -EINVAL;
206
207                 mutex_lock(&brx->entity.lock);
208                 sel->r = *brx_get_compose(brx, config, sel->pad);
209                 mutex_unlock(&brx->entity.lock);
210                 return 0;
211
212         default:
213                 return -EINVAL;
214         }
215 }
216
217 static int brx_set_selection(struct v4l2_subdev *subdev,
218                              struct v4l2_subdev_pad_config *cfg,
219                              struct v4l2_subdev_selection *sel)
220 {
221         struct vsp1_brx *brx = to_brx(subdev);
222         struct v4l2_subdev_pad_config *config;
223         struct v4l2_mbus_framefmt *format;
224         struct v4l2_rect *compose;
225         int ret = 0;
226
227         if (sel->pad == brx->entity.source_pad)
228                 return -EINVAL;
229
230         if (sel->target != V4L2_SEL_TGT_COMPOSE)
231                 return -EINVAL;
232
233         mutex_lock(&brx->entity.lock);
234
235         config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which);
236         if (!config) {
237                 ret = -EINVAL;
238                 goto done;
239         }
240
241         /*
242          * The compose rectangle top left corner must be inside the output
243          * frame.
244          */
245         format = vsp1_entity_get_pad_format(&brx->entity, config,
246                                             brx->entity.source_pad);
247         sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
248         sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
249
250         /*
251          * Scaling isn't supported, the compose rectangle size must be identical
252          * to the sink format size.
253          */
254         format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad);
255         sel->r.width = format->width;
256         sel->r.height = format->height;
257
258         compose = brx_get_compose(brx, config, sel->pad);
259         *compose = sel->r;
260
261 done:
262         mutex_unlock(&brx->entity.lock);
263         return ret;
264 }
265
266 static const struct v4l2_subdev_pad_ops brx_pad_ops = {
267         .init_cfg = vsp1_entity_init_cfg,
268         .enum_mbus_code = brx_enum_mbus_code,
269         .enum_frame_size = brx_enum_frame_size,
270         .get_fmt = vsp1_subdev_get_pad_format,
271         .set_fmt = brx_set_format,
272         .get_selection = brx_get_selection,
273         .set_selection = brx_set_selection,
274 };
275
276 static const struct v4l2_subdev_ops brx_ops = {
277         .pad    = &brx_pad_ops,
278 };
279
280 /* -----------------------------------------------------------------------------
281  * VSP1 Entity Operations
282  */
283
284 static void brx_configure_stream(struct vsp1_entity *entity,
285                                  struct vsp1_pipeline *pipe,
286                                  struct vsp1_dl_body *dlb)
287 {
288         struct vsp1_brx *brx = to_brx(&entity->subdev);
289         struct v4l2_mbus_framefmt *format;
290         unsigned int flags;
291         unsigned int i;
292
293         format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config,
294                                             brx->entity.source_pad);
295
296         /*
297          * The hardware is extremely flexible but we have no userspace API to
298          * expose all the parameters, nor is it clear whether we would have use
299          * cases for all the supported modes. Let's just harcode the parameters
300          * to sane default values for now.
301          */
302
303         /*
304          * Disable dithering and enable color data normalization unless the
305          * format at the pipeline output is premultiplied.
306          */
307         flags = pipe->output ? pipe->output->format.flags : 0;
308         vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,
309                        flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
310                        0 : VI6_BRU_INCTRL_NRM);
311
312         /*
313          * Set the background position to cover the whole output image and
314          * configure its color.
315          */
316         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,
317                        (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
318                        (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
319         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);
320
321         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |
322                        (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
323
324         /*
325          * Route BRU input 1 as SRC input to the ROP unit and configure the ROP
326          * unit with a NOP operation to make BRU input 1 available as the
327          * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP
328          * unit.
329          */
330         if (entity->type == VSP1_ENTITY_BRU)
331                 vsp1_brx_write(brx, dlb, VI6_BRU_ROP,
332                                VI6_BRU_ROP_DSTSEL_BRUIN(1) |
333                                VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
334                                VI6_BRU_ROP_AROP(VI6_ROP_NOP));
335
336         for (i = 0; i < brx->entity.source_pad; ++i) {
337                 bool premultiplied = false;
338                 u32 ctrl = 0;
339
340                 /*
341                  * Configure all Blend/ROP units corresponding to an enabled BRx
342                  * input for alpha blending. Blend/ROP units corresponding to
343                  * disabled BRx inputs are used in ROP NOP mode to ignore the
344                  * SRC input.
345                  */
346                 if (brx->inputs[i].rpf) {
347                         ctrl |= VI6_BRU_CTRL_RBC;
348
349                         premultiplied = brx->inputs[i].rpf->format.flags
350                                       & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
351                 } else {
352                         ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
353                              |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
354                 }
355
356                 /*
357                  * Select the virtual RPF as the Blend/ROP unit A DST input to
358                  * serve as a background color.
359                  */
360                 if (i == 0)
361                         ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
362
363                 /*
364                  * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D
365                  * in that order. In the BRU the Blend/ROP unit B SRC is
366                  * hardwired to the ROP unit output, the corresponding register
367                  * bits must be set to 0. The BRS has no ROP unit and doesn't
368                  * need any special processing.
369                  */
370                 if (!(entity->type == VSP1_ENTITY_BRU && i == 1))
371                         ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
372
373                 vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
374
375                 /*
376                  * Harcode the blending formula to
377                  *
378                  *      DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
379                  *      DSTa = DSTa * (1 - SRCa) + SRCa
380                  *
381                  * when the SRC input isn't premultiplied, and to
382                  *
383                  *      DSTc = DSTc * (1 - SRCa) + SRCc
384                  *      DSTa = DSTa * (1 - SRCa) + SRCa
385                  *
386                  * otherwise.
387                  */
388                 vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),
389                                VI6_BRU_BLD_CCMDX_255_SRC_A |
390                                (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
391                                                 VI6_BRU_BLD_CCMDY_SRC_A) |
392                                VI6_BRU_BLD_ACMDX_255_SRC_A |
393                                VI6_BRU_BLD_ACMDY_COEFY |
394                                (0xff << VI6_BRU_BLD_COEFY_SHIFT));
395         }
396 }
397
398 static const struct vsp1_entity_operations brx_entity_ops = {
399         .configure_stream = brx_configure_stream,
400 };
401
402 /* -----------------------------------------------------------------------------
403  * Initialization and Cleanup
404  */
405
406 struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
407                                  enum vsp1_entity_type type)
408 {
409         struct vsp1_brx *brx;
410         unsigned int num_pads;
411         const char *name;
412         int ret;
413
414         brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);
415         if (brx == NULL)
416                 return ERR_PTR(-ENOMEM);
417
418         brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
419         brx->entity.ops = &brx_entity_ops;
420         brx->entity.type = type;
421
422         if (type == VSP1_ENTITY_BRU) {
423                 num_pads = vsp1->info->num_bru_inputs + 1;
424                 name = "bru";
425         } else {
426                 num_pads = 3;
427                 name = "brs";
428         }
429
430         ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,
431                                MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
432         if (ret < 0)
433                 return ERR_PTR(ret);
434
435         /* Initialize the control handler. */
436         v4l2_ctrl_handler_init(&brx->ctrls, 1);
437         v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,
438                           0, 0xffffff, 1, 0);
439
440         brx->bgcolor = 0;
441
442         brx->entity.subdev.ctrl_handler = &brx->ctrls;
443
444         if (brx->ctrls.error) {
445                 dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
446                 ret = brx->ctrls.error;
447                 vsp1_entity_destroy(&brx->entity);
448                 return ERR_PTR(ret);
449         }
450
451         return brx;
452 }