media: marvell-ccic: use async notifier to get the sensor
authorLubomir Rintel <lkundrak@v3.sk>
Tue, 28 May 2019 09:07:30 +0000 (05:07 -0400)
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>
Mon, 24 Jun 2019 15:32:24 +0000 (11:32 -0400)
An instance of a sensor on DT-based MMP2 platform is always going to be
created asynchronously.

Let's move the manual device creation away from the core to the Cafe
driver (used on OLPC XO-1, not present in DT) and set up appropriate
async matches: I2C on Cafe, FWNODE on MMP (OLPC XO-1.75).

Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
drivers/media/platform/marvell-ccic/cafe-driver.c
drivers/media/platform/marvell-ccic/mcam-core.c
drivers/media/platform/marvell-ccic/mcam-core.h
drivers/media/platform/marvell-ccic/mmp-driver.c
include/linux/platform_data/media/mmp-camera.h

index cd108b1..fe85368 100644 (file)
@@ -9,6 +9,7 @@
  *
  * Copyright 2006-11 One Laptop Per Child Association, Inc.
  * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net>
+ * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
  *
  * Written by Jonathan Corbet, corbet@lwn.net.
  *
@@ -25,6 +26,7 @@
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
+#include <media/i2c/ov7670.h>
 #include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
@@ -50,6 +52,7 @@ struct cafe_camera {
        int registered;                 /* Fully initialized? */
        struct mcam_camera mcam;
        struct pci_dev *pdev;
+       struct i2c_adapter *i2c_adapter;
        wait_queue_head_t smbus_wait;   /* Waiting on i2c events */
 };
 
@@ -349,15 +352,15 @@ static int cafe_smbus_setup(struct cafe_camera *cam)
                return ret;
        }
 
-       cam->mcam.i2c_adapter = adap;
+       cam->i2c_adapter = adap;
        cafe_smbus_enable_irq(cam);
        return 0;
 }
 
 static void cafe_smbus_shutdown(struct cafe_camera *cam)
 {
-       i2c_del_adapter(cam->mcam.i2c_adapter);
-       kfree(cam->mcam.i2c_adapter);
+       i2c_del_adapter(cam->i2c_adapter);
+       kfree(cam->i2c_adapter);
 }
 
 
@@ -450,6 +453,29 @@ static irqreturn_t cafe_irq(int irq, void *data)
        return IRQ_RETVAL(handled);
 }
 
