Commit 67a8dbbc4e04cd256987b189352472a59aff73be

Authored by Jonathan Corbet
Committed by Mauro Carvalho Chehab
1 parent 595a93a47a

[media] marvell-cam: Basic working MMP camera driver

Now we have a camera working over the marvell cam controller core.  It
works like the cafe driver and has all the same limitations, contiguous DMA
only being one of them.  But it's a start.

Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

Showing 7 changed files with 386 additions and 9 deletions Side-by-side Diff

drivers/media/video/Makefile
... ... @@ -128,6 +128,7 @@
128 128 obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
129 129  
130 130 obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
  131 +obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
131 132  
132 133 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
133 134  
drivers/media/video/marvell-ccic/Kconfig
... ... @@ -6,4 +6,15 @@
6 6 This is a video4linux2 driver for the Marvell 88ALP01 integrated
7 7 CMOS camera controller. This is the controller found on first-
8 8 generation OLPC systems.
  9 +
  10 +config VIDEO_MMP_CAMERA
  11 + tristate "Marvell Armada 610 integrated camera controller support"
  12 + depends on ARCH_MMP && I2C && VIDEO_V4L2
  13 + select VIDEO_OV7670
  14 + select I2C_GPIO
  15 + ---help---
  16 + This is a Video4Linux2 driver for the integrated camera
  17 + controller found on Marvell Armada 610 application
  18 + processors (and likely beyond). This is the controller found
  19 + in OLPC XO 1.75 systems.
drivers/media/video/marvell-ccic/Makefile
1 1 obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
2 2 cafe_ccic-y := cafe-driver.o mcam-core.o
  3 +
  4 +obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o
  5 +mmp_camera-y := mmp-driver.o mcam-core.o
drivers/media/video/marvell-ccic/mcam-core.c
... ... @@ -167,7 +167,7 @@
167 167  
168 168  
169 169 /*
170   - * Debugging and related. FIXME these are broken
  170 + * Debugging and related.
171 171 */
172 172 #define cam_err(cam, fmt, arg...) \
173 173 dev_err((cam)->dev, fmt, ##arg);
... ... @@ -202,7 +202,8 @@
202 202 mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
203 203 } else
204 204 mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
205   - mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */
  205 + if (cam->chip_id == V4L2_IDENT_CAFE)
  206 + mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
206 207 }
207 208  
208 209 static void mcam_ctlr_image(struct mcam_camera *cam)
209 210  
... ... @@ -358,8 +359,8 @@
358 359 unsigned long flags;
359 360  
360 361 spin_lock_irqsave(&cam->dev_lock, flags);
361   - mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
362 362 cam->plat_power_up(cam);
  363 + mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
363 364 spin_unlock_irqrestore(&cam->dev_lock, flags);
364 365 msleep(5); /* Just to be sure */
365 366 }
366 367  
... ... @@ -369,8 +370,13 @@
369 370 unsigned long flags;
370 371  
371 372 spin_lock_irqsave(&cam->dev_lock, flags);
372   - cam->plat_power_down(cam);
  373 + /*
  374 + * School of hard knocks department: be sure we do any register
  375 + * twiddling on the controller *before* calling the platform
  376 + * power down routine.
  377 + */
373 378 mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
  379 + cam->plat_power_down(cam);
