2 * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit
4 * Copyright (C) 2013 Renesas Corporation
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
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.
14 #include <linux/device.h>
15 #include <linux/gfp.h>
17 #include <media/v4l2-subdev.h>
22 #define BRU_MIN_SIZE 4U
23 #define BRU_MAX_SIZE 8190U
25 /* -----------------------------------------------------------------------------
29 static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg)
31 return vsp1_read(bru->entity.vsp1, reg);
34 static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
36 vsp1_write(bru->entity.vsp1, reg, data);
39 /* -----------------------------------------------------------------------------
40 * V4L2 Subdevice Core Operations
43 static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input)
45 return media_entity_remote_pad(&bru->entity.pads[input]) != NULL;
48 static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
50 struct vsp1_bru *bru = to_bru(subdev);
51 struct v4l2_mbus_framefmt *format;
57 format = &bru->entity.formats[BRU_PAD_SOURCE];
59 /* The hardware is extremely flexible but we have no userspace API to
60 * expose all the parameters, nor is it clear whether we would have use
61 * cases for all the supported modes. Let's just harcode the parameters
62 * to sane default values for now.
65 /* Disable both color data normalization and dithering. */
66 vsp1_bru_write(bru, VI6_BRU_INCTRL, 0);
68 /* Set the background position to cover the whole output image and
69 * set its color to opaque black.
71 vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE,
72 (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
73 (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
74 vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0);
75 vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL,
76 0xff << VI6_BRU_VIRRPF_COL_A_SHIFT);
78 /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
79 * unit with a NOP operation to make BRU input 1 available as the
80 * Blend/ROP unit B SRC input.
82 vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
83 VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
84 VI6_BRU_ROP_AROP(VI6_ROP_NOP));
86 for (i = 0; i < 4; ++i) {
89 /* Configure all Blend/ROP units corresponding to an enabled BRU
90 * input for alpha blending. Blend/ROP units corresponding to
91 * disabled BRU inputs are used in ROP NOP mode to ignore the
94 if (bru_is_input_enabled(bru, i))
95 ctrl |= VI6_BRU_CTRL_RBC;
97 ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
98 | VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
100 /* Select the virtual RPF as the Blend/ROP unit A DST input to
101 * serve as a background color.
104 ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
106 /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to
107 * D in that order. The Blend/ROP unit B SRC is hardwired to the
108 * ROP unit output, the corresponding register bits must be set
112 ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
114 vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl);
116 /* Harcode the blending formula to
118 * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
119 * DSTa = DSTa * (1 - SRCa) + SRCa
121 vsp1_bru_write(bru, VI6_BRU_BLD(i),
122 VI6_BRU_BLD_CCMDX_255_SRC_A |
123 VI6_BRU_BLD_CCMDY_SRC_A |
124 VI6_BRU_BLD_ACMDX_255_SRC_A |
125 VI6_BRU_BLD_ACMDY_COEFY |
126 (0xff << VI6_BRU_BLD_COEFY_SHIFT));
132 /* -----------------------------------------------------------------------------
133 * V4L2 Subdevice Pad Operations
137 * The BRU can't perform format conversion, all sink and source formats must be
138 * identical. We pick the format on the first sink pad (pad 0) and propagate it
142 static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
143 struct v4l2_subdev_fh *fh,
144 struct v4l2_subdev_mbus_code_enum *code)
146 static const unsigned int codes[] = {
147 V4L2_MBUS_FMT_ARGB8888_1X32,
148 V4L2_MBUS_FMT_AYUV8_1X32,
150 struct v4l2_mbus_framefmt *format;
152 if (code->pad == BRU_PAD_SINK(0)) {
153 if (code->index >= ARRAY_SIZE(codes))
156 code->code = codes[code->index];
161 format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0));
162 code->code = format->code;
168 static int bru_enum_frame_size(struct v4l2_subdev *subdev,
169 struct v4l2_subdev_fh *fh,
170 struct v4l2_subdev_frame_size_enum *fse)
175 if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
176 fse->code != V4L2_MBUS_FMT_AYUV8_1X32)
179 fse->min_width = BRU_MIN_SIZE;
180 fse->max_width = BRU_MAX_SIZE;
181 fse->min_height = BRU_MIN_SIZE;
182 fse->max_height = BRU_MAX_SIZE;
187 static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
188 struct v4l2_subdev_fh *fh,
189 unsigned int pad, u32 which)
192 case V4L2_SUBDEV_FORMAT_TRY:
193 return v4l2_subdev_get_try_crop(fh, pad);
194 case V4L2_SUBDEV_FORMAT_ACTIVE:
195 return &bru->compose[pad];
201 static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
202 struct v4l2_subdev_format *fmt)
204 struct vsp1_bru *bru = to_bru(subdev);
206 fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
212 static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh,
213 unsigned int pad, struct v4l2_mbus_framefmt *fmt,
214 enum v4l2_subdev_format_whence which)
216 struct v4l2_mbus_framefmt *format;
219 case BRU_PAD_SINK(0):
220 /* Default to YUV if the requested format is not supported. */
221 if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
222 fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
223 fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
227 /* The BRU can't perform format conversion. */
228 format = vsp1_entity_get_pad_format(&bru->entity, fh,
229 BRU_PAD_SINK(0), which);
230 fmt->code = format->code;
234 fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
235 fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
236 fmt->field = V4L2_FIELD_NONE;
237 fmt->colorspace = V4L2_COLORSPACE_SRGB;
240 static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
241 struct v4l2_subdev_format *fmt)
243 struct vsp1_bru *bru = to_bru(subdev);
244 struct v4l2_mbus_framefmt *format;
246 bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which);
248 format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
250 *format = fmt->format;
252 /* Reset the compose rectangle */
253 if (fmt->pad != BRU_PAD_SOURCE) {
254 struct v4l2_rect *compose;
256 compose = bru_get_compose(bru, fh, fmt->pad, fmt->which);
259 compose->width = format->width;
260 compose->height = format->height;
263 /* Propagate the format code to all pads */
264 if (fmt->pad == BRU_PAD_SINK(0)) {
267 for (i = 0; i <= BRU_PAD_SOURCE; ++i) {
268 format = vsp1_entity_get_pad_format(&bru->entity, fh,
270 format->code = fmt->format.code;
277 static int bru_get_selection(struct v4l2_subdev *subdev,
278 struct v4l2_subdev_fh *fh,
279 struct v4l2_subdev_selection *sel)
281 struct vsp1_bru *bru = to_bru(subdev);
283 if (sel->pad == BRU_PAD_SOURCE)
286 switch (sel->target) {
287 case V4L2_SEL_TGT_COMPOSE_BOUNDS:
290 sel->r.width = BRU_MAX_SIZE;
291 sel->r.height = BRU_MAX_SIZE;
294 case V4L2_SEL_TGT_COMPOSE:
295 sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which);
303 static int bru_set_selection(struct v4l2_subdev *subdev,
304 struct v4l2_subdev_fh *fh,
305 struct v4l2_subdev_selection *sel)
307 struct vsp1_bru *bru = to_bru(subdev);
308 struct v4l2_mbus_framefmt *format;
309 struct v4l2_rect *compose;
311 if (sel->pad == BRU_PAD_SOURCE)
314 if (sel->target != V4L2_SEL_TGT_COMPOSE)
317 /* The compose rectangle top left corner must be inside the output
320 format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE,
322 sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
323 sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
325 /* Scaling isn't supported, the compose rectangle size must be identical
326 * to the sink format size.
328 format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad,
330 sel->r.width = format->width;
331 sel->r.height = format->height;
333 compose = bru_get_compose(bru, fh, sel->pad, sel->which);
339 /* -----------------------------------------------------------------------------
340 * V4L2 Subdevice Operations
343 static struct v4l2_subdev_video_ops bru_video_ops = {
344 .s_stream = bru_s_stream,
347 static struct v4l2_subdev_pad_ops bru_pad_ops = {
348 .enum_mbus_code = bru_enum_mbus_code,
349 .enum_frame_size = bru_enum_frame_size,
350 .get_fmt = bru_get_format,
351 .set_fmt = bru_set_format,
352 .get_selection = bru_get_selection,
353 .set_selection = bru_set_selection,
356 static struct v4l2_subdev_ops bru_ops = {
357 .video = &bru_video_ops,
361 /* -----------------------------------------------------------------------------
362 * Initialization and Cleanup
365 struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
367 struct v4l2_subdev *subdev;
368 struct vsp1_bru *bru;
371 bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
373 return ERR_PTR(-ENOMEM);
375 bru->entity.type = VSP1_ENTITY_BRU;
377 ret = vsp1_entity_init(vsp1, &bru->entity, 5);
381 /* Initialize the V4L2 subdev. */
382 subdev = &bru->entity.subdev;
383 v4l2_subdev_init(subdev, &bru_ops);
385 subdev->entity.ops = &vsp1_media_ops;
386 subdev->internal_ops = &vsp1_subdev_internal_ops;
387 snprintf(subdev->name, sizeof(subdev->name), "%s bru",
388 dev_name(vsp1->dev));
389 v4l2_set_subdevdata(subdev, bru);
390 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
392 vsp1_entity_init_formats(subdev, NULL);