File Index
Files and directories
Sign in
FILE

vsp1_sru.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/renesas/vsp1/vsp1_sru.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * vsp1_sru.c  --  R-Car VSP1 Super Resolution Unit
  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_dl.h"
 17#include "vsp1_entity.h"
 18#include "vsp1_pipe.h"
 19#include "vsp1_sru.h"
 20
 21#define SRU_MIN_SIZE				4U
 22#define SRU_MAX_SIZE				8190U
 23
 24/* -----------------------------------------------------------------------------
 25 * Device Access
 26 */
 27
 28static inline void vsp1_sru_write(struct vsp1_sru *sru,
 29				  struct vsp1_dl_body *dlb, u32 reg, u32 data)
 30{
 31	vsp1_dl_body_write(dlb, reg, data);
 32}
 33
 34/* -----------------------------------------------------------------------------
 35 * Controls
 36 */
 37
 38#define V4L2_CID_VSP1_SRU_INTENSITY		(V4L2_CID_USER_BASE | 0x1001)
 39
 40struct vsp1_sru_param {
 41	u32 ctrl0;
 42	u32 ctrl2;
 43};
 44
 45#define VI6_SRU_CTRL0_PARAMS(p0, p1)			\
 46	(((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) |		\
 47	 ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
 48
 49#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8)		\
 50	(((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) |		\
 51	 ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) |		\
 52	 ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
 53
 54static const struct vsp1_sru_param vsp1_sru_params[] = {
 55	{
 56		.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
 57		.ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255),
 58	}, {
 59		.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
 60		.ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255),
 61	}, {
 62		.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
 63		.ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255),
 64	}, {
 65		.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
 66		.ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255),
 67	}, {
 68		.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
 69		.ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255),
 70	}, {
 71		.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
 72		.ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255),
 73	},
 74};
 75
 76static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
 77{
 78	struct vsp1_sru *sru =
 79		container_of(ctrl->handler, struct vsp1_sru, ctrls);
 80
 81	switch (ctrl->id) {
 82	case V4L2_CID_VSP1_SRU_INTENSITY:
 83		sru->intensity = ctrl->val;
 84		break;
 85	}
 86
 87	return 0;
 88}
 89
 90static const struct v4l2_ctrl_ops sru_ctrl_ops = {
 91	.s_ctrl = sru_s_ctrl,
 92};
 93
 94static const struct v4l2_ctrl_config sru_intensity_control = {
 95	.ops = &sru_ctrl_ops,
 96	.id = V4L2_CID_VSP1_SRU_INTENSITY,
 97	.name = "Intensity",
 98	.type = V4L2_CTRL_TYPE_INTEGER,
 99	.min = 1,
100	.max = 6,
101	.def = 1,
102	.step = 1,
103};
104
105/* -----------------------------------------------------------------------------
106 * V4L2 Subdevice Operations
107 */
108
109static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
110			      struct v4l2_subdev_state *sd_state,
111			      struct v4l2_subdev_mbus_code_enum *code)
112{
113	static const unsigned int codes[] = {
114		MEDIA_BUS_FMT_ARGB8888_1X32,
115		MEDIA_BUS_FMT_AYUV8_1X32,
116	};
117
118	return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes,
119					  ARRAY_SIZE(codes));
120}
121
122static int sru_enum_frame_size(struct v4l2_subdev *subdev,
123			       struct v4l2_subdev_state *sd_state,
124			       struct v4l2_subdev_frame_size_enum *fse)
125{
126	struct vsp1_sru *sru = to_sru(subdev);
127	struct v4l2_subdev_state *state;
128	struct v4l2_mbus_framefmt *format;
129	int ret = 0;
130
131	state = vsp1_entity_get_state(&sru->entity, sd_state, fse->which);
132	if (!state)
133		return -EINVAL;
134
135	format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
136
137	mutex_lock(&sru->entity.lock);
138
139	if (fse->index || fse->code != format->code) {
140		ret = -EINVAL;
141		goto done;
142	}
143
144	if (fse->pad == SRU_PAD_SINK) {
145		fse->min_width = SRU_MIN_SIZE;
146		fse->max_width = SRU_MAX_SIZE;
147		fse->min_height = SRU_MIN_SIZE;
148		fse->max_height = SRU_MAX_SIZE;
149	} else {
150		fse->min_width = format->width;
151		fse->min_height = format->height;
152		if (format->width <= SRU_MAX_SIZE / 2 &&
153		    format->height <= SRU_MAX_SIZE / 2) {
154			fse->max_width = format->width * 2;
155			fse->max_height = format->height * 2;
156		} else {
157			fse->max_width = format->width;
158			fse->max_height = format->height;
159		}
160	}
161
162done:
163	mutex_unlock(&sru->entity.lock);
164	return ret;
165}
166
167static void sru_try_format(struct vsp1_sru *sru,
168			   struct v4l2_subdev_state *sd_state,
169			   unsigned int pad, struct v4l2_mbus_framefmt *fmt)
170{
171	struct v4l2_mbus_framefmt *format;
172	unsigned int input_area;
173	unsigned int output_area;
174
175	switch (pad) {
176	case SRU_PAD_SINK:
177		/* Default to YUV if the requested format is not supported. */
178		if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
179		    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
180			fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
181
182		vsp1_entity_adjust_color_space(fmt);
183
184		fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
185		fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
186		break;
187
188	case SRU_PAD_SOURCE:
189		/* The SRU can't perform format conversion. */
190		format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK);
191		fmt->code = format->code;
192
193		fmt->colorspace = format->colorspace;
194		fmt->xfer_func = format->xfer_func;
195		fmt->ycbcr_enc = format->ycbcr_enc;
196		fmt->quantization = format->quantization;
197
198		/*
199		 * We can upscale by 2 in both direction, but not independently.
200		 * Compare the input and output rectangles areas (avoiding
201		 * integer overflows on the output): if the requested output
202		 * area is larger than 1.5^2 the input area upscale by two,
203		 * otherwise don't scale.
204		 */
205		input_area = format->width * format->height;
206		output_area = min(fmt->width, SRU_MAX_SIZE)
207			    * min(fmt->height, SRU_MAX_SIZE);
208
209		if (fmt->width <= SRU_MAX_SIZE / 2 &&
210		    fmt->height <= SRU_MAX_SIZE / 2 &&
211		    output_area > input_area * 9 / 4) {
212			fmt->width = format->width * 2;
213			fmt->height = format->height * 2;
214		} else {
215			fmt->width = format->width;
216			fmt->height = format->height;
217		}
218		break;
219	}
220
221	fmt->field = V4L2_FIELD_NONE;
222}
223
224static int sru_set_format(struct v4l2_subdev *subdev,
225			  struct v4l2_subdev_state *sd_state,
226			  struct v4l2_subdev_format *fmt)
227{
228	struct vsp1_sru *sru = to_sru(subdev);
229	struct v4l2_subdev_state *state;
230	struct v4l2_mbus_framefmt *format;
231	int ret = 0;
232
233	mutex_lock(&sru->entity.lock);
234
235	state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which);
236	if (!state) {
237		ret = -EINVAL;
238		goto done;
239	}
240
241	sru_try_format(sru, state, fmt->pad, &fmt->format);
242
243	format = v4l2_subdev_state_get_format(state, fmt->pad);
244	*format = fmt->format;
245
246	if (fmt->pad == SRU_PAD_SINK) {
247		/* Propagate the format to the source pad. */
248		format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
249		*format = fmt->format;
250
251		sru_try_format(sru, state, SRU_PAD_SOURCE, format);
252	}
253
254done:
255	mutex_unlock(&sru->entity.lock);
256	return ret;
257}
258
259static const struct v4l2_subdev_pad_ops sru_pad_ops = {
260	.enum_mbus_code = sru_enum_mbus_code,
261	.enum_frame_size = sru_enum_frame_size,
262	.get_fmt = vsp1_subdev_get_pad_format,
263	.set_fmt = sru_set_format,
264};
265
266static const struct v4l2_subdev_ops sru_ops = {
267	.pad    = &sru_pad_ops,
268};
269
270/* -----------------------------------------------------------------------------
271 * VSP1 Entity Operations
272 */
273
274static void sru_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	const struct vsp1_sru_param *param;
281	struct vsp1_sru *sru = to_sru(&entity->subdev);
282	struct v4l2_mbus_framefmt *input;
283	struct v4l2_mbus_framefmt *output;
284	u32 ctrl0;
285
286	input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
287	output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
288
289	if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
290		ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
291		      | VI6_SRU_CTRL0_PARAM4;
292	else
293		ctrl0 = VI6_SRU_CTRL0_PARAM3;
294
295	if (input->width != output->width)
296		ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
297
298	param = &vsp1_sru_params[sru->intensity - 1];
299
300	ctrl0 |= param->ctrl0;
301
302	vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0);
303	vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
304	vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2);
305}
306
307static unsigned int sru_max_width(struct vsp1_entity *entity,
308				  struct v4l2_subdev_state *state,
309				  struct vsp1_pipeline *pipe)
310{
311	struct v4l2_mbus_framefmt *input;
312	struct v4l2_mbus_framefmt *output;
313
314	input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
315	output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
316
317	/*
318	 * The maximum input width of the SRU is 288 input pixels, but 32
319	 * pixels are reserved to support overlapping partition windows when
320	 * scaling.
321	 */
322	if (input->width != output->width)
323		return 512;
324	else
325		return 256;
326}
327
328static void sru_partition(struct vsp1_entity *entity,
329			  struct v4l2_subdev_state *state,
330			  struct vsp1_pipeline *pipe,
331			  struct vsp1_partition *partition,
332			  unsigned int partition_idx,
333			  struct v4l2_rect *window)
334{
335	struct v4l2_mbus_framefmt *input;
336	struct v4l2_mbus_framefmt *output;
337
338	input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
339	output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
340
341	/* Adapt if SRUx2 is enabled. */
342	if (input->width != output->width) {
343		window->width /= 2;
344		window->left /= 2;
345		window->height /= 2;
346		window->top /= 2;
347	}
348
349	partition->sru = *window;
350}
351
352static const struct vsp1_entity_operations sru_entity_ops = {
353	.configure_stream = sru_configure_stream,
354	.max_width = sru_max_width,
355	.partition = sru_partition,
356};
357
358/* -----------------------------------------------------------------------------
359 * Initialization and Cleanup
360 */
361
362struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
363{
364	struct vsp1_sru *sru;
365	int ret;
366
367	sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
368	if (sru == NULL)
369		return ERR_PTR(-ENOMEM);
370
371	sru->entity.ops = &sru_entity_ops;
372	sru->entity.type = VSP1_ENTITY_SRU;
373
374	ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
375			       MEDIA_ENT_F_PROC_VIDEO_SCALER);
376	if (ret < 0)
377		return ERR_PTR(ret);
378
379	/* Initialize the control handler. */
380	v4l2_ctrl_handler_init(&sru->ctrls, 1);
381	v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
382
383	sru->intensity = 1;
384
385	sru->entity.subdev.ctrl_handler = &sru->ctrls;
386
387	if (sru->ctrls.error) {
388		dev_err(vsp1->dev, "sru: failed to initialize controls\n");
389		ret = sru->ctrls.error;
390		vsp1_entity_destroy(&sru->entity);
391		return ERR_PTR(ret);
392	}
393
394	return sru;
395}
~/ Read-only file preview.