[PATCH v2 03/25] media: atmel: introduce microchip csi2dc driver

Jacopo Mondi jacopo at jmondi.org
Fri Dec 10 04:59:29 PST 2021


Hello Eugen

On Fri, Dec 10, 2021 at 11:07:15AM +0000, Eugen.Hristev at microchip.com wrote:
> On 12/6/21 2:06 PM, Jacopo Mondi wrote:
> > Hi Eugen
> >
> > On Mon, Dec 06, 2021 at 11:42:25AM +0000, Eugen.Hristev at microchip.com wrote:
> >> On 12/6/21 12:51 PM, Jacopo Mondi wrote:
> >>> Hello Eugen
> >>>
> >>>       thanks for addressing all my previous comments, just a few more
> >>>       things and the driver looks good to me
> >>
> >> Thanks for reviewing. I tried my best to accomodate your suggestions.
> >>>
> >>> On Fri, Nov 12, 2021 at 04:24:47PM +0200, Eugen Hristev wrote:

[snip]

> >>>> +
> >>>> +static int csi2dc_probe(struct platform_device *pdev)
> >>>> +{
> >>>> +     struct device *dev = &pdev->dev;
> >>>> +     struct csi2dc_device *csi2dc;
> >>>> +     int ret = 0;
> >>>> +     u32 ver;
> >>>> +
> >>>> +     csi2dc = devm_kzalloc(dev, sizeof(*csi2dc), GFP_KERNEL);
> >>>> +     if (!csi2dc)
> >>>> +             return -ENOMEM;
> >>>> +
> >>>> +     csi2dc->dev = dev;
> >>>> +
> >>>> +     csi2dc->base = devm_platform_ioremap_resource(pdev, 0);
> >>>> +     if (IS_ERR(csi2dc->base)) {
> >>>> +             dev_err(dev, "base address not set\n");
> >>>> +             return PTR_ERR(csi2dc->base);
> >>>> +     }
> >>>> +
> >>>> +     csi2dc->pclk = devm_clk_get(dev, "pclk");
> >>>> +     if (IS_ERR(csi2dc->pclk)) {
> >>>> +             ret = PTR_ERR(csi2dc->pclk);
> >>>> +             dev_err(dev, "failed to get pclk: %d\n", ret);
> >>>> +             return ret;
> >>>> +     }
> >>>> +
> >>>> +     csi2dc->scck = devm_clk_get(dev, "scck");
> >>>> +     if (IS_ERR(csi2dc->scck)) {
> >>>> +             ret = PTR_ERR(csi2dc->scck);
> >>>> +             dev_err(dev, "failed to get scck: %d\n", ret);
> >>>> +             return ret;
> >>>> +     }
> >>>> +
> >>>> +     v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops);
> >>>> +
> >>>> +     csi2dc->csi2dc_sd.owner = THIS_MODULE;
> >>>> +     csi2dc->csi2dc_sd.dev = dev;
> >>>> +     snprintf(csi2dc->csi2dc_sd.name, sizeof(csi2dc->csi2dc_sd.name),
> >>>> +              "csi2dc");
> >>>> +
> >>>> +     csi2dc->csi2dc_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> >>>> +     csi2dc->csi2dc_sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> >>>> +
> >>>> +     platform_set_drvdata(pdev, csi2dc);
> >>>> +
> >>>> +     ret = csi2dc_of_parse(csi2dc, dev->of_node);
> >>>> +     if (ret)
> >>>> +             goto csi2dc_probe_cleanup_entity;
> >>>> +
> >>>> +     csi2dc->pads[CSI2DC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> >>>> +     if (csi2dc->video_pipe)
> >>>> +             csi2dc->pads[CSI2DC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> >>>> +
> >>>> +     ret = media_entity_pads_init(&csi2dc->csi2dc_sd.entity,
> >>>> +                                  csi2dc->video_pipe ? CSI2DC_PADS_NUM : 1,
> >>>> +                                  csi2dc->pads);
> >>>> +     if (ret < 0) {
> >>>> +             dev_err(dev, "media entity init failed\n");
> >>>> +             goto csi2dc_probe_cleanup_notifier;
> >>>> +     }
> >>>> +
> >>>> +     csi2dc_default_format(csi2dc);
> >>>> +
> >>>> +     /* turn power on to validate capabilities */
> >>>> +     ret = csi2dc_power(csi2dc, true);
> >>>> +     if (ret < 0)
> >>>> +             goto csi2dc_probe_cleanup_notifier;
> >>>> +
> >>>> +     pm_runtime_set_active(dev);
> >>>> +     pm_runtime_enable(dev);
> >>>> +     ver = csi2dc_readl(csi2dc, CSI2DC_VERSION);
> >>>
> >>> Shouldn't you verify that the version is the expected one, or that, at
> >>> least the device can be accessed ?
> >>
> >> If the read from the register worked, the device can be accessed. If the
> >> device won't respond to this, we will get a bus fault or a freeze.
> >> And printing the version number can help all the time by inspecting the
> >> bootlog to see if something valid was printed.
> >> Version changes from product to product but seeing a bootlog later will
> >> make us easily understand which product was it, and if the version was
> >> read correctly
> >>
> >>>
> >>>> +     pm_request_idle(dev);
> >>>
> >>> I'm not well versed when it comes to runtime_pm but I can read that
> >>>
> >>> Documentation/power/runtime_pm.rst-  `int pm_request_idle(struct device *dev);`
> >>> Documentation/power/runtime_pm.rst-    - submit a request to execute the subsystem-level idle callback for the
> >>> Documentation/power/runtime_pm.rst-      device (the request is represented by a work item in pm_wq); returns 0 on
> >>> Documentation/power/runtime_pm.rst-      success or error code if the request has not been queued up
> >>>
> >>> And looking below you have
> >>>
> >>> +static const struct dev_pm_ops csi2dc_dev_pm_ops = {
> >>> +       SET_RUNTIME_PM_OPS(csi2dc_runtime_suspend, csi2dc_runtime_resume, NULL)
> >>> +};
> >>>
> >>> Which expands to
> >>>
> >>> include/linux/pm.h:     SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
> >>>
> >>> so it seems to me you've no idle func defined, hence you above call
> >>> has no effect so you're device stays powered on after your above call
> >>> to
> >>>
> >>>           ret = csi2dc_power(csi2dc, true);
> >>>
> >>> Also trying to suspend using runtime_pm while having powered on by
> >>> calling the resume function directly would leave the pm status
> >>> unbalanced ?
> >>>
> >>> I would raher call pm_runtime_resume() and
> >>> pm_runtime_suspend() or call the power routine directly, and later
> >>> enable pm_runtime
> >>>
> >>>           csi2dc_power(true)
> >>>           readl();
> >>>           csi2fd_power(false);
> >>>
> >>>           pm_runtime_set_active();
> >>>           pm_runtime_enable();
> >>>
> >>>           v4l2_async_register();
> >>>
> >>> Again, not well versed in runtime_pm, so don't assume the above makes
> >>> sense :)
> >>
> >> I am not versed in runtime_pm either, I will try to remove this
> >> 'pm_request_idle' to see what happens at the first
> >> pm_runtime_resume_and_get, and print messages in power on/power off to
> >> see how and when they are called.
> >>
> >
>
>
> Hi Jacopo,
>
> I added traces in my power on / power off functions to see who calls them.
>
> Here is the log :
>
> power on
> ------------[ cut here ]------------
> WARNING: CPU: 0 PID: 1 at
> drivers/media/platform/atmel/microchip-csi2dc.c:331 csi2dc_power+0xd0/0x134
> Modules linked in:
> CPU: 0 PID: 1 Comm: swapper Not tainted 5.16.0-rc4+ #62
> Hardware name: Microchip SAMA7
> [<c010b9dc>] (unwind_backtrace) from [<c010975c>] (show_stack+0x10/0x14)
> [<c010975c>] (show_stack) from [<c0755aa0>] (__warn+0xd0/0x128)
> [<c0755aa0>] (__warn) from [<c0755b70>] (warn_slowpath_fmt+0x78/0xac)
> [<c0755b70>] (warn_slowpath_fmt) from [<c0501114>] (csi2dc_power+0xd0/0x134)
> [<c0501114>] (csi2dc_power) from [<c05012dc>] (csi2dc_probe+0x158/0x23c)
> [<c05012dc>] (csi2dc_probe) from [<c04067cc>] (platform_probe+0x5c/0xb8)
> [<c04067cc>] (platform_probe) from [<c04043f8>]
> (really_probe.part.0+0x9c/0x32c)
> [<c04043f8>] (really_probe.part.0) from [<c0404730>]
> (__driver_probe_device+0xa8/0x13c)
> [<c0404730>] (__driver_probe_device) from [<c04047f8>]
> (driver_probe_device+0x34/0x108)
> [<c04047f8>] (driver_probe_device) from [<c0404ea0>]
> (__driver_attach+0xb4/0x180)
> [<c0404ea0>] (__driver_attach) from [<c040245c>]
> (bus_for_each_dev+0x74/0xc0)
> [<c040245c>] (bus_for_each_dev) from [<c0403804>]
> (bus_add_driver+0x15c/0x1e0)
> [<c0403804>] (bus_add_driver) from [<c040572c>] (driver_register+0x8c/0x124)
> [<c040572c>] (driver_register) from [<c01014b0>]
> (do_one_initcall+0x54/0x1d0)
> [<c01014b0>] (do_one_initcall) from [<c0b0114c>]
> (kernel_init_freeable+0x19c/0x200)
> [<c0b0114c>] (kernel_init_freeable) from [<c075c9e0>]
> (kernel_init+0x18/0x128)
> [<c075c9e0>] (kernel_init) from [<c0100140>] (ret_from_fork+0x14/0x34)
> Exception stack(0xc4033fb0 to 0xc4033ff8)
> 3fa0:                                     00000000 00000000 00000000
> 00000000
> 3fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> 00000000
> 3fe0: 00000000 00000000 00000000 00000000 00000013 00000000
> ---[ end trace a5faddd6c4e11ca2 ]---
> microchip-csi2dc e1404000.csi2dc: Microchip CSI2DC version 145
> power off
> ------------[ cut here ]------------
> WARNING: CPU: 0 PID: 28 at
> drivers/media/platform/atmel/microchip-csi2dc.c:335
> csi2dc_runtime_suspend+0x34/0x6c
> Modules linked in:
> CPU: 0 PID: 28 Comm: kworker/0:9 Tainted: G        W         5.16.0-rc4+ #62
> Hardware name: Microchip SAMA7
> Workqueue: pm pm_runtime_work
> [<c010b9dc>] (unwind_backtrace) from [<c010975c>] (show_stack+0x10/0x14)
> [<c010975c>] (show_stack) from [<c0755aa0>] (__warn+0xd0/0x128)
> [<c0755aa0>] (__warn) from [<c0755b70>] (warn_slowpath_fmt+0x78/0xac)
> [<c0755b70>] (warn_slowpath_fmt) from [<c075b4cc>]
> (csi2dc_runtime_suspend+0x34/0x6c)
> [<c075b4cc>] (csi2dc_runtime_suspend) from [<c04114b8>]
> (__rpm_callback+0x30/0xe4)
> [<c04114b8>] (__rpm_callback) from [<c04115cc>] (rpm_callback+0x60/0x64)
> [<c04115cc>] (rpm_callback) from [<c0410e28>] (rpm_suspend+0xd0/0x4bc)
> [<c0410e28>] (rpm_suspend) from [<c041244c>] (pm_runtime_work+0x90/0x98)
> [<c041244c>] (pm_runtime_work) from [<c012b568>]
> (process_one_work+0x198/0x410)
> [<c012b568>] (process_one_work) from [<c012b85c>] (worker_thread+0x7c/0x570)
> [<c012b85c>] (worker_thread) from [<c0132168>] (kthread+0x154/0x174)
> [<c0132168>] (kthread) from [<c0100140>] (ret_from_fork+0x14/0x34)
> Exception stack(0xc4327fb0 to 0xc4327ff8)
> 7fa0:                                     00000000 00000000 00000000
> 00000000
> 7fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> 00000000
> 7fe0: 00000000 00000000 00000000 00000000 00000013 00000000
> ---[ end trace a5faddd6c4e11ca3 ]---
> >
>
>
> So it looks like after probing is finished, the runtime PM system
> switches the device back to suspend mode (as probably there are no 'get'
> operations on it ).
>
> I removed the pm_request_idle as it looks you are right, it does nothing.
>
> When trying to capture a frame, this happens:
>
> # cam -c1 -C1
> [0:01:14.779664000] [229]  INFO IPAManager ipa_manager.cpp:138 libcamera
> is not installed. Adding '//src/ipa' to the IPA search path
> [0:01:14.784039600] [229]  WARN IPAManager ipa_manager.cpp:149 No IPA
> found in '/usr/lib/libcamera'
> [0:01:14.784219400] [229]  INFO Camera camera_manager.cpp:293 libcamera
> v0.0.0+60036-2021.11-rc1-182-gd72a7997-dirty (2021-11-15T11:27:09+02:00)
> [0:01:14.800801800] [230]  WARN CameraSensor camera_sensor.cpp:197
> 'imx219 1-0010': Recommended V4L2 control 0x009a0922 not supported
> [0:01:14.800952200] [230]  WARN CameraSensor camera_sensor.cpp:249
> 'imx219 1-0010': The sensor kernel driver needs to be fixed
> [0:01:14.801018400] [230]  WARN CameraSensor camera_sensor.cpp:251
> 'imx219 1-0010': See Documentation/sensor_driver_requirements.rst in the
> libcamera sources for more information
> [0:01:14.803197200] [230]  WARN CameraSensor camera_sensor.cpp:414
> 'imx219 1-0010': Failed to retrieve the camera location
> [0:01:14.805444000] [230]  WARN V4L2 v4l2_pixelformat.cpp:287
> Unsupported V4L2 pixel format Y16
> [0:01:14.805541200] [230]  WARN V4L2 v4l2_pixelformat.cpp:287
> Unsupported V4L2 pixel format AR12
> [0:01:14.805612200] [230]  WARN V4L2 v4l2_pixelformat.cpp:287
> Unsupported V4L2 pixel format AR15
> Using camera /base/soc/flexcom at e2818000/i2c at 600/camera at 10 as cam0
> [0:01:14.806657000] [229]  INFO Camera camera.cpp:945 configuring
> streams: (0) 3264x2464-R8
> power on
> ------------[ cut here ]------------
> WARNING: CPU: 0 PID: 230 at
> drivers/media/platform/atmel/microchip-csi2dc.c:331 csi2dc_power+0xd0/0x134
> Modules linked in: imx219 dw_csi dw_dphy
> CPU: 0 PID: 230 Comm: cam Tainted: G        W         5.16.0-rc4+ #63
> Hardware name: Microchip SAMA7
> [<c010b9dc>] (unwind_backtrace) from [<c010975c>] (show_stack+0x10/0x14)
> [<c010975c>] (show_stack) from [<c0755aa0>] (__warn+0xd0/0x128)
> [<c0755aa0>] (__warn) from [<c0755b70>] (warn_slowpath_fmt+0x78/0xac)
> [<c0755b70>] (warn_slowpath_fmt) from [<c0501114>] (csi2dc_power+0xd0/0x134)
> [<c0501114>] (csi2dc_power) from [<c04114b8>] (__rpm_callback+0x30/0xe4)
> [<c04114b8>] (__rpm_callback) from [<c04115cc>] (rpm_callback+0x60/0x64)
> [<c04115cc>] (rpm_callback) from [<c0411984>] (rpm_resume+0x3b4/0x550)
> [<c0411984>] (rpm_resume) from [<c0411b54>] (__pm_runtime_resume+0x34/0x3c)
> [<c0411b54>] (__pm_runtime_resume) from [<c0500dcc>]
> (csi2dc_s_stream+0xb8/0x330)
> [<c0500dcc>] (csi2dc_s_stream) from [<c04fee9c>]
> (isc_start_streaming+0x3a4/0x498)
> [<c04fee9c>] (isc_start_streaming) from [<c04f5280>]
> (vb2_start_streaming+0x98/0x1a8)
> [<c04f5280>] (vb2_start_streaming) from [<c04f57d0>]
> (vb2_core_qbuf+0x440/0x5d4)
> [<c04f57d0>] (vb2_core_qbuf) from [<c04f9490>] (vb2_qbuf+0x80/0xdc)
> [<c04f9490>] (vb2_qbuf) from [<c04dd0dc>] (__video_do_ioctl+0x234/0x490)
> [<c04dd0dc>] (__video_do_ioctl) from [<c04de410>]
> (video_usercopy+0x1b4/0x550)
> [<c04de410>] (video_usercopy) from [<c01e77b8>] (sys_ioctl+0x224/0xa0c)
> [<c01e77b8>] (sys_ioctl) from [<c0100060>] (ret_fast_syscall+0x0/0x50)
> Exception stack(0xc46e7fa8 to 0xc46e7ff0)
> 7fa0:                   b5d07088 b5d113c8 00000011 c044560f b66da594
> b6e14000
> 7fc0: b5d07088 b5d113c8 b66da594 00000036 b66da5d8 fffffff8 b66da6cc
> b66da8e0
> 7fe0: b6fb63a8 b66da57c b6f70d88 b6ba9fec
> ---[ end trace 7204e1871cae96e7 ]---
> cam0dw-csi e1400000.csi2host: number of lanes: 2
>
> phy phy-e1400040.dphy.0: PHY Stop state not reached 0
> 75.378082power off
>   ------------[ cut here ]----e-------
> 4WARNING: CPU: 0 PID: 230 at
> drivers/media/platform/atmel/microchip-csi2dc.c:335
> csi2dc_runtime_suspend+0x34/0x6c
> Modules linked in: imx219 dw_csi dw_dphy
> CPU: 0 PID: 230 Comm: cam Tainted: G        W         5.16.0-rc4+ #63
> Hardware name: Microchip SAMA7
> [<c010b9dc>] (unwind_backtrace) from [<c010975c>] (show_stack+0x10/0x14)
> [<c010975c>] (show_stack) from [<c0755aa0>] (__warn+0xd0/0x128)
> [<c0755aa0>] (__warn) from [<c0755b70>] (warn_slowpath_fmt+0x78/0xac)
> [<c0755b70>] (warn_slowpath_fmt) from [<c075b4cc>]
> (csi2dc_runtime_suspend+0x34/0x6c)
> [<c075b4cc>] (csi2dc_runtime_suspend) from [<c04114b8>]
> (__rpm_callback+0x30/0xe4)
> [<c04114b8>] (__rpm_callback) from [<c04115cc>] (rpm_callback+0x60/0x64)
> [<c04115cc>] (rpm_callback) from [<c0410e28>] (rpm_suspend+0xd0/0x4bc)
> [<c0410e28>] (rpm_suspend) from [<c041136c>] (__pm_runtime_idle+0x3c/0x4c)
> [<c041136c>] (__pm_runtime_idle) from [<c0500d98>]
> (csi2dc_s_stream+0x84/0x330)
> [<c0500d98>] (csi2dc_s_stream) from [<c04fcc7c>]
> (isc_stop_streaming+0x134/0x174)
> [<c04fcc7c>] (isc_stop_streaming) from [<c04f5dd8>]
> (__vb2_queue_cancel+0x28/0x2a4)
> [<c04f5dd8>] (__vb2_queue_cancel) from [<c04f606c>]
> (vb2_core_streamoff+0x18/0xac)
> [<c04f606c>] (vb2_core_streamoff) from [<c04dd0dc>]
> (__video_do_ioctl+0x234/0x490)
> [<c04dd0dc>] (__video_do_ioctl) from [<c04de410>]
> (video_usercopy+0x1b4/0x550)
> [<c04de410>] (video_usercopy) from [<c01e77b8>] (sys_ioctl+0x224/0xa0c)
> [<c01e77b8>] (sys_ioctl) from [<c0100060>] (ret_fast_syscall+0x0/0x50)
> Exception stack(0xc46e7fa8 to 0xc46e7ff0)
> 7fa0:                   b5d07088 b6fb5d58 00000011 40045613 b5d071bc
> 00000001
> 7fc0: b5d07088 b6fb5d58 b5d01e20 00000036 b5d008b8 00000000 b5d01e20
> 0054ec00
> 7fe0: b6fb63a8 b66da7b4 b6f70d88 b6ba9fec
> ---[ end trace 7204e1871cae96e8 ]---
> #
> #
>
> Which is expected, on start streaming, the power on is called, and on
> stop streaming the power off.
>
>
> So it looks like the driver does pm_runtime_set_active, meaning the
> power state is active (which is something that I do by calling first
> csi2dc_power(true)), and after probing is done, it's automatically sent
> to suspend mode.
>
> So if I do csi2dc_power(false) before finish probing, there will be a
> problem because it will be called again by the subsystem, leading to
> issues with double clock unpreparing... etc.
>
> So you are fine with this sequence?
>
> ..init this..
> ..init that..
> csi2dc_power(true)
> readl();
>
> pm_runtime_set_active();
> pm_runtime_enable();
>
> ..finish probing..
>

Thanks for the detailed analysis !

If it all works like it's expected and the device gets powered off
after probe and works well at s_stream() time, I'm of course ok with
that :)

