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