]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/media/platform/vsp1/vsp1_brx.c
Merge tag 'io_uring-5.6-2020-03-07' of git://git.kernel.dk/linux-block
[linux.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_list *dl,
287                                  struct vsp1_dl_body *dlb)
288 {
289         struct vsp1_brx *brx = to_brx(&entity->subdev);
290         struct v4l2_mbus_framefmt *format;
291         unsigned int flags;
292         unsigned int i;
293
294         format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config,
295                                             brx->entity.source_pad);
296
297         /*
298          * The hardware is extremely flexible but we have no userspace API to
299          * expose all the parameters, nor is it clear whether we would have use
300          * cases for all the supported modes. Let's just hardcode the parameters
301          * to sane default values for now.
302          */
303
304         /*
305          * Disable dithering and enable color data normalization unless the
306          * format at the pipeline output is premultiplied.
307          */
308         flags = pipe->output ? pipe->output->format.flags : 0;
309         vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,
310                        flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
311                        0 : VI6_BRU_INCTRL_NRM);
312
313         /*
314          * Set the background position to cover the whole output image and
315          * configure its color.
316          */
317         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,
318                        (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
319                        (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
320         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);
321
322         vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |
323                        (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
324
325         /*
326          * Route BRU input 1 as SRC input to the ROP unit and configure the ROP
327          * unit with a NOP operation to make BRU input 1 available as the
328          * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP
329          * unit.
330          */
331         if (entity->type == VSP1_ENTITY_BRU)
332                 vsp1_brx_write(brx, dlb, VI6_BRU_ROP,
333                                VI6_BRU_ROP_DSTSEL_BRUIN(1) |
334                                VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
335                                VI6_BRU_ROP_AROP(VI6_ROP_NOP));
336
337         for (i = 0; i < brx->entity.source_pad; ++i) {
338                 bool premultiplied = false;
339                 u32 ctrl = 0;
340
341                 /*
342                  * Configure all Blend/ROP units corresponding to an enabled BRx
343                  * input for alpha blending. Blend/ROP units corresponding to
344                  * disabled BRx inputs are used in ROP NOP mode to ignore the
345                  * SRC input.
346                  */
347                 if (brx->inputs[i].rpf) {
348                         ctrl |= VI6_BRU_CTRL_RBC;
349
350                         premultiplied = brx->inputs[i].rpf->format.flags
351                                       & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
352                 } else {
353                         ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
354                              |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
355                 }
356
357                 /*
358                  * Select the virtual RPF as the Blend/ROP unit A DST input to
359                  * serve as a background color.
360                  */
361                 if (i == 0)
362                         ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
363
364                 /*
365                  * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D
366                  * in that order. In the BRU the Blend/ROP unit B SRC is
367                  * hardwired to the ROP unit output, the corresponding register
368                  * bits must be set to 0. The BRS has no ROP unit and doesn't
369                  * need any special processing.
370                  */
371                 if (!(entity->type == VSP1_ENTITY_BRU && i == 1))
372                         ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
373
374                 vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
375
376                 /*
377                  * Hardcode the blending formula to
378                  *
379                  *      DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
380                  *      DSTa = DSTa * (1 - SRCa) + SRCa
381                  *
382                  * when the SRC input isn't premultiplied, and to
383                  *
384                  *      DSTc = DSTc * (1 - SRCa) + SRCc
385                  *      DSTa = DSTa * (1 - SRCa) + SRCa
386                  *
387                  * otherwise.
388                  */
389                 vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),
390                                VI6_BRU_BLD_CCMDX_255_SRC_A |
391                                (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
392                                                 VI6_BRU_BLD_CCMDY_SRC_A) |
393                                VI6_BRU_BLD_ACMDX_255_SRC_A |
394                                VI6_BRU_BLD_ACMDY_COEFY |
395                                (0xff << VI6_BRU_BLD_COEFY_SHIFT));
396         }
397 }
398
399 static const struct vsp1_entity_operations brx_entity_ops = {
400         .configure_stream = brx_configure_stream,
401 };
402
403 /* -----------------------------------------------------------------------------
404  * Initialization and Cleanup
405  */
406
407 struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
408                                  enum vsp1_entity_type type)
409 {
410         struct vsp1_brx *brx;
411         unsigned int num_pads;
412         const char *name;
413         int ret;
414
415         brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);
416         if (brx == NULL)
417                 return ERR_PTR(-ENOMEM);
418
419         brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
420         brx->entity.ops = &brx_entity_ops;
421         brx->entity.type = type;
422
423         if (type == VSP1_ENTITY_BRU) {
424                 num_pads = vsp1->info->num_bru_inputs + 1;
425                 name = "bru";
426         } else {
427                 num_pads = 3;
428                 name = "brs";
429         }
430
431         ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,
432                                MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
433         if (ret < 0)
434                 return ERR_PTR(ret);
435
436         /* Initialize the control handler. */
437         v4l2_ctrl_handler_init(&brx->ctrls, 1);
438         v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,
439                           0, 0xffffff, 1, 0);
440
441         brx->bgcolor = 0;
442
443         brx->entity.subdev.ctrl_handler = &brx->ctrls;
444
445         if (brx->ctrls.error) {
446                 dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
447                 ret = brx->ctrls.error;
448                 vsp1_entity_destroy(&brx->entity);
449                 return ERR_PTR(ret);
450         }
451
452         return brx;
453 }