+/* -------------------------------------------------------------------------- */
+
+static struct ov7670_config sensor_cfg = {
+       /*
+        * Exclude QCIF mode, because it only captures a tiny portion
+        * of the sensor FOV
+        */
+       .min_width = 320,
+       .min_height = 240,
+
+       /*
+        * Set the clock speed for the XO 1; I don't believe this
+        * driver has ever run anywhere else.
+        */
+       .clock_speed = 45,
+       .use_smbus = 1,
+};
+
+struct i2c_board_info ov7670_info = {
+       .type = "ov7670",
+       .addr = 0x42 >> 1,
+       .platform_data = &sensor_cfg,
+};
 
 /* -------------------------------------------------------------------------- */
 /*
@@ -480,12 +506,6 @@ static int cafe_pci_probe(struct pci_dev *pdev,
        mcam->dev = &pdev->dev;
        snprintf(mcam->bus_info, sizeof(mcam->bus_info), "PCI:%s", pci_name(pdev));
        /*
-        * Set the clock speed for the XO 1; I don't believe this
-        * driver has ever run anywhere else.
-        */
-       mcam->clock_speed = 45;
-       mcam->use_smbus = 1;
-       /*
         * Vmalloc mode for buffers is traditional with this driver.
         * We *might* be able to run DMA_contig, especially on a system
         * with CMA in it.
@@ -525,12 +545,21 @@ static int cafe_pci_probe(struct pci_dev *pdev,
        if (ret)
                goto out_pdown;
 
+       mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C;
+       mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter);
+       mcam->asd.match.i2c.address = ov7670_info.addr;
+
        ret = mccic_register(mcam);
-       if (ret == 0) {
+       if (ret)
+               goto out_smbus_shutdown;
+
+       if (i2c_new_device(cam->i2c_adapter, &ov7670_info)) {
                cam->registered = 1;
                return 0;
        }
 
+       mccic_shutdown(mcam);
+out_smbus_shutdown:
        cafe_smbus_shutdown(cam);
 out_pdown:
        cafe_ctlr_power_down(mcam);
index 76641d5..7dc7d9d 100644 (file)
@@ -4,6 +4,7 @@
  * so it needs platform-specific support outside of the core.
  *
  * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -26,7 +27,6 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-event.h>
-#include <media/i2c/ov7670.h>
 #include <media/videobuf2-vmalloc.h>
 #include <media/videobuf2-dma-contig.h>
 #include <media/videobuf2-dma-sg.h>
@@ -93,6 +93,9 @@ MODULE_PARM_DESC(buffer_mode,
 #define sensor_call(cam, o, f, args...) \
        v4l2_subdev_call(cam->sensor, o, f, ##args)
 
+#define notifier_to_mcam(notifier) \
+       container_of(notifier, struct mcam_camera, notifier)
+
 static struct mcam_format_struct {
        __u8 *desc;
        __u32 pixelformat;
@@ -1715,23 +1718,94 @@ EXPORT_SYMBOL_GPL(mccic_irq);
 /*
  * Registration and such.
  */
-static struct ov7670_config sensor_cfg = {
+
+static int mccic_notify_bound(struct v4l2_async_notifier *notifier,
+       struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd)
+{
+       struct mcam_camera *cam = notifier_to_mcam(notifier);
+       int ret;
+
+       mutex_lock(&cam->s_mutex);
+       if (cam->sensor) {
+               cam_err(cam, "sensor already bound\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       v4l2_set_subdev_hostdata(subdev, cam);
+       cam->sensor = subdev;
+
+       ret = mcam_cam_init(cam);
+       if (ret) {
+               cam->sensor = NULL;
+               goto out;
+       }
+
+       ret = mcam_setup_vb2(cam);
+       if (ret) {
+               cam->sensor = NULL;
+               goto out;
+       }
+
+       cam->vdev = mcam_v4l_template;
+       cam->vdev.v4l2_dev = &cam->v4l2_dev;
+       cam->vdev.lock = &cam->s_mutex;
+       cam->vdev.queue = &cam->vb_queue;
+       video_set_drvdata(&cam->vdev, cam);
+       ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+       if (ret) {
+               cam->sensor = NULL;
+               goto out;
+       }
+
+       cam_dbg(cam, "sensor %s bound\n", subdev->name);
+out:
+       mutex_unlock(&cam->s_mutex);
+       return ret;
+}
+
+static void mccic_notify_unbind(struct v4l2_async_notifier *notifier,
+       struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd)
+{
+       struct mcam_camera *cam = notifier_to_mcam(notifier);
+
+       mutex_lock(&cam->s_mutex);
+       if (cam->sensor != subdev) {
+               cam_err(cam, "sensor %s not bound\n", subdev->name);
+               goto out;
+       }
+
+       video_unregister_device(&cam->vdev);
+       cam->sensor = NULL;
+       cam_dbg(cam, "sensor %s unbound\n", subdev->name);
+
+out:
+       mutex_unlock(&cam->s_mutex);
+}
+
+static int mccic_notify_complete(struct v4l2_async_notifier *notifier)
+{
+       struct mcam_camera *cam = notifier_to_mcam(notifier);
+       int ret;
+
        /*
-        * Exclude QCIF mode, because it only captures a tiny portion
-        * of the sensor FOV
+        * Get the v4l2 setup done.
         */
-       .min_width = 320,
-       .min_height = 240,
-};
+       ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10);
+       if (!ret)
+               cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler;
+
+       return ret;
+}
 
+static const struct v4l2_async_notifier_operations mccic_notify_ops = {
+       .bound = mccic_notify_bound,
+       .unbind = mccic_notify_unbind,
+       .complete = mccic_notify_complete,
+};
 
 int mccic_register(struct mcam_camera *cam)
 {
-       struct i2c_board_info ov7670_info = {
-               .type = "ov7670",
-               .addr = 0x42 >> 1,
-               .platform_data = &sensor_cfg,
-       };
        int ret;
 
        /*
@@ -1744,17 +1818,20 @@ int mccic_register(struct mcam_camera *cam)
                printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, attempting vmalloc mode instead\n");
                cam->buffer_mode = B_vmalloc;
        }
+
        if (!mcam_buffer_mode_supported(cam->buffer_mode)) {
                printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n",
                                cam->buffer_mode);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
+
        /*
         * Register with V4L
         */
        ret = v4l2_device_register(cam->dev, &cam->v4l2_dev);
        if (ret)
-               return ret;
+               goto out;
 
        mutex_init(&cam->s_mutex);
        cam->state = S_NOTREADY;
@@ -1764,43 +1841,20 @@ int mccic_register(struct mcam_camera *cam)
        mcam_ctlr_init(cam);
 
        /*
-        * Get the v4l2 setup done.
+        * Register sensor notifier.
         */
-       ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10);
-       if (ret)
-               goto out_unregister;
-       cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler;
-
-       /*
-        * Try to find the sensor.
-        */
-       sensor_cfg.clock_speed = cam->clock_speed;
-       sensor_cfg.use_smbus = cam->use_smbus;
-       cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev,
-                       cam->i2c_adapter, &ov7670_info, NULL);
-       if (cam->sensor == NULL) {
-               ret = -ENODEV;
-               goto out_unregister;
+       v4l2_async_notifier_init(&cam->notifier);
+       ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd);
+       if (ret) {
+               cam_warn(cam, "failed to add subdev to a notifier");
+               goto out;
        }
 
