1/*
2 * vsp1_sru.c  --  R-Car VSP1 Super Resolution Unit
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/device.h>
15#include <linux/gfp.h>
16
17#include <media/v4l2-subdev.h>
18
19#include "vsp1.h"
20#include "vsp1_sru.h"
21
22#define SRU_MIN_SIZE				4U
23#define SRU_MAX_SIZE				8190U
24
25/* -----------------------------------------------------------------------------
26 * Device Access
27 */
28
29static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg)
30{
31	return vsp1_read(sru->entity.vsp1, reg);
32}
33
34static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data)
35{
36	vsp1_write(sru->entity.vsp1, reg, data);
37}
38
39/* -----------------------------------------------------------------------------
40 * Controls
41 */
42
43#define V4L2_CID_VSP1_SRU_INTENSITY		(V4L2_CID_USER_BASE + 1)
44
45struct vsp1_sru_param {
46	u32 ctrl0;
47	u32 ctrl2;
48};
49
50#define VI6_SRU_CTRL0_PARAMS(p0, p1)			\
51	(((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) |		\
52	 ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
53
54#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8)		\
55	(((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) |		\
56	 ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) |		\
57	 ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
58
59static const struct vsp1_sru_param vsp1_sru_params[] = {
60	{
61		.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
62		.ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255),
63	}, {
64		.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
65		.ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255),
66	}, {
67		.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
68		.ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255),
69	}, {
70		.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
71		.ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255),
72	}, {
73		.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
74		.ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255),
75	}, {
76		.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
77		.ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255),
78	},
79};
80
81static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
82{
83	struct vsp1_sru *sru =
84		container_of(ctrl->handler, struct vsp1_sru, ctrls);
85	const struct vsp1_sru_param *param;
86	u32 value;
87
88	switch (ctrl->id) {
89	case V4L2_CID_VSP1_SRU_INTENSITY:
90		param = &vsp1_sru_params[ctrl->val - 1];
91
92		value = vsp1_sru_read(sru, VI6_SRU_CTRL0);
93		value &= ~(VI6_SRU_CTRL0_PARAM0_MASK |
94			   VI6_SRU_CTRL0_PARAM1_MASK);
95		value |= param->ctrl0;
96		vsp1_sru_write(sru, VI6_SRU_CTRL0, value);
97
98		vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2);
99		break;
100	}
101
102	return 0;
103}
104
105static const struct v4l2_ctrl_ops sru_ctrl_ops = {
106	.s_ctrl = sru_s_ctrl,
107};
108
109static const struct v4l2_ctrl_config sru_intensity_control = {
110	.ops = &sru_ctrl_ops,
111	.id = V4L2_CID_VSP1_SRU_INTENSITY,
112	.name = "Intensity",
113	.type = V4L2_CTRL_TYPE_INTEGER,
114	.min = 1,
115	.max = 6,
116	.def = 1,
117	.step = 1,
118};
119
120/* -----------------------------------------------------------------------------
121 * V4L2 Subdevice Core Operations
122 */
123
124static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
125{
126	struct vsp1_sru *sru = to_sru(subdev);
127	struct v4l2_mbus_framefmt *input;
128	struct v4l2_mbus_framefmt *output;
129	u32 ctrl0;
130	int ret;
131
132	ret = vsp1_entity_set_streaming(&sru->entity, enable);
133	if (ret < 0)
134		return ret;
135
136	if (!enable)
137		return 0;
138
139	input = &sru->entity.formats[SRU_PAD_SINK];
140	output = &sru->entity.formats[SRU_PAD_SOURCE];
141
142	if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
143		ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
144		      | VI6_SRU_CTRL0_PARAM4;
145	else
146		ctrl0 = VI6_SRU_CTRL0_PARAM3;
147
148	if (input->width != output->width)
149		ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
150
151	/* Take the control handler lock to ensure that the CTRL0 value won't be
152	 * changed behind our back by a set control operation.
153	 */
154	mutex_lock(sru->ctrls.lock);
155	ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
156	       & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
157	mutex_unlock(sru->ctrls.lock);
158
159	vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
160
161	return 0;
162}
163
164/* -----------------------------------------------------------------------------
165 * V4L2 Subdevice Pad Operations
166 */
167
168static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
169			      struct v4l2_subdev_pad_config *cfg,
170			      struct v4l2_subdev_mbus_code_enum *code)
171{
172	static const unsigned int codes[] = {
173		MEDIA_BUS_FMT_ARGB8888_1X32,
174		MEDIA_BUS_FMT_AYUV8_1X32,
175	};
176	struct vsp1_sru *sru = to_sru(subdev);
177	struct v4l2_mbus_framefmt *format;
178
179	if (code->pad == SRU_PAD_SINK) {
180		if (code->index >= ARRAY_SIZE(codes))
181			return -EINVAL;
182
183		code->code = codes[code->index];
184	} else {
185		/* The SRU can't perform format conversion, the sink format is
186		 * always identical to the source format.
187		 */
188		if (code->index)
189			return -EINVAL;
190
191		format = vsp1_entity_get_pad_format(&sru->entity, cfg,
192						    SRU_PAD_SINK, code->which);
193		code->code = format->code;
194	}
195
196	return 0;
197}
198
199static int sru_enum_frame_size(struct v4l2_subdev *subdev,
200			       struct v4l2_subdev_pad_config *cfg,
201			       struct v4l2_subdev_frame_size_enum *fse)
202{
203	struct vsp1_sru *sru = to_sru(subdev);
204	struct v4l2_mbus_framefmt *format;
205
206	format = vsp1_entity_get_pad_format(&sru->entity, cfg,
207					    SRU_PAD_SINK, fse->which);
208
209	if (fse->index || fse->code != format->code)
210		return -EINVAL;
211
212	if (fse->pad == SRU_PAD_SINK) {
213		fse->min_width = SRU_MIN_SIZE;
214		fse->max_width = SRU_MAX_SIZE;
215		fse->min_height = SRU_MIN_SIZE;
216		fse->max_height = SRU_MAX_SIZE;
217	} else {
218		fse->min_width = format->width;
219		fse->min_height = format->height;
220		if (format->width <= SRU_MAX_SIZE / 2 &&
221		    format->height <= SRU_MAX_SIZE / 2) {
222			fse->max_width = format->width * 2;
223			fse->max_height = format->height * 2;
224		} else {
225			fse->max_width = format->width;
226			fse->max_height = format->height;
227		}
228	}
229
230	return 0;
231}
232
233static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
234			  struct v4l2_subdev_format *fmt)
235{
236	struct vsp1_sru *sru = to_sru(subdev);
237
238	fmt->format = *vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad,
239						  fmt->which);
240
241	return 0;
242}
243
244static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_pad_config *cfg,
245			   unsigned int pad, struct v4l2_mbus_framefmt *fmt,
246			   enum v4l2_subdev_format_whence which)
247{
248	struct v4l2_mbus_framefmt *format;
249	unsigned int input_area;
250	unsigned int output_area;
251
252	switch (pad) {
253	case SRU_PAD_SINK:
254		/* Default to YUV if the requested format is not supported. */
255		if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
256		    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
257			fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
258
259		fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
260		fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
261		break;
262
263	case SRU_PAD_SOURCE:
264		/* The SRU can't perform format conversion. */
265		format = vsp1_entity_get_pad_format(&sru->entity, cfg,
266						    SRU_PAD_SINK, which);
267		fmt->code = format->code;
268
269		/* We can upscale by 2 in both direction, but not independently.
270		 * Compare the input and output rectangles areas (avoiding
271		 * integer overflows on the output): if the requested output
272		 * area is larger than 1.5^2 the input area upscale by two,
273		 * otherwise don't scale.
274		 */
275		input_area = format->width * format->height;
276		output_area = min(fmt->width, SRU_MAX_SIZE)
277			    * min(fmt->height, SRU_MAX_SIZE);
278
279		if (fmt->width <= SRU_MAX_SIZE / 2 &&
280		    fmt->height <= SRU_MAX_SIZE / 2 &&
281		    output_area > input_area * 9 / 4) {
282			fmt->width = format->width * 2;
283			fmt->height = format->height * 2;
284		} else {
285			fmt->width = format->width;
286			fmt->height = format->height;
287		}
288		break;
289	}
290
291	fmt->field = V4L2_FIELD_NONE;
292	fmt->colorspace = V4L2_COLORSPACE_SRGB;
293}
294
295static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
296			  struct v4l2_subdev_format *fmt)
297{
298	struct vsp1_sru *sru = to_sru(subdev);
299	struct v4l2_mbus_framefmt *format;
300
301	sru_try_format(sru, cfg, fmt->pad, &fmt->format, fmt->which);
302
303	format = vsp1_entity_get_pad_format(&sru->entity, cfg, fmt->pad,
304					    fmt->which);
305	*format = fmt->format;
306
307	if (fmt->pad == SRU_PAD_SINK) {
308		/* Propagate the format to the source pad. */
309		format = vsp1_entity_get_pad_format(&sru->entity, cfg,
310						    SRU_PAD_SOURCE, fmt->which);
311		*format = fmt->format;
312
313		sru_try_format(sru, cfg, SRU_PAD_SOURCE, format, fmt->which);
314	}
315
316	return 0;
317}
318
319/* -----------------------------------------------------------------------------
320 * V4L2 Subdevice Operations
321 */
322
323static struct v4l2_subdev_video_ops sru_video_ops = {
324	.s_stream = sru_s_stream,
325};
326
327static struct v4l2_subdev_pad_ops sru_pad_ops = {
328	.enum_mbus_code = sru_enum_mbus_code,
329	.enum_frame_size = sru_enum_frame_size,
330	.get_fmt = sru_get_format,
331	.set_fmt = sru_set_format,
332};
333
334static struct v4l2_subdev_ops sru_ops = {
335	.video	= &sru_video_ops,
336	.pad    = &sru_pad_ops,
337};
338
339/* -----------------------------------------------------------------------------
340 * Initialization and Cleanup
341 */
342
343struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
344{
345	struct v4l2_subdev *subdev;
346	struct vsp1_sru *sru;
347	int ret;
348
349	sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
350	if (sru == NULL)
351		return ERR_PTR(-ENOMEM);
352
353	sru->entity.type = VSP1_ENTITY_SRU;
354
355	ret = vsp1_entity_init(vsp1, &sru->entity, 2);
356	if (ret < 0)
357		return ERR_PTR(ret);
358
359	/* Initialize the V4L2 subdev. */
360	subdev = &sru->entity.subdev;
361	v4l2_subdev_init(subdev, &sru_ops);
362
363	subdev->entity.ops = &vsp1_media_ops;
364	subdev->internal_ops = &vsp1_subdev_internal_ops;
365	snprintf(subdev->name, sizeof(subdev->name), "%s sru",
366		 dev_name(vsp1->dev));
367	v4l2_set_subdevdata(subdev, sru);
368	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
369
370	vsp1_entity_init_formats(subdev, NULL);
371
372	/* Initialize the control handler. */
373	v4l2_ctrl_handler_init(&sru->ctrls, 1);
374	v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
375
376	sru->entity.subdev.ctrl_handler = &sru->ctrls;
377
378	if (sru->ctrls.error) {
379		dev_err(vsp1->dev, "sru: failed to initialize controls\n");
380		ret = sru->ctrls.error;
381		vsp1_entity_destroy(&sru->entity);
382		return ERR_PTR(ret);
383	}
384
385	return sru;
386}
387