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