-       ret = mcam_cam_init(cam);
-       if (ret)
-               goto out_unregister;
-
-       ret = mcam_setup_vb2(cam);
-       if (ret)
-               goto out_unregister;
-
-       mutex_lock(&cam->s_mutex);
-       cam->vdev = mcam_v4l_template;
-       cam->vdev.v4l2_dev = &cam->v4l2_dev;
-       cam->vdev.lock = &cam->s_mutex;
-       cam->vdev.queue = &cam->vb_queue;
-       video_set_drvdata(&cam->vdev, cam);
-       ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
-       if (ret) {
-               mutex_unlock(&cam->s_mutex);
-               goto out_unregister;
+       cam->notifier.ops = &mccic_notify_ops;
+       ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier);
+       if (ret < 0) {
+               cam_warn(cam, "failed to register a sensor notifier");
+               goto out;
        }
 
        /*
@@ -1811,11 +1865,10 @@ int mccic_register(struct mcam_camera *cam)
                        cam_warn(cam, "Unable to alloc DMA buffers at load will try again later.");
        }
 
-       mutex_unlock(&cam->s_mutex);
        return 0;
 
-out_unregister:
-       v4l2_ctrl_handler_free(&cam->ctrl_handler);
+out:
+       v4l2_async_notifier_unregister(&cam->notifier);
        v4l2_device_unregister(&cam->v4l2_dev);
        return ret;
 }
@@ -1835,8 +1888,8 @@ void mccic_shutdown(struct mcam_camera *cam)
        }
        if (cam->buffer_mode == B_vmalloc)
                mcam_free_dma_bufs(cam);
-       video_unregister_device(&cam->vdev);
        v4l2_ctrl_handler_free(&cam->ctrl_handler);
+       v4l2_async_notifier_unregister(&cam->notifier);
        v4l2_device_unregister(&cam->v4l2_dev);
 }
 EXPORT_SYMBOL_GPL(mccic_shutdown);
index b828b1b..4a72213 100644 (file)
@@ -102,14 +102,11 @@ struct mcam_camera {
         * These fields should be set by the platform code prior to
         * calling mcam_register().
         */
