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 = ¶ms->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 = ¶ms->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(¶ms->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 = ¶ms->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 = ¶ms->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(¶ms->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 = ¶ms->video_dev;
564
565 vb2_video_unregister_device(video_dev);
566 media_entity_cleanup(&video_dev->entity);
567 mutex_destroy(¶ms->lock);
568 }
569