Thanks


>
> Thanks again,
> Eugen
>
>
> >>
> >>>
> >>>> +
> >>>> +     /*
> >>>> +      * we must register the subdev after PM runtime has been requested,
> >>>> +      * otherwise we might bound immediately and request pm_runtime_resume
> >>>> +      * before runtime_enable.
> >>>> +      */
> >>>> +     ret = v4l2_async_register_subdev(&csi2dc->csi2dc_sd);
> >>>> +     if (ret) {
> >>>> +             dev_err(csi2dc->dev, "failed to register the subdevice\n");
> >>>> +             goto csi2dc_probe_cleanup_notifier;
> >>>> +     }
> >>>> +
> >>>> +     dev_info(dev, "Microchip CSI2DC version %x\n", ver);
> >>>> +
> >>>> +     return 0;
> >>>> +
> >>>> +csi2dc_probe_cleanup_notifier:
> >>>> +     v4l2_async_nf_cleanup(&csi2dc->notifier);
> >>>> +csi2dc_probe_cleanup_entity:
> >>>> +     media_entity_cleanup(&csi2dc->csi2dc_sd.entity);
> >>>
> >>> I wasn't honestly expecting this
> >>>
> >>> /**
> >>>    * media_entity_cleanup() - free resources associated with an entity
> >>>    *
> >>>    * @entity:     entity where the pads belong
> >>>    *
> >>>    * This function must be called during the cleanup phase after unregistering
> >>>    * the entity (currently, it does nothing).
> >>>    */
> >>> #if IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
> >>> static inline void media_entity_cleanup(struct media_entity *entity) {}
> >>>
> >>> :)
> >>
> >> I saw it as well. But I thought that who knows, in the future it might
> >> do something, so it's better to have it in the driver rather than not
> >> have it at all.
> >>
> >>
> >> Thanks again for reviewing,
> >> Eugen
> >>
> >>>
> >>>> +
> >>>> +     return ret;
> >>>> +}
> >>>> +
> >>>> +static int csi2dc_remove(struct platform_device *pdev)
> >>>> +{
> >>>> +     struct csi2dc_device *csi2dc = platform_get_drvdata(pdev);
> >>>> +
> >>>> +     pm_runtime_disable(&pdev->dev);
> >>>> +
> >>>> +     v4l2_async_unregister_subdev(&csi2dc->csi2dc_sd);
> >>>> +     v4l2_async_nf_unregister(&csi2dc->notifier);
> >>>> +     v4l2_async_nf_cleanup(&csi2dc->notifier);
> >>>> +     media_entity_cleanup(&csi2dc->csi2dc_sd.entity);
> >>>> +
> >>>> +     return 0;
> >>>> +}
> >>>> +
> >>>> +static int __maybe_unused csi2dc_runtime_suspend(struct device *dev)
> >>>> +{
> >>>> +     struct csi2dc_device *csi2dc = dev_get_drvdata(dev);
> >>>> +
> >>>> +     return csi2dc_power(csi2dc, false);
> >>>> +}
> >>>> +
> >>>> +static int __maybe_unused csi2dc_runtime_resume(struct device *dev)
> >>>> +{
> >>>> +     struct csi2dc_device *csi2dc = dev_get_drvdata(dev);
> >>>> +
> >>>> +     return csi2dc_power(csi2dc, true);
> >>>> +}
> >>>> +
> >>>> +static const struct dev_pm_ops csi2dc_dev_pm_ops = {
> >>>> +     SET_RUNTIME_PM_OPS(csi2dc_runtime_suspend, csi2dc_runtime_resume, NULL)
> >>>> +};
> >>>> +
> >>>> +static const struct of_device_id csi2dc_of_match[] = {
> >>>> +     { .compatible = "microchip,sama7g5-csi2dc" },
> >>>> +     { }
> >>>> +};
> >>>> +
> >>>> +MODULE_DEVICE_TABLE(of, csi2dc_of_match);
> >>>> +
> >>>> +static struct platform_driver csi2dc_driver = {
> >>>> +     .probe  = csi2dc_probe,
> >>>> +     .remove = csi2dc_remove,
> >>>> +     .driver = {
> >>>> +             .name =                 "microchip-csi2dc",
> >>>> +             .pm =                   &csi2dc_dev_pm_ops,
> >>>> +             .of_match_table =       of_match_ptr(csi2dc_of_match),
> >>>> +     },
> >>>> +};
> >>>> +
> >>>> +module_platform_driver(csi2dc_driver);
> >>>> +
> >>>> +MODULE_AUTHOR("Eugen Hristev <eugen.hristev at microchip.com>");
> >>>> +MODULE_DESCRIPTION("Microchip CSI2 Demux Controller driver");
> >>>> +MODULE_LICENSE("GPL v2");
> >>>> --
> >>>> 2.25.1
> >>>>
> >>
>



More information about the linux-arm-kernel mailing list