GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / media / platform / renesas / 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_state *sd_state,
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, sd_state, code, codes,
77                                           ARRAY_SIZE(codes));
78 }
79
80 static int brx_enum_frame_size(struct v4l2_subdev *subdev,
81                                struct v4l2_subdev_state *sd_state,
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_state *sd_state,
101                                          unsigned int pad)
102 {
103         return v4l2_subdev_get_try_compose(&brx->entity.subdev, sd_state, pad);
104 }
105
106 static void brx_try_format(struct vsp1_brx *brx,
107                            struct v4l2_subdev_state *sd_state,
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, sd_state,
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_state *sd_state,
136                           struct v4l2_subdev_format *fmt)
137 {
138         struct vsp1_brx *brx = to_brx(subdev);
139         struct v4l2_subdev_state *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, sd_state,
146                                             fmt->which);
147         if (!config) {
148                 ret = -EINVAL;
149                 goto done;
150         }
151
152         brx_try_format(brx, config, fmt->pad, &fmt->format);
153
154         format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad);
155         *format = fmt->format;
156
157         /* Reset the compose rectangle. */
158         if (fmt->pad != brx->entity.source_pad) {
159                 struct v4l2_rect *compose;
160
161                 compose = brx_get_compose(brx, config, fmt->pad);
162                 compose->left = 0;
163                 compose->top = 0;
164                 compose->width = format->width;
165                 compose->height = format->height;
166         }
167
168         /* Propagate the format code to all pads. */
169         if (fmt->pad == BRX_PAD_SINK(0)) {
170                 unsigned int i;
171
172                 for (i = 0; i <= brx->entity.source_pad; ++i) {
173                         format = vsp1_entity_get_pad_format(&brx->entity,
174                                                             config, i);
175                         format->code = fmt->format.code;
176                 }
177         }
178
179 done:
180         mutex_unlock(&brx->entity.lock);
181         return ret;
182 }
183
184 static int brx_get_selection(struct v4l2_subdev *subdev,
185                              struct v4l2_subdev_state *sd_state,
186                              struct v4l2_subdev_selection *sel)
187 {
188         struct vsp1_brx *brx = to_brx(subdev);
189         struct v4l2_subdev_state *config;
190
191         if (sel->pad == brx->entity.source_pad)
192                 return -EINVAL;
193
194         switch (sel->target) {
195         case V4L2_SEL_TGT_COMPOSE_BOUNDS:
196                 sel->r.left = 0;
197                 sel->r.top = 0;
198                 sel->r.width = BRX_MAX_SIZE;
199                 sel->r.height = BRX_MAX_SIZE;
200                 return 0;
201
202         case V4L2_SEL_TGT_COMPOSE:
203                 config = vsp1_entity_get_pad_config(&brx->entity, sd_state,
204                                                     sel->which);
205                 if (!config)
206                         return -EINVAL;
207
208                 mutex_lock(&brx->entity.lock);
209                 sel->r = *brx_get_compose(brx, config, sel->pad);
210                 mutex_unlock(&brx->entity.lock);
211                 return 0;
212
213         default:
214                 return -EINVAL;
215         }
216 }
217
218 static int brx_set_selection(struct v4l2_subdev *subdev,
219                              struct v4l2_subdev_state *sd_state,
220                              struct v4l2_subdev_selection *sel)
221 {
222         struct vsp1_brx *brx = to_brx(subdev);
223         struct v4l2_subdev_state *config;
224         struct v4l2_mbus_framefmt *format;
225         struct v4l2_rect *compose;
226         int ret = 0;
227
228         if (sel->pad == brx->entity.source_pad)
229                 return -EINVAL;
230
231         if (sel->target != V4L2_SEL_TGT_COMPOSE)
232                 return -EINVAL;
233
234         mutex_lock(&brx->entity.lock);
235
236         config = vsp1_entity_get_pad_config(&brx->entity, sd_state,
237                                             sel->which);
238         if (!config) {
239                 ret = -EINVAL;
240                 goto done;
241         }
242
243         /*
244          * The compose rectangle top left corner must be inside the output
245          * frame.
246          */
247         format = vsp1_entity_get_pad_format(&brx->entity, config,
248                                             brx->entity.source_pad);
249         sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
250         sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
251
252         /*
253          * Scaling isn't supported, the compose rectangle size must be identical
254          * to the sink format size.
255          */
256         format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad);
257         sel->r.width = format->width;
258         sel->r.height = format->height;
259
260         compose = brx_get_compose(brx, config, sel->pad);
261         *compose = sel->r;
262
263 done:
264         mutex_unlock(&brx->entity.lock);
265         return ret;
266 }
267
268 static const struct v4l2_subdev_pad_ops brx_pad_ops = {
269         .init_cfg = vsp1_entity_init_cfg,
270         .enum_mbus_code = brx_enum_mbus_code,
271         .enum_frame_size = brx_enum_frame_size,
272         .get_fmt = vsp1_subdev_get_pad_format,
273         .set_fmt = brx_set_format,
274         .get_selection = brx_get_selection,
275         .set_selection = brx_set_selection,
276 };
277
278 static const struct v4l2_subdev_ops brx_ops = {
279         .pad    = &brx_pad_ops,
280 };
281
282 /* -----------------------------------------------------------------------------
283  * VSP1 Entity Operations
284  */
285
286 static void brx_configure_stream(struct vsp1_entity *entity,
287                                  struct vsp1_pipeline *pipe,
288                                  struct vsp1_dl_list *dl,
289                                  struct vsp1_dl_body *dlb)
290 {
291         struct vsp1_brx *brx = to_brx(&entity->subdev);
292         struct v4l2_mbus_framefmt *format;
293         unsigned int flags;
294         unsigned int i;
295
296         format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config,
297                                             brx->entity.source_pad);
298
299         /*
300          * The hardware is extremely flexible but we have no userspace API to
301          * expose all the parameters, nor is it clear whether we would have use
302          * cases for all the supported modes. Let's just hardcode the parameters
303          * to sane default values for now.
304          */
305
306         /*
307          * Disable dithering and enable color data normalization unless the
308          * format at the pipeline output is premultiplied.
309          */
310         flags = pipe->output ? pipe->output->format.flags : 0;
311         vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,
312                        flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
313                        0 : VI6_BRU_INCTRL_NRM);
314
315         /*
316          * Set the background position to cover the whole output image and
317          * configure its color.
318          */
319         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,
320                        (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
321                        (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
322         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);
323
324         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |
325                        (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
326
327         /*
328          * Route BRU input 1 as SRC input to the ROP unit and configure the ROP
329          * unit with a NOP operation to make BRU input 1 available as the
330          * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP
331          * unit.
332          */
333         if (entity->type == VSP1_ENTITY_BRU)
334                 vsp1_brx_write(brx, dlb, VI6_BRU_ROP,
335                                VI6_BRU_ROP_DSTSEL_BRUIN(1) |
336                                VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
337                                VI6_BRU_ROP_AROP(VI6_ROP_NOP));
338
339         for (i = 0; i < brx->entity.source_pad; ++i) {
340                 bool premultiplied = false;
341                 u32 ctrl = 0;
342
343                 /*
344                  * Configure all Blend/ROP units corresponding to an enabled BRx
345                  * input for alpha blending. Blend/ROP units corresponding to
346                  * disabled BRx inputs are used in ROP NOP mode to ignore the
347                  * SRC input.
348                  */
349                 if (brx->inputs[i].rpf) {
350                         ctrl |= VI6_BRU_CTRL_RBC;
351
352                         premultiplied = brx->inputs[i].rpf->format.flags
353                                       & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
354                 } else {
355                         ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
356                              |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
357                 }
358
359                 /*
360                  * Select the virtual RPF as the Blend/ROP unit A DST input to
361                  * serve as a background color.
362                  */
363                 if (i == 0)
364                         ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
365
366                 /*
367                  * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D
368                  * in that order. In the BRU the Blend/ROP unit B SRC is
369                  * hardwired to the ROP unit output, the corresponding register
370                  * bits must be set to 0. The BRS has no ROP unit and doesn't
371                  * need any special processing.
372                  */
373                 if (!(entity->type == VSP1_ENTITY_BRU && i == 1))
374                         ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
375
376                 vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
377
378                 /*
379                  * Hardcode the blending formula to
380                  *
381                  *      DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
382                  *      DSTa = DSTa * (1 - SRCa) + SRCa
383                  *
384                  * when the SRC input isn't premultiplied, and to
385                  *
386                  *      DSTc = DSTc * (1 - SRCa) + SRCc
387                  *      DSTa = DSTa * (1 - SRCa) + SRCa
388                  *
389                  * otherwise.
390                  */
391                 vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),
392                                VI6_BRU_BLD_CCMDX_255_SRC_A |
393                                (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
394                                                 VI6_BRU_BLD_CCMDY_SRC_A) |
395                                VI6_BRU_BLD_ACMDX_255_SRC_A |
396                                VI6_BRU_BLD_ACMDY_COEFY |
397                                (0xff << VI6_BRU_BLD_COEFY_SHIFT));
398         }
399 }
400
401 static const struct vsp1_entity_operations brx_entity_ops = {
402         .configure_stream = brx_configure_stream,
403 };
404
405 /* -----------------------------------------------------------------------------
406  * Initialization and Cleanup
407  */
408
409 struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
410                                  enum vsp1_entity_type type)
411 {
412         struct vsp1_brx *brx;
413         unsigned int num_pads;
414         const char *name;
415         int ret;
416
417         brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);
418         if (brx == NULL)
419                 return ERR_PTR(-ENOMEM);
420
421         brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
422         brx->entity.ops = &brx_entity_ops;
423         brx->entity.type = type;
424
425         if (type == VSP1_ENTITY_BRU) {
426                 num_pads = vsp1->info->num_bru_inputs + 1;
427                 name = "bru";
428         } else {
429                 num_pads = 3;
430                 name = "brs";
431         }
432
433         ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,
434                                MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
435         if (ret < 0)
436                 return ERR_PTR(ret);
437
438         /* Initialize the control handler. */
439         v4l2_ctrl_handler_init(&brx->ctrls, 1);
440         v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,
441                           0, 0xffffff, 1, 0);
442
443         brx->bgcolor = 0;
444
445         brx->entity.subdev.ctrl_handler = &brx->ctrls;
446
447         if (brx->ctrls.error) {
448                 dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
449                 ret = brx->ctrls.error;
450                 vsp1_entity_destroy(&brx->entity);
451                 return ERR_PTR(ret);
452         }
453
454         return brx;
455 }