1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021-2022 Bootlin
4  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
5  */
6 
7 #include <media/v4l2-device.h>
8 #include <media/v4l2-event.h>
9 #include <media/v4l2-ioctl.h>
10 #include <media/v4l2-mc.h>
11 #include <media/videobuf2-vmalloc.h>
12 #include <media/videobuf2-v4l2.h>
13 
14 #include "sun6i_isp.h"
15 #include "sun6i_isp_params.h"
16 #include "sun6i_isp_reg.h"
17 #include "uapi/sun6i-isp-config.h"
18 
19 /* Params */
20 
21 static const struct sun6i_isp_params_config sun6i_isp_params_config_default = {
22 	.modules_used = SUN6I_ISP_MODULE_BAYER,
23 
24 	.bayer = {
25 		.offset_r	= 32,
26 		.offset_gr	= 32,
27 		.offset_gb	= 32,
28 		.offset_b	= 32,
29 
30 		.gain_r		= 256,
31 		.gain_gr	= 256,
32 		.gain_gb	= 256,
33 		.gain_b		= 256,
34 
35 	},
36 
37 	.bdnf = {
38 		.in_dis_min		= 8,
39 		.in_dis_max		= 16,
40 
41 		.coefficients_g		= { 15, 4, 1 },
42 		.coefficients_rb	= { 15, 4 },
43 	},
44 };
45 
sun6i_isp_params_configure_ob(struct sun6i_isp_device * isp_dev)46 static void sun6i_isp_params_configure_ob(struct sun6i_isp_device *isp_dev)
47 {
48 	unsigned int width, height;
49 
50 	sun6i_isp_proc_dimensions(isp_dev, &width, &height);
51 
52 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SIZE_REG,
53 			     SUN6I_ISP_OB_SIZE_WIDTH(width) |
54 			     SUN6I_ISP_OB_SIZE_HEIGHT(height));
55 
56 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_VALID_REG,
57 			     SUN6I_ISP_OB_VALID_WIDTH(width) |
58 			     SUN6I_ISP_OB_VALID_HEIGHT(height));
59 
60 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SRC0_VALID_START_REG,
61 			     SUN6I_ISP_OB_SRC0_VALID_START_HORZ(0) |
62 			     SUN6I_ISP_OB_SRC0_VALID_START_VERT(0));
63 }
64 
sun6i_isp_params_configure_ae(struct sun6i_isp_device * isp_dev)65 static void sun6i_isp_params_configure_ae(struct sun6i_isp_device *isp_dev)
66 {
67 	/* These are default values that need to be set to get an output. */
68 
69 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_AE_CFG_REG,
70 			     SUN6I_ISP_AE_CFG_LOW_BRI_TH(0xff) |
71 			     SUN6I_ISP_AE_CFG_HORZ_NUM(8) |
72 			     SUN6I_ISP_AE_CFG_HIGH_BRI_TH(0xf00) |
73 			     SUN6I_ISP_AE_CFG_VERT_NUM(8));
74 }
75 
76 static void
sun6i_isp_params_configure_bayer(struct sun6i_isp_device * isp_dev,const struct sun6i_isp_params_config * config)77 sun6i_isp_params_configure_bayer(struct sun6i_isp_device *isp_dev,
78 				 const struct sun6i_isp_params_config *config)
79 {
80 	const struct sun6i_isp_params_config_bayer *bayer = &config->bayer;
81 
82 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET0_REG,
83 			     SUN6I_ISP_BAYER_OFFSET0_R(bayer->offset_r) |
84 			     SUN6I_ISP_BAYER_OFFSET0_GR(bayer->offset_gr));
85 
86 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET1_REG,
87 			     SUN6I_ISP_BAYER_OFFSET1_GB(bayer->offset_gb) |
88 			     SUN6I_ISP_BAYER_OFFSET1_B(bayer->offset_b));
89 
90 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN0_REG,
91 			     SUN6I_ISP_BAYER_GAIN0_R(bayer->gain_r) |
92 			     SUN6I_ISP_BAYER_GAIN0_GR(bayer->gain_gr));
93 
94 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN1_REG,
95 			     SUN6I_ISP_BAYER_GAIN1_GB(bayer->gain_gb) |
96 			     SUN6I_ISP_BAYER_GAIN1_B(bayer->gain_b));
97 }
98 
sun6i_isp_params_configure_wb(struct sun6i_isp_device * isp_dev)99 static void sun6i_isp_params_configure_wb(struct sun6i_isp_device *isp_dev)
100 {
101 	/* These are default values that need to be set to get an output. */
102 
103 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN0_REG,
104 			     SUN6I_ISP_WB_GAIN0_R(256) |
105 			     SUN6I_ISP_WB_GAIN0_GR(256));
106 
107 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN1_REG,
108 			     SUN6I_ISP_WB_GAIN1_GB(256) |
109 			     SUN6I_ISP_WB_GAIN1_B(256));
110 
111 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_CFG_REG,
112 			     SUN6I_ISP_WB_CFG_CLIP(0xfff));
113 }
114 
sun6i_isp_params_configure_base(struct sun6i_isp_device * isp_dev)115 static void sun6i_isp_params_configure_base(struct sun6i_isp_device *isp_dev)
116 {
117 	sun6i_isp_params_configure_ae(isp_dev);
118 	sun6i_isp_params_configure_ob(isp_dev);
119 	sun6i_isp_params_configure_wb(isp_dev);
120 }
121 
122 static void
sun6i_isp_params_configure_bdnf(struct sun6i_isp_device * isp_dev,const struct sun6i_isp_params_config * config)123 sun6i_isp_params_configure_bdnf(struct sun6i_isp_device *isp_dev,
124 				const struct sun6i_isp_params_config *config)
125 {
126 	const struct sun6i_isp_params_config_bdnf *bdnf = &config->bdnf;
127 
128 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_CFG_REG,
129 			     SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(bdnf->in_dis_min) |
130 			     SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(bdnf->in_dis_max));
131 
132 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_RB_REG,
133 			     SUN6I_ISP_BDNF_COEF_RB(0, bdnf->coefficients_rb[0]) |
134 			     SUN6I_ISP_BDNF_COEF_RB(1, bdnf->coefficients_rb[1]) |
135 			     SUN6I_ISP_BDNF_COEF_RB(2, bdnf->coefficients_rb[2]) |
136 			     SUN6I_ISP_BDNF_COEF_RB(3, bdnf->coefficients_rb[3]) |
137 			     SUN6I_ISP_BDNF_COEF_RB(4, bdnf->coefficients_rb[4]));
138 
139 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_G_REG,
140 			     SUN6I_ISP_BDNF_COEF_G(0, bdnf->coefficients_g[0]) |
141 			     SUN6I_ISP_BDNF_COEF_G(1, bdnf->coefficients_g[1]) |
142 			     SUN6I_ISP_BDNF_COEF_G(2, bdnf->coefficients_g[2]) |
143 			     SUN6I_ISP_BDNF_COEF_G(3, bdnf->coefficients_g[3]) |
144 			     SUN6I_ISP_BDNF_COEF_G(4, bdnf->coefficients_g[4]) |
145 			     SUN6I_ISP_BDNF_COEF_G(5, bdnf->coefficients_g[5]) |
146 			     SUN6I_ISP_BDNF_COEF_G(6, bdnf->coefficients_g[6]));
147 }
148 
149 static void
sun6i_isp_params_configure_modules(struct sun6i_isp_device * isp_dev,const struct sun6i_isp_params_config * config)150 sun6i_isp_params_configure_modules(struct sun6i_isp_device *isp_dev,
151 				   const struct sun6i_isp_params_config *config)
152 {
153 	u32 value;
154 
155 	if (config->modules_used & SUN6I_ISP_MODULE_BDNF)
156 		sun6i_isp_params_configure_bdnf(isp_dev, config);
157 
158 	if (config->modules_used & SUN6I_ISP_MODULE_BAYER)
159 		sun6i_isp_params_configure_bayer(isp_dev, config);
160 
161 	value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG);
162 	/* Clear all modules but keep input configuration. */
163 	value &= SUN6I_ISP_MODULE_EN_SRC0 | SUN6I_ISP_MODULE_EN_SRC1;
164 
165 	if (config->modules_used & SUN6I_ISP_MODULE_BDNF)
166 		value |= SUN6I_ISP_MODULE_EN_BDNF;
167 
168 	/* Bayer stage is always enabled. */
169 
170 	sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value);
171 }
172 
sun6i_isp_params_configure(struct sun6i_isp_device * isp_dev)173 void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev)
174 {
175 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
176 	unsigned long flags;
177 
178 	spin_lock_irqsave(&state->lock, flags);
179 
180 	sun6i_isp_params_configure_base(isp_dev);
181 
182 	/* Default config is only applied at the very first stream start. */
183 	if (state->configured)
184 		goto complete;
185 
186 	sun6i_isp_params_configure_modules(isp_dev,
187 					   &sun6i_isp_params_config_default);
188 
189 	state->configured = true;
190 
191 complete:
192 	spin_unlock_irqrestore(&state->lock, flags);
193 }
194 
195 /* State */
196 
sun6i_isp_params_state_cleanup(struct sun6i_isp_device * isp_dev,bool error)197 static void sun6i_isp_params_state_cleanup(struct sun6i_isp_device *isp_dev,
198 					   bool error)
199 {
200 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
201 	struct sun6i_isp_buffer *isp_buffer;
202 	struct vb2_buffer *vb2_buffer;
203 	unsigned long flags;
204 
205 	spin_lock_irqsave(&state->lock, flags);
206 
207 	if (state->pending) {
208 		vb2_buffer = &state->pending->v4l2_buffer.vb2_buf;
209 		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
210 				VB2_BUF_STATE_QUEUED);
211 
212 		state->pending = NULL;
213 	}
214 
215 	list_for_each_entry(isp_buffer, &state->queue, list) {
216 		vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
217 		vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :
218 				VB2_BUF_STATE_QUEUED);
219 	}
220 
221 	INIT_LIST_HEAD(&state->queue);
222 
223 	spin_unlock_irqrestore(&state->lock, flags);
224 }
225 
sun6i_isp_params_state_update(struct sun6i_isp_device * isp_dev,bool * update)226 void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev,
227 				   bool *update)
228 {
229 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
230 	struct sun6i_isp_buffer *isp_buffer;
231 	struct vb2_buffer *vb2_buffer;
232 	const struct sun6i_isp_params_config *config;
233 	unsigned long flags;
234 
235 	spin_lock_irqsave(&state->lock, flags);
236 
237 	if (list_empty(&state->queue))
238 		goto complete;
239 
240 	if (state->pending)
241 		goto complete;
242 
243 	isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer,
244 				      list);
245 
246 	vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
247 	config = vb2_plane_vaddr(vb2_buffer, 0);
248 
249 	sun6i_isp_params_configure_modules(isp_dev, config);
250 
251 	list_del(&isp_buffer->list);
252 
253 	state->pending = isp_buffer;
254 
255 	if (update)
256 		*update = true;
257 
258 complete:
259 	spin_unlock_irqrestore(&state->lock, flags);
260 }
261 
sun6i_isp_params_state_complete(struct sun6i_isp_device * isp_dev)262 void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev)
263 {
264 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
265 	struct sun6i_isp_buffer *isp_buffer;
266 	struct vb2_buffer *vb2_buffer;
267 	unsigned long flags;
268 
269 	spin_lock_irqsave(&state->lock, flags);
270 
271 	if (!state->pending)
272 		goto complete;
273 
274 	isp_buffer = state->pending;
275 	vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf;
276 
277 	vb2_buffer->timestamp = ktime_get_ns();
278 
279 	/* Parameters will be applied starting from the next frame. */
280 	isp_buffer->v4l2_buffer.sequence = isp_dev->capture.state.sequence + 1;
281 
282 	vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);
283 
284 	state->pending = NULL;
285 
286 complete:
287 	spin_unlock_irqrestore(&state->lock, flags);
288 }
289 
290 /* Queue */
291 
sun6i_isp_params_queue_setup(struct vb2_queue * queue,unsigned int * buffers_count,unsigned int * planes_count,unsigned int sizes[],struct device * alloc_devs[])292 static int sun6i_isp_params_queue_setup(struct vb2_queue *queue,
293 					unsigned int *buffers_count,
294 					unsigned int *planes_count,
295 					unsigned int sizes[],
296 					struct device *alloc_devs[])
297 {
298 	struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
299 	unsigned int size = isp_dev->params.format.fmt.meta.buffersize;
300 
301 	if (*planes_count)
302 		return sizes[0] < size ? -EINVAL : 0;
303 
304 	*planes_count = 1;
305 	sizes[0] = size;
306 
307 	return 0;
308 }
309 
sun6i_isp_params_buffer_prepare(struct vb2_buffer * vb2_buffer)310 static int sun6i_isp_params_buffer_prepare(struct vb2_buffer *vb2_buffer)
311 {
312 	struct sun6i_isp_device *isp_dev =
313 		vb2_get_drv_priv(vb2_buffer->vb2_queue);
314 	struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
315 	unsigned int size = isp_dev->params.format.fmt.meta.buffersize;
316 
317 	if (vb2_plane_size(vb2_buffer, 0) < size) {
318 		v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n",
319 			 vb2_plane_size(vb2_buffer, 0), size);
320 		return -EINVAL;
321 	}
322 
323 	vb2_set_plane_payload(vb2_buffer, 0, size);
324 
325 	return 0;
326 }
327 
sun6i_isp_params_buffer_queue(struct vb2_buffer * vb2_buffer)328 static void sun6i_isp_params_buffer_queue(struct vb2_buffer *vb2_buffer)
329 {
330 	struct sun6i_isp_device *isp_dev =
331 		vb2_get_drv_priv(vb2_buffer->vb2_queue);
332 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
333 	struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer);
334 	struct sun6i_isp_buffer *isp_buffer =
335 		container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer);
336 	bool capture_streaming = isp_dev->capture.state.streaming;
337 	unsigned long flags;
338 
339 	spin_lock_irqsave(&state->lock, flags);
340 	list_add_tail(&isp_buffer->list, &state->queue);
341 	spin_unlock_irqrestore(&state->lock, flags);
342 
343 	if (state->streaming && capture_streaming)
344 		sun6i_isp_state_update(isp_dev, false);
345 }
346 
sun6i_isp_params_start_streaming(struct vb2_queue * queue,unsigned int count)347 static int sun6i_isp_params_start_streaming(struct vb2_queue *queue,
348 					    unsigned int count)
349 {
350 	struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
351 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
352 	bool capture_streaming = isp_dev->capture.state.streaming;
353 
354 	state->streaming = true;
355 
356 	/*
357 	 * Update the state as soon as possible if capture is streaming,
358 	 * otherwise it will be applied when capture starts streaming.
359 	 */
360 
361 	if (capture_streaming)
362 		sun6i_isp_state_update(isp_dev, false);
363 
364 	return 0;
365 }
366 
sun6i_isp_params_stop_streaming(struct vb2_queue * queue)367 static void sun6i_isp_params_stop_streaming(struct vb2_queue *queue)
368 {
369 	struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue);
370 	struct sun6i_isp_params_state *state = &isp_dev->params.state;
371 
372 	state->streaming = false;
373 	sun6i_isp_params_state_cleanup(isp_dev, true);
374 }
375 
376 static const struct vb2_ops sun6i_isp_params_queue_ops = {
377 	.queue_setup		= sun6i_isp_params_queue_setup,
378 	.buf_prepare		= sun6i_isp_params_buffer_prepare,
379 	.buf_queue		= sun6i_isp_params_buffer_queue,
380 	.start_streaming	= sun6i_isp_params_start_streaming,
381 	.stop_streaming		= sun6i_isp_params_stop_streaming,
382 	.wait_prepare		= vb2_ops_wait_prepare,
383 	.wait_finish		= vb2_ops_wait_finish,
384 };
385 
386 /* Video Device */
387 
sun6i_isp_params_querycap(struct file * file,void * private,struct v4l2_capability * capability)388 static int sun6i_isp_params_querycap(struct file *file, void *private,
389 				     struct v4l2_capability *capability)
390 {
391 	struct sun6i_isp_device *isp_dev = video_drvdata(file);
392 	struct video_device *video_dev = &isp_dev->params.video_dev;
393 
394 	strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver));
395 	strscpy(capability->card, video_dev->name, sizeof(capability->card));
396 	snprintf(capability->bus_info, sizeof(capability->bus_info),
397 		 "platform:%s", dev_name(isp_dev->dev));
398 
399 	return 0;
400 }
401 
sun6i_isp_params_enum_fmt(struct file * file,void * private,struct v4l2_fmtdesc * fmtdesc)402 static int sun6i_isp_params_enum_fmt(struct file *file, void *private,
403 				     struct v4l2_fmtdesc *fmtdesc)
404 {
405 	struct sun6i_isp_device *isp_dev = video_drvdata(file);
406 	struct v4l2_meta_format *params_format =
407 		&isp_dev->params.format.fmt.meta;
408 
409 	if (fmtdesc->index > 0)
410 		return -EINVAL;
411 
412 	fmtdesc->pixelformat = params_format->dataformat;
413 
414 	return 0;
415 }
416 
sun6i_isp_params_g_fmt(struct file * file,void * private,struct v4l2_format * format)417 static int sun6i_isp_params_g_fmt(struct file *file, void *private,
418 				  struct v4l2_format *format)
419 {
420 	struct sun6i_isp_device *isp_dev = video_drvdata(file);
421 
422 	*format = isp_dev->params.format;
423 
424 	return 0;
425 }
426 
427 static const struct v4l2_ioctl_ops sun6i_isp_params_ioctl_ops = {
428 	.vidioc_querycap		= sun6i_isp_params_querycap,
429 
430 	.vidioc_enum_fmt_meta_out	= sun6i_isp_params_enum_fmt,
431 	.vidioc_g_fmt_meta_out		= sun6i_isp_params_g_fmt,
432 	.vidioc_s_fmt_meta_out		= sun6i_isp_params_g_fmt,
433 	.vidioc_try_fmt_meta_out	= sun6i_isp_params_g_fmt,
434 
435 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
436 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
437 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
438 	.vidioc_querybuf		= vb2_ioctl_querybuf,
439 	.vidioc_expbuf			= vb2_ioctl_expbuf,
440 	.vidioc_qbuf			= vb2_ioctl_qbuf,
441 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
442 	.vidioc_streamon		= vb2_ioctl_streamon,
443 	.vidioc_streamoff		= vb2_ioctl_streamoff,
444 };
445 
446 static const struct v4l2_file_operations sun6i_isp_params_fops = {
447 	.owner		= THIS_MODULE,
448 	.unlocked_ioctl	= video_ioctl2,
449 	.open		= v4l2_fh_open,
450 	.release	= vb2_fop_release,
451 	.mmap		= vb2_fop_mmap,
452 	.poll		= vb2_fop_poll,
453 };
454 
455 /* Params */
456 
sun6i_isp_params_setup(struct sun6i_isp_device * isp_dev)457 int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev)
458 {
459 	struct sun6i_isp_params *params = &isp_dev->params;
460 	struct sun6i_isp_params_state *state = &params->state;
461 	struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev;
462 	struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev;
463 	struct video_device *video_dev = &params->video_dev;
464 	struct vb2_queue *queue = &isp_dev->params.queue;
465 	struct media_pad *pad = &isp_dev->params.pad;
466 	struct v4l2_format *format = &isp_dev->params.format;
467 	struct v4l2_meta_format *params_format = &format->fmt.meta;
468 	int ret;
469 
470 	/* State */
471 
472 	INIT_LIST_HEAD(&state->queue);
473 	spin_lock_init(&state->lock);
474 
475 	/* Media Pads */
476 
477 	pad->flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
478 
479 	ret = media_entity_pads_init(&video_dev->entity, 1, pad);
480 	if (ret)
481 		goto error_mutex;
482 
483 	/* Queue */
484 
485 	mutex_init(&params->lock);
486 
487 	queue->type = V4L2_BUF_TYPE_META_OUTPUT;
488 	queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
489 	queue->buf_struct_size = sizeof(struct sun6i_isp_buffer);
490 	queue->ops = &sun6i_isp_params_queue_ops;
491 	queue->mem_ops = &vb2_vmalloc_memops;
492 	queue->min_buffers_needed = 1;
493 	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
494 	queue->lock = &params->lock;
495 	queue->dev = isp_dev->dev;
496 	queue->drv_priv = isp_dev;
497 
498 	ret = vb2_queue_init(queue);
499 	if (ret) {
500 		v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
501 		goto error_media_entity;
502 	}
503 
504 	/* V4L2 Format */
505 
506 	format->type = queue->type;
507 	params_format->dataformat = V4L2_META_FMT_SUN6I_ISP_PARAMS;
508 	params_format->buffersize = sizeof(struct sun6i_isp_params_config);
509 
510 	/* Video Device */
511 
512 	strscpy(video_dev->name, SUN6I_ISP_PARAMS_NAME,
513 		sizeof(video_dev->name));
514 	video_dev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING;
515 	video_dev->vfl_dir = VFL_DIR_TX;
516 	video_dev->release = video_device_release_empty;
517 	video_dev->fops = &sun6i_isp_params_fops;
518 	video_dev->ioctl_ops = &sun6i_isp_params_ioctl_ops;
519 	video_dev->v4l2_dev = v4l2_dev;
520 	video_dev->queue = queue;
521 	video_dev->lock = &params->lock;
522 
523 	video_set_drvdata(video_dev, isp_dev);
524 
525 	ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
526 	if (ret) {
527 		v4l2_err(v4l2_dev, "failed to register video device: %d\n",
528 			 ret);
529 		goto error_media_entity;
530 	}
531 
532 	/* Media Pad Link */
533 
534 	ret = media_create_pad_link(&video_dev->entity, 0,
535 				    &proc_subdev->entity,
536 				    SUN6I_ISP_PROC_PAD_SINK_PARAMS,
537 				    MEDIA_LNK_FL_ENABLED |
538 				    MEDIA_LNK_FL_IMMUTABLE);
539 	if (ret < 0) {
540 		v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",
541 			 video_dev->entity.name, 0, proc_subdev->entity.name,
542 			 SUN6I_ISP_PROC_PAD_SINK_PARAMS);
543 		goto error_video_device;
544 	}
545 
546 	return 0;
547 
548 error_video_device:
549 	vb2_video_unregister_device(video_dev);
550 
551 error_media_entity:
552 	media_entity_cleanup(&video_dev->entity);
553 
554 error_mutex:
555 	mutex_destroy(&params->lock);
556 
557 	return ret;
558 }
559 
sun6i_isp_params_cleanup(struct sun6i_isp_device * isp_dev)560 void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev)
561 {
562 	struct sun6i_isp_params *params = &isp_dev->params;
563 	struct video_device *video_dev = &params->video_dev;
564 
565 	vb2_video_unregister_device(video_dev);
566 	media_entity_cleanup(&video_dev->entity);
567 	mutex_destroy(&params->lock);
568 }
569