brintos

brintos / linux-shallow public Read only

0
0
Text · 11.4 KiB · 5dee049 Raw
444 lines · 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 Corporation6 *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				1U23#define BRX_MAX_SIZE				8190U24 25/* -----------------------------------------------------------------------------26 * Device Access27 */28 29static 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 * Controls37 */38 39static 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 53static const struct v4l2_ctrl_ops brx_ctrl_ops = {54	.s_ctrl = brx_s_ctrl,55};56 57/* -----------------------------------------------------------------------------58 * V4L2 Subdevice Operations59 */60 61/*62 * The BRx can't perform format conversion, all sink and source formats must be63 * identical. We pick the format on the first sink pad (pad 0) and propagate it64 * to all other pads.65 */66 67static 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 80static 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 99static void brx_try_format(struct vsp1_brx *brx,100			   struct v4l2_subdev_state *sd_state,101			   unsigned int pad, struct v4l2_mbus_framefmt *fmt)102{103	struct v4l2_mbus_framefmt *format;104 105	switch (pad) {106	case BRX_PAD_SINK(0):107		/* Default to YUV if the requested format is not supported. */108		if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&109		    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)110			fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;111		break;112 113	default:114		/* The BRx can't perform format conversion. */115		format = v4l2_subdev_state_get_format(sd_state,116						      BRX_PAD_SINK(0));117		fmt->code = format->code;118		break;119	}120 121	fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE);122	fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE);123	fmt->field = V4L2_FIELD_NONE;124	fmt->colorspace = V4L2_COLORSPACE_SRGB;125}126 127static int brx_set_format(struct v4l2_subdev *subdev,128			  struct v4l2_subdev_state *sd_state,129			  struct v4l2_subdev_format *fmt)130{131	struct vsp1_brx *brx = to_brx(subdev);132	struct v4l2_subdev_state *state;133	struct v4l2_mbus_framefmt *format;134	int ret = 0;135 136	mutex_lock(&brx->entity.lock);137 138	state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which);139	if (!state) {140		ret = -EINVAL;141		goto done;142	}143 144	brx_try_format(brx, state, fmt->pad, &fmt->format);145 146	format = v4l2_subdev_state_get_format(state, fmt->pad);147	*format = fmt->format;148 149	/* Reset the compose rectangle. */150	if (fmt->pad != brx->entity.source_pad) {151		struct v4l2_rect *compose;152 153		compose = v4l2_subdev_state_get_compose(state, fmt->pad);154		compose->left = 0;155		compose->top = 0;156		compose->width = format->width;157		compose->height = format->height;158	}159 160	/* Propagate the format code to all pads. */161	if (fmt->pad == BRX_PAD_SINK(0)) {162		unsigned int i;163 164		for (i = 0; i <= brx->entity.source_pad; ++i) {165			format = v4l2_subdev_state_get_format(state, i);166			format->code = fmt->format.code;167		}168	}169 170done:171	mutex_unlock(&brx->entity.lock);172	return ret;173}174 175static int brx_get_selection(struct v4l2_subdev *subdev,176			     struct v4l2_subdev_state *sd_state,177			     struct v4l2_subdev_selection *sel)178{179	struct vsp1_brx *brx = to_brx(subdev);180	struct v4l2_subdev_state *state;181 182	if (sel->pad == brx->entity.source_pad)183		return -EINVAL;184 185	switch (sel->target) {186	case V4L2_SEL_TGT_COMPOSE_BOUNDS:187		sel->r.left = 0;188		sel->r.top = 0;189		sel->r.width = BRX_MAX_SIZE;190		sel->r.height = BRX_MAX_SIZE;191		return 0;192 193	case V4L2_SEL_TGT_COMPOSE:194		state = vsp1_entity_get_state(&brx->entity, sd_state,195					      sel->which);196		if (!state)197			return -EINVAL;198 199		mutex_lock(&brx->entity.lock);200		sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);201		mutex_unlock(&brx->entity.lock);202		return 0;203 204	default:205		return -EINVAL;206	}207}208 209static int brx_set_selection(struct v4l2_subdev *subdev,210			     struct v4l2_subdev_state *sd_state,211			     struct v4l2_subdev_selection *sel)212{213	struct vsp1_brx *brx = to_brx(subdev);214	struct v4l2_subdev_state *state;215	struct v4l2_mbus_framefmt *format;216	struct v4l2_rect *compose;217	int ret = 0;218 219	if (sel->pad == brx->entity.source_pad)220		return -EINVAL;221 222	if (sel->target != V4L2_SEL_TGT_COMPOSE)223		return -EINVAL;224 225	mutex_lock(&brx->entity.lock);226 227	state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which);228	if (!state) {229		ret = -EINVAL;230		goto done;231	}232 233	/*234	 * The compose rectangle top left corner must be inside the output235	 * frame.236	 */237	format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);238	sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);239	sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);240 241	/*242	 * Scaling isn't supported, the compose rectangle size must be identical243	 * to the sink format size.244	 */245	format = v4l2_subdev_state_get_format(state, sel->pad);246	sel->r.width = format->width;247	sel->r.height = format->height;248 249	compose = v4l2_subdev_state_get_compose(state, sel->pad);250	*compose = sel->r;251 252done:253	mutex_unlock(&brx->entity.lock);254	return ret;255}256 257static const struct v4l2_subdev_pad_ops brx_pad_ops = {258	.enum_mbus_code = brx_enum_mbus_code,259	.enum_frame_size = brx_enum_frame_size,260	.get_fmt = vsp1_subdev_get_pad_format,261	.set_fmt = brx_set_format,262	.get_selection = brx_get_selection,263	.set_selection = brx_set_selection,264};265 266static const struct v4l2_subdev_ops brx_ops = {267	.pad    = &brx_pad_ops,268};269 270/* -----------------------------------------------------------------------------271 * VSP1 Entity Operations272 */273 274static void brx_configure_stream(struct vsp1_entity *entity,275				 struct v4l2_subdev_state *state,276				 struct vsp1_pipeline *pipe,277				 struct vsp1_dl_list *dl,278				 struct vsp1_dl_body *dlb)279{280	struct vsp1_brx *brx = to_brx(&entity->subdev);281	struct v4l2_mbus_framefmt *format;282	unsigned int flags;283	unsigned int i;284 285	format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);286 287	/*288	 * The hardware is extremely flexible but we have no userspace API to289	 * expose all the parameters, nor is it clear whether we would have use290	 * cases for all the supported modes. Let's just hardcode the parameters291	 * to sane default values for now.292	 */293 294	/*295	 * Disable dithering and enable color data normalization unless the296	 * format at the pipeline output is premultiplied.297	 */298	flags = pipe->output ? pipe->output->format.flags : 0;299	vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,300		       flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?301		       0 : VI6_BRU_INCTRL_NRM);302 303	/*304	 * Set the background position to cover the whole output image and305	 * configure its color.306	 */307	vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,308		       (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |309		       (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));310	vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);311 312	vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |313		       (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));314 315	/*316	 * Route BRU input 1 as SRC input to the ROP unit and configure the ROP317	 * unit with a NOP operation to make BRU input 1 available as the318	 * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP319	 * unit.320	 */321	if (entity->type == VSP1_ENTITY_BRU)322		vsp1_brx_write(brx, dlb, VI6_BRU_ROP,323			       VI6_BRU_ROP_DSTSEL_BRUIN(1) |324			       VI6_BRU_ROP_CROP(VI6_ROP_NOP) |325			       VI6_BRU_ROP_AROP(VI6_ROP_NOP));326 327	for (i = 0; i < brx->entity.source_pad; ++i) {328		bool premultiplied = false;329		u32 ctrl = 0;330 331		/*332		 * Configure all Blend/ROP units corresponding to an enabled BRx333		 * input for alpha blending. Blend/ROP units corresponding to334		 * disabled BRx inputs are used in ROP NOP mode to ignore the335		 * SRC input.336		 */337		if (brx->inputs[i].rpf) {338			ctrl |= VI6_BRU_CTRL_RBC;339 340			premultiplied = brx->inputs[i].rpf->format.flags341				      & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;342		} else {343			ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)344			     |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP);345		}346 347		/*348		 * Select the virtual RPF as the Blend/ROP unit A DST input to349		 * serve as a background color.350		 */351		if (i == 0)352			ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;353 354		/*355		 * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D356		 * in that order. In the BRU the Blend/ROP unit B SRC is357		 * hardwired to the ROP unit output, the corresponding register358		 * bits must be set to 0. The BRS has no ROP unit and doesn't359		 * need any special processing.360		 */361		if (!(entity->type == VSP1_ENTITY_BRU && i == 1))362			ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);363 364		vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);365 366		/*367		 * Hardcode the blending formula to368		 *369		 *	DSTc = DSTc * (1 - SRCa) + SRCc * SRCa370		 *	DSTa = DSTa * (1 - SRCa) + SRCa371		 *372		 * when the SRC input isn't premultiplied, and to373		 *374		 *	DSTc = DSTc * (1 - SRCa) + SRCc375		 *	DSTa = DSTa * (1 - SRCa) + SRCa376		 *377		 * otherwise.378		 */379		vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),380			       VI6_BRU_BLD_CCMDX_255_SRC_A |381			       (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :382						VI6_BRU_BLD_CCMDY_SRC_A) |383			       VI6_BRU_BLD_ACMDX_255_SRC_A |384			       VI6_BRU_BLD_ACMDY_COEFY |385			       (0xff << VI6_BRU_BLD_COEFY_SHIFT));386	}387}388 389static const struct vsp1_entity_operations brx_entity_ops = {390	.configure_stream = brx_configure_stream,391};392 393/* -----------------------------------------------------------------------------394 * Initialization and Cleanup395 */396 397struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,398				 enum vsp1_entity_type type)399{400	struct vsp1_brx *brx;401	unsigned int num_pads;402	const char *name;403	int ret;404 405	brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);406	if (brx == NULL)407		return ERR_PTR(-ENOMEM);408 409	brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;410	brx->entity.ops = &brx_entity_ops;411	brx->entity.type = type;412 413	if (type == VSP1_ENTITY_BRU) {414		num_pads = vsp1->info->num_bru_inputs + 1;415		name = "bru";416	} else {417		num_pads = 3;418		name = "brs";419	}420 421	ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,422			       MEDIA_ENT_F_PROC_VIDEO_COMPOSER);423	if (ret < 0)424		return ERR_PTR(ret);425 426	/* Initialize the control handler. */427	v4l2_ctrl_handler_init(&brx->ctrls, 1);428	v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,429			  0, 0xffffff, 1, 0);430 431	brx->bgcolor = 0;432 433	brx->entity.subdev.ctrl_handler = &brx->ctrls;434 435	if (brx->ctrls.error) {436		dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);437		ret = brx->ctrls.error;438		vsp1_entity_destroy(&brx->entity);439		return ERR_PTR(ret);440	}441 442	return brx;443}444