Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Jonathan Corbet and committed by
Mauro Carvalho Chehab
67a8dbbc 595a93a4

+386 -9
+1
drivers/media/video/Makefile
··· 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
+11
drivers/media/video/marvell-ccic/Kconfig
··· 7 7 CMOS camera controller. This is the controller found on first- 8 8 generation OLPC systems. 9 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. 20 +
+4
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 6 +
+20 -8
drivers/media/video/marvell-ccic/mcam-core.c
··· 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 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) ··· 359 358 unsigned long flags; 360 359 361 360 spin_lock_irqsave(&cam->dev_lock, flags); 362 - mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); 363 361 cam->plat_power_up(cam); 362 + mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); 364 363 spin_unlock_irqrestore(&cam->dev_lock, flags); 365 364 msleep(5); /* Just to be sure */ 366 365 } ··· 370 369 unsigned long flags; 371 370 372 371 spin_lock_irqsave(&cam->dev_lock, flags); 373 - cam->plat_power_down(cam); 372 + /* 373 + * School of hard knocks department: be sure we do any register 374 + * twiddling on the controller *before* calling the platform 375 + * power down routine. 376 + */ 374 377 mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); 378 + cam->plat_power_down(cam); 375 379 spin_unlock_irqrestore(&cam->dev_lock, flags); 376 380 } 377 381 ··· 1628 1622 1629 1623 void mccic_shutdown(struct mcam_camera *cam) 1630 1624 { 1631 - if (cam->users > 0) 1625 + /* 1626 + * If we have no users (and we really, really should have no 1627 + * users) the device will already be powered down. Trying to 1628 + * take it down again will wedge the machine, which is frowned 1629 + * upon. 1630 + */ 1631 + if (cam->users > 0) { 1632 1632 cam_warn(cam, "Removing a device with users!\n"); 1633 + mcam_ctlr_power_down(cam); 1634 + } 1635 + mcam_free_dma_bufs(cam); 1633 1636 if (cam->n_sbufs > 0) 1634 1637 /* What if they are still mapped? Shouldn't be, but... */ 1635 1638 mcam_free_sio_buffers(cam); 1636 - mcam_ctlr_stop_dma(cam); 1637 - mcam_ctlr_power_down(cam); 1638 - mcam_free_dma_bufs(cam); 1639 1639 video_unregister_device(&cam->vdev); 1640 1640 v4l2_device_unregister(&cam->v4l2_dev); 1641 1641 }
+339
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);
+9
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 + };
+2 -1
include/media/v4l2-chip-ident.h
··· 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,