diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2019-05-28 05:07:30 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+samsung@kernel.org> | 2019-06-24 11:32:24 -0400 |
commit | 3eefe36cc00c5391b1ca2a68c5f01e9aa127c2a6 (patch) | |
tree | 92b2b06271a9d8735d1b66c8c216eca3b69a57c2 | |
parent | 83c40e6611ec1e548ece34f6940f516333abc16a (diff) | |
download | linux-3eefe36cc00c5391b1ca2a68c5f01e9aa127c2a6.tar.bz2 |
media: marvell-ccic: use async notifier to get the sensor
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>
-rw-r--r-- | drivers/media/platform/marvell-ccic/cafe-driver.c | 49 | ||||
-rw-r--r-- | drivers/media/platform/marvell-ccic/mcam-core.c | 157 | ||||
-rw-r--r-- | drivers/media/platform/marvell-ccic/mcam-core.h | 5 | ||||
-rw-r--r-- | drivers/media/platform/marvell-ccic/mmp-driver.c | 27 | ||||
-rw-r--r-- | include/linux/platform_data/media/mmp-camera.h | 1 |
5 files changed, 162 insertions, 77 deletions
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index cd108b14b715..fe85368675cb 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -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); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 76641d5211ab..7dc7d9d91782 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -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); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index b828b1bb59d3..4a72213aca1a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -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 */ diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 492663a8a29d..92061e4adbfd 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -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. diff --git a/include/linux/platform_data/media/mmp-camera.h b/include/linux/platform_data/media/mmp-camera.h index 4c3a80a45883..c573ebc40035 100644 --- a/include/linux/platform_data/media/mmp-camera.h +++ b/include/linux/platform_data/media/mmp-camera.h @@ -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; |