-       struct i2c_adapter *i2c_adapter;
        unsigned char __iomem *regs;
        unsigned regs_size; /* size in bytes of the register space */
        spinlock_t dev_lock;
        struct device *dev; /* For messages, dma alloc */
        enum mcam_chip_id chip_id;
-       short int clock_speed;  /* Sensor clock speed, default 30 */
-       short int use_smbus;    /* SMBUS or straight I2c? */
        enum mcam_buffer_mode buffer_mode;
 
        int mclk_src;   /* which clock source the mclk derives from */
@@ -150,6 +147,8 @@ struct mcam_camera {
         * Subsystem structures.
         */
        struct video_device vdev;
+       struct v4l2_async_notifier notifier;
+       struct v4l2_async_subdev asd;
        struct v4l2_subdev *sensor;
 
        /* Videobuf2 stuff */
index 492663a..92061e4 100644 (file)
@@ -4,12 +4,12 @@
  * to work with the Armada 610 as used in the OLPC 1.75 system.
  *
  * Copyright 2011 Jonathan Corbet <corbet@lwn.net>
+ * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
  */
 
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
@@ -314,6 +314,7 @@ static int mmpcam_probe(struct platform_device *pdev)
        struct mmp_camera *cam;
        struct mcam_camera *mcam;
        struct resource *res;
+       struct fwnode_handle *ep;
        struct mmp_camera_platform_data *pdata;
        int ret;
 
@@ -328,7 +329,6 @@ static int mmpcam_probe(struct platform_device *pdev)
        mcam->plat_power_down = mmpcam_power_down;
        mcam->calc_dphy = mmpcam_calc_dphy;
        mcam->dev = &pdev->dev;
-       mcam->use_smbus = 0;
        pdata = pdev->dev.platform_data;
        if (pdata) {
                mcam->mclk_src = pdata->mclk_src;
@@ -373,15 +373,6 @@ static int mmpcam_probe(struct platform_device *pdev)
        if (IS_ERR(cam->power_regs))
                return PTR_ERR(cam->power_regs);
        /*
-        * Find the i2c adapter.  This assumes, of course, that the
-        * i2c bus is already up and functioning.
-        */
-       mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
-       if (mcam->i2c_adapter == NULL) {
-               dev_err(&pdev->dev, "No i2c adapter\n");
-               return -ENODEV;
-       }
-       /*
         * Sensor GPIO pins.
         */
        ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio,
@@ -404,6 +395,19 @@ static int mmpcam_probe(struct platform_device *pdev)
        mcam_init_clk(mcam);
 
        /*
+        * Create a match of the sensor against its OF node.
+        */
+       ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node),
+                                           NULL);
+       if (!ep)
+               return -ENODEV;
+
+       mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+       mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep);
+
+       fwnode_handle_put(ep);
+
+       /*
         * Power the device up and hand it off to the core.
         */
        ret = mmpcam_power_up(mcam);
@@ -412,6 +416,7 @@ static int mmpcam_probe(struct platform_device *pdev)
        ret = mccic_register(mcam);
        if (ret)
                goto out_power_down;
+
        /*
         * Finally, set up our IRQ now that the core is ready to
         * deal with it.
index 4c3a80a..c573ebc 100644 (file)
@@ -12,7 +12,6 @@ enum dphy3_algo {
 };
 
 struct mmp_camera_platform_data {
-       struct platform_device *i2c_device;
        int sensor_power_gpio;
        int sensor_reset_gpio;
        enum v4l2_mbus_type bus_type;