1 // SPDX-License-Identifier: GPL-2.0+
3 * vsp1_sru.c -- R-Car VSP1 Super Resolution Unit
5 * Copyright (C) 2013 Renesas Corporation
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
10 #include <linux/device.h>
11 #include <linux/gfp.h>
13 #include <media/v4l2-subdev.h>
17 #include "vsp1_pipe.h"
20 #define SRU_MIN_SIZE 4U
21 #define SRU_MAX_SIZE 8190U
23 /* -----------------------------------------------------------------------------
27 static inline void vsp1_sru_write(struct vsp1_sru
*sru
,
28 struct vsp1_dl_body
*dlb
, u32 reg
, u32 data
)
30 vsp1_dl_body_write(dlb
, reg
, data
);
33 /* -----------------------------------------------------------------------------
37 #define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE | 0x1001)
39 struct vsp1_sru_param
{
44 #define VI6_SRU_CTRL0_PARAMS(p0, p1) \
45 (((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) | \
46 ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
48 #define VI6_SRU_CTRL2_PARAMS(p6, p7, p8) \
49 (((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) | \
50 ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) | \
51 ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
53 static const struct vsp1_sru_param vsp1_sru_params
[] = {
55 .ctrl0
= VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN
,
56 .ctrl2
= VI6_SRU_CTRL2_PARAMS(24, 40, 255),
58 .ctrl0
= VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN
,
59 .ctrl2
= VI6_SRU_CTRL2_PARAMS(8, 16, 255),
61 .ctrl0
= VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN
,
62 .ctrl2
= VI6_SRU_CTRL2_PARAMS(36, 60, 255),
64 .ctrl0
= VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN
,
65 .ctrl2
= VI6_SRU_CTRL2_PARAMS(12, 27, 255),
67 .ctrl0
= VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN
,
68 .ctrl2
= VI6_SRU_CTRL2_PARAMS(48, 80, 255),
70 .ctrl0
= VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN
,
71 .ctrl2
= VI6_SRU_CTRL2_PARAMS(16, 36, 255),
75 static int sru_s_ctrl(struct v4l2_ctrl
*ctrl
)
77 struct vsp1_sru
*sru
=
78 container_of(ctrl
->handler
, struct vsp1_sru
, ctrls
);
81 case V4L2_CID_VSP1_SRU_INTENSITY
:
82 sru
->intensity
= ctrl
->val
;
89 static const struct v4l2_ctrl_ops sru_ctrl_ops
= {
93 static const struct v4l2_ctrl_config sru_intensity_control
= {
95 .id
= V4L2_CID_VSP1_SRU_INTENSITY
,
97 .type
= V4L2_CTRL_TYPE_INTEGER
,
104 /* -----------------------------------------------------------------------------
105 * V4L2 Subdevice Operations
108 static int sru_enum_mbus_code(struct v4l2_subdev
*subdev
,
109 struct v4l2_subdev_pad_config
*cfg
,
110 struct v4l2_subdev_mbus_code_enum
*code
)
112 static const unsigned int codes
[] = {
113 MEDIA_BUS_FMT_ARGB8888_1X32
,
114 MEDIA_BUS_FMT_AYUV8_1X32
,
117 return vsp1_subdev_enum_mbus_code(subdev
, cfg
, code
, codes
,
121 static int sru_enum_frame_size(struct v4l2_subdev
*subdev
,
122 struct v4l2_subdev_pad_config
*cfg
,
123 struct v4l2_subdev_frame_size_enum
*fse
)
125 struct vsp1_sru
*sru
= to_sru(subdev
);
126 struct v4l2_subdev_pad_config
*config
;
127 struct v4l2_mbus_framefmt
*format
;
130 config
= vsp1_entity_get_pad_config(&sru
->entity
, cfg
, fse
->which
);
134 format
= vsp1_entity_get_pad_format(&sru
->entity
, config
, SRU_PAD_SINK
);
136 mutex_lock(&sru
->entity
.lock
);
138 if (fse
->index
|| fse
->code
!= format
->code
) {
143 if (fse
->pad
== SRU_PAD_SINK
) {
144 fse
->min_width
= SRU_MIN_SIZE
;
145 fse
->max_width
= SRU_MAX_SIZE
;
146 fse
->min_height
= SRU_MIN_SIZE
;
147 fse
->max_height
= SRU_MAX_SIZE
;
149 fse
->min_width
= format
->width
;
150 fse
->min_height
= format
->height
;
151 if (format
->width
<= SRU_MAX_SIZE
/ 2 &&
152 format
->height
<= SRU_MAX_SIZE
/ 2) {
153 fse
->max_width
= format
->width
* 2;
154 fse
->max_height
= format
->height
* 2;
156 fse
->max_width
= format
->width
;
157 fse
->max_height
= format
->height
;
162 mutex_unlock(&sru
->entity
.lock
);
166 static void sru_try_format(struct vsp1_sru
*sru
,
167 struct v4l2_subdev_pad_config
*config
,
168 unsigned int pad
, struct v4l2_mbus_framefmt
*fmt
)
170 struct v4l2_mbus_framefmt
*format
;
171 unsigned int input_area
;
172 unsigned int output_area
;
176 /* Default to YUV if the requested format is not supported. */
177 if (fmt
->code
!= MEDIA_BUS_FMT_ARGB8888_1X32
&&
178 fmt
->code
!= MEDIA_BUS_FMT_AYUV8_1X32
)
179 fmt
->code
= MEDIA_BUS_FMT_AYUV8_1X32
;
181 fmt
->width
= clamp(fmt
->width
, SRU_MIN_SIZE
, SRU_MAX_SIZE
);
182 fmt
->height
= clamp(fmt
->height
, SRU_MIN_SIZE
, SRU_MAX_SIZE
);
186 /* The SRU can't perform format conversion. */
187 format
= vsp1_entity_get_pad_format(&sru
->entity
, config
,
189 fmt
->code
= format
->code
;
192 * We can upscale by 2 in both direction, but not independently.
193 * Compare the input and output rectangles areas (avoiding
194 * integer overflows on the output): if the requested output
195 * area is larger than 1.5^2 the input area upscale by two,
196 * otherwise don't scale.
198 input_area
= format
->width
* format
->height
;
199 output_area
= min(fmt
->width
, SRU_MAX_SIZE
)
200 * min(fmt
->height
, SRU_MAX_SIZE
);
202 if (fmt
->width
<= SRU_MAX_SIZE
/ 2 &&
203 fmt
->height
<= SRU_MAX_SIZE
/ 2 &&
204 output_area
> input_area
* 9 / 4) {
205 fmt
->width
= format
->width
* 2;
206 fmt
->height
= format
->height
* 2;
208 fmt
->width
= format
->width
;
209 fmt
->height
= format
->height
;
214 fmt
->field
= V4L2_FIELD_NONE
;
215 fmt
->colorspace
= V4L2_COLORSPACE_SRGB
;
218 static int sru_set_format(struct v4l2_subdev
*subdev
,
219 struct v4l2_subdev_pad_config
*cfg
,
220 struct v4l2_subdev_format
*fmt
)
222 struct vsp1_sru
*sru
= to_sru(subdev
);
223 struct v4l2_subdev_pad_config
*config
;
224 struct v4l2_mbus_framefmt
*format
;
227 mutex_lock(&sru
->entity
.lock
);
229 config
= vsp1_entity_get_pad_config(&sru
->entity
, cfg
, fmt
->which
);
235 sru_try_format(sru
, config
, fmt
->pad
, &fmt
->format
);
237 format
= vsp1_entity_get_pad_format(&sru
->entity
, config
, fmt
->pad
);
238 *format
= fmt
->format
;
240 if (fmt
->pad
== SRU_PAD_SINK
) {
241 /* Propagate the format to the source pad. */
242 format
= vsp1_entity_get_pad_format(&sru
->entity
, config
,
244 *format
= fmt
->format
;
246 sru_try_format(sru
, config
, SRU_PAD_SOURCE
, format
);
250 mutex_unlock(&sru
->entity
.lock
);
254 static const struct v4l2_subdev_pad_ops sru_pad_ops
= {
255 .init_cfg
= vsp1_entity_init_cfg
,
256 .enum_mbus_code
= sru_enum_mbus_code
,
257 .enum_frame_size
= sru_enum_frame_size
,
258 .get_fmt
= vsp1_subdev_get_pad_format
,
259 .set_fmt
= sru_set_format
,
262 static const struct v4l2_subdev_ops sru_ops
= {
266 /* -----------------------------------------------------------------------------
267 * VSP1 Entity Operations
270 static void sru_configure_stream(struct vsp1_entity
*entity
,
271 struct vsp1_pipeline
*pipe
,
272 struct vsp1_dl_list
*dl
,
273 struct vsp1_dl_body
*dlb
)
275 const struct vsp1_sru_param
*param
;
276 struct vsp1_sru
*sru
= to_sru(&entity
->subdev
);
277 struct v4l2_mbus_framefmt
*input
;
278 struct v4l2_mbus_framefmt
*output
;
281 input
= vsp1_entity_get_pad_format(&sru
->entity
, sru
->entity
.config
,
283 output
= vsp1_entity_get_pad_format(&sru
->entity
, sru
->entity
.config
,
286 if (input
->code
== MEDIA_BUS_FMT_ARGB8888_1X32
)
287 ctrl0
= VI6_SRU_CTRL0_PARAM2
| VI6_SRU_CTRL0_PARAM3
288 | VI6_SRU_CTRL0_PARAM4
;
290 ctrl0
= VI6_SRU_CTRL0_PARAM3
;
292 if (input
->width
!= output
->width
)
293 ctrl0
|= VI6_SRU_CTRL0_MODE_UPSCALE
;
295 param
= &vsp1_sru_params
[sru
->intensity
- 1];
297 ctrl0
|= param
->ctrl0
;
299 vsp1_sru_write(sru
, dlb
, VI6_SRU_CTRL0
, ctrl0
);
300 vsp1_sru_write(sru
, dlb
, VI6_SRU_CTRL1
, VI6_SRU_CTRL1_PARAM5
);
301 vsp1_sru_write(sru
, dlb
, VI6_SRU_CTRL2
, param
->ctrl2
);
304 static unsigned int sru_max_width(struct vsp1_entity
*entity
,
305 struct vsp1_pipeline
*pipe
)
307 struct vsp1_sru
*sru
= to_sru(&entity
->subdev
);
308 struct v4l2_mbus_framefmt
*input
;
309 struct v4l2_mbus_framefmt
*output
;
311 input
= vsp1_entity_get_pad_format(&sru
->entity
, sru
->entity
.config
,
313 output
= vsp1_entity_get_pad_format(&sru
->entity
, sru
->entity
.config
,
317 * The maximum input width of the SRU is 288 input pixels, but 32
318 * pixels are reserved to support overlapping partition windows when
321 if (input
->width
!= output
->width
)
327 static void sru_partition(struct vsp1_entity
*entity
,
328 struct vsp1_pipeline
*pipe
,
329 struct vsp1_partition
*partition
,
330 unsigned int partition_idx
,
331 struct vsp1_partition_window
*window
)
333 struct vsp1_sru
*sru
= to_sru(&entity
->subdev
);
334 struct v4l2_mbus_framefmt
*input
;
335 struct v4l2_mbus_framefmt
*output
;
337 input
= vsp1_entity_get_pad_format(&sru
->entity
, sru
->entity
.config
,
339 output
= vsp1_entity_get_pad_format(&sru
->entity
, sru
->entity
.config
,
342 /* Adapt if SRUx2 is enabled. */
343 if (input
->width
!= output
->width
) {
348 partition
->sru
= *window
;
351 static const struct vsp1_entity_operations sru_entity_ops
= {
352 .configure_stream
= sru_configure_stream
,
353 .max_width
= sru_max_width
,
354 .partition
= sru_partition
,
357 /* -----------------------------------------------------------------------------
358 * Initialization and Cleanup
361 struct vsp1_sru
*vsp1_sru_create(struct vsp1_device
*vsp1
)
363 struct vsp1_sru
*sru
;
366 sru
= devm_kzalloc(vsp1
->dev
, sizeof(*sru
), GFP_KERNEL
);
368 return ERR_PTR(-ENOMEM
);
370 sru
->entity
.ops
= &sru_entity_ops
;
371 sru
->entity
.type
= VSP1_ENTITY_SRU
;
373 ret
= vsp1_entity_init(vsp1
, &sru
->entity
, "sru", 2, &sru_ops
,
374 MEDIA_ENT_F_PROC_VIDEO_SCALER
);
378 /* Initialize the control handler. */
379 v4l2_ctrl_handler_init(&sru
->ctrls
, 1);
380 v4l2_ctrl_new_custom(&sru
->ctrls
, &sru_intensity_control
, NULL
);
384 sru
->entity
.subdev
.ctrl_handler
= &sru
->ctrls
;
386 if (sru
->ctrls
.error
) {
387 dev_err(vsp1
->dev
, "sru: failed to initialize controls\n");
388 ret
= sru
->ctrls
.error
;
389 vsp1_entity_destroy(&sru
->entity
);