1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */
3
4 #include <linux/compat.h>
5 #include <linux/delay.h>
6 #include <linux/interrupt.h>
7 #include <linux/iommu.h>
8 #include <linux/pm_runtime.h>
9 #include <linux/videodev2.h>
10 #include <media/media-entity.h>
11 #include <media/videobuf2-dma-contig.h>
12 #include <media/v4l2-event.h>
13
14 #include "dev.h"
15 #include "regs.h"
16
cal_fec_mesh(u32 width,u32 height,u32 mode)17 u32 cal_fec_mesh(u32 width, u32 height, u32 mode)
18 {
19 u32 mesh_size, mesh_left_height;
20 u32 w = ALIGN(width, 32);
21 u32 h = ALIGN(height, 32);
22 u32 spb_num = (h + 127) >> 7;
23 u32 left_height = h & 127;
24 u32 mesh_width = mode ? (w / 32 + 1) : (w / 16 + 1);
25 u32 mesh_height = mode ? 9 : 17;
26
27 if (!left_height)
28 left_height = 128;
29 mesh_left_height = mode ? (left_height / 16 + 1) :
30 (left_height / 8 + 1);
31 mesh_size = (spb_num - 1) * mesh_width * mesh_height +
32 mesh_width * mesh_left_height;
33
34 return mesh_size;
35 }
36
37 static const struct isppsd_fmt rkispp_formats[] = {
38 {
39 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
40 .fourcc = V4L2_PIX_FMT_NV16,
41 .wr_fmt = FMT_YUV422,
42 },
43 };
44
find_fmt(u32 mbus_code)45 static const struct isppsd_fmt *find_fmt(u32 mbus_code)
46 {
47 const struct isppsd_fmt *fmt;
48 int i, array_size = ARRAY_SIZE(rkispp_formats);
49
50 for (i = 0; i < array_size; i++) {
51 fmt = &rkispp_formats[i];
52 if (fmt->mbus_code == mbus_code)
53 return fmt;
54 }
55
56 return NULL;
57 }
58
rkispp_subdev_link_setup(struct media_entity * entity,const struct media_pad * local,const struct media_pad * remote,u32 flags)59 static int rkispp_subdev_link_setup(struct media_entity *entity,
60 const struct media_pad *local,
61 const struct media_pad *remote,
62 u32 flags)
63 {
64 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
65 struct rkispp_subdev *ispp_sdev;
66 struct rkispp_device *dev;
67 struct rkispp_stream_vdev *vdev;
68 struct rkispp_stream *stream = NULL;
69
70 if (local->index != RKISPP_PAD_SINK &&
71 local->index != RKISPP_PAD_SOURCE)
72 return 0;
73
74 if (!sd)
75 return -ENODEV;
76 ispp_sdev = v4l2_get_subdevdata(sd);
77 dev = ispp_sdev->dev;
78 vdev = &dev->stream_vdev;
79
80 if (!strcmp(remote->entity->name, II_VDEV_NAME)) {
81 stream = &vdev->stream[STREAM_II];
82 if (ispp_sdev->state & ISPP_START)
83 return -EBUSY;
84 if (flags & MEDIA_LNK_FL_ENABLED)
85 dev->inp = INP_DDR;
86 else if (ispp_sdev->remote_sd)
87 dev->inp = INP_ISP;
88 else
89 dev->inp = INP_INVAL;
90 stream->linked = flags & MEDIA_LNK_FL_ENABLED;
91 v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev,
92 "input:%d\n", dev->inp);
93 } else if (!strcmp(remote->entity->name, MB_VDEV_NAME)) {
94 stream = &vdev->stream[STREAM_MB];
95 } else if (!strcmp(remote->entity->name, S0_VDEV_NAME)) {
96 stream = &vdev->stream[STREAM_S0];
97 } else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) {
98 stream = &vdev->stream[STREAM_S1];
99 } else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) {
100 stream = &vdev->stream[STREAM_S2];
101 }
102 if (stream && dev->stream_sync) {
103 stream->linked = flags & MEDIA_LNK_FL_ENABLED;
104 v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev,
105 "stream:%d linked:%d\n",
106 stream->id, stream->linked);
107 }
108 return 0;
109 }
110
rkispp_sd_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)111 static int rkispp_sd_get_fmt(struct v4l2_subdev *sd,
112 struct v4l2_subdev_pad_config *cfg,
113 struct v4l2_subdev_format *fmt)
114 {
115 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
116 struct v4l2_mbus_framefmt *mf;
117 const struct isppsd_fmt *ispp_fmt;
118 int ret = 0;
119
120 if (!fmt)
121 goto err;
122
123 if (fmt->pad != RKISPP_PAD_SINK &&
124 fmt->pad != RKISPP_PAD_SOURCE)
125 goto err;
126
127 mf = &fmt->format;
128 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
129 if (!cfg)
130 goto err;
131 mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
132 }
133
134 *mf = ispp_sdev->in_fmt;
135 if (fmt->pad == RKISPP_PAD_SINK && ispp_sdev->dev->inp == INP_ISP) {
136 ret = v4l2_subdev_call(ispp_sdev->remote_sd,
137 pad, get_fmt, cfg, fmt);
138 if (!ret) {
139 ispp_fmt = find_fmt(fmt->format.code);
140 if (!ispp_fmt)
141 goto err;
142 ispp_sdev->in_fmt = *mf;
143 ispp_sdev->out_fmt = *ispp_fmt;
144 }
145 } else if (fmt->pad == RKISPP_PAD_SOURCE) {
146 *mf = ispp_sdev->in_fmt;
147 mf->width = ispp_sdev->out_fmt.width;
148 mf->height = ispp_sdev->out_fmt.height;
149 }
150 return ret;
151 err:
152 return -EINVAL;
153 }
154
rkispp_sd_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)155 static int rkispp_sd_set_fmt(struct v4l2_subdev *sd,
156 struct v4l2_subdev_pad_config *cfg,
157 struct v4l2_subdev_format *fmt)
158 {
159 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
160 struct v4l2_mbus_framefmt *mf;
161
162 if (!fmt)
163 return -EINVAL;
164
165 /* format from isp output */
166 if (fmt->pad == RKISPP_PAD_SINK && ispp_sdev->dev->inp == INP_ISP)
167 return 0;
168
169 mf = &fmt->format;
170 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
171 if (!cfg)
172 return -EINVAL;
173 mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
174 }
175
176 if (fmt->pad == RKISPP_PAD_SINK) {
177 ispp_sdev->in_fmt = *mf;
178 } else {
179 ispp_sdev->out_fmt.width = mf->width;
180 ispp_sdev->out_fmt.height = mf->height;
181 }
182
183 return 0;
184 }
185
rkispp_sd_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_selection * sel)186 static int rkispp_sd_get_selection(struct v4l2_subdev *sd,
187 struct v4l2_subdev_pad_config *cfg,
188 struct v4l2_subdev_selection *sel)
189 {
190 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
191 struct v4l2_rect *crop;
192 int ret = 0;
193
194 if (!sel)
195 goto err;
196 if (sel->pad != RKISPP_PAD_SINK)
197 goto err;
198
199 crop = &sel->r;
200 if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
201 if (!cfg)
202 goto err;
203 crop = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
204 }
205
206 if (ispp_sdev->dev->inp != INP_ISP) {
207 crop->left = 0;
208 crop->top = 0;
209 crop->width = ispp_sdev->in_fmt.width;
210 crop->height = ispp_sdev->in_fmt.height;
211 return 0;
212 }
213
214 ret = v4l2_subdev_call(ispp_sdev->remote_sd,
215 pad, get_selection, cfg, sel);
216 if (!ret && sel->target == V4L2_SEL_TGT_CROP) {
217 ispp_sdev->out_fmt.width = crop->width;
218 ispp_sdev->out_fmt.height = crop->height;
219 }
220
221 return ret;
222 err:
223 return -EINVAL;
224 }
225
rkispp_sd_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_selection * sel)226 static int rkispp_sd_set_selection(struct v4l2_subdev *sd,
227 struct v4l2_subdev_pad_config *cfg,
228 struct v4l2_subdev_selection *sel)
229 {
230 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
231 struct v4l2_rect *crop;
232 int ret = 0;
233
234 if (!sel)
235 goto err;
236 if (sel->pad != RKISPP_PAD_SINK ||
237 sel->target != V4L2_SEL_TGT_CROP)
238 goto err;
239
240 crop = &sel->r;
241 if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
242 if (!cfg)
243 goto err;
244 crop = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
245 }
246
247 if (ispp_sdev->dev->inp != INP_ISP) {
248 crop->left = 0;
249 crop->top = 0;
250 crop->width = ispp_sdev->in_fmt.width;
251 crop->height = ispp_sdev->in_fmt.height;
252 return 0;
253 }
254
255 ret = v4l2_subdev_call(ispp_sdev->remote_sd,
256 pad, set_selection, cfg, sel);
257 if (!ret) {
258 ispp_sdev->out_fmt.width = crop->width;
259 ispp_sdev->out_fmt.height = crop->height;
260 }
261
262 return ret;
263 err:
264 return -EINVAL;
265 }
266
rkispp_sd_s_stream(struct v4l2_subdev * sd,int on)267 static int rkispp_sd_s_stream(struct v4l2_subdev *sd, int on)
268 {
269 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
270 struct rkispp_device *dev = ispp_sdev->dev;
271 int ret = 0;
272
273 v4l2_dbg(1, rkispp_debug, &ispp_sdev->dev->v4l2_dev,
274 "s_stream on:%d\n", on);
275
276 if (on) {
277 ispp_sdev->state = ISPP_START;
278 ispp_sdev->frm_sync_seq = -1;
279 ispp_sdev->frame_timestamp = 0;
280 rkispp_event_handle(dev, CMD_STREAM, &ispp_sdev->state);
281 }
282
283 if (dev->inp == INP_ISP)
284 ret = v4l2_subdev_call(ispp_sdev->remote_sd, video, s_stream, on);
285
286 if ((on && ret) || (!on && !ret)) {
287 ispp_sdev->state = ISPP_STOP;
288 if (dev->stream_vdev.monitor.is_en) {
289 dev->stream_vdev.monitor.is_en = false;
290 if (!completion_done(&dev->stream_vdev.monitor.cmpl))
291 complete(&dev->stream_vdev.monitor.cmpl);
292 if (!completion_done(&dev->stream_vdev.monitor.tnr.cmpl))
293 complete(&dev->stream_vdev.monitor.tnr.cmpl);
294 if (!completion_done(&dev->stream_vdev.monitor.nr.cmpl))
295 complete(&dev->stream_vdev.monitor.nr.cmpl);
296 if (!completion_done(&dev->stream_vdev.monitor.fec.cmpl))
297 complete(&dev->stream_vdev.monitor.fec.cmpl);
298 }
299 rkispp_event_handle(dev, CMD_STREAM, &ispp_sdev->state);
300 }
301 return ret;
302 }
303
rkispp_sd_s_rx_buffer(struct v4l2_subdev * sd,void * buf,unsigned int * size)304 static int rkispp_sd_s_rx_buffer(struct v4l2_subdev *sd,
305 void *buf, unsigned int *size)
306 {
307 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
308 struct rkispp_device *dev = ispp_sdev->dev;
309 u32 cmd = CMD_INIT_POOL;
310
311 /* size isn't using now */
312 if (!buf)
313 return -EINVAL;
314
315 if (ispp_sdev->state == ISPP_START) {
316 struct rkisp_ispp_buf *dbufs = buf;
317 struct rkispp_stream_vdev *vdev = &dev->stream_vdev;
318 u64 ns = ktime_get_ns();
319
320 vdev->dbg.interval = ns - vdev->dbg.timestamp;
321 vdev->dbg.timestamp = ns;
322 vdev->dbg.delay = ns - dbufs->frame_timestamp;
323 vdev->dbg.id = dbufs->frame_id;
324 cmd = CMD_QUEUE_DMABUF;
325 }
326
327 return rkispp_event_handle(dev, cmd, buf);
328 }
329
rkispp_sd_s_power(struct v4l2_subdev * sd,int on)330 static int rkispp_sd_s_power(struct v4l2_subdev *sd, int on)
331 {
332 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
333 struct rkispp_device *ispp_dev = ispp_sdev->dev;
334 int ret;
335
336 v4l2_dbg(1, rkispp_debug, &ispp_dev->v4l2_dev,
337 "s_power on:%d\n", on);
338 if (on) {
339 if (ispp_dev->inp == INP_ISP) {
340 struct v4l2_subdev_format fmt;
341 struct v4l2_subdev_selection sel;
342
343 /* update format, if ispp input change */
344 fmt.pad = RKISPP_PAD_SINK;
345 fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
346 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
347 if (ret) {
348 v4l2_err(&ispp_dev->v4l2_dev,
349 "%s get format fail:%d\n",
350 __func__, ret);
351 return ret;
352 }
353 sel.pad = RKISPP_PAD_SINK;
354 sel.target = V4L2_SEL_TGT_CROP;
355 sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
356 ret = v4l2_subdev_call(sd, pad,
357 get_selection, NULL, &sel);
358 if (ret) {
359 v4l2_err(&ispp_dev->v4l2_dev,
360 "%s get crop fail:%d\n",
361 __func__, ret);
362 return ret;
363 }
364
365 ret = v4l2_subdev_call(ispp_sdev->remote_sd,
366 core, s_power, 1);
367 if (ret < 0) {
368 v4l2_err(&ispp_dev->v4l2_dev,
369 "%s set isp power on fail:%d\n",
370 __func__, ret);
371 return ret;
372 }
373 }
374 ret = pm_runtime_get_sync(ispp_dev->dev);
375 if (ret < 0) {
376 v4l2_err(&ispp_dev->v4l2_dev,
377 "%s runtime get failed:%d\n",
378 __func__, ret);
379 if (ispp_dev->inp == INP_ISP)
380 v4l2_subdev_call(ispp_sdev->remote_sd,
381 core, s_power, 0);
382 return ret;
383 }
384 } else {
385 if (ispp_dev->inp == INP_ISP)
386 v4l2_subdev_call(ispp_sdev->remote_sd, core, s_power, 0);
387 ret = pm_runtime_put_sync(ispp_dev->dev);
388 if (ret < 0)
389 v4l2_err(&ispp_dev->v4l2_dev,
390 "%s runtime put failed:%d\n",
391 __func__, ret);
392 }
393
394 return ret;
395 }
396
rkispp_ioctl(struct v4l2_subdev * sd,unsigned int cmd,void * arg)397 static long rkispp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
398 {
399 struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
400 struct rkispp_device *ispp_dev = ispp_sdev->dev;
401 struct rkispp_fecbuf_info *fecbuf;
402 struct rkispp_fecbuf_size *fecsize;
403 struct rkisp_ispp_reg **reg_buf;
404 bool *rkispp_reg_withstream;
405 long ret = 0;
406
407 if (!arg)
408 return -EINVAL;
409
410 switch (cmd) {
411 case RKISPP_CMD_GET_FECBUF_INFO:
412 fecbuf = (struct rkispp_fecbuf_info *)arg;
413 rkispp_params_get_fecbuf_inf(&ispp_dev->params_vdev, fecbuf);
414 break;
415 case RKISPP_CMD_SET_FECBUF_SIZE:
416 fecsize = (struct rkispp_fecbuf_size *)arg;
417 rkispp_params_set_fecbuf_size(&ispp_dev->params_vdev, fecsize);
418 break;
419 case RKISP_ISPP_CMD_REQUEST_REGBUF:
420 reg_buf = (struct rkisp_ispp_reg **)arg;
421 rkispp_request_regbuf(ispp_dev, reg_buf);
422 break;
423 case RKISP_ISPP_CMD_GET_REG_WITHSTREAM:
424 rkispp_reg_withstream = arg;
425 *rkispp_reg_withstream = rkispp_is_reg_withstream_global();
426 break;
427 #if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISPP_VERSION_V10)
428 case RKISPP_CMD_TRIGGER_YNRRUN:
429 rkispp_sendbuf_to_nr(ispp_dev, (struct rkispp_tnr_inf *)arg);
430 break;
431 case RKISPP_CMD_GET_TNRBUF_FD:
432 ret = rkispp_get_tnrbuf_fd(ispp_dev, (struct rkispp_buf_idxfd *)arg);
433 break;
434 case RKISPP_CMD_TRIGGER_MODE:
435 rkispp_set_trigger_mode(ispp_dev, (struct rkispp_trigger_mode *)arg);
436 break;
437 #endif
438 default:
439 ret = -ENOIOCTLCMD;
440 }
441
442 return ret;
443 }
444
445 #ifdef CONFIG_COMPAT
rkispp_compat_ioctl32(struct v4l2_subdev * sd,unsigned int cmd,unsigned long arg)446 static long rkispp_compat_ioctl32(struct v4l2_subdev *sd,
447 unsigned int cmd, unsigned long arg)
448 {
449 void __user *up = compat_ptr(arg);
450 struct rkispp_fecbuf_info fecbuf;
451 struct rkispp_fecbuf_size fecsize;
452 struct rkispp_tnr_inf tnr_inf;
453 struct rkispp_buf_idxfd idxfd;
454 struct rkispp_trigger_mode t_mode;
455 long ret = 0;
456
457 if (!up)
458 return -EINVAL;
459
460 switch (cmd) {
461 case RKISPP_CMD_GET_FECBUF_INFO:
462 ret = rkispp_ioctl(sd, cmd, &fecbuf);
463 if (!ret && copy_to_user(up, &fecbuf, sizeof(fecbuf)))
464 ret = -EFAULT;
465 break;
466 case RKISPP_CMD_SET_FECBUF_SIZE:
467 if (copy_from_user(&fecsize, up, sizeof(fecsize)))
468 return -EFAULT;
469 ret = rkispp_ioctl(sd, cmd, &fecsize);
470 break;
471 case RKISPP_CMD_TRIGGER_YNRRUN:
472 if (copy_from_user(&tnr_inf, up, sizeof(tnr_inf)))
473 return -EFAULT;
474 ret = rkispp_ioctl(sd, cmd, &tnr_inf);
475 break;
476 case RKISPP_CMD_GET_TNRBUF_FD:
477 ret = rkispp_ioctl(sd, cmd, &idxfd);
478 if (!ret && copy_to_user(up, &idxfd, sizeof(idxfd)))
479 ret = -EFAULT;
480 break;
481 case RKISPP_CMD_TRIGGER_MODE:
482 if (copy_from_user(&t_mode, up, sizeof(t_mode)))
483 return -EFAULT;
484 ret = rkispp_ioctl(sd, cmd, &t_mode);
485 break;
486 default:
487 ret = -ENOIOCTLCMD;
488 }
489
490 return ret;
491 }
492 #endif
493
rkispp_subscribe_event(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)494 static int rkispp_subscribe_event(struct v4l2_subdev *sd,
495 struct v4l2_fh *fh,
496 struct v4l2_event_subscription *sub)
497 {
498 switch (sub->type) {
499 case RKISPP_V4L2_EVENT_TNR_COMPLETE:
500 return v4l2_event_subscribe(fh, sub, RKISPP_BUF_MAX, NULL);
501 default:
502 return -EINVAL;
503 }
504 }
505
506 static const struct media_entity_operations rkispp_sd_media_ops = {
507 .link_setup = rkispp_subdev_link_setup,
508 .link_validate = v4l2_subdev_link_validate,
509 };
510
511 static const struct v4l2_subdev_pad_ops rkispp_sd_pad_ops = {
512 .get_fmt = rkispp_sd_get_fmt,
513 .set_fmt = rkispp_sd_set_fmt,
514 .get_selection = rkispp_sd_get_selection,
515 .set_selection = rkispp_sd_set_selection,
516 };
517
518 static const struct v4l2_subdev_video_ops rkispp_sd_video_ops = {
519 .s_stream = rkispp_sd_s_stream,
520 .s_rx_buffer = rkispp_sd_s_rx_buffer,
521 };
522
523 static const struct v4l2_subdev_core_ops rkispp_sd_core_ops = {
524 .s_power = rkispp_sd_s_power,
525 .ioctl = rkispp_ioctl,
526 #ifdef CONFIG_COMPAT
527 .compat_ioctl32 = rkispp_compat_ioctl32,
528 #endif
529 .subscribe_event = rkispp_subscribe_event,
530 .unsubscribe_event = v4l2_event_subdev_unsubscribe,
531 };
532
533 static struct v4l2_subdev_ops rkispp_sd_ops = {
534 .core = &rkispp_sd_core_ops,
535 .video = &rkispp_sd_video_ops,
536 .pad = &rkispp_sd_pad_ops,
537 };
538
rkispp_register_subdev(struct rkispp_device * dev,struct v4l2_device * v4l2_dev)539 int rkispp_register_subdev(struct rkispp_device *dev,
540 struct v4l2_device *v4l2_dev)
541 {
542 struct rkispp_subdev *ispp_sdev = &dev->ispp_sdev;
543 struct v4l2_subdev *sd;
544 int ret;
545
546 memset(ispp_sdev, 0, sizeof(*ispp_sdev));
547 ispp_sdev->dev = dev;
548 sd = &ispp_sdev->sd;
549 ispp_sdev->state = ISPP_STOP;
550 v4l2_subdev_init(sd, &rkispp_sd_ops);
551 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
552 sd->entity.ops = &rkispp_sd_media_ops;
553 snprintf(sd->name, sizeof(sd->name), "rkispp-subdev");
554 sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
555 ispp_sdev->pads[RKISPP_PAD_SINK].flags =
556 MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
557 ispp_sdev->pads[RKISPP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
558 ispp_sdev->pads[RKISPP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
559 ispp_sdev->pads[RKISPP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
560
561 ret = media_entity_pads_init(&sd->entity, RKISPP_PAD_MAX,
562 ispp_sdev->pads);
563 if (ret < 0)
564 return ret;
565 sd->owner = THIS_MODULE;
566 v4l2_set_subdevdata(sd, ispp_sdev);
567 sd->grp_id = GRP_ID_ISPP;
568 ret = v4l2_device_register_subdev(v4l2_dev, sd);
569 if (ret < 0)
570 goto free_media;
571
572 ret = v4l2_device_register_subdev_nodes(v4l2_dev);
573 if (ret < 0)
574 goto free_subdev;
575 return ret;
576 free_subdev:
577 v4l2_device_unregister_subdev(sd);
578 free_media:
579 media_entity_cleanup(&sd->entity);
580 v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret);
581 return ret;
582 }
583
rkispp_unregister_subdev(struct rkispp_device * dev)584 void rkispp_unregister_subdev(struct rkispp_device *dev)
585 {
586 struct v4l2_subdev *sd = &dev->ispp_sdev.sd;
587
588 v4l2_device_unregister_subdev(sd);
589 media_entity_cleanup(&sd->entity);
590 }
591