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 == V4L2_MBUS_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_fh *fh,
170			      struct v4l2_subdev_mbus_code_enum *code)
171{
172	static const unsigned int codes[] = {
173		V4L2_MBUS_FMT_ARGB8888_1X32,
174		V4L2_MBUS_FMT_AYUV8_1X32,
175	};
176	struct v4l2_mbus_framefmt *format;
177
178	if (code->pad == SRU_PAD_SINK) {
179		if (code->index >= ARRAY_SIZE(codes))
180			return -EINVAL;
181
182		code->code = codes[code->index];
183	} else {
184		/* The SRU can't perform format conversion, the sink format is
185		 * always identical to the source format.
186		 */
187		if (code->index)
188			return -EINVAL;
189
190		format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
191		code->code = format->code;
192	}
193
194	return 0;
195}
196
197static int sru_enum_frame_size(struct v4l2_subdev *subdev,
198			       struct v4l2_subdev_fh *fh,
199			       struct v4l2_subdev_frame_size_enum *fse)
200{
201	struct v4l2_mbus_framefmt *format;
202
203	format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
204
205	if (fse->index || fse->code != format->code)
206		return -EINVAL;
207
208	if (fse->pad == SRU_PAD_SINK) {
209		fse->min_width = SRU_MIN_SIZE;
210		fse->max_width = SRU_MAX_SIZE;
211		fse->min_height = SRU_MIN_SIZE;
212		fse->max_height = SRU_MAX_SIZE;
213	} else {
214		fse->min_width = format->width;
215		fse->min_height = format->height;
216		if (format->width <= SRU_MAX_SIZE / 2 &&
217		    format->height <= SRU_MAX_SIZE / 2) {
218			fse->max_width = format->width * 2;
219			fse->max_height = format->height * 2;
220		} else {
221			fse->max_width = format->width;
222			fse->max_height = format->height;
223		}
224	}
225
226	return 0;
227}
228
229static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
230			  struct v4l2_subdev_format *fmt)
231{
232	struct vsp1_sru *sru = to_sru(subdev);
233
234	fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
235						  fmt->which);
236
237	return 0;
238}
239
240static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh,
241			   unsigned int pad, struct v4l2_mbus_framefmt *fmt,
242			   enum v4l2_subdev_format_whence which)
243{
244	struct v4l2_mbus_framefmt *format;
245	unsigned int input_area;
246	unsigned int output_area;
247
248	switch (pad) {
249	case SRU_PAD_SINK:
250		/* Default to YUV if the requested format is not supported. */
251		if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
252		    fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
253			fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
254
255		fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
256		fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
257		break;
258
259	case SRU_PAD_SOURCE:
260		/* The SRU can't perform format conversion. */
261		format = vsp1_entity_get_pad_format(&sru->entity, fh,
262						    SRU_PAD_SINK, which);
263		fmt->code = format->code;
264
265		/* We can upscale by 2 in both direction, but not independently.
266		 * Compare the input and output rectangles areas (avoiding
267		 * integer overflows on the output): if the requested output
268		 * area is larger than 1.5^2 the input area upscale by two,
269		 * otherwise don't scale.
270		 */
271		input_area = format->width * format->height;
272		output_area = min(fmt->width, SRU_MAX_SIZE)
273			    * min(fmt->height, SRU_MAX_SIZE);
274
275		if (fmt->width <= SRU_MAX_SIZE / 2 &&
276		    fmt->height <= SRU_MAX_SIZE / 2 &&
277		    output_area > input_area * 9 / 4) {
278			fmt->width = format->width * 2;
279			fmt->height = format->height * 2;
280		} else {
281			fmt->width = format->width;
282			fmt->height = format->height;
283		}
284		break;
285	}
286
287	fmt->field = V4L2_FIELD_NONE;
288	fmt->colorspace = V4L2_COLORSPACE_SRGB;
289}
290
291static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
292			  struct v4l2_subdev_format *fmt)
293{
294	struct vsp1_sru *sru = to_sru(subdev);
295	struct v4l2_mbus_framefmt *format;
296
297	sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which);
298
299	format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
300					    fmt->which);
301	*format = fmt->format;
302
303	if (fmt->pad == SRU_PAD_SINK) {
304		/* Propagate the format to the source pad. */
305		format = vsp1_entity_get_pad_format(&sru->entity, fh,
306						    SRU_PAD_SOURCE, fmt->which);
307		*format = fmt->format;
308
309		sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which);
310	}
311
312	return 0;
313}
314
315/* -----------------------------------------------------------------------------
316 * V4L2 Subdevice Operations
317 */
318
319static struct v4l2_subdev_video_ops sru_video_ops = {
320	.s_stream = sru_s_stream,
321};
322
323static struct v4l2_subdev_pad_ops sru_pad_ops = {
324	.enum_mbus_code = sru_enum_mbus_code,
325	.enum_frame_size = sru_enum_frame_size,
326	.get_fmt = sru_get_format,
327	.set_fmt = sru_set_format,
328};
329
330static struct v4l2_subdev_ops sru_ops = {
331	.video	= &sru_video_ops,
332	.pad    = &sru_pad_ops,
333};
334
335/* -----------------------------------------------------------------------------
336 * Initialization and Cleanup
337 */
338
339struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
340{
341	struct v4l2_subdev *subdev;
342	struct vsp1_sru *sru;
343	int ret;
344
345	sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
346	if (sru == NULL)
347		return ERR_PTR(-ENOMEM);
348
349	sru->entity.type = VSP1_ENTITY_SRU;
350
351	ret = vsp1_entity_init(vsp1, &sru->entity, 2);
352	if (ret < 0)
353		return ERR_PTR(ret);
354
355	/* Initialize the V4L2 subdev. */
356	subdev = &sru->entity.subdev;
357	v4l2_subdev_init(subdev, &sru_ops);
358
359	subdev->entity.ops = &vsp1_media_ops;
360	subdev->internal_ops = &vsp1_subdev_internal_ops;
361	snprintf(subdev->name, sizeof(subdev->name), "%s sru",
362		 dev_name(vsp1->dev));
363	v4l2_set_subdevdata(subdev, sru);
364	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
365
366	vsp1_entity_init_formats(subdev, NULL);
367
368	/* Initialize the control handler. */
369	v4l2_ctrl_handler_init(&sru->ctrls, 1);
370	v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
371
372	sru->entity.subdev.ctrl_handler = &sru->ctrls;
373
374	if (sru->ctrls.error) {
375		dev_err(vsp1->dev, "sru: failed to initialize controls\n");
376		ret = sru->ctrls.error;
377		vsp1_entity_destroy(&sru->entity);
378		return ERR_PTR(ret);
379	}
380
381	return sru;
382}
383