1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Copyright (C) 2021 Renesas Electronics Corp. |
4 | * |
5 | * Driver for Renesas R-Car ISP Channel Selector |
6 | * |
7 | * The ISP hardware is capable of more than just channel selection, features |
8 | * such as demosaicing, white balance control and color space conversion are |
9 | * also possible. These more advanced features are not supported by the driver |
10 | * due to lack of documentation. |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/mutex.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/reset.h> |
19 | |
20 | #include <media/mipi-csi2.h> |
21 | #include <media/v4l2-subdev.h> |
22 | |
23 | #define ISPINPUTSEL0_REG 0x0008 |
24 | #define ISPINPUTSEL0_SEL_CSI0 BIT(31) |
25 | |
26 | #define ISPSTART_REG 0x0014 |
27 | #define ISPSTART_START 0xffff |
28 | #define ISPSTART_STOP 0x0000 |
29 | |
30 | #define ISPPROCMODE_DT_REG(n) (0x1100 + (0x4 * (n))) |
31 | #define ISPPROCMODE_DT_PROC_MODE_VC3(pm) (((pm) & 0x3f) << 24) |
32 | #define ISPPROCMODE_DT_PROC_MODE_VC2(pm) (((pm) & 0x3f) << 16) |
33 | #define ISPPROCMODE_DT_PROC_MODE_VC1(pm) (((pm) & 0x3f) << 8) |
34 | #define ISPPROCMODE_DT_PROC_MODE_VC0(pm) ((pm) & 0x3f) |
35 | |
36 | #define ISPCS_FILTER_ID_CH_REG(n) (0x3000 + (0x0100 * (n))) |
37 | |
38 | #define ISPCS_DT_CODE03_CH_REG(n) (0x3008 + (0x100 * (n))) |
39 | #define ISPCS_DT_CODE03_EN3 BIT(31) |
40 | #define ISPCS_DT_CODE03_DT3(dt) (((dt) & 0x3f) << 24) |
41 | #define ISPCS_DT_CODE03_EN2 BIT(23) |
42 | #define ISPCS_DT_CODE03_DT2(dt) (((dt) & 0x3f) << 16) |
43 | #define ISPCS_DT_CODE03_EN1 BIT(15) |
44 | #define ISPCS_DT_CODE03_DT1(dt) (((dt) & 0x3f) << 8) |
45 | #define ISPCS_DT_CODE03_EN0 BIT(7) |
46 | #define ISPCS_DT_CODE03_DT0(dt) ((dt) & 0x3f) |
47 | |
48 | struct rcar_isp_format { |
49 | u32 code; |
50 | unsigned int datatype; |
51 | unsigned int procmode; |
52 | }; |
53 | |
54 | static const struct rcar_isp_format rcar_isp_formats[] = { |
55 | { |
56 | .code = MEDIA_BUS_FMT_RGB888_1X24, |
57 | .datatype = MIPI_CSI2_DT_RGB888, |
58 | .procmode = 0x15 |
59 | }, { |
60 | .code = MEDIA_BUS_FMT_Y10_1X10, |
61 | .datatype = MIPI_CSI2_DT_RAW10, |
62 | .procmode = 0x10, |
63 | }, { |
64 | .code = MEDIA_BUS_FMT_UYVY8_1X16, |
65 | .datatype = MIPI_CSI2_DT_YUV422_8B, |
66 | .procmode = 0x0c, |
67 | }, { |
68 | .code = MEDIA_BUS_FMT_YUYV8_1X16, |
69 | .datatype = MIPI_CSI2_DT_YUV422_8B, |
70 | .procmode = 0x0c, |
71 | }, { |
72 | .code = MEDIA_BUS_FMT_UYVY8_2X8, |
73 | .datatype = MIPI_CSI2_DT_YUV422_8B, |
74 | .procmode = 0x0c, |
75 | }, { |
76 | .code = MEDIA_BUS_FMT_YUYV10_2X10, |
77 | .datatype = MIPI_CSI2_DT_YUV422_8B, |
78 | .procmode = 0x0c, |
79 | }, { |
80 | .code = MEDIA_BUS_FMT_SBGGR8_1X8, |
81 | .datatype = MIPI_CSI2_DT_RAW8, |
82 | .procmode = 0x00, |
83 | }, { |
84 | .code = MEDIA_BUS_FMT_SGBRG8_1X8, |
85 | .datatype = MIPI_CSI2_DT_RAW8, |
86 | .procmode = 0x00, |
87 | }, { |
88 | .code = MEDIA_BUS_FMT_SGRBG8_1X8, |
89 | .datatype = MIPI_CSI2_DT_RAW8, |
90 | .procmode = 0x00, |
91 | }, { |
92 | .code = MEDIA_BUS_FMT_SRGGB8_1X8, |
93 | .datatype = MIPI_CSI2_DT_RAW8, |
94 | .procmode = 0x00, |
95 | }, { |
96 | .code = MEDIA_BUS_FMT_SBGGR10_1X10, |
97 | .datatype = MIPI_CSI2_DT_RAW10, |
98 | .procmode = 0x01, |
99 | }, { |
100 | .code = MEDIA_BUS_FMT_SGBRG10_1X10, |
101 | .datatype = MIPI_CSI2_DT_RAW10, |
102 | .procmode = 0x01, |
103 | }, { |
104 | .code = MEDIA_BUS_FMT_SGRBG10_1X10, |
105 | .datatype = MIPI_CSI2_DT_RAW10, |
106 | .procmode = 0x01, |
107 | }, { |
108 | .code = MEDIA_BUS_FMT_SRGGB10_1X10, |
109 | .datatype = MIPI_CSI2_DT_RAW10, |
110 | .procmode = 0x01, |
111 | }, { |
112 | .code = MEDIA_BUS_FMT_SBGGR12_1X12, |
113 | .datatype = MIPI_CSI2_DT_RAW12, |
114 | .procmode = 0x02, |
115 | }, { |
116 | .code = MEDIA_BUS_FMT_SGBRG12_1X12, |
117 | .datatype = MIPI_CSI2_DT_RAW12, |
118 | .procmode = 0x02, |
119 | }, { |
120 | .code = MEDIA_BUS_FMT_SGRBG12_1X12, |
121 | .datatype = MIPI_CSI2_DT_RAW12, |
122 | .procmode = 0x02, |
123 | }, { |
124 | .code = MEDIA_BUS_FMT_SRGGB12_1X12, |
125 | .datatype = MIPI_CSI2_DT_RAW12, |
126 | .procmode = 0x02, |
127 | }, |
128 | }; |
129 | |
130 | static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code) |
131 | { |
132 | unsigned int i; |
133 | |
134 | for (i = 0; i < ARRAY_SIZE(rcar_isp_formats); i++) { |
135 | if (rcar_isp_formats[i].code == code) |
136 | return &rcar_isp_formats[i]; |
137 | } |
138 | |
139 | return NULL; |
140 | } |
141 | |
142 | enum rcar_isp_input { |
143 | RISP_CSI_INPUT0, |
144 | RISP_CSI_INPUT1, |
145 | }; |
146 | |
147 | enum rcar_isp_pads { |
148 | RCAR_ISP_SINK, |
149 | RCAR_ISP_PORT0, |
150 | RCAR_ISP_PORT1, |
151 | RCAR_ISP_PORT2, |
152 | RCAR_ISP_PORT3, |
153 | RCAR_ISP_PORT4, |
154 | RCAR_ISP_PORT5, |
155 | RCAR_ISP_PORT6, |
156 | RCAR_ISP_PORT7, |
157 | RCAR_ISP_NUM_PADS, |
158 | }; |
159 | |
160 | struct rcar_isp { |
161 | struct device *dev; |
162 | void __iomem *csbase; |
163 | struct reset_control *rstc; |
164 | |
165 | enum rcar_isp_input csi_input; |
166 | |
167 | struct v4l2_subdev subdev; |
168 | struct media_pad pads[RCAR_ISP_NUM_PADS]; |
169 | |
170 | struct v4l2_async_notifier notifier; |
171 | struct v4l2_subdev *remote; |
172 | unsigned int remote_pad; |
173 | |
174 | int stream_count; |
175 | }; |
176 | |
177 | static inline struct rcar_isp *sd_to_isp(struct v4l2_subdev *sd) |
178 | { |
179 | return container_of(sd, struct rcar_isp, subdev); |
180 | } |
181 | |
182 | static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n) |
183 | { |
184 | return container_of(n, struct rcar_isp, notifier); |
185 | } |
186 | |
187 | static void risp_write_cs(struct rcar_isp *isp, u32 offset, u32 value) |
188 | { |
189 | iowrite32(value, isp->csbase + offset); |
190 | } |
191 | |
192 | static u32 risp_read_cs(struct rcar_isp *isp, u32 offset) |
193 | { |
194 | return ioread32(isp->csbase + offset); |
195 | } |
196 | |
197 | static int risp_power_on(struct rcar_isp *isp) |
198 | { |
199 | int ret; |
200 | |
201 | ret = pm_runtime_resume_and_get(dev: isp->dev); |
202 | if (ret < 0) |
203 | return ret; |
204 | |
205 | ret = reset_control_deassert(rstc: isp->rstc); |
206 | if (ret < 0) { |
207 | pm_runtime_put(dev: isp->dev); |
208 | return ret; |
209 | } |
210 | |
211 | return 0; |
212 | } |
213 | |
214 | static void risp_power_off(struct rcar_isp *isp) |
215 | { |
216 | reset_control_assert(rstc: isp->rstc); |
217 | pm_runtime_put(dev: isp->dev); |
218 | } |
219 | |
220 | static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state) |
221 | { |
222 | const struct v4l2_mbus_framefmt *fmt; |
223 | const struct rcar_isp_format *format; |
224 | unsigned int vc; |
225 | u32 sel_csi = 0; |
226 | int ret; |
227 | |
228 | fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK); |
229 | if (!fmt) |
230 | return -EINVAL; |
231 | |
232 | format = risp_code_to_fmt(code: fmt->code); |
233 | if (!format) { |
234 | dev_err(isp->dev, "Unsupported bus format\n"); |
235 | return -EINVAL; |
236 | } |
237 | |
238 | ret = risp_power_on(isp); |
239 | if (ret) { |
240 | dev_err(isp->dev, "Failed to power on ISP\n"); |
241 | return ret; |
242 | } |
243 | |
244 | /* Select CSI-2 input source. */ |
245 | if (isp->csi_input == RISP_CSI_INPUT1) |
246 | sel_csi = ISPINPUTSEL0_SEL_CSI0; |
247 | |
248 | risp_write_cs(isp, ISPINPUTSEL0_REG, |
249 | value: risp_read_cs(isp, ISPINPUTSEL0_REG) | sel_csi); |
250 | |
251 | /* Configure Channel Selector. */ |
252 | for (vc = 0; vc < 4; vc++) { |
253 | u8 ch = vc + 4; |
254 | u8 dt = format->datatype; |
255 | |
256 | risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc)); |
257 | risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch), |
258 | ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) | |
259 | ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) | |
260 | ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) | |
261 | ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt)); |
262 | } |
263 | |
264 | /* Setup processing method. */ |
265 | risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype), |
266 | ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) | |
267 | ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) | |
268 | ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) | |
269 | ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode)); |
270 | |
271 | /* Start ISP. */ |
272 | risp_write_cs(isp, ISPSTART_REG, ISPSTART_START); |
273 | |
274 | ret = v4l2_subdev_enable_streams(sd: isp->remote, pad: isp->remote_pad, |
275 | BIT_ULL(0)); |
276 | if (ret) |
277 | risp_power_off(isp); |
278 | |
279 | return ret; |
280 | } |
281 | |
282 | static void risp_stop(struct rcar_isp *isp) |
283 | { |
284 | v4l2_subdev_disable_streams(sd: isp->remote, pad: isp->remote_pad, BIT_ULL(0)); |
285 | |
286 | /* Stop ISP. */ |
287 | risp_write_cs(isp, ISPSTART_REG, ISPSTART_STOP); |
288 | |
289 | risp_power_off(isp); |
290 | } |
291 | |
292 | static int risp_enable_streams(struct v4l2_subdev *sd, |
293 | struct v4l2_subdev_state *state, u32 source_pad, |
294 | u64 source_streams_mask) |
295 | { |
296 | struct rcar_isp *isp = sd_to_isp(sd); |
297 | int ret = 0; |
298 | |
299 | if (source_streams_mask != 1) |
300 | return -EINVAL; |
301 | |
302 | if (!isp->remote) |
303 | return -ENODEV; |
304 | |
305 | if (isp->stream_count == 0) { |
306 | ret = risp_start(isp, state); |
307 | if (ret) |
308 | return ret; |
309 | } |
310 | |
311 | isp->stream_count += 1; |
312 | |
313 | return ret; |
314 | } |
315 | |
316 | static int risp_disable_streams(struct v4l2_subdev *sd, |
317 | struct v4l2_subdev_state *state, u32 source_pad, |
318 | u64 source_streams_mask) |
319 | { |
320 | struct rcar_isp *isp = sd_to_isp(sd); |
321 | |
322 | if (source_streams_mask != 1) |
323 | return -EINVAL; |
324 | |
325 | if (!isp->remote) |
326 | return -ENODEV; |
327 | |
328 | if (isp->stream_count == 1) |
329 | risp_stop(isp); |
330 | |
331 | isp->stream_count -= 1; |
332 | |
333 | return 0; |
334 | } |
335 | |
336 | static int risp_set_pad_format(struct v4l2_subdev *sd, |
337 | struct v4l2_subdev_state *state, |
338 | struct v4l2_subdev_format *format) |
339 | { |
340 | struct v4l2_mbus_framefmt *framefmt; |
341 | |
342 | if (format->pad > RCAR_ISP_SINK) |
343 | return v4l2_subdev_get_fmt(sd, state, format); |
344 | |
345 | if (!risp_code_to_fmt(code: format->format.code)) |
346 | format->format.code = rcar_isp_formats[0].code; |
347 | |
348 | for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) { |
349 | framefmt = v4l2_subdev_state_get_format(state, i); |
350 | *framefmt = format->format; |
351 | } |
352 | |
353 | return 0; |
354 | } |
355 | |
356 | static const struct v4l2_subdev_pad_ops risp_pad_ops = { |
357 | .enable_streams = risp_enable_streams, |
358 | .disable_streams = risp_disable_streams, |
359 | .set_fmt = risp_set_pad_format, |
360 | .get_fmt = v4l2_subdev_get_fmt, |
361 | .link_validate = v4l2_subdev_link_validate_default, |
362 | }; |
363 | |
364 | static const struct v4l2_subdev_ops rcar_isp_subdev_ops = { |
365 | .pad = &risp_pad_ops, |
366 | }; |
367 | |
368 | /* ----------------------------------------------------------------------------- |
369 | * Async handling and registration of subdevices and links |
370 | */ |
371 | |
372 | static int risp_notify_bound(struct v4l2_async_notifier *notifier, |
373 | struct v4l2_subdev *subdev, |
374 | struct v4l2_async_connection *asd) |
375 | { |
376 | struct rcar_isp *isp = notifier_to_isp(n: notifier); |
377 | int pad; |
378 | |
379 | pad = media_entity_get_fwnode_pad(entity: &subdev->entity, fwnode: asd->match.fwnode, |
380 | MEDIA_PAD_FL_SOURCE); |
381 | if (pad < 0) { |
382 | dev_err(isp->dev, "Failed to find pad for %s\n", subdev->name); |
383 | return pad; |
384 | } |
385 | |
386 | isp->remote = subdev; |
387 | isp->remote_pad = pad; |
388 | |
389 | dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad); |
390 | |
391 | return media_create_pad_link(source: &subdev->entity, source_pad: pad, |
392 | sink: &isp->subdev.entity, sink_pad: 0, |
393 | MEDIA_LNK_FL_ENABLED | |
394 | MEDIA_LNK_FL_IMMUTABLE); |
395 | } |
396 | |
397 | static void risp_notify_unbind(struct v4l2_async_notifier *notifier, |
398 | struct v4l2_subdev *subdev, |
399 | struct v4l2_async_connection *asd) |
400 | { |
401 | struct rcar_isp *isp = notifier_to_isp(n: notifier); |
402 | |
403 | isp->remote = NULL; |
404 | |
405 | dev_dbg(isp->dev, "Unbind %s\n", subdev->name); |
406 | } |
407 | |
408 | static const struct v4l2_async_notifier_operations risp_notify_ops = { |
409 | .bound = risp_notify_bound, |
410 | .unbind = risp_notify_unbind, |
411 | }; |
412 | |
413 | static int risp_parse_dt(struct rcar_isp *isp) |
414 | { |
415 | struct v4l2_async_connection *asd; |
416 | struct fwnode_handle *fwnode; |
417 | struct fwnode_handle *ep; |
418 | unsigned int id; |
419 | int ret; |
420 | |
421 | for (id = 0; id < 2; id++) { |
422 | ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), |
423 | port: 0, endpoint: id, flags: 0); |
424 | if (ep) |
425 | break; |
426 | } |
427 | |
428 | if (!ep) { |
429 | dev_err(isp->dev, "Not connected to subdevice\n"); |
430 | return -EINVAL; |
431 | } |
432 | |
433 | if (id == 1) |
434 | isp->csi_input = RISP_CSI_INPUT1; |
435 | |
436 | fwnode = fwnode_graph_get_remote_endpoint(fwnode: ep); |
437 | fwnode_handle_put(fwnode: ep); |
438 | |
439 | dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode)); |
440 | |
441 | v4l2_async_subdev_nf_init(notifier: &isp->notifier, sd: &isp->subdev); |
442 | isp->notifier.ops = &risp_notify_ops; |
443 | |
444 | asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode, |
445 | struct v4l2_async_connection); |
446 | fwnode_handle_put(fwnode); |
447 | if (IS_ERR(ptr: asd)) |
448 | return PTR_ERR(ptr: asd); |
449 | |
450 | ret = v4l2_async_nf_register(notifier: &isp->notifier); |
451 | if (ret) |
452 | v4l2_async_nf_cleanup(notifier: &isp->notifier); |
453 | |
454 | return ret; |
455 | } |
456 | |
457 | /* ----------------------------------------------------------------------------- |
458 | * Platform Device Driver |
459 | */ |
460 | |
461 | static const struct media_entity_operations risp_entity_ops = { |
462 | .link_validate = v4l2_subdev_link_validate, |
463 | }; |
464 | |
465 | static int risp_probe_resources(struct rcar_isp *isp, |
466 | struct platform_device *pdev) |
467 | { |
468 | struct resource *res; |
469 | |
470 | /* |
471 | * For backward compatibility allow cs base to be the only reg if no |
472 | * reg-names are set in DT. |
473 | */ |
474 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); |
475 | if (!res) |
476 | isp->csbase = devm_platform_ioremap_resource(pdev, index: 0); |
477 | else |
478 | isp->csbase = devm_ioremap_resource(dev: &pdev->dev, res); |
479 | |
480 | if (IS_ERR(ptr: isp->csbase)) |
481 | return PTR_ERR(ptr: isp->csbase); |
482 | |
483 | isp->rstc = devm_reset_control_get(dev: &pdev->dev, NULL); |
484 | |
485 | return PTR_ERR_OR_ZERO(ptr: isp->rstc); |
486 | } |
487 | |
488 | static const struct of_device_id risp_of_id_table[] = { |
489 | { .compatible = "renesas,r8a779a0-isp"}, |
490 | { .compatible = "renesas,r8a779g0-isp"}, |
491 | /* Keep above for compatibility with old DTB files. */ |
492 | { .compatible = "renesas,rcar-gen4-isp"}, |
493 | { /* sentinel */ } |
494 | }; |
495 | MODULE_DEVICE_TABLE(of, risp_of_id_table); |
496 | |
497 | static int risp_probe(struct platform_device *pdev) |
498 | { |
499 | struct rcar_isp *isp; |
500 | unsigned int i; |
501 | int ret; |
502 | |
503 | isp = devm_kzalloc(dev: &pdev->dev, size: sizeof(*isp), GFP_KERNEL); |
504 | if (!isp) |
505 | return -ENOMEM; |
506 | |
507 | isp->dev = &pdev->dev; |
508 | |
509 | ret = risp_probe_resources(isp, pdev); |
510 | if (ret) { |
511 | dev_err(isp->dev, "Failed to get resources\n"); |
512 | return ret; |
513 | } |
514 | |
515 | platform_set_drvdata(pdev, data: isp); |
516 | |
517 | pm_runtime_enable(dev: &pdev->dev); |
518 | |
519 | ret = risp_parse_dt(isp); |
520 | if (ret) |
521 | goto error_pm; |
522 | |
523 | isp->subdev.owner = THIS_MODULE; |
524 | isp->subdev.dev = &pdev->dev; |
525 | v4l2_subdev_init(sd: &isp->subdev, ops: &rcar_isp_subdev_ops); |
526 | v4l2_set_subdevdata(sd: &isp->subdev, p: &pdev->dev); |
527 | snprintf(buf: isp->subdev.name, size: sizeof(isp->subdev.name), fmt: "%s %s", |
528 | KBUILD_MODNAME, dev_name(dev: &pdev->dev)); |
529 | isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; |
530 | |
531 | isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX; |
532 | isp->subdev.entity.ops = &risp_entity_ops; |
533 | |
534 | isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK; |
535 | for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++) |
536 | isp->pads[i].flags = MEDIA_PAD_FL_SOURCE; |
537 | |
538 | ret = media_entity_pads_init(entity: &isp->subdev.entity, num_pads: RCAR_ISP_NUM_PADS, |
539 | pads: isp->pads); |
540 | if (ret) |
541 | goto error_notifier; |
542 | |
543 | ret = v4l2_subdev_init_finalize(&isp->subdev); |
544 | if (ret) |
545 | goto error_notifier; |
546 | |
547 | ret = v4l2_async_register_subdev(&isp->subdev); |
548 | if (ret < 0) |
549 | goto error_subdev; |
550 | |
551 | dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input); |
552 | |
553 | return 0; |
554 | |
555 | error_subdev: |
556 | v4l2_subdev_cleanup(sd: &isp->subdev); |
557 | error_notifier: |
558 | v4l2_async_nf_unregister(notifier: &isp->notifier); |
559 | v4l2_async_nf_cleanup(notifier: &isp->notifier); |
560 | error_pm: |
561 | pm_runtime_disable(dev: &pdev->dev); |
562 | |
563 | return ret; |
564 | } |
565 | |
566 | static void risp_remove(struct platform_device *pdev) |
567 | { |
568 | struct rcar_isp *isp = platform_get_drvdata(pdev); |
569 | |
570 | v4l2_async_nf_unregister(notifier: &isp->notifier); |
571 | v4l2_async_nf_cleanup(notifier: &isp->notifier); |
572 | |
573 | v4l2_async_unregister_subdev(sd: &isp->subdev); |
574 | v4l2_subdev_cleanup(sd: &isp->subdev); |
575 | |
576 | pm_runtime_disable(dev: &pdev->dev); |
577 | } |
578 | |
579 | static struct platform_driver rcar_isp_driver = { |
580 | .driver = { |
581 | .name = "rcar-isp", |
582 | .suppress_bind_attrs = true, |
583 | .of_match_table = risp_of_id_table, |
584 | }, |
585 | .probe = risp_probe, |
586 | .remove = risp_remove, |
587 | }; |
588 | |
589 | module_platform_driver(rcar_isp_driver); |
590 | |
591 | MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); |
592 | MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver"); |
593 | MODULE_LICENSE("GPL"); |
594 |
Definitions
- rcar_isp_format
- rcar_isp_formats
- risp_code_to_fmt
- rcar_isp_input
- rcar_isp_pads
- rcar_isp
- sd_to_isp
- notifier_to_isp
- risp_write_cs
- risp_read_cs
- risp_power_on
- risp_power_off
- risp_start
- risp_stop
- risp_enable_streams
- risp_disable_streams
- risp_set_pad_format
- risp_pad_ops
- rcar_isp_subdev_ops
- risp_notify_bound
- risp_notify_unbind
- risp_notify_ops
- risp_parse_dt
- risp_entity_ops
- risp_probe_resources
- risp_of_id_table
- risp_probe
- risp_remove
Improve your Profiling and Debugging skills
Find out more