374 380 spin_unlock_irqrestore(&cam->dev_lock, flags);
375 381 }
376 382  
377 383  
378 384  
... ... @@ -1622,14 +1628,20 @@
1622 1628  
1623 1629 void mccic_shutdown(struct mcam_camera *cam)
1624 1630 {
1625   - if (cam->users > 0)
  1631 + /*
  1632 + * If we have no users (and we really, really should have no
  1633 + * users) the device will already be powered down. Trying to
  1634 + * take it down again will wedge the machine, which is frowned
  1635 + * upon.
  1636 + */
  1637 + if (cam->users > 0) {
1626 1638 cam_warn(cam, "Removing a device with users!\n");
  1639 + mcam_ctlr_power_down(cam);
  1640 + }
  1641 + mcam_free_dma_bufs(cam);
1627 1642 if (cam->n_sbufs > 0)
1628 1643 /* What if they are still mapped? Shouldn't be, but... */
1629 1644 mcam_free_sio_buffers(cam);
1630   - mcam_ctlr_stop_dma(cam);
1631   - mcam_ctlr_power_down(cam);
1632   - mcam_free_dma_bufs(cam);
1633 1645 video_unregister_device(&cam->vdev);
1634 1646 v4l2_device_unregister(&cam->v4l2_dev);
1635 1647 }
drivers/media/video/marvell-ccic/mmp-driver.c
  1 +/*
  2 + * Support for the camera device found on Marvell MMP processors; known
  3 + * to work with the Armada 610 as used in the OLPC 1.75 system.
  4 + *
  5 + * Copyright 2011 Jonathan Corbet <corbet@lwn.net>
  6 + *
  7 + * This file may be distributed under the terms of the GNU General
  8 + * Public License, version 2.
  9 + */
  10 +
  11 +#include <linux/init.h>
  12 +#include <linux/kernel.h>
  13 +#include <linux/module.h>
  14 +#include <linux/i2c.h>
  15 +#include <linux/i2c-gpio.h>
  16 +#include <linux/interrupt.h>
  17 +#include <linux/spinlock.h>
  18 +#include <linux/slab.h>
  19 +#include <linux/videodev2.h>
  20 +#include <media/v4l2-device.h>
  21 +#include <media/v4l2-chip-ident.h>
  22 +#include <media/mmp-camera.h>
  23 +#include <linux/device.h>
  24 +#include <linux/platform_device.h>
  25 +#include <linux/gpio.h>
  26 +#include <linux/io.h>
  27 +#include <linux/delay.h>
  28 +#include <linux/list.h>
  29 +
  30 +#include "mcam-core.h"
  31 +
  32 +MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
  33 +MODULE_LICENSE("GPL");
  34 +
  35 +struct mmp_camera {
  36 + void *power_regs;
  37 + struct platform_device *pdev;
  38 + struct mcam_camera mcam;
  39 + struct list_head devlist;
  40 + int irq;
  41 +};
  42 +
  43 +static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam)
  44 +{
  45 + return container_of(mcam, struct mmp_camera, mcam);
  46 +}
  47 +
  48 +/*
  49 + * A silly little infrastructure so we can keep track of our devices.
  50 + * Chances are that we will never have more than one of them, but
  51 + * the Armada 610 *does* have two controllers...
  52 + */
  53 +
  54 +static LIST_HEAD(mmpcam_devices);
  55 +static struct mutex mmpcam_devices_lock;
  56 +
  57 +static void mmpcam_add_device(struct mmp_camera *cam)
  58 +{
  59 + mutex_lock(&mmpcam_devices_lock);
  60 + list_add(&cam->devlist, &mmpcam_devices);
  61 + mutex_unlock(&mmpcam_devices_lock);
  62 +}
  63 +
  64 +static void mmpcam_remove_device(struct mmp_camera *cam)
  65 +{
  66 + mutex_lock(&mmpcam_devices_lock);
  67 + list_del(&cam->devlist);
  68 + mutex_unlock(&mmpcam_devices_lock);
  69 +}
  70 +
  71 +/*
  72 + * Platform dev remove passes us a platform_device, and there's
  73 + * no handy unused drvdata to stash a backpointer in. So just
  74 + * dig it out of our list.
  75 + */
  76 +static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
  77 +{
  78 + struct mmp_camera *cam;
  79 +
  80 + mutex_lock(&mmpcam_devices_lock);
  81 + list_for_each_entry(cam, &mmpcam_devices, devlist) {
  82 + if (cam->pdev == pdev) {
  83 + mutex_unlock(&mmpcam_devices_lock);
  84 + return cam;
  85 + }
  86 + }
  87 + mutex_unlock(&mmpcam_devices_lock);
  88 + return NULL;
  89 +}
  90 +
  91 +
  92 +
  93 +
  94 +/*
  95 + * Power-related registers; this almost certainly belongs
  96 + * somewhere else.
  97 + *
  98 + * ARMADA 610 register manual, sec 7.2.1, p1842.
  99 + */
  100 +#define CPU_SUBSYS_PMU_BASE 0xd4282800
  101 +#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */
  102 +#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */
  103 +
  104 +/*
  105 + * Power control.
  106 + */
  107 +static void mmpcam_power_up(struct mcam_camera *mcam)
  108 +{
  109 + struct mmp_camera *cam = mcam_to_cam(mcam);
  110 + struct mmp_camera_platform_data *pdata;
  111 +/*
  112 + * Turn on power and clocks to the controller.
  113 + */
  114 + iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
  115 + iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
  116 + mdelay(1);
  117 +/*
  118 + * Provide power to the sensor.
  119 + */
  120 + mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002);
  121 + pdata = cam->pdev->dev.platform_data;
  122 + gpio_set_value(pdata->sensor_power_gpio, 1);
  123 + mdelay(5);
  124 + mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000);
  125 + gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */
  126 + mdelay(5);
  127 + gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
  128 + mdelay(5);
  129 +}
  130 +
  131 +static void mmpcam_power_down(struct mcam_camera *mcam)
  132 +{
  133 + struct mmp_camera *cam = mcam_to_cam(mcam);
  134 + struct mmp_camera_platform_data *pdata;
  135 +/*
  136 + * Turn off clocks and set reset lines
  137 + */
  138 + iowrite32(0, cam->power_regs + REG_CCIC_DCGCR);
  139 + iowrite32(0, cam->power_regs + REG_CCIC_CRCR);
  140 +/*
  141 + * Shut down the sensor.
  142 + */
  143 + pdata = cam->pdev->dev.platform_data;
  144 + gpio_set_value(pdata->sensor_power_gpio, 0);
  145 + gpio_set_value(pdata->sensor_reset_gpio, 0);
  146 +}
  147 +
  148 +
  149 +static irqreturn_t mmpcam_irq(int irq, void *data)
  150 +{
  151 + struct mcam_camera *mcam = data;
  152 + unsigned int irqs, handled;
  153 +
  154 + spin_lock(&mcam->dev_lock);
  155 + irqs = mcam_reg_read(mcam, REG_IRQSTAT);
  156 + handled = mccic_irq(mcam, irqs);
  157 + spin_unlock(&mcam->dev_lock);
  158 + return IRQ_RETVAL(handled);
  159 +}
  160 +
  161 +
  162 +static int mmpcam_probe(struct platform_device *pdev)
  163 +{
  164 + struct mmp_camera *cam;
  165 + struct mcam_camera *mcam;
  166 + struct resource *res;
  167 + struct mmp_camera_platform_data *pdata;
  168 + int ret;
  169 +
  170 + cam = kzalloc(sizeof(*cam), GFP_KERNEL);
  171 + if (cam == NULL)
  172 + return -ENOMEM;
  173 + cam->pdev = pdev;
  174 + INIT_LIST_HEAD(&cam->devlist);
  175 +
  176 + mcam = &cam->mcam;
  177 + mcam->platform = MHP_Armada610;
  178 + mcam->plat_power_up = mmpcam_power_up;
  179 + mcam->plat_power_down = mmpcam_power_down;
  180 + mcam->dev = &pdev->dev;
  181 + mcam->use_smbus = 0;
  182 + mcam->chip_id = V4L2_IDENT_ARMADA610;
  183 + spin_lock_init(&mcam->dev_lock);
  184 + /*
  185 + * Get our I/O memory.
  186 + */
  187 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  188 + if (res == NULL) {
  189 + dev_err(&pdev->dev, "no iomem resource!\n");
  190 + ret = -ENODEV;
  191 + goto out_free;
  192 + }
  193 + mcam->regs = ioremap(res->start, resource_size(res));
  194 + if (mcam->regs == NULL) {
  195 + dev_err(&pdev->dev, "MMIO ioremap fail\n");
  196 + ret = -ENODEV;
  197 + goto out_free;
  198 + }
  199 + /*
  200 + * Power/clock memory is elsewhere; get it too. Perhaps this
  201 + * should really be managed outside of this driver?
  202 + */
  203 + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  204 + if (res == NULL) {
  205 + dev_err(&pdev->dev, "no power resource!\n");
  206 + ret = -ENODEV;
  207 + goto out_unmap1;
  208 + }
  209 + cam->power_regs = ioremap(res->start, resource_size(res));
  210 + if (cam->power_regs == NULL) {
  211 + dev_err(&pdev->dev, "power MMIO ioremap fail\n");
  212 + ret = -ENODEV;
  213 + goto out_unmap1;
  214 + }
  215 + /*
  216 + * Find the i2c adapter. This assumes, of course, that the
  217 + * i2c bus is already up and functioning.
  218 + */
  219 + pdata = pdev->dev.platform_data;
  220 + mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
  221 + if (mcam->i2c_adapter == NULL) {
  222 + ret = -ENODEV;
  223 + dev_err(&pdev->dev, "No i2c adapter\n");
  224 + goto out_unmap2;
  225 + }
  226 + /*
  227 + * Sensor GPIO pins.
  228 + */
  229 + ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
  230 + if (ret) {
  231 + dev_err(&pdev->dev, "Can't get sensor power gpio %d",
  232 + pdata->sensor_power_gpio);
  233 + goto out_unmap2;
  234 + }
  235 + gpio_direction_output(pdata->sensor_power_gpio, 0);
  236 + ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
  237 + if (ret) {
  238 + dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
  239 + pdata->sensor_reset_gpio);
  240 + goto out_gpio;
  241 + }
  242 + gpio_direction_output(pdata->sensor_reset_gpio, 0);
  243 + /*
  244 + * Power the device up and hand it off to the core.
  245 + */
  246 + mmpcam_power_up(mcam);
  247 + ret = mccic_register(mcam);
  248 + if (ret)
  249 + goto out_gpio2;
  250 + /*
  251 + * Finally, set up our IRQ now that the core is ready to
  252 + * deal with it.
  253 + */
  254 + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  255 + if (res == NULL) {
  256 + ret = -ENODEV;
  257 + goto out_unregister;
  258 + }
  259 + cam->irq = res->start;
  260 + ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
  261 + "mmp-camera", mcam);
  262 + if (ret == 0) {
  263 + mmpcam_add_device(cam);
  264 + return 0;
  265 + }
  266 +
  267 +out_unregister:
  268 + mccic_shutdown(mcam);
  269 + mmpcam_power_down(mcam);
  270 +out_gpio2:
  271 + gpio_free(pdata->sensor_reset_gpio);
  272 +out_gpio:
  273 + gpio_free(pdata->sensor_power_gpio);
  274 +out_unmap2:
  275 + iounmap(cam->power_regs);
  276 +out_unmap1:
  277 + iounmap(mcam->regs);
  278 +out_free:
  279 + kfree(cam);
  280 + return ret;
  281 +}
  282 +
  283 +
  284 +static int mmpcam_remove(struct mmp_camera *cam)
  285 +{
  286 + struct mcam_camera *mcam = &cam->mcam;
  287 + struct mmp_camera_platform_data *pdata;
  288 +
  289 + mmpcam_remove_device(cam);
  290 + free_irq(cam->irq, mcam);
  291 + mccic_shutdown(mcam);
  292 + mmpcam_power_down(mcam);
  293 + pdata = cam->pdev->dev.platform_data;
  294 + gpio_free(pdata->sensor_reset_gpio);
  295 + gpio_free(pdata->sensor_power_gpio);
  296 + iounmap(cam->power_regs);
  297 + iounmap(mcam->regs);
  298 + kfree(cam);
  299 + return 0;
  300 +}
  301 +
  302 +static int mmpcam_platform_remove(struct platform_device *pdev)
  303 +{
  304 + struct mmp_camera *cam = mmpcam_find_device(pdev);
  305 +
  306 + if (cam == NULL)
  307 + return -ENODEV;
  308 + return mmpcam_remove(cam);
  309 +}
  310 +
  311 +
  312 +static struct platform_driver mmpcam_driver = {
  313 + .probe = mmpcam_probe,
  314 + .remove = mmpcam_platform_remove,
  315 + .driver = {
  316 + .name = "mmp-camera",
  317 + .owner = THIS_MODULE
  318 + }
  319 +};
  320 +
  321 +
  322 +static int __init mmpcam_init_module(void)
  323 +{
  324 + mutex_init(&mmpcam_devices_lock);
  325 + return platform_driver_register(&mmpcam_driver);
  326 +}
  327 +
  328 +static void __exit mmpcam_exit_module(void)
  329 +{
  330 + platform_driver_unregister(&mmpcam_driver);
  331 + /*
  332 + * platform_driver_unregister() should have emptied the list
  333 + */
  334 + if (!list_empty(&mmpcam_devices))
  335 + printk(KERN_ERR "mmp_camera leaving devices behind\n");
  336 +}
  337 +
  338 +module_init(mmpcam_init_module);
  339 +module_exit(mmpcam_exit_module);
include/media/mmp-camera.h
  1 +/*
  2 + * Information for the Marvell Armada MMP camera
  3 + */
  4 +
  5 +struct mmp_camera_platform_data {
  6 + struct platform_device *i2c_device;
  7 + int sensor_power_gpio;
  8 + int sensor_reset_gpio;
  9 +};
include/media/v4l2-chip-ident.h
... ... @@ -185,8 +185,9 @@
185 185 /* module wm8775: just ident 8775 */
186 186 V4L2_IDENT_WM8775 = 8775,
187 187  
188   - /* module cafe_ccic, just ident 8801 */
  188 + /* Marvell controllers starting at 8801 */
189 189 V4L2_IDENT_CAFE = 8801,
  190 + V4L2_IDENT_ARMADA610 = 8802,
190 191  
191 192 /* AKM AK8813/AK8814 */
192 193 V4L2_IDENT_AK8813 = 8813,