brintos

brintos / linux-shallow public Read only

0
0
Text · 11.0 KiB · c5a3847 Raw
415 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * vsp1_uds.c  --  R-Car VSP1 Up and Down Scaler4 *5 * Copyright (C) 2013-2014 Renesas Electronics 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_dl.h"17#include "vsp1_pipe.h"18#include "vsp1_uds.h"19 20#define UDS_MIN_SIZE				4U21#define UDS_MAX_SIZE				8190U22 23#define UDS_MIN_FACTOR				0x010024#define UDS_MAX_FACTOR				0xffff25 26/* -----------------------------------------------------------------------------27 * Device Access28 */29 30static inline void vsp1_uds_write(struct vsp1_uds *uds,31				  struct vsp1_dl_body *dlb, u32 reg, u32 data)32{33	vsp1_dl_body_write(dlb, reg + uds->entity.index * VI6_UDS_OFFSET, data);34}35 36/* -----------------------------------------------------------------------------37 * Scaling Computation38 */39 40void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_body *dlb,41			unsigned int alpha)42{43	struct vsp1_uds *uds = to_uds(&entity->subdev);44 45	vsp1_uds_write(uds, dlb, VI6_UDS_ALPVAL,46		       alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);47}48 49/*50 * uds_output_size - Return the output size for an input size and scaling ratio51 * @input: input size in pixels52 * @ratio: scaling ratio in U4.12 fixed-point format53 */54static unsigned int uds_output_size(unsigned int input, unsigned int ratio)55{56	if (ratio > 4096) {57		/* Down-scaling */58		unsigned int mp;59 60		mp = ratio / 4096;61		mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);62 63		return (input - 1) / mp * mp * 4096 / ratio + 1;64	} else {65		/* Up-scaling */66		return (input - 1) * 4096 / ratio + 1;67	}68}69 70/*71 * uds_output_limits - Return the min and max output sizes for an input size72 * @input: input size in pixels73 * @minimum: minimum output size (returned)74 * @maximum: maximum output size (returned)75 */76static void uds_output_limits(unsigned int input,77			      unsigned int *minimum, unsigned int *maximum)78{79	*minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE);80	*maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE);81}82 83/*84 * uds_passband_width - Return the passband filter width for a scaling ratio85 * @ratio: scaling ratio in U4.12 fixed-point format86 */87static unsigned int uds_passband_width(unsigned int ratio)88{89	if (ratio >= 4096) {90		/* Down-scaling */91		unsigned int mp;92 93		mp = ratio / 4096;94		mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);95 96		return 64 * 4096 * mp / ratio;97	} else {98		/* Up-scaling */99		return 64;100	}101}102 103static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)104{105	/* TODO: This is an approximation that will need to be refined. */106	return (input - 1) * 4096 / (output - 1);107}108 109/* -----------------------------------------------------------------------------110 * V4L2 Subdevice Pad Operations111 */112 113static int uds_enum_mbus_code(struct v4l2_subdev *subdev,114			      struct v4l2_subdev_state *sd_state,115			      struct v4l2_subdev_mbus_code_enum *code)116{117	static const unsigned int codes[] = {118		MEDIA_BUS_FMT_ARGB8888_1X32,119		MEDIA_BUS_FMT_AYUV8_1X32,120	};121 122	return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes,123					  ARRAY_SIZE(codes));124}125 126static int uds_enum_frame_size(struct v4l2_subdev *subdev,127			       struct v4l2_subdev_state *sd_state,128			       struct v4l2_subdev_frame_size_enum *fse)129{130	struct vsp1_uds *uds = to_uds(subdev);131	struct v4l2_subdev_state *state;132	struct v4l2_mbus_framefmt *format;133	int ret = 0;134 135	state = vsp1_entity_get_state(&uds->entity, sd_state, fse->which);136	if (!state)137		return -EINVAL;138 139	format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);140 141	mutex_lock(&uds->entity.lock);142 143	if (fse->index || fse->code != format->code) {144		ret = -EINVAL;145		goto done;146	}147 148	if (fse->pad == UDS_PAD_SINK) {149		fse->min_width = UDS_MIN_SIZE;150		fse->max_width = UDS_MAX_SIZE;151		fse->min_height = UDS_MIN_SIZE;152		fse->max_height = UDS_MAX_SIZE;153	} else {154		uds_output_limits(format->width, &fse->min_width,155				  &fse->max_width);156		uds_output_limits(format->height, &fse->min_height,157				  &fse->max_height);158	}159 160done:161	mutex_unlock(&uds->entity.lock);162	return ret;163}164 165static void uds_try_format(struct vsp1_uds *uds,166			   struct v4l2_subdev_state *sd_state,167			   unsigned int pad, struct v4l2_mbus_framefmt *fmt)168{169	struct v4l2_mbus_framefmt *format;170	unsigned int minimum;171	unsigned int maximum;172 173	switch (pad) {174	case UDS_PAD_SINK:175		/* Default to YUV if the requested format is not supported. */176		if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&177		    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)178			fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;179 180		fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);181		fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);182		break;183 184	case UDS_PAD_SOURCE:185		/* The UDS scales but can't perform format conversion. */186		format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK);187		fmt->code = format->code;188 189		uds_output_limits(format->width, &minimum, &maximum);190		fmt->width = clamp(fmt->width, minimum, maximum);191		uds_output_limits(format->height, &minimum, &maximum);192		fmt->height = clamp(fmt->height, minimum, maximum);193		break;194	}195 196	fmt->field = V4L2_FIELD_NONE;197	fmt->colorspace = V4L2_COLORSPACE_SRGB;198}199 200static int uds_set_format(struct v4l2_subdev *subdev,201			  struct v4l2_subdev_state *sd_state,202			  struct v4l2_subdev_format *fmt)203{204	struct vsp1_uds *uds = to_uds(subdev);205	struct v4l2_subdev_state *state;206	struct v4l2_mbus_framefmt *format;207	int ret = 0;208 209	mutex_lock(&uds->entity.lock);210 211	state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which);212	if (!state) {213		ret = -EINVAL;214		goto done;215	}216 217	uds_try_format(uds, state, fmt->pad, &fmt->format);218 219	format = v4l2_subdev_state_get_format(state, fmt->pad);220	*format = fmt->format;221 222	if (fmt->pad == UDS_PAD_SINK) {223		/* Propagate the format to the source pad. */224		format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);225		*format = fmt->format;226 227		uds_try_format(uds, state, UDS_PAD_SOURCE, format);228	}229 230done:231	mutex_unlock(&uds->entity.lock);232	return ret;233}234 235/* -----------------------------------------------------------------------------236 * V4L2 Subdevice Operations237 */238 239static const struct v4l2_subdev_pad_ops uds_pad_ops = {240	.enum_mbus_code = uds_enum_mbus_code,241	.enum_frame_size = uds_enum_frame_size,242	.get_fmt = vsp1_subdev_get_pad_format,243	.set_fmt = uds_set_format,244};245 246static const struct v4l2_subdev_ops uds_ops = {247	.pad    = &uds_pad_ops,248};249 250/* -----------------------------------------------------------------------------251 * VSP1 Entity Operations252 */253 254static void uds_configure_stream(struct vsp1_entity *entity,255				 struct v4l2_subdev_state *state,256				 struct vsp1_pipeline *pipe,257				 struct vsp1_dl_list *dl,258				 struct vsp1_dl_body *dlb)259{260	struct vsp1_uds *uds = to_uds(&entity->subdev);261	const struct v4l2_mbus_framefmt *output;262	const struct v4l2_mbus_framefmt *input;263	unsigned int hscale;264	unsigned int vscale;265	bool multitap;266 267	input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);268	output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);269 270	hscale = uds_compute_ratio(input->width, output->width);271	vscale = uds_compute_ratio(input->height, output->height);272 273	dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale);274 275	/*276	 * Multi-tap scaling can't be enabled along with alpha scaling when277	 * scaling down with a factor lower than or equal to 1/2 in either278	 * direction.279	 */280	if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192))281		multitap = false;282	else283		multitap = true;284 285	vsp1_uds_write(uds, dlb, VI6_UDS_CTRL,286		       (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |287		       (multitap ? VI6_UDS_CTRL_BC : 0));288 289	vsp1_uds_write(uds, dlb, VI6_UDS_PASS_BWIDTH,290		       (uds_passband_width(hscale)291				<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |292		       (uds_passband_width(vscale)293				<< VI6_UDS_PASS_BWIDTH_V_SHIFT));294 295	/* Set the scaling ratios. */296	vsp1_uds_write(uds, dlb, VI6_UDS_SCALE,297		       (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |298		       (vscale << VI6_UDS_SCALE_VFRAC_SHIFT));299}300 301static void uds_configure_partition(struct vsp1_entity *entity,302				    struct vsp1_pipeline *pipe,303				    const struct vsp1_partition *partition,304				    struct vsp1_dl_list *dl,305				    struct vsp1_dl_body *dlb)306{307	struct vsp1_uds *uds = to_uds(&entity->subdev);308 309	/* Input size clipping. */310	vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |311		       (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) |312		       (partition->uds_sink.width313				<< VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT));314 315	/* Output size clipping. */316	vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE,317		       (partition->uds_source.width318				<< VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |319		       (partition->uds_source.height320				<< VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));321}322 323static unsigned int uds_max_width(struct vsp1_entity *entity,324				  struct v4l2_subdev_state *state,325				  struct vsp1_pipeline *pipe)326{327	const struct v4l2_mbus_framefmt *output;328	const struct v4l2_mbus_framefmt *input;329	unsigned int hscale;330 331	input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);332	output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);333	hscale = output->width / input->width;334 335	/*336	 * The maximum width of the UDS is 304 pixels. These are input pixels337	 * in the event of up-scaling, and output pixels in the event of338	 * downscaling.339	 *340	 * To support overlapping partition windows we clamp at units of 256 and341	 * the remaining pixels are reserved.342	 */343	if (hscale <= 2)344		return 256;345	else if (hscale <= 4)346		return 512;347	else if (hscale <= 8)348		return 1024;349	else350		return 2048;351}352 353/* -----------------------------------------------------------------------------354 * Partition Algorithm Support355 */356 357static void uds_partition(struct vsp1_entity *entity,358			  struct v4l2_subdev_state *state,359			  struct vsp1_pipeline *pipe,360			  struct vsp1_partition *partition,361			  unsigned int partition_idx,362			  struct v4l2_rect *window)363{364	const struct v4l2_mbus_framefmt *output;365	const struct v4l2_mbus_framefmt *input;366 367	input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);368	output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);369 370	partition->uds_sink.width = window->width * input->width371				  / output->width;372	partition->uds_sink.left = window->left * input->width373				 / output->width;374	partition->uds_sink.height = input->height;375	partition->uds_sink.top = 0;376 377	partition->uds_source = *window;378 379	*window = partition->uds_sink;380}381 382static const struct vsp1_entity_operations uds_entity_ops = {383	.configure_stream = uds_configure_stream,384	.configure_partition = uds_configure_partition,385	.max_width = uds_max_width,386	.partition = uds_partition,387};388 389/* -----------------------------------------------------------------------------390 * Initialization and Cleanup391 */392 393struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)394{395	struct vsp1_uds *uds;396	char name[6];397	int ret;398 399	uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL);400	if (uds == NULL)401		return ERR_PTR(-ENOMEM);402 403	uds->entity.ops = &uds_entity_ops;404	uds->entity.type = VSP1_ENTITY_UDS;405	uds->entity.index = index;406 407	sprintf(name, "uds.%u", index);408	ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops,409			       MEDIA_ENT_F_PROC_VIDEO_SCALER);410	if (ret < 0)411		return ERR_PTR(ret);412 413	return uds;414}415