xref: /openbmc/linux/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1577bbf23SMaxime Ripard // SPDX-License-Identifier: GPL-2.0+
2577bbf23SMaxime Ripard /*
3577bbf23SMaxime Ripard  * Copyright (C) 2016 NextThing Co
4577bbf23SMaxime Ripard  * Copyright (C) 2016-2019 Bootlin
5577bbf23SMaxime Ripard  *
6577bbf23SMaxime Ripard  * Author: Maxime Ripard <maxime.ripard@bootlin.com>
7577bbf23SMaxime Ripard  */
8577bbf23SMaxime Ripard 
9577bbf23SMaxime Ripard #include <linux/device.h>
10577bbf23SMaxime Ripard #include <linux/pm_runtime.h>
11577bbf23SMaxime Ripard 
12577bbf23SMaxime Ripard #include <media/v4l2-ioctl.h>
13577bbf23SMaxime Ripard #include <media/v4l2-mc.h>
14577bbf23SMaxime Ripard #include <media/videobuf2-v4l2.h>
15577bbf23SMaxime Ripard 
16577bbf23SMaxime Ripard #include "sun4i_csi.h"
17577bbf23SMaxime Ripard 
18577bbf23SMaxime Ripard #define CSI_DEFAULT_WIDTH	640
19577bbf23SMaxime Ripard #define CSI_DEFAULT_HEIGHT	480
20577bbf23SMaxime Ripard 
2120a438d5SMaxime Ripard static const struct sun4i_csi_format sun4i_csi_formats[] = {
22577bbf23SMaxime Ripard 	/* YUV422 inputs */
23577bbf23SMaxime Ripard 	{
24577bbf23SMaxime Ripard 		.mbus		= MEDIA_BUS_FMT_YUYV8_2X8,
25577bbf23SMaxime Ripard 		.fourcc		= V4L2_PIX_FMT_YUV420M,
26577bbf23SMaxime Ripard 		.input		= CSI_INPUT_YUV,
27577bbf23SMaxime Ripard 		.output		= CSI_OUTPUT_YUV_420_PLANAR,
28577bbf23SMaxime Ripard 		.num_planes	= 3,
29577bbf23SMaxime Ripard 		.bpp		= { 8, 8, 8 },
30577bbf23SMaxime Ripard 		.hsub		= 2,
31577bbf23SMaxime Ripard 		.vsub		= 2,
32577bbf23SMaxime Ripard 	},
33577bbf23SMaxime Ripard };
34577bbf23SMaxime Ripard 
sun4i_csi_find_format(const u32 * fourcc,const u32 * mbus)35577bbf23SMaxime Ripard const struct sun4i_csi_format *sun4i_csi_find_format(const u32 *fourcc,
36577bbf23SMaxime Ripard 						     const u32 *mbus)
37577bbf23SMaxime Ripard {
38577bbf23SMaxime Ripard 	unsigned int i;
39577bbf23SMaxime Ripard 
40577bbf23SMaxime Ripard 	for (i = 0; i < ARRAY_SIZE(sun4i_csi_formats); i++) {
41577bbf23SMaxime Ripard 		if (fourcc && *fourcc != sun4i_csi_formats[i].fourcc)
42577bbf23SMaxime Ripard 			continue;
43577bbf23SMaxime Ripard 
44577bbf23SMaxime Ripard 		if (mbus && *mbus != sun4i_csi_formats[i].mbus)
45577bbf23SMaxime Ripard 			continue;
46577bbf23SMaxime Ripard 
47577bbf23SMaxime Ripard 		return &sun4i_csi_formats[i];
48577bbf23SMaxime Ripard 	}
49577bbf23SMaxime Ripard 
50577bbf23SMaxime Ripard 	return NULL;
51577bbf23SMaxime Ripard }
52577bbf23SMaxime Ripard 
sun4i_csi_querycap(struct file * file,void * priv,struct v4l2_capability * cap)53577bbf23SMaxime Ripard static int sun4i_csi_querycap(struct file *file, void *priv,
54577bbf23SMaxime Ripard 			      struct v4l2_capability *cap)
55577bbf23SMaxime Ripard {
56577bbf23SMaxime Ripard 	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
57577bbf23SMaxime Ripard 	strscpy(cap->card, "sun4i-csi", sizeof(cap->card));
58577bbf23SMaxime Ripard 
59577bbf23SMaxime Ripard 	return 0;
60577bbf23SMaxime Ripard }
61577bbf23SMaxime Ripard 
sun4i_csi_enum_input(struct file * file,void * priv,struct v4l2_input * inp)62577bbf23SMaxime Ripard static int sun4i_csi_enum_input(struct file *file, void *priv,
63577bbf23SMaxime Ripard 				struct v4l2_input *inp)
64577bbf23SMaxime Ripard {
65577bbf23SMaxime Ripard 	if (inp->index != 0)
66577bbf23SMaxime Ripard 		return -EINVAL;
67577bbf23SMaxime Ripard 
68577bbf23SMaxime Ripard 	inp->type = V4L2_INPUT_TYPE_CAMERA;
69577bbf23SMaxime Ripard 	strscpy(inp->name, "Camera", sizeof(inp->name));
70577bbf23SMaxime Ripard 
71577bbf23SMaxime Ripard 	return 0;
72577bbf23SMaxime Ripard }
73577bbf23SMaxime Ripard 
sun4i_csi_g_input(struct file * file,void * fh,unsigned int * i)74577bbf23SMaxime Ripard static int sun4i_csi_g_input(struct file *file, void *fh,
75577bbf23SMaxime Ripard 			     unsigned int *i)
76577bbf23SMaxime Ripard {
77577bbf23SMaxime Ripard 	*i = 0;
78577bbf23SMaxime Ripard 
79577bbf23SMaxime Ripard 	return 0;
80577bbf23SMaxime Ripard }
81577bbf23SMaxime Ripard 
sun4i_csi_s_input(struct file * file,void * fh,unsigned int i)82577bbf23SMaxime Ripard static int sun4i_csi_s_input(struct file *file, void *fh,
83577bbf23SMaxime Ripard 			     unsigned int i)
84577bbf23SMaxime Ripard {
85577bbf23SMaxime Ripard 	if (i != 0)
86577bbf23SMaxime Ripard 		return -EINVAL;
87577bbf23SMaxime Ripard 
88577bbf23SMaxime Ripard 	return 0;
89577bbf23SMaxime Ripard }
90577bbf23SMaxime Ripard 
_sun4i_csi_try_fmt(struct sun4i_csi * csi,struct v4l2_pix_format_mplane * pix)91577bbf23SMaxime Ripard static void _sun4i_csi_try_fmt(struct sun4i_csi *csi,
92577bbf23SMaxime Ripard 			       struct v4l2_pix_format_mplane *pix)
93577bbf23SMaxime Ripard {
94577bbf23SMaxime Ripard 	const struct sun4i_csi_format *_fmt;
95577bbf23SMaxime Ripard 	unsigned int height, width;
96577bbf23SMaxime Ripard 	unsigned int i;
97577bbf23SMaxime Ripard 
98577bbf23SMaxime Ripard 	_fmt = sun4i_csi_find_format(&pix->pixelformat, NULL);
99577bbf23SMaxime Ripard 	if (!_fmt)
100577bbf23SMaxime Ripard 		_fmt = &sun4i_csi_formats[0];
101577bbf23SMaxime Ripard 
102577bbf23SMaxime Ripard 	pix->field = V4L2_FIELD_NONE;
103577bbf23SMaxime Ripard 	pix->colorspace = V4L2_COLORSPACE_SRGB;
104577bbf23SMaxime Ripard 	pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
105577bbf23SMaxime Ripard 	pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
106577bbf23SMaxime Ripard 	pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace,
107577bbf23SMaxime Ripard 							  pix->ycbcr_enc);
108577bbf23SMaxime Ripard 
109577bbf23SMaxime Ripard 	pix->num_planes = _fmt->num_planes;
110577bbf23SMaxime Ripard 	pix->pixelformat = _fmt->fourcc;
111577bbf23SMaxime Ripard 
112577bbf23SMaxime Ripard 	/* Align the width and height on the subsampling */
113577bbf23SMaxime Ripard 	width = ALIGN(pix->width, _fmt->hsub);
114577bbf23SMaxime Ripard 	height = ALIGN(pix->height, _fmt->vsub);
115577bbf23SMaxime Ripard 
116577bbf23SMaxime Ripard 	/* Clamp the width and height to our capabilities */
117577bbf23SMaxime Ripard 	pix->width = clamp(width, _fmt->hsub, CSI_MAX_WIDTH);
118577bbf23SMaxime Ripard 	pix->height = clamp(height, _fmt->vsub, CSI_MAX_HEIGHT);
119577bbf23SMaxime Ripard 
120577bbf23SMaxime Ripard 	for (i = 0; i < _fmt->num_planes; i++) {
121577bbf23SMaxime Ripard 		unsigned int hsub = i > 0 ? _fmt->hsub : 1;
122577bbf23SMaxime Ripard 		unsigned int vsub = i > 0 ? _fmt->vsub : 1;
123577bbf23SMaxime Ripard 		unsigned int bpl;
124577bbf23SMaxime Ripard 
125577bbf23SMaxime Ripard 		bpl = pix->width / hsub * _fmt->bpp[i] / 8;
126577bbf23SMaxime Ripard 		pix->plane_fmt[i].bytesperline = bpl;
127577bbf23SMaxime Ripard 		pix->plane_fmt[i].sizeimage = bpl * pix->height / vsub;
128577bbf23SMaxime Ripard 	}
129577bbf23SMaxime Ripard }
130577bbf23SMaxime Ripard 
sun4i_csi_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)131577bbf23SMaxime Ripard static int sun4i_csi_try_fmt_vid_cap(struct file *file, void *priv,
132577bbf23SMaxime Ripard 				     struct v4l2_format *f)
133577bbf23SMaxime Ripard {
134577bbf23SMaxime Ripard 	struct sun4i_csi *csi = video_drvdata(file);
135577bbf23SMaxime Ripard 
136577bbf23SMaxime Ripard 	_sun4i_csi_try_fmt(csi, &f->fmt.pix_mp);
137577bbf23SMaxime Ripard 
138577bbf23SMaxime Ripard 	return 0;
139577bbf23SMaxime Ripard }
140577bbf23SMaxime Ripard 
sun4i_csi_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)141577bbf23SMaxime Ripard static int sun4i_csi_s_fmt_vid_cap(struct file *file, void *priv,
142577bbf23SMaxime Ripard 				   struct v4l2_format *f)
143577bbf23SMaxime Ripard {
144577bbf23SMaxime Ripard 	struct sun4i_csi *csi = video_drvdata(file);
145577bbf23SMaxime Ripard 
146577bbf23SMaxime Ripard 	_sun4i_csi_try_fmt(csi, &f->fmt.pix_mp);
147577bbf23SMaxime Ripard 	csi->fmt = f->fmt.pix_mp;
148577bbf23SMaxime Ripard 
149577bbf23SMaxime Ripard 	return 0;
150577bbf23SMaxime Ripard }
151577bbf23SMaxime Ripard 
sun4i_csi_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)152577bbf23SMaxime Ripard static int sun4i_csi_g_fmt_vid_cap(struct file *file, void *priv,
153577bbf23SMaxime Ripard 				   struct v4l2_format *f)
154577bbf23SMaxime Ripard {
155577bbf23SMaxime Ripard 	struct sun4i_csi *csi = video_drvdata(file);
156577bbf23SMaxime Ripard 
157577bbf23SMaxime Ripard 	f->fmt.pix_mp = csi->fmt;
158577bbf23SMaxime Ripard 
159577bbf23SMaxime Ripard 	return 0;
160577bbf23SMaxime Ripard }
161577bbf23SMaxime Ripard 
sun4i_csi_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)162577bbf23SMaxime Ripard static int sun4i_csi_enum_fmt_vid_cap(struct file *file, void *priv,
163577bbf23SMaxime Ripard 				      struct v4l2_fmtdesc *f)
164577bbf23SMaxime Ripard {
165577bbf23SMaxime Ripard 	if (f->index >= ARRAY_SIZE(sun4i_csi_formats))
166577bbf23SMaxime Ripard 		return -EINVAL;
167577bbf23SMaxime Ripard 
168577bbf23SMaxime Ripard 	f->pixelformat = sun4i_csi_formats[f->index].fourcc;
169577bbf23SMaxime Ripard 
170577bbf23SMaxime Ripard 	return 0;
171577bbf23SMaxime Ripard }
172577bbf23SMaxime Ripard 
173577bbf23SMaxime Ripard static const struct v4l2_ioctl_ops sun4i_csi_ioctl_ops = {
174577bbf23SMaxime Ripard 	.vidioc_querycap		= sun4i_csi_querycap,
175577bbf23SMaxime Ripard 
176577bbf23SMaxime Ripard 	.vidioc_enum_fmt_vid_cap	= sun4i_csi_enum_fmt_vid_cap,
177577bbf23SMaxime Ripard 	.vidioc_g_fmt_vid_cap_mplane	= sun4i_csi_g_fmt_vid_cap,
178577bbf23SMaxime Ripard 	.vidioc_s_fmt_vid_cap_mplane	= sun4i_csi_s_fmt_vid_cap,
179577bbf23SMaxime Ripard 	.vidioc_try_fmt_vid_cap_mplane	= sun4i_csi_try_fmt_vid_cap,
180577bbf23SMaxime Ripard 
181577bbf23SMaxime Ripard 	.vidioc_enum_input		= sun4i_csi_enum_input,
182577bbf23SMaxime Ripard 	.vidioc_g_input			= sun4i_csi_g_input,
183577bbf23SMaxime Ripard 	.vidioc_s_input			= sun4i_csi_s_input,
184577bbf23SMaxime Ripard 
185577bbf23SMaxime Ripard 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
186577bbf23SMaxime Ripard 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
187577bbf23SMaxime Ripard 	.vidioc_querybuf		= vb2_ioctl_querybuf,
188577bbf23SMaxime Ripard 	.vidioc_qbuf			= vb2_ioctl_qbuf,
189577bbf23SMaxime Ripard 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
190577bbf23SMaxime Ripard 	.vidioc_expbuf			= vb2_ioctl_expbuf,
191577bbf23SMaxime Ripard 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
192577bbf23SMaxime Ripard 	.vidioc_streamon		= vb2_ioctl_streamon,
193577bbf23SMaxime Ripard 	.vidioc_streamoff		= vb2_ioctl_streamoff,
194577bbf23SMaxime Ripard };
195577bbf23SMaxime Ripard 
sun4i_csi_open(struct file * file)196577bbf23SMaxime Ripard static int sun4i_csi_open(struct file *file)
197577bbf23SMaxime Ripard {
198577bbf23SMaxime Ripard 	struct sun4i_csi *csi = video_drvdata(file);
199577bbf23SMaxime Ripard 	int ret;
200577bbf23SMaxime Ripard 
201577bbf23SMaxime Ripard 	ret = mutex_lock_interruptible(&csi->lock);
202577bbf23SMaxime Ripard 	if (ret)
203577bbf23SMaxime Ripard 		return ret;
204577bbf23SMaxime Ripard 
20579e790ffSMauro Carvalho Chehab 	ret = pm_runtime_resume_and_get(csi->dev);
206577bbf23SMaxime Ripard 	if (ret < 0)
20779e790ffSMauro Carvalho Chehab 		goto err_unlock;
208577bbf23SMaxime Ripard 
2098fd390b8SEzequiel Garcia 	ret = v4l2_pipeline_pm_get(&csi->vdev.entity);
210577bbf23SMaxime Ripard 	if (ret)
211577bbf23SMaxime Ripard 		goto err_pm_put;
212577bbf23SMaxime Ripard 
213577bbf23SMaxime Ripard 	ret = v4l2_fh_open(file);
214577bbf23SMaxime Ripard 	if (ret)
215577bbf23SMaxime Ripard 		goto err_pipeline_pm_put;
216577bbf23SMaxime Ripard 
217577bbf23SMaxime Ripard 	mutex_unlock(&csi->lock);
218577bbf23SMaxime Ripard 
219577bbf23SMaxime Ripard 	return 0;
220577bbf23SMaxime Ripard 
221577bbf23SMaxime Ripard err_pipeline_pm_put:
2228fd390b8SEzequiel Garcia 	v4l2_pipeline_pm_put(&csi->vdev.entity);
223577bbf23SMaxime Ripard 
224577bbf23SMaxime Ripard err_pm_put:
225577bbf23SMaxime Ripard 	pm_runtime_put(csi->dev);
22679e790ffSMauro Carvalho Chehab 
22779e790ffSMauro Carvalho Chehab err_unlock:
228577bbf23SMaxime Ripard 	mutex_unlock(&csi->lock);
229577bbf23SMaxime Ripard 
230577bbf23SMaxime Ripard 	return ret;
231577bbf23SMaxime Ripard }
232577bbf23SMaxime Ripard 
sun4i_csi_release(struct file * file)233577bbf23SMaxime Ripard static int sun4i_csi_release(struct file *file)
234577bbf23SMaxime Ripard {
235577bbf23SMaxime Ripard 	struct sun4i_csi *csi = video_drvdata(file);
236577bbf23SMaxime Ripard 
237577bbf23SMaxime Ripard 	mutex_lock(&csi->lock);
238577bbf23SMaxime Ripard 
239503ebad4SHans Verkuil 	_vb2_fop_release(file, NULL);
240503ebad4SHans Verkuil 
2418fd390b8SEzequiel Garcia 	v4l2_pipeline_pm_put(&csi->vdev.entity);
242577bbf23SMaxime Ripard 	pm_runtime_put(csi->dev);
243577bbf23SMaxime Ripard 
244577bbf23SMaxime Ripard 	mutex_unlock(&csi->lock);
245577bbf23SMaxime Ripard 
246577bbf23SMaxime Ripard 	return 0;
247577bbf23SMaxime Ripard }
248577bbf23SMaxime Ripard 
249577bbf23SMaxime Ripard static const struct v4l2_file_operations sun4i_csi_fops = {
250577bbf23SMaxime Ripard 	.owner		= THIS_MODULE,
251577bbf23SMaxime Ripard 	.open		= sun4i_csi_open,
252577bbf23SMaxime Ripard 	.release	= sun4i_csi_release,
253577bbf23SMaxime Ripard 	.unlocked_ioctl	= video_ioctl2,
254577bbf23SMaxime Ripard 	.poll		= vb2_fop_poll,
255577bbf23SMaxime Ripard 	.mmap		= vb2_fop_mmap,
256577bbf23SMaxime Ripard };
257577bbf23SMaxime Ripard 
258577bbf23SMaxime Ripard static const struct v4l2_mbus_framefmt sun4i_csi_pad_fmt_default = {
259577bbf23SMaxime Ripard 	.width = CSI_DEFAULT_WIDTH,
260577bbf23SMaxime Ripard 	.height = CSI_DEFAULT_HEIGHT,
261577bbf23SMaxime Ripard 	.code = MEDIA_BUS_FMT_YUYV8_2X8,
262577bbf23SMaxime Ripard 	.field = V4L2_FIELD_NONE,
263577bbf23SMaxime Ripard 	.colorspace = V4L2_COLORSPACE_RAW,
264577bbf23SMaxime Ripard 	.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
265577bbf23SMaxime Ripard 	.quantization = V4L2_QUANTIZATION_DEFAULT,
266577bbf23SMaxime Ripard 	.xfer_func = V4L2_XFER_FUNC_DEFAULT,
267577bbf23SMaxime Ripard };
268577bbf23SMaxime Ripard 
sun4i_csi_subdev_init_cfg(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state)269577bbf23SMaxime Ripard static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev,
270*0d346d2aSTomi Valkeinen 				     struct v4l2_subdev_state *sd_state)
271577bbf23SMaxime Ripard {
272577bbf23SMaxime Ripard 	struct v4l2_mbus_framefmt *fmt;
273577bbf23SMaxime Ripard 
274*0d346d2aSTomi Valkeinen 	fmt = v4l2_subdev_get_try_format(subdev, sd_state, CSI_SUBDEV_SINK);
275577bbf23SMaxime Ripard 	*fmt = sun4i_csi_pad_fmt_default;
276577bbf23SMaxime Ripard 
277577bbf23SMaxime Ripard 	return 0;
278577bbf23SMaxime Ripard }
279577bbf23SMaxime Ripard 
sun4i_csi_subdev_get_fmt(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)280577bbf23SMaxime Ripard static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev,
281*0d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
282577bbf23SMaxime Ripard 				    struct v4l2_subdev_format *fmt)
283577bbf23SMaxime Ripard {
284577bbf23SMaxime Ripard 	struct sun4i_csi *csi = container_of(subdev, struct sun4i_csi, subdev);
285577bbf23SMaxime Ripard 	struct v4l2_mbus_framefmt *subdev_fmt;
286577bbf23SMaxime Ripard 
287577bbf23SMaxime Ripard 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
288*0d346d2aSTomi Valkeinen 		subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state,
289*0d346d2aSTomi Valkeinen 							fmt->pad);
290577bbf23SMaxime Ripard 	else
291577bbf23SMaxime Ripard 		subdev_fmt = &csi->subdev_fmt;
292577bbf23SMaxime Ripard 
293577bbf23SMaxime Ripard 	fmt->format = *subdev_fmt;
294577bbf23SMaxime Ripard 
295577bbf23SMaxime Ripard 	return 0;
296577bbf23SMaxime Ripard }
297577bbf23SMaxime Ripard 
sun4i_csi_subdev_set_fmt(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)298577bbf23SMaxime Ripard static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev,
299*0d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
300577bbf23SMaxime Ripard 				    struct v4l2_subdev_format *fmt)
301577bbf23SMaxime Ripard {
302577bbf23SMaxime Ripard 	struct sun4i_csi *csi = container_of(subdev, struct sun4i_csi, subdev);
303577bbf23SMaxime Ripard 	struct v4l2_mbus_framefmt *subdev_fmt;
304577bbf23SMaxime Ripard 
305577bbf23SMaxime Ripard 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
306*0d346d2aSTomi Valkeinen 		subdev_fmt = v4l2_subdev_get_try_format(subdev, sd_state,
307*0d346d2aSTomi Valkeinen 							fmt->pad);
308577bbf23SMaxime Ripard 	else
309577bbf23SMaxime Ripard 		subdev_fmt = &csi->subdev_fmt;
310577bbf23SMaxime Ripard 
311577bbf23SMaxime Ripard 	/* We can only set the format on the sink pad */
312577bbf23SMaxime Ripard 	if (fmt->pad == CSI_SUBDEV_SINK) {
313577bbf23SMaxime Ripard 		/* It's the sink, only allow changing the frame size */
314577bbf23SMaxime Ripard 		subdev_fmt->width = fmt->format.width;
315577bbf23SMaxime Ripard 		subdev_fmt->height = fmt->format.height;
316577bbf23SMaxime Ripard 		subdev_fmt->code = fmt->format.code;
317577bbf23SMaxime Ripard 	}
318577bbf23SMaxime Ripard 
319577bbf23SMaxime Ripard 	fmt->format = *subdev_fmt;
320577bbf23SMaxime Ripard 
321577bbf23SMaxime Ripard 	return 0;
322577bbf23SMaxime Ripard }
323577bbf23SMaxime Ripard 
324577bbf23SMaxime Ripard static int
sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * mbus)325577bbf23SMaxime Ripard sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
326*0d346d2aSTomi Valkeinen 				struct v4l2_subdev_state *sd_state,
327577bbf23SMaxime Ripard 				struct v4l2_subdev_mbus_code_enum *mbus)
328577bbf23SMaxime Ripard {
329577bbf23SMaxime Ripard 	if (mbus->index >= ARRAY_SIZE(sun4i_csi_formats))
330577bbf23SMaxime Ripard 		return -EINVAL;
331577bbf23SMaxime Ripard 
332577bbf23SMaxime Ripard 	mbus->code = sun4i_csi_formats[mbus->index].mbus;
333577bbf23SMaxime Ripard 
334577bbf23SMaxime Ripard 	return 0;
335577bbf23SMaxime Ripard }
336577bbf23SMaxime Ripard 
337577bbf23SMaxime Ripard static const struct v4l2_subdev_pad_ops sun4i_csi_subdev_pad_ops = {
338577bbf23SMaxime Ripard 	.link_validate	= v4l2_subdev_link_validate_default,
339577bbf23SMaxime Ripard 	.init_cfg	= sun4i_csi_subdev_init_cfg,
340577bbf23SMaxime Ripard 	.get_fmt	= sun4i_csi_subdev_get_fmt,
341577bbf23SMaxime Ripard 	.set_fmt	= sun4i_csi_subdev_set_fmt,
342577bbf23SMaxime Ripard 	.enum_mbus_code	= sun4i_csi_subdev_enum_mbus_code,
343577bbf23SMaxime Ripard };
344577bbf23SMaxime Ripard 
345577bbf23SMaxime Ripard const struct v4l2_subdev_ops sun4i_csi_subdev_ops = {
346577bbf23SMaxime Ripard 	.pad = &sun4i_csi_subdev_pad_ops,
347577bbf23SMaxime Ripard };
348577bbf23SMaxime Ripard 
sun4i_csi_v4l2_register(struct sun4i_csi * csi)349577bbf23SMaxime Ripard int sun4i_csi_v4l2_register(struct sun4i_csi *csi)
350577bbf23SMaxime Ripard {
351577bbf23SMaxime Ripard 	struct video_device *vdev = &csi->vdev;
352577bbf23SMaxime Ripard 	int ret;
353577bbf23SMaxime Ripard 
354577bbf23SMaxime Ripard 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING;
355577bbf23SMaxime Ripard 	vdev->v4l2_dev = &csi->v4l;
356577bbf23SMaxime Ripard 	vdev->queue = &csi->queue;
357577bbf23SMaxime Ripard 	strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
358577bbf23SMaxime Ripard 	vdev->release = video_device_release_empty;
359577bbf23SMaxime Ripard 	vdev->lock = &csi->lock;
360577bbf23SMaxime Ripard 
361577bbf23SMaxime Ripard 	/* Set a default format */
362835fd614SJulia Lawall 	csi->fmt.pixelformat = sun4i_csi_formats[0].fourcc;
363577bbf23SMaxime Ripard 	csi->fmt.width = CSI_DEFAULT_WIDTH;
364577bbf23SMaxime Ripard 	csi->fmt.height = CSI_DEFAULT_HEIGHT;
365577bbf23SMaxime Ripard 	_sun4i_csi_try_fmt(csi, &csi->fmt);
366577bbf23SMaxime Ripard 	csi->subdev_fmt = sun4i_csi_pad_fmt_default;
367577bbf23SMaxime Ripard 
368577bbf23SMaxime Ripard 	vdev->fops = &sun4i_csi_fops;
369577bbf23SMaxime Ripard 	vdev->ioctl_ops = &sun4i_csi_ioctl_ops;
370577bbf23SMaxime Ripard 	video_set_drvdata(vdev, csi);
371577bbf23SMaxime Ripard 
37270cad449SHans Verkuil 	ret = video_register_device(&csi->vdev, VFL_TYPE_VIDEO, -1);
373577bbf23SMaxime Ripard 	if (ret)
374577bbf23SMaxime Ripard 		return ret;
375577bbf23SMaxime Ripard 
376577bbf23SMaxime Ripard 	dev_info(csi->dev, "Device registered as %s\n",
377577bbf23SMaxime Ripard 		 video_device_node_name(vdev));
378577bbf23SMaxime Ripard 
379577bbf23SMaxime Ripard 	return 0;
380577bbf23SMaxime Ripard }
381