aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorJonathan Corbet <corbet@lwn.net>2011-06-11 13:46:49 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-07-27 16:53:01 -0400
commit67a8dbbc4e04cd256987b189352472a59aff73be (patch)
tree767486631bf85293111a634737dfabb954f98950 /drivers/media
parent595a93a47a3b7dc1be84160fbd73b1406074f411 (diff)
[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>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/marvell-ccic/Kconfig11
-rw-r--r--drivers/media/video/marvell-ccic/Makefile4
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c28
-rw-r--r--drivers/media/video/marvell-ccic/mmp-driver.c339
5 files changed, 375 insertions, 8 deletions
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 42b6a7a6482..89478f01731 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -128,6 +128,7 @@ obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
128obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o 128obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
129 129
130obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ 130obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
131obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
131 132
132obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o 133obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
133 134
diff --git a/drivers/media/video/marvell-ccic/Kconfig b/drivers/media/video/marvell-ccic/Kconfig
index 80136a8771e..b4f72605997 100644
--- a/drivers/media/video/marvell-ccic/Kconfig
+++ b/drivers/media/video/marvell-ccic/Kconfig
@@ -7,3 +7,14 @@ config VIDEO_CAFE_CCIC
7 CMOS camera controller. This is the controller found on first- 7 CMOS camera controller. This is the controller found on first-
8 generation OLPC systems. 8 generation OLPC systems.
9 9
10config 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
diff --git a/drivers/media/video/marvell-ccic/Makefile b/drivers/media/video/marvell-ccic/Makefile
index 462b385c623..05a792c579a 100644
--- a/drivers/media/video/marvell-ccic/Makefile
+++ b/drivers/media/video/marvell-ccic/Makefile
@@ -1,2 +1,6 @@
1obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o 1obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
2cafe_ccic-y := cafe-driver.o mcam-core.o 2cafe_ccic-y := cafe-driver.o mcam-core.o
3
4obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o
5mmp_camera-y := mmp-driver.o mcam-core.o
6
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
index 014b70b5a9b..3e6a5e8b0cd 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.c
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -167,7 +167,7 @@ static void mcam_set_config_needed(struct mcam_camera *cam, int needed)
167 167
168 168
169/* 169/*
170 * Debugging and related. FIXME these are broken 170 * Debugging and related.
171 */ 171 */
172#define cam_err(cam, fmt, arg...) \ 172#define cam_err(cam, fmt, arg...) \
173 dev_err((cam)->dev, fmt, ##arg); 173 dev_err((cam)->dev, fmt, ##arg);
@@ -202,7 +202,8 @@ static void mcam_ctlr_dma(struct mcam_camera *cam)
202 mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); 202 mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
203 } else 203 } else
204 mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); 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
208static void mcam_ctlr_image(struct mcam_camera *cam) 209static void mcam_ctlr_image(struct mcam_camera *cam)
@@ -358,8 +359,8 @@ static void mcam_ctlr_power_up(struct mcam_camera *cam)
358 unsigned long flags; 359 unsigned long flags;
359 360
360 spin_lock_irqsave(&cam->dev_lock, flags); 361 spin_lock_irqsave(&cam->dev_lock, flags);
361 mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
362 cam->plat_power_up(cam); 362 cam->plat_power_up(cam);
363 mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
363 spin_unlock_irqrestore(&cam->dev_lock, flags); 364 spin_unlock_irqrestore(&cam->dev_lock, flags);
364 msleep(5); /* Just to be sure */ 365 msleep(5); /* Just to be sure */
365} 366}
@@ -369,8 +370,13 @@ static void mcam_ctlr_power_down(struct mcam_camera *cam)
369 unsigned long flags; 370 unsigned long flags;
370 371
371 spin_lock_irqsave(&cam->dev_lock, flags); 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 mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); 378 mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
379 cam->plat_power_down(cam);
374 spin_unlock_irqrestore(&cam->dev_lock, flags); 380 spin_unlock_irqrestore(&cam->dev_lock, flags);
375} 381}
376 382
@@ -1622,14 +1628,20 @@ out_unregister:
1622 1628
1623void mccic_shutdown(struct mcam_camera *cam) 1629void 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 cam_warn(cam, "Removing a device with users!\n"); 1638 cam_warn(cam, "Removing a device with users!\n");
1639 mcam_ctlr_power_down(cam);
1640 }
1641 mcam_free_dma_bufs(cam);
1627 if (cam->n_sbufs > 0) 1642 if (cam->n_sbufs > 0)
1628 /* What if they are still mapped? Shouldn't be, but... */ 1643 /* What if they are still mapped? Shouldn't be, but... */
1629 mcam_free_sio_buffers(cam); 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 video_unregister_device(&cam->vdev); 1645 video_unregister_device(&cam->vdev);
1634 v4l2_device_unregister(&cam->v4l2_dev); 1646 v4l2_device_unregister(&cam->v4l2_dev);
1635} 1647}
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c
new file mode 100644
index 00000000000..ac9976f23a6
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mmp-driver.c
@@ -0,0 +1,339 @@
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
32MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
33MODULE_LICENSE("GPL");
34
35struct 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
43static 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
54static LIST_HEAD(mmpcam_devices);
55static struct mutex mmpcam_devices_lock;
56
57static 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
64static 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 */
76static 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 */
107static 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
131static 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
149static 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
162static 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
267out_unregister:
268 mccic_shutdown(mcam);
269 mmpcam_power_down(mcam);
270out_gpio2:
271 gpio_free(pdata->sensor_reset_gpio);
272out_gpio:
273 gpio_free(pdata->sensor_power_gpio);
274out_unmap2:
275 iounmap(cam->power_regs);
276out_unmap1:
277 iounmap(mcam->regs);
278out_free:
279 kfree(cam);
280 return ret;
281}
282
283
284static 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
302static 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
312static 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
322static int __init mmpcam_init_module(void)
323{
324 mutex_init(&mmpcam_devices_lock);
325 return platform_driver_register(&mmpcam_driver);
326}
327
328static 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
338module_init(mmpcam_init_module);
339module_exit(mmpcam_exit_module);