diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/mx1_camera.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/mx1_camera.c')
-rw-r--r-- | drivers/media/video/mx1_camera.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c new file mode 100644 index 00000000000..087db12a3a6 --- /dev/null +++ b/drivers/media/video/mx1_camera.c | |||
@@ -0,0 +1,885 @@ | |||
1 | /* | ||
2 | * V4L2 Driver for i.MXL/i.MXL camera (CSI) host | ||
3 | * | ||
4 | * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> | ||
5 | * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com> | ||
6 | * | ||
7 | * Based on PXA SoC camera driver | ||
8 | * Copyright (C) 2006, Sascha Hauer, Pengutronix | ||
9 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/clk.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/dma-mapping.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/mm.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/mutex.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/time.h> | ||
34 | #include <linux/videodev2.h> | ||
35 | |||
36 | #include <media/soc_camera.h> | ||
37 | #include <media/v4l2-common.h> | ||
38 | #include <media/v4l2-dev.h> | ||
39 | #include <media/videobuf-dma-contig.h> | ||
40 | #include <media/soc_mediabus.h> | ||
41 | |||
42 | #include <asm/dma.h> | ||
43 | #include <asm/fiq.h> | ||
44 | #include <mach/dma-mx1-mx2.h> | ||
45 | #include <mach/hardware.h> | ||
46 | #include <mach/mx1_camera.h> | ||
47 | |||
48 | /* | ||
49 | * CSI registers | ||
50 | */ | ||
51 | #define CSICR1 0x00 /* CSI Control Register 1 */ | ||
52 | #define CSISR 0x08 /* CSI Status Register */ | ||
53 | #define CSIRXR 0x10 /* CSI RxFIFO Register */ | ||
54 | |||
55 | #define CSICR1_RXFF_LEVEL(x) (((x) & 0x3) << 19) | ||
56 | #define CSICR1_SOF_POL (1 << 17) | ||
57 | #define CSICR1_SOF_INTEN (1 << 16) | ||
58 | #define CSICR1_MCLKDIV(x) (((x) & 0xf) << 12) | ||
59 | #define CSICR1_MCLKEN (1 << 9) | ||
60 | #define CSICR1_FCC (1 << 8) | ||
61 | #define CSICR1_BIG_ENDIAN (1 << 7) | ||
62 | #define CSICR1_CLR_RXFIFO (1 << 5) | ||
63 | #define CSICR1_GCLK_MODE (1 << 4) | ||
64 | #define CSICR1_DATA_POL (1 << 2) | ||
65 | #define CSICR1_REDGE (1 << 1) | ||
66 | #define CSICR1_EN (1 << 0) | ||
67 | |||
68 | #define CSISR_SFF_OR_INT (1 << 25) | ||
69 | #define CSISR_RFF_OR_INT (1 << 24) | ||
70 | #define CSISR_STATFF_INT (1 << 21) | ||
71 | #define CSISR_RXFF_INT (1 << 18) | ||
72 | #define CSISR_SOF_INT (1 << 16) | ||
73 | #define CSISR_DRDY (1 << 0) | ||
74 | |||
75 | #define DRIVER_VERSION "0.0.2" | ||
76 | #define DRIVER_NAME "mx1-camera" | ||
77 | |||
78 | #define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \ | ||
79 | CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT) | ||
80 | |||
81 | #define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \ | ||
82 | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \ | ||
83 | SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \ | ||
84 | SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \ | ||
85 | SOCAM_DATAWIDTH_8) | ||
86 | |||
87 | #define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */ | ||
88 | |||
89 | /* | ||
90 | * Structures | ||
91 | */ | ||
92 | |||
93 | /* buffer for one video frame */ | ||
94 | struct mx1_buffer { | ||
95 | /* common v4l buffer stuff -- must be first */ | ||
96 | struct videobuf_buffer vb; | ||
97 | enum v4l2_mbus_pixelcode code; | ||
98 | int inwork; | ||
99 | }; | ||
100 | |||
101 | /* | ||
102 | * i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor | ||
103 | * Interface. If anyone ever builds hardware to enable more than | ||
104 | * one camera, they will have to modify this driver too | ||
105 | */ | ||
106 | struct mx1_camera_dev { | ||
107 | struct soc_camera_host soc_host; | ||
108 | struct soc_camera_device *icd; | ||
109 | struct mx1_camera_pdata *pdata; | ||
110 | struct mx1_buffer *active; | ||
111 | struct resource *res; | ||
112 | struct clk *clk; | ||
113 | struct list_head capture; | ||
114 | |||
115 | void __iomem *base; | ||
116 | int dma_chan; | ||
117 | unsigned int irq; | ||
118 | unsigned long mclk; | ||
119 | |||
120 | spinlock_t lock; | ||
121 | }; | ||
122 | |||
123 | /* | ||
124 | * Videobuf operations | ||
125 | */ | ||
126 | static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, | ||
127 | unsigned int *size) | ||
128 | { | ||
129 | struct soc_camera_device *icd = vq->priv_data; | ||
130 | int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, | ||
131 | icd->current_fmt->host_fmt); | ||
132 | |||
133 | if (bytes_per_line < 0) | ||
134 | return bytes_per_line; | ||
135 | |||
136 | *size = bytes_per_line * icd->user_height; | ||
137 | |||
138 | if (!*count) | ||
139 | *count = 32; | ||
140 | |||
141 | if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) | ||
142 | *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; | ||
143 | |||
144 | dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf) | ||
150 | { | ||
151 | struct soc_camera_device *icd = vq->priv_data; | ||
152 | struct videobuf_buffer *vb = &buf->vb; | ||
153 | |||
154 | BUG_ON(in_interrupt()); | ||
155 | |||
156 | dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
157 | vb, vb->baddr, vb->bsize); | ||
158 | |||
159 | /* | ||
160 | * This waits until this buffer is out of danger, i.e., until it is no | ||
161 | * longer in STATE_QUEUED or STATE_ACTIVE | ||
162 | */ | ||
163 | videobuf_waiton(vq, vb, 0, 0); | ||
164 | videobuf_dma_contig_free(vq, vb); | ||
165 | |||
166 | vb->state = VIDEOBUF_NEEDS_INIT; | ||
167 | } | ||
168 | |||
169 | static int mx1_videobuf_prepare(struct videobuf_queue *vq, | ||
170 | struct videobuf_buffer *vb, enum v4l2_field field) | ||
171 | { | ||
172 | struct soc_camera_device *icd = vq->priv_data; | ||
173 | struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); | ||
174 | int ret; | ||
175 | int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, | ||
176 | icd->current_fmt->host_fmt); | ||
177 | |||
178 | if (bytes_per_line < 0) | ||
179 | return bytes_per_line; | ||
180 | |||
181 | dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
182 | vb, vb->baddr, vb->bsize); | ||
183 | |||
184 | /* Added list head initialization on alloc */ | ||
185 | WARN_ON(!list_empty(&vb->queue)); | ||
186 | |||
187 | BUG_ON(NULL == icd->current_fmt); | ||
188 | |||
189 | /* | ||
190 | * I think, in buf_prepare you only have to protect global data, | ||
191 | * the actual buffer is yours | ||
192 | */ | ||
193 | buf->inwork = 1; | ||
194 | |||
195 | if (buf->code != icd->current_fmt->code || | ||
196 | vb->width != icd->user_width || | ||
197 | vb->height != icd->user_height || | ||
198 | vb->field != field) { | ||
199 | buf->code = icd->current_fmt->code; | ||
200 | vb->width = icd->user_width; | ||
201 | vb->height = icd->user_height; | ||
202 | vb->field = field; | ||
203 | vb->state = VIDEOBUF_NEEDS_INIT; | ||
204 | } | ||
205 | |||
206 | vb->size = bytes_per_line * vb->height; | ||
207 | if (0 != vb->baddr && vb->bsize < vb->size) { | ||
208 | ret = -EINVAL; | ||
209 | goto out; | ||
210 | } | ||
211 | |||
212 | if (vb->state == VIDEOBUF_NEEDS_INIT) { | ||
213 | ret = videobuf_iolock(vq, vb, NULL); | ||
214 | if (ret) | ||
215 | goto fail; | ||
216 | |||
217 | vb->state = VIDEOBUF_PREPARED; | ||
218 | } | ||
219 | |||
220 | buf->inwork = 0; | ||
221 | |||
222 | return 0; | ||
223 | |||
224 | fail: | ||
225 | free_buffer(vq, buf); | ||
226 | out: | ||
227 | buf->inwork = 0; | ||
228 | return ret; | ||
229 | } | ||
230 | |||
231 | static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) | ||
232 | { | ||
233 | struct videobuf_buffer *vbuf = &pcdev->active->vb; | ||
234 | struct device *dev = pcdev->icd->parent; | ||
235 | int ret; | ||
236 | |||
237 | if (unlikely(!pcdev->active)) { | ||
238 | dev_err(dev, "DMA End IRQ with no active buffer\n"); | ||
239 | return -EFAULT; | ||
240 | } | ||
241 | |||
242 | /* setup sg list for future DMA */ | ||
243 | ret = imx_dma_setup_single(pcdev->dma_chan, | ||
244 | videobuf_to_dma_contig(vbuf), | ||
245 | vbuf->size, pcdev->res->start + | ||
246 | CSIRXR, DMA_MODE_READ); | ||
247 | if (unlikely(ret)) | ||
248 | dev_err(dev, "Failed to setup DMA sg list\n"); | ||
249 | |||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | /* Called under spinlock_irqsave(&pcdev->lock, ...) */ | ||
254 | static void mx1_videobuf_queue(struct videobuf_queue *vq, | ||
255 | struct videobuf_buffer *vb) | ||
256 | { | ||
257 | struct soc_camera_device *icd = vq->priv_data; | ||
258 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
259 | struct mx1_camera_dev *pcdev = ici->priv; | ||
260 | struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); | ||
261 | |||
262 | dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
263 | vb, vb->baddr, vb->bsize); | ||
264 | |||
265 | list_add_tail(&vb->queue, &pcdev->capture); | ||
266 | |||
267 | vb->state = VIDEOBUF_ACTIVE; | ||
268 | |||
269 | if (!pcdev->active) { | ||
270 | pcdev->active = buf; | ||
271 | |||
272 | /* setup sg list for future DMA */ | ||
273 | if (!mx1_camera_setup_dma(pcdev)) { | ||
274 | unsigned int temp; | ||
275 | /* enable SOF irq */ | ||
276 | temp = __raw_readl(pcdev->base + CSICR1) | | ||
277 | CSICR1_SOF_INTEN; | ||
278 | __raw_writel(temp, pcdev->base + CSICR1); | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | static void mx1_videobuf_release(struct videobuf_queue *vq, | ||
284 | struct videobuf_buffer *vb) | ||
285 | { | ||
286 | struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); | ||
287 | #ifdef DEBUG | ||
288 | struct soc_camera_device *icd = vq->priv_data; | ||
289 | struct device *dev = icd->parent; | ||
290 | |||
291 | dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
292 | vb, vb->baddr, vb->bsize); | ||
293 | |||
294 | switch (vb->state) { | ||
295 | case VIDEOBUF_ACTIVE: | ||
296 | dev_dbg(dev, "%s (active)\n", __func__); | ||
297 | break; | ||
298 | case VIDEOBUF_QUEUED: | ||
299 | dev_dbg(dev, "%s (queued)\n", __func__); | ||
300 | break; | ||
301 | case VIDEOBUF_PREPARED: | ||
302 | dev_dbg(dev, "%s (prepared)\n", __func__); | ||
303 | break; | ||
304 | default: | ||
305 | dev_dbg(dev, "%s (unknown)\n", __func__); | ||
306 | break; | ||
307 | } | ||
308 | #endif | ||
309 | |||
310 | free_buffer(vq, buf); | ||
311 | } | ||
312 | |||
313 | static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, | ||
314 | struct videobuf_buffer *vb, | ||
315 | struct mx1_buffer *buf) | ||
316 | { | ||
317 | /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ | ||
318 | list_del_init(&vb->queue); | ||
319 | vb->state = VIDEOBUF_DONE; | ||
320 | do_gettimeofday(&vb->ts); | ||
321 | vb->field_count++; | ||
322 | wake_up(&vb->done); | ||
323 | |||
324 | if (list_empty(&pcdev->capture)) { | ||
325 | pcdev->active = NULL; | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | pcdev->active = list_entry(pcdev->capture.next, | ||
330 | struct mx1_buffer, vb.queue); | ||
331 | |||
332 | /* setup sg list for future DMA */ | ||
333 | if (likely(!mx1_camera_setup_dma(pcdev))) { | ||
334 | unsigned int temp; | ||
335 | |||
336 | /* enable SOF irq */ | ||
337 | temp = __raw_readl(pcdev->base + CSICR1) | CSICR1_SOF_INTEN; | ||
338 | __raw_writel(temp, pcdev->base + CSICR1); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | static void mx1_camera_dma_irq(int channel, void *data) | ||
343 | { | ||
344 | struct mx1_camera_dev *pcdev = data; | ||
345 | struct device *dev = pcdev->icd->parent; | ||
346 | struct mx1_buffer *buf; | ||
347 | struct videobuf_buffer *vb; | ||
348 | unsigned long flags; | ||
349 | |||
350 | spin_lock_irqsave(&pcdev->lock, flags); | ||
351 | |||
352 | imx_dma_disable(channel); | ||
353 | |||
354 | if (unlikely(!pcdev->active)) { | ||
355 | dev_err(dev, "DMA End IRQ with no active buffer\n"); | ||
356 | goto out; | ||
357 | } | ||
358 | |||
359 | vb = &pcdev->active->vb; | ||
360 | buf = container_of(vb, struct mx1_buffer, vb); | ||
361 | WARN_ON(buf->inwork || list_empty(&vb->queue)); | ||
362 | dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
363 | vb, vb->baddr, vb->bsize); | ||
364 | |||
365 | mx1_camera_wakeup(pcdev, vb, buf); | ||
366 | out: | ||
367 | spin_unlock_irqrestore(&pcdev->lock, flags); | ||
368 | } | ||
369 | |||
370 | static struct videobuf_queue_ops mx1_videobuf_ops = { | ||
371 | .buf_setup = mx1_videobuf_setup, | ||
372 | .buf_prepare = mx1_videobuf_prepare, | ||
373 | .buf_queue = mx1_videobuf_queue, | ||
374 | .buf_release = mx1_videobuf_release, | ||
375 | }; | ||
376 | |||
377 | static void mx1_camera_init_videobuf(struct videobuf_queue *q, | ||
378 | struct soc_camera_device *icd) | ||
379 | { | ||
380 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
381 | struct mx1_camera_dev *pcdev = ici->priv; | ||
382 | |||
383 | videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, | ||
384 | &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
385 | V4L2_FIELD_NONE, | ||
386 | sizeof(struct mx1_buffer), icd, &icd->video_lock); | ||
387 | } | ||
388 | |||
389 | static int mclk_get_divisor(struct mx1_camera_dev *pcdev) | ||
390 | { | ||
391 | unsigned int mclk = pcdev->mclk; | ||
392 | unsigned long div; | ||
393 | unsigned long lcdclk; | ||
394 | |||
395 | lcdclk = clk_get_rate(pcdev->clk); | ||
396 | |||
397 | /* | ||
398 | * We verify platform_mclk_10khz != 0, so if anyone breaks it, here | ||
399 | * they get a nice Oops | ||
400 | */ | ||
401 | div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; | ||
402 | |||
403 | dev_dbg(pcdev->icd->parent, | ||
404 | "System clock %lukHz, target freq %dkHz, divisor %lu\n", | ||
405 | lcdclk / 1000, mclk / 1000, div); | ||
406 | |||
407 | return div; | ||
408 | } | ||
409 | |||
410 | static void mx1_camera_activate(struct mx1_camera_dev *pcdev) | ||
411 | { | ||
412 | unsigned int csicr1 = CSICR1_EN; | ||
413 | |||
414 | dev_dbg(pcdev->icd->parent, "Activate device\n"); | ||
415 | |||
416 | clk_enable(pcdev->clk); | ||
417 | |||
418 | /* enable CSI before doing anything else */ | ||
419 | __raw_writel(csicr1, pcdev->base + CSICR1); | ||
420 | |||
421 | csicr1 |= CSICR1_MCLKEN | CSICR1_FCC | CSICR1_GCLK_MODE; | ||
422 | csicr1 |= CSICR1_MCLKDIV(mclk_get_divisor(pcdev)); | ||
423 | csicr1 |= CSICR1_RXFF_LEVEL(2); /* 16 words */ | ||
424 | |||
425 | __raw_writel(csicr1, pcdev->base + CSICR1); | ||
426 | } | ||
427 | |||
428 | static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) | ||
429 | { | ||
430 | dev_dbg(pcdev->icd->parent, "Deactivate device\n"); | ||
431 | |||
432 | /* Disable all CSI interface */ | ||
433 | __raw_writel(0x00, pcdev->base + CSICR1); | ||
434 | |||
435 | clk_disable(pcdev->clk); | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * The following two functions absolutely depend on the fact, that | ||
440 | * there can be only one camera on i.MX1/i.MXL camera sensor interface | ||
441 | */ | ||
442 | static int mx1_camera_add_device(struct soc_camera_device *icd) | ||
443 | { | ||
444 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
445 | struct mx1_camera_dev *pcdev = ici->priv; | ||
446 | |||
447 | if (pcdev->icd) | ||
448 | return -EBUSY; | ||
449 | |||
450 | dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n", | ||
451 | icd->devnum); | ||
452 | |||
453 | mx1_camera_activate(pcdev); | ||
454 | |||
455 | pcdev->icd = icd; | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | static void mx1_camera_remove_device(struct soc_camera_device *icd) | ||
461 | { | ||
462 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
463 | struct mx1_camera_dev *pcdev = ici->priv; | ||
464 | unsigned int csicr1; | ||
465 | |||
466 | BUG_ON(icd != pcdev->icd); | ||
467 | |||
468 | /* disable interrupts */ | ||
469 | csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK; | ||
470 | __raw_writel(csicr1, pcdev->base + CSICR1); | ||
471 | |||
472 | /* Stop DMA engine */ | ||
473 | imx_dma_disable(pcdev->dma_chan); | ||
474 | |||
475 | dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n", | ||
476 | icd->devnum); | ||
477 | |||
478 | mx1_camera_deactivate(pcdev); | ||
479 | |||
480 | pcdev->icd = NULL; | ||
481 | } | ||
482 | |||
483 | static int mx1_camera_set_crop(struct soc_camera_device *icd, | ||
484 | struct v4l2_crop *a) | ||
485 | { | ||
486 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
487 | |||
488 | return v4l2_subdev_call(sd, video, s_crop, a); | ||
489 | } | ||
490 | |||
491 | static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) | ||
492 | { | ||
493 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
494 | struct mx1_camera_dev *pcdev = ici->priv; | ||
495 | unsigned long camera_flags, common_flags; | ||
496 | unsigned int csicr1; | ||
497 | int ret; | ||
498 | |||
499 | camera_flags = icd->ops->query_bus_param(icd); | ||
500 | |||
501 | /* MX1 supports only 8bit buswidth */ | ||
502 | common_flags = soc_camera_bus_param_compatible(camera_flags, | ||
503 | CSI_BUS_FLAGS); | ||
504 | if (!common_flags) | ||
505 | return -EINVAL; | ||
506 | |||
507 | /* Make choises, based on platform choice */ | ||
508 | if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && | ||
509 | (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { | ||
510 | if (!pcdev->pdata || | ||
511 | pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH) | ||
512 | common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; | ||
513 | else | ||
514 | common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; | ||
515 | } | ||
516 | |||
517 | if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && | ||
518 | (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { | ||
519 | if (!pcdev->pdata || | ||
520 | pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING) | ||
521 | common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; | ||
522 | else | ||
523 | common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; | ||
524 | } | ||
525 | |||
526 | if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && | ||
527 | (common_flags & SOCAM_DATA_ACTIVE_LOW)) { | ||
528 | if (!pcdev->pdata || | ||
529 | pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH) | ||
530 | common_flags &= ~SOCAM_DATA_ACTIVE_LOW; | ||
531 | else | ||
532 | common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; | ||
533 | } | ||
534 | |||
535 | ret = icd->ops->set_bus_param(icd, common_flags); | ||
536 | if (ret < 0) | ||
537 | return ret; | ||
538 | |||
539 | csicr1 = __raw_readl(pcdev->base + CSICR1); | ||
540 | |||
541 | if (common_flags & SOCAM_PCLK_SAMPLE_RISING) | ||
542 | csicr1 |= CSICR1_REDGE; | ||
543 | if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH) | ||
544 | csicr1 |= CSICR1_SOF_POL; | ||
545 | if (common_flags & SOCAM_DATA_ACTIVE_LOW) | ||
546 | csicr1 |= CSICR1_DATA_POL; | ||
547 | |||
548 | __raw_writel(csicr1, pcdev->base + CSICR1); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static int mx1_camera_set_fmt(struct soc_camera_device *icd, | ||
554 | struct v4l2_format *f) | ||
555 | { | ||
556 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
557 | const struct soc_camera_format_xlate *xlate; | ||
558 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
559 | struct v4l2_mbus_framefmt mf; | ||
560 | int ret, buswidth; | ||
561 | |||
562 | xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); | ||
563 | if (!xlate) { | ||
564 | dev_warn(icd->parent, "Format %x not found\n", | ||
565 | pix->pixelformat); | ||
566 | return -EINVAL; | ||
567 | } | ||
568 | |||
569 | buswidth = xlate->host_fmt->bits_per_sample; | ||
570 | if (buswidth > 8) { | ||
571 | dev_warn(icd->parent, | ||
572 | "bits-per-sample %d for format %x unsupported\n", | ||
573 | buswidth, pix->pixelformat); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | mf.width = pix->width; | ||
578 | mf.height = pix->height; | ||
579 | mf.field = pix->field; | ||
580 | mf.colorspace = pix->colorspace; | ||
581 | mf.code = xlate->code; | ||
582 | |||
583 | ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); | ||
584 | if (ret < 0) | ||
585 | return ret; | ||
586 | |||
587 | if (mf.code != xlate->code) | ||
588 | return -EINVAL; | ||
589 | |||
590 | pix->width = mf.width; | ||
591 | pix->height = mf.height; | ||
592 | pix->field = mf.field; | ||
593 | pix->colorspace = mf.colorspace; | ||
594 | icd->current_fmt = xlate; | ||
595 | |||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | static int mx1_camera_try_fmt(struct soc_camera_device *icd, | ||
600 | struct v4l2_format *f) | ||
601 | { | ||
602 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
603 | const struct soc_camera_format_xlate *xlate; | ||
604 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
605 | struct v4l2_mbus_framefmt mf; | ||
606 | int ret; | ||
607 | /* TODO: limit to mx1 hardware capabilities */ | ||
608 | |||
609 | xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); | ||
610 | if (!xlate) { | ||
611 | dev_warn(icd->parent, "Format %x not found\n", | ||
612 | pix->pixelformat); | ||
613 | return -EINVAL; | ||
614 | } | ||
615 | |||
616 | mf.width = pix->width; | ||
617 | mf.height = pix->height; | ||
618 | mf.field = pix->field; | ||
619 | mf.colorspace = pix->colorspace; | ||
620 | mf.code = xlate->code; | ||
621 | |||
622 | /* limit to sensor capabilities */ | ||
623 | ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); | ||
624 | if (ret < 0) | ||
625 | return ret; | ||
626 | |||
627 | pix->width = mf.width; | ||
628 | pix->height = mf.height; | ||
629 | pix->field = mf.field; | ||
630 | pix->colorspace = mf.colorspace; | ||
631 | |||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | static int mx1_camera_reqbufs(struct soc_camera_device *icd, | ||
636 | struct v4l2_requestbuffers *p) | ||
637 | { | ||
638 | int i; | ||
639 | |||
640 | /* | ||
641 | * This is for locking debugging only. I removed spinlocks and now I | ||
642 | * check whether .prepare is ever called on a linked buffer, or whether | ||
643 | * a dma IRQ can occur for an in-work or unlinked buffer. Until now | ||
644 | * it hadn't triggered | ||
645 | */ | ||
646 | for (i = 0; i < p->count; i++) { | ||
647 | struct mx1_buffer *buf = container_of(icd->vb_vidq.bufs[i], | ||
648 | struct mx1_buffer, vb); | ||
649 | buf->inwork = 0; | ||
650 | INIT_LIST_HEAD(&buf->vb.queue); | ||
651 | } | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | static unsigned int mx1_camera_poll(struct file *file, poll_table *pt) | ||
657 | { | ||
658 | struct soc_camera_device *icd = file->private_data; | ||
659 | struct mx1_buffer *buf; | ||
660 | |||
661 | buf = list_entry(icd->vb_vidq.stream.next, struct mx1_buffer, | ||
662 | vb.stream); | ||
663 | |||
664 | poll_wait(file, &buf->vb.done, pt); | ||
665 | |||
666 | if (buf->vb.state == VIDEOBUF_DONE || | ||
667 | buf->vb.state == VIDEOBUF_ERROR) | ||
668 | return POLLIN | POLLRDNORM; | ||
669 | |||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int mx1_camera_querycap(struct soc_camera_host *ici, | ||
674 | struct v4l2_capability *cap) | ||
675 | { | ||
676 | /* cap->name is set by the friendly caller:-> */ | ||
677 | strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card)); | ||
678 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
679 | |||
680 | return 0; | ||
681 | } | ||
682 | |||
683 | static struct soc_camera_host_ops mx1_soc_camera_host_ops = { | ||
684 | .owner = THIS_MODULE, | ||
685 | .add = mx1_camera_add_device, | ||
686 | .remove = mx1_camera_remove_device, | ||
687 | .set_bus_param = mx1_camera_set_bus_param, | ||
688 | .set_crop = mx1_camera_set_crop, | ||
689 | .set_fmt = mx1_camera_set_fmt, | ||
690 | .try_fmt = mx1_camera_try_fmt, | ||
691 | .init_videobuf = mx1_camera_init_videobuf, | ||
692 | .reqbufs = mx1_camera_reqbufs, | ||
693 | .poll = mx1_camera_poll, | ||
694 | .querycap = mx1_camera_querycap, | ||
695 | }; | ||
696 | |||
697 | static struct fiq_handler fh = { | ||
698 | .name = "csi_sof" | ||
699 | }; | ||
700 | |||
701 | static int __init mx1_camera_probe(struct platform_device *pdev) | ||
702 | { | ||
703 | struct mx1_camera_dev *pcdev; | ||
704 | struct resource *res; | ||
705 | struct pt_regs regs; | ||
706 | struct clk *clk; | ||
707 | void __iomem *base; | ||
708 | unsigned int irq; | ||
709 | int err = 0; | ||
710 | |||
711 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
712 | irq = platform_get_irq(pdev, 0); | ||
713 | if (!res || (int)irq <= 0) { | ||
714 | err = -ENODEV; | ||
715 | goto exit; | ||
716 | } | ||
717 | |||
718 | clk = clk_get(&pdev->dev, "csi_clk"); | ||
719 | if (IS_ERR(clk)) { | ||
720 | err = PTR_ERR(clk); | ||
721 | goto exit; | ||
722 | } | ||
723 | |||
724 | pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); | ||
725 | if (!pcdev) { | ||
726 | dev_err(&pdev->dev, "Could not allocate pcdev\n"); | ||
727 | err = -ENOMEM; | ||
728 | goto exit_put_clk; | ||
729 | } | ||
730 | |||
731 | pcdev->res = res; | ||
732 | pcdev->clk = clk; | ||
733 | |||
734 | pcdev->pdata = pdev->dev.platform_data; | ||
735 | |||
736 | if (pcdev->pdata) | ||
737 | pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; | ||
738 | |||
739 | if (!pcdev->mclk) { | ||
740 | dev_warn(&pdev->dev, | ||
741 | "mclk_10khz == 0! Please, fix your platform data. " | ||
742 | "Using default 20MHz\n"); | ||
743 | pcdev->mclk = 20000000; | ||
744 | } | ||
745 | |||
746 | INIT_LIST_HEAD(&pcdev->capture); | ||
747 | spin_lock_init(&pcdev->lock); | ||
748 | |||
749 | /* | ||
750 | * Request the regions. | ||
751 | */ | ||
752 | if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) { | ||
753 | err = -EBUSY; | ||
754 | goto exit_kfree; | ||
755 | } | ||
756 | |||
757 | base = ioremap(res->start, resource_size(res)); | ||
758 | if (!base) { | ||
759 | err = -ENOMEM; | ||
760 | goto exit_release; | ||
761 | } | ||
762 | pcdev->irq = irq; | ||
763 | pcdev->base = base; | ||
764 | |||
765 | /* request dma */ | ||
766 | pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); | ||
767 | if (pcdev->dma_chan < 0) { | ||
768 | dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n"); | ||
769 | err = -EBUSY; | ||
770 | goto exit_iounmap; | ||
771 | } | ||
772 | dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan); | ||
773 | |||
774 | imx_dma_setup_handlers(pcdev->dma_chan, mx1_camera_dma_irq, NULL, | ||
775 | pcdev); | ||
776 | |||
777 | imx_dma_config_channel(pcdev->dma_chan, IMX_DMA_TYPE_FIFO, | ||
778 | IMX_DMA_MEMSIZE_32, MX1_DMA_REQ_CSI_R, 0); | ||
779 | /* burst length : 16 words = 64 bytes */ | ||
780 | imx_dma_config_burstlen(pcdev->dma_chan, 0); | ||
781 | |||
782 | /* request irq */ | ||
783 | err = claim_fiq(&fh); | ||
784 | if (err) { | ||
785 | dev_err(&pdev->dev, "Camera interrupt register failed \n"); | ||
786 | goto exit_free_dma; | ||
787 | } | ||
788 | |||
789 | set_fiq_handler(&mx1_camera_sof_fiq_start, &mx1_camera_sof_fiq_end - | ||
790 | &mx1_camera_sof_fiq_start); | ||
791 | |||
792 | regs.ARM_r8 = (long)MX1_DMA_DIMR; | ||
793 | regs.ARM_r9 = (long)MX1_DMA_CCR(pcdev->dma_chan); | ||
794 | regs.ARM_r10 = (long)pcdev->base + CSICR1; | ||
795 | regs.ARM_fp = (long)pcdev->base + CSISR; | ||
796 | regs.ARM_sp = 1 << pcdev->dma_chan; | ||
797 | set_fiq_regs(®s); | ||
798 | |||
799 | mxc_set_irq_fiq(irq, 1); | ||
800 | enable_fiq(irq); | ||
801 | |||
802 | pcdev->soc_host.drv_name = DRIVER_NAME; | ||
803 | pcdev->soc_host.ops = &mx1_soc_camera_host_ops; | ||
804 | pcdev->soc_host.priv = pcdev; | ||
805 | pcdev->soc_host.v4l2_dev.dev = &pdev->dev; | ||
806 | pcdev->soc_host.nr = pdev->id; | ||
807 | err = soc_camera_host_register(&pcdev->soc_host); | ||
808 | if (err) | ||
809 | goto exit_free_irq; | ||
810 | |||
811 | dev_info(&pdev->dev, "MX1 Camera driver loaded\n"); | ||
812 | |||
813 | return 0; | ||
814 | |||
815 | exit_free_irq: | ||
816 | disable_fiq(irq); | ||
817 | mxc_set_irq_fiq(irq, 0); | ||
818 | release_fiq(&fh); | ||
819 | exit_free_dma: | ||
820 | imx_dma_free(pcdev->dma_chan); | ||
821 | exit_iounmap: | ||
822 | iounmap(base); | ||
823 | exit_release: | ||
824 | release_mem_region(res->start, resource_size(res)); | ||
825 | exit_kfree: | ||
826 | kfree(pcdev); | ||
827 | exit_put_clk: | ||
828 | clk_put(clk); | ||
829 | exit: | ||
830 | return err; | ||
831 | } | ||
832 | |||
833 | static int __exit mx1_camera_remove(struct platform_device *pdev) | ||
834 | { | ||
835 | struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); | ||
836 | struct mx1_camera_dev *pcdev = container_of(soc_host, | ||
837 | struct mx1_camera_dev, soc_host); | ||
838 | struct resource *res; | ||
839 | |||
840 | imx_dma_free(pcdev->dma_chan); | ||
841 | disable_fiq(pcdev->irq); | ||
842 | mxc_set_irq_fiq(pcdev->irq, 0); | ||
843 | release_fiq(&fh); | ||
844 | |||
845 | clk_put(pcdev->clk); | ||
846 | |||
847 | soc_camera_host_unregister(soc_host); | ||
848 | |||
849 | iounmap(pcdev->base); | ||
850 | |||
851 | res = pcdev->res; | ||
852 | release_mem_region(res->start, resource_size(res)); | ||
853 | |||
854 | kfree(pcdev); | ||
855 | |||
856 | dev_info(&pdev->dev, "MX1 Camera driver unloaded\n"); | ||
857 | |||
858 | return 0; | ||
859 | } | ||
860 | |||
861 | static struct platform_driver mx1_camera_driver = { | ||
862 | .driver = { | ||
863 | .name = DRIVER_NAME, | ||
864 | }, | ||
865 | .remove = __exit_p(mx1_camera_remove), | ||
866 | }; | ||
867 | |||
868 | static int __init mx1_camera_init(void) | ||
869 | { | ||
870 | return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe); | ||
871 | } | ||
872 | |||
873 | static void __exit mx1_camera_exit(void) | ||
874 | { | ||
875 | return platform_driver_unregister(&mx1_camera_driver); | ||
876 | } | ||
877 | |||
878 | module_init(mx1_camera_init); | ||
879 | module_exit(mx1_camera_exit); | ||
880 | |||
881 | MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver"); | ||
882 | MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>"); | ||
883 | MODULE_LICENSE("GPL v2"); | ||
884 | MODULE_VERSION(DRIVER_VERSION); | ||
885 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||