diff options
-rw-r--r-- | drivers/media/video/Kconfig | 8 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 657 | ||||
-rw-r--r-- | include/media/sh_mobile_ceu.h | 12 |
4 files changed, 678 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9a4265e5cd84..fdeaff257caf 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -969,4 +969,12 @@ config VIDEO_PXA27x | |||
969 | ---help--- | 969 | ---help--- |
970 | This is a v4l2 driver for the PXA27x Quick Capture Interface | 970 | This is a v4l2 driver for the PXA27x Quick Capture Interface |
971 | 971 | ||
972 | config VIDEO_SH_MOBILE_CEU | ||
973 | tristate "SuperH Mobile CEU Interface driver" | ||
974 | depends on VIDEO_DEV | ||
975 | select SOC_CAMERA | ||
976 | select VIDEOBUF_DMA_CONTIG | ||
977 | ---help--- | ||
978 | This is a v4l2 driver for the SuperH Mobile CEU Interface | ||
979 | |||
972 | endif # VIDEO_CAPTURE_DRIVERS | 980 | endif # VIDEO_CAPTURE_DRIVERS |
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 3106eaa893d6..6dd51b01ef90 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -133,6 +133,7 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o | |||
133 | obj-$(CONFIG_VIDEO_CX23885) += cx23885/ | 133 | obj-$(CONFIG_VIDEO_CX23885) += cx23885/ |
134 | 134 | ||
135 | obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o | 135 | obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o |
136 | obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o | ||
136 | obj-$(CONFIG_SOC_CAMERA) += soc_camera.o | 137 | obj-$(CONFIG_SOC_CAMERA) += soc_camera.o |
137 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o | 138 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o |
138 | obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o | 139 | obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o |
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c new file mode 100644 index 000000000000..6560ff496989 --- /dev/null +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -0,0 +1,657 @@ | |||
1 | /* | ||
2 | * V4L2 Driver for SuperH Mobile CEU interface | ||
3 | * | ||
4 | * Copyright (C) 2008 Magnus Damm | ||
5 | * | ||
6 | * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", | ||
7 | * | ||
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 as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #include <linux/init.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include <linux/errno.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/mm.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <linux/version.h> | ||
30 | #include <linux/device.h> | ||
31 | #include <linux/platform_device.h> | ||
32 | #include <linux/mutex.h> | ||
33 | #include <linux/videodev2.h> | ||
34 | |||
35 | #include <media/v4l2-common.h> | ||
36 | #include <media/v4l2-dev.h> | ||
37 | #include <media/soc_camera.h> | ||
38 | #include <media/sh_mobile_ceu.h> | ||
39 | #include <media/videobuf-dma-contig.h> | ||
40 | |||
41 | /* register offsets for sh7722 / sh7723 */ | ||
42 | |||
43 | #define CAPSR 0x00 | ||
44 | #define CAPCR 0x04 | ||
45 | #define CAMCR 0x08 | ||
46 | #define CMCYR 0x0c | ||
47 | #define CAMOR 0x10 | ||
48 | #define CAPWR 0x14 | ||
49 | #define CAIFR 0x18 | ||
50 | #define CSTCR 0x20 /* not on sh7723 */ | ||
51 | #define CSECR 0x24 /* not on sh7723 */ | ||
52 | #define CRCNTR 0x28 | ||
53 | #define CRCMPR 0x2c | ||
54 | #define CFLCR 0x30 | ||
55 | #define CFSZR 0x34 | ||
56 | #define CDWDR 0x38 | ||
57 | #define CDAYR 0x3c | ||
58 | #define CDACR 0x40 | ||
59 | #define CDBYR 0x44 | ||
60 | #define CDBCR 0x48 | ||
61 | #define CBDSR 0x4c | ||
62 | #define CFWCR 0x5c | ||
63 | #define CLFCR 0x60 | ||
64 | #define CDOCR 0x64 | ||
65 | #define CDDCR 0x68 | ||
66 | #define CDDAR 0x6c | ||
67 | #define CEIER 0x70 | ||
68 | #define CETCR 0x74 | ||
69 | #define CSTSR 0x7c | ||
70 | #define CSRTR 0x80 | ||
71 | #define CDSSR 0x84 | ||
72 | #define CDAYR2 0x90 | ||
73 | #define CDACR2 0x94 | ||
74 | #define CDBYR2 0x98 | ||
75 | #define CDBCR2 0x9c | ||
76 | |||
77 | static DEFINE_MUTEX(camera_lock); | ||
78 | |||
79 | /* per video frame buffer */ | ||
80 | struct sh_mobile_ceu_buffer { | ||
81 | struct videobuf_buffer vb; /* v4l buffer must be first */ | ||
82 | const struct soc_camera_data_format *fmt; | ||
83 | }; | ||
84 | |||
85 | struct sh_mobile_ceu_dev { | ||
86 | struct device *dev; | ||
87 | struct soc_camera_host ici; | ||
88 | struct soc_camera_device *icd; | ||
89 | |||
90 | unsigned int irq; | ||
91 | void __iomem *base; | ||
92 | unsigned long video_limit; | ||
93 | |||
94 | spinlock_t lock; | ||
95 | struct list_head capture; | ||
96 | struct videobuf_buffer *active; | ||
97 | |||
98 | struct sh_mobile_ceu_info *pdata; | ||
99 | }; | ||
100 | |||
101 | static void ceu_write(struct sh_mobile_ceu_dev *priv, | ||
102 | unsigned long reg_offs, unsigned long data) | ||
103 | { | ||
104 | iowrite32(data, priv->base + reg_offs); | ||
105 | } | ||
106 | |||
107 | static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv, | ||
108 | unsigned long reg_offs) | ||
109 | { | ||
110 | return ioread32(priv->base + reg_offs); | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Videobuf operations | ||
115 | */ | ||
116 | static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, | ||
117 | unsigned int *count, | ||
118 | unsigned int *size) | ||
119 | { | ||
120 | struct soc_camera_device *icd = vq->priv_data; | ||
121 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
122 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
123 | int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3; | ||
124 | |||
125 | *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel); | ||
126 | |||
127 | if (0 == *count) | ||
128 | *count = 2; | ||
129 | |||
130 | if (pcdev->video_limit) { | ||
131 | while (*size * *count > pcdev->video_limit) | ||
132 | (*count)--; | ||
133 | } | ||
134 | |||
135 | dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static void free_buffer(struct videobuf_queue *vq, | ||
141 | struct sh_mobile_ceu_buffer *buf) | ||
142 | { | ||
143 | struct soc_camera_device *icd = vq->priv_data; | ||
144 | |||
145 | dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
146 | &buf->vb, buf->vb.baddr, buf->vb.bsize); | ||
147 | |||
148 | if (in_interrupt()) | ||
149 | BUG(); | ||
150 | |||
151 | videobuf_dma_contig_free(vq, &buf->vb); | ||
152 | dev_dbg(&icd->dev, "%s freed\n", __func__); | ||
153 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | ||
154 | } | ||
155 | |||
156 | static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | ||
157 | { | ||
158 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1); | ||
159 | ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313); | ||
160 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1); | ||
161 | |||
162 | ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000); | ||
163 | |||
164 | ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10); | ||
165 | |||
166 | if (pcdev->active) { | ||
167 | ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active)); | ||
168 | ceu_write(pcdev, CAPSR, 0x1); /* start capture */ | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, | ||
173 | struct videobuf_buffer *vb, | ||
174 | enum v4l2_field field) | ||
175 | { | ||
176 | struct soc_camera_device *icd = vq->priv_data; | ||
177 | struct sh_mobile_ceu_buffer *buf; | ||
178 | int ret; | ||
179 | |||
180 | buf = container_of(vb, struct sh_mobile_ceu_buffer, vb); | ||
181 | |||
182 | dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
183 | vb, vb->baddr, vb->bsize); | ||
184 | |||
185 | /* Added list head initialization on alloc */ | ||
186 | WARN_ON(!list_empty(&vb->queue)); | ||
187 | |||
188 | #ifdef DEBUG | ||
189 | /* This can be useful if you want to see if we actually fill | ||
190 | * the buffer with something */ | ||
191 | memset((void *)vb->baddr, 0xaa, vb->bsize); | ||
192 | #endif | ||
193 | |||
194 | BUG_ON(NULL == icd->current_fmt); | ||
195 | |||
196 | if (buf->fmt != icd->current_fmt || | ||
197 | vb->width != icd->width || | ||
198 | vb->height != icd->height || | ||
199 | vb->field != field) { | ||
200 | buf->fmt = icd->current_fmt; | ||
201 | vb->width = icd->width; | ||
202 | vb->height = icd->height; | ||
203 | vb->field = field; | ||
204 | vb->state = VIDEOBUF_NEEDS_INIT; | ||
205 | } | ||
206 | |||
207 | vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); | ||
208 | if (0 != vb->baddr && vb->bsize < vb->size) { | ||
209 | ret = -EINVAL; | ||
210 | goto out; | ||
211 | } | ||
212 | |||
213 | if (vb->state == VIDEOBUF_NEEDS_INIT) { | ||
214 | ret = videobuf_iolock(vq, vb, NULL); | ||
215 | if (ret) | ||
216 | goto fail; | ||
217 | vb->state = VIDEOBUF_PREPARED; | ||
218 | } | ||
219 | |||
220 | return 0; | ||
221 | fail: | ||
222 | free_buffer(vq, buf); | ||
223 | out: | ||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, | ||
228 | struct videobuf_buffer *vb) | ||
229 | { | ||
230 | struct soc_camera_device *icd = vq->priv_data; | ||
231 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
232 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
233 | unsigned long flags; | ||
234 | |||
235 | dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
236 | vb, vb->baddr, vb->bsize); | ||
237 | |||
238 | vb->state = VIDEOBUF_ACTIVE; | ||
239 | spin_lock_irqsave(&pcdev->lock, flags); | ||
240 | list_add_tail(&vb->queue, &pcdev->capture); | ||
241 | |||
242 | if (!pcdev->active) { | ||
243 | pcdev->active = vb; | ||
244 | sh_mobile_ceu_capture(pcdev); | ||
245 | } | ||
246 | |||
247 | spin_unlock_irqrestore(&pcdev->lock, flags); | ||
248 | } | ||
249 | |||
250 | static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, | ||
251 | struct videobuf_buffer *vb) | ||
252 | { | ||
253 | free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); | ||
254 | } | ||
255 | |||
256 | static struct videobuf_queue_ops sh_mobile_ceu_videobuf_ops = { | ||
257 | .buf_setup = sh_mobile_ceu_videobuf_setup, | ||
258 | .buf_prepare = sh_mobile_ceu_videobuf_prepare, | ||
259 | .buf_queue = sh_mobile_ceu_videobuf_queue, | ||
260 | .buf_release = sh_mobile_ceu_videobuf_release, | ||
261 | }; | ||
262 | |||
263 | static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) | ||
264 | { | ||
265 | struct sh_mobile_ceu_dev *pcdev = data; | ||
266 | struct videobuf_buffer *vb; | ||
267 | unsigned long flags; | ||
268 | |||
269 | spin_lock_irqsave(&pcdev->lock, flags); | ||
270 | |||
271 | vb = pcdev->active; | ||
272 | list_del_init(&vb->queue); | ||
273 | |||
274 | if (!list_empty(&pcdev->capture)) | ||
275 | pcdev->active = list_entry(pcdev->capture.next, | ||
276 | struct videobuf_buffer, queue); | ||
277 | else | ||
278 | pcdev->active = NULL; | ||
279 | |||
280 | sh_mobile_ceu_capture(pcdev); | ||
281 | |||
282 | vb->state = VIDEOBUF_DONE; | ||
283 | do_gettimeofday(&vb->ts); | ||
284 | vb->field_count++; | ||
285 | wake_up(&vb->done); | ||
286 | spin_unlock_irqrestore(&pcdev->lock, flags); | ||
287 | |||
288 | return IRQ_HANDLED; | ||
289 | } | ||
290 | |||
291 | static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) | ||
292 | { | ||
293 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
294 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
295 | int ret = -EBUSY; | ||
296 | |||
297 | mutex_lock(&camera_lock); | ||
298 | |||
299 | if (pcdev->icd) | ||
300 | goto err; | ||
301 | |||
302 | dev_info(&icd->dev, | ||
303 | "SuperH Mobile CEU driver attached to camera %d\n", | ||
304 | icd->devnum); | ||
305 | |||
306 | if (pcdev->pdata->enable_camera) | ||
307 | pcdev->pdata->enable_camera(); | ||
308 | |||
309 | ret = icd->ops->init(icd); | ||
310 | if (ret) | ||
311 | goto err; | ||
312 | |||
313 | ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ | ||
314 | while (ceu_read(pcdev, CSTSR) & 1) | ||
315 | msleep(1); | ||
316 | |||
317 | pcdev->icd = icd; | ||
318 | err: | ||
319 | mutex_unlock(&camera_lock); | ||
320 | |||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | ||
325 | { | ||
326 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
327 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
328 | |||
329 | BUG_ON(icd != pcdev->icd); | ||
330 | |||
331 | /* disable capture, disable interrupts */ | ||
332 | ceu_write(pcdev, CEIER, 0); | ||
333 | ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ | ||
334 | icd->ops->release(icd); | ||
335 | if (pcdev->pdata->disable_camera) | ||
336 | pcdev->pdata->disable_camera(); | ||
337 | |||
338 | dev_info(&icd->dev, | ||
339 | "SuperH Mobile CEU driver detached from camera %d\n", | ||
340 | icd->devnum); | ||
341 | |||
342 | pcdev->icd = NULL; | ||
343 | } | ||
344 | |||
345 | static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | ||
346 | __u32 pixfmt) | ||
347 | { | ||
348 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
349 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
350 | int ret, buswidth, width, cfszr_width, cdwdr_width; | ||
351 | unsigned long camera_flags, common_flags, value; | ||
352 | |||
353 | camera_flags = icd->ops->query_bus_param(icd); | ||
354 | common_flags = soc_camera_bus_param_compatible(camera_flags, | ||
355 | pcdev->pdata->flags); | ||
356 | if (!common_flags) | ||
357 | return -EINVAL; | ||
358 | |||
359 | ret = icd->ops->set_bus_param(icd, common_flags); | ||
360 | if (ret < 0) | ||
361 | return ret; | ||
362 | |||
363 | switch (common_flags & SOCAM_DATAWIDTH_MASK) { | ||
364 | case SOCAM_DATAWIDTH_8: | ||
365 | buswidth = 8; | ||
366 | break; | ||
367 | case SOCAM_DATAWIDTH_16: | ||
368 | buswidth = 16; | ||
369 | break; | ||
370 | default: | ||
371 | return -EINVAL; | ||
372 | } | ||
373 | |||
374 | ceu_write(pcdev, CRCNTR, 0); | ||
375 | ceu_write(pcdev, CRCMPR, 0); | ||
376 | |||
377 | value = 0x00000010; | ||
378 | value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0; | ||
379 | value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0; | ||
380 | value |= (buswidth == 16) ? (1 << 12) : 0; | ||
381 | ceu_write(pcdev, CAMCR, value); | ||
382 | |||
383 | ceu_write(pcdev, CAPCR, 0x00300000); | ||
384 | ceu_write(pcdev, CAIFR, 0); | ||
385 | |||
386 | mdelay(1); | ||
387 | |||
388 | width = icd->width * (icd->current_fmt->depth / 8); | ||
389 | width = (buswidth == 16) ? width / 2 : width; | ||
390 | cfszr_width = (buswidth == 8) ? width / 2 : width; | ||
391 | cdwdr_width = (buswidth == 16) ? width * 2 : width; | ||
392 | |||
393 | ceu_write(pcdev, CAMOR, 0); | ||
394 | ceu_write(pcdev, CAPWR, (icd->height << 16) | width); | ||
395 | ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */ | ||
396 | ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width); | ||
397 | ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */ | ||
398 | ceu_write(pcdev, CDOCR, 0x00000016); | ||
399 | |||
400 | ceu_write(pcdev, CDWDR, cdwdr_width); | ||
401 | ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ | ||
402 | |||
403 | /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ | ||
404 | /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */ | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, | ||
410 | __u32 pixfmt) | ||
411 | { | ||
412 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
413 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
414 | unsigned long camera_flags, common_flags; | ||
415 | |||
416 | camera_flags = icd->ops->query_bus_param(icd); | ||
417 | common_flags = soc_camera_bus_param_compatible(camera_flags, | ||
418 | pcdev->pdata->flags); | ||
419 | if (!common_flags) | ||
420 | return -EINVAL; | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, | ||
426 | __u32 pixfmt, struct v4l2_rect *rect) | ||
427 | { | ||
428 | return icd->ops->set_fmt_cap(icd, pixfmt, rect); | ||
429 | } | ||
430 | |||
431 | static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, | ||
432 | struct v4l2_format *f) | ||
433 | { | ||
434 | /* FIXME: calculate using depth and bus width */ | ||
435 | |||
436 | if (f->fmt.pix.height < 4) | ||
437 | f->fmt.pix.height = 4; | ||
438 | if (f->fmt.pix.height > 1920) | ||
439 | f->fmt.pix.height = 1920; | ||
440 | if (f->fmt.pix.width < 2) | ||
441 | f->fmt.pix.width = 2; | ||
442 | if (f->fmt.pix.width > 2560) | ||
443 | f->fmt.pix.width = 2560; | ||
444 | f->fmt.pix.width &= ~0x01; | ||
445 | f->fmt.pix.height &= ~0x03; | ||
446 | |||
447 | /* limit to sensor capabilities */ | ||
448 | return icd->ops->try_fmt_cap(icd, f); | ||
449 | } | ||
450 | |||
451 | static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, | ||
452 | struct v4l2_requestbuffers *p) | ||
453 | { | ||
454 | int i; | ||
455 | |||
456 | /* This is for locking debugging only. I removed spinlocks and now I | ||
457 | * check whether .prepare is ever called on a linked buffer, or whether | ||
458 | * a dma IRQ can occur for an in-work or unlinked buffer. Until now | ||
459 | * it hadn't triggered */ | ||
460 | for (i = 0; i < p->count; i++) { | ||
461 | struct sh_mobile_ceu_buffer *buf; | ||
462 | |||
463 | buf = container_of(icf->vb_vidq.bufs[i], | ||
464 | struct sh_mobile_ceu_buffer, vb); | ||
465 | INIT_LIST_HEAD(&buf->vb.queue); | ||
466 | } | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) | ||
472 | { | ||
473 | struct soc_camera_file *icf = file->private_data; | ||
474 | struct sh_mobile_ceu_buffer *buf; | ||
475 | |||
476 | buf = list_entry(icf->vb_vidq.stream.next, | ||
477 | struct sh_mobile_ceu_buffer, vb.stream); | ||
478 | |||
479 | poll_wait(file, &buf->vb.done, pt); | ||
480 | |||
481 | if (buf->vb.state == VIDEOBUF_DONE || | ||
482 | buf->vb.state == VIDEOBUF_ERROR) | ||
483 | return POLLIN|POLLRDNORM; | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, | ||
489 | struct v4l2_capability *cap) | ||
490 | { | ||
491 | strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); | ||
492 | cap->version = KERNEL_VERSION(0, 0, 5); | ||
493 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, | ||
498 | struct soc_camera_device *icd) | ||
499 | { | ||
500 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
501 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
502 | |||
503 | videobuf_queue_dma_contig_init(q, | ||
504 | &sh_mobile_ceu_videobuf_ops, | ||
505 | &ici->dev, &pcdev->lock, | ||
506 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
507 | V4L2_FIELD_NONE, | ||
508 | sizeof(struct sh_mobile_ceu_buffer), | ||
509 | icd); | ||
510 | } | ||
511 | |||
512 | static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { | ||
513 | .owner = THIS_MODULE, | ||
514 | .add = sh_mobile_ceu_add_device, | ||
515 | .remove = sh_mobile_ceu_remove_device, | ||
516 | .set_fmt_cap = sh_mobile_ceu_set_fmt_cap, | ||
517 | .try_fmt_cap = sh_mobile_ceu_try_fmt_cap, | ||
518 | .reqbufs = sh_mobile_ceu_reqbufs, | ||
519 | .poll = sh_mobile_ceu_poll, | ||
520 | .querycap = sh_mobile_ceu_querycap, | ||
521 | .try_bus_param = sh_mobile_ceu_try_bus_param, | ||
522 | .set_bus_param = sh_mobile_ceu_set_bus_param, | ||
523 | .init_videobuf = sh_mobile_ceu_init_videobuf, | ||
524 | }; | ||
525 | |||
526 | static int sh_mobile_ceu_probe(struct platform_device *pdev) | ||
527 | { | ||
528 | struct sh_mobile_ceu_dev *pcdev; | ||
529 | struct resource *res; | ||
530 | void __iomem *base; | ||
531 | unsigned int irq; | ||
532 | int err = 0; | ||
533 | |||
534 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
535 | irq = platform_get_irq(pdev, 0); | ||
536 | if (!res || !irq) { | ||
537 | dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); | ||
538 | err = -ENODEV; | ||
539 | goto exit; | ||
540 | } | ||
541 | |||
542 | pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); | ||
543 | if (!pcdev) { | ||
544 | dev_err(&pdev->dev, "Could not allocate pcdev\n"); | ||
545 | err = -ENOMEM; | ||
546 | goto exit; | ||
547 | } | ||
548 | |||
549 | platform_set_drvdata(pdev, pcdev); | ||
550 | INIT_LIST_HEAD(&pcdev->capture); | ||
551 | spin_lock_init(&pcdev->lock); | ||
552 | |||
553 | pcdev->pdata = pdev->dev.platform_data; | ||
554 | if (!pcdev->pdata) { | ||
555 | err = -EINVAL; | ||
556 | dev_err(&pdev->dev, "CEU platform data not set.\n"); | ||
557 | goto exit_kfree; | ||
558 | } | ||
559 | |||
560 | base = ioremap_nocache(res->start, res->end - res->start + 1); | ||
561 | if (!base) { | ||
562 | err = -ENXIO; | ||
563 | dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); | ||
564 | goto exit_kfree; | ||
565 | } | ||
566 | |||
567 | pcdev->irq = irq; | ||
568 | pcdev->base = base; | ||
569 | pcdev->video_limit = 0; /* only enabled if second resource exists */ | ||
570 | pcdev->dev = &pdev->dev; | ||
571 | |||
572 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
573 | if (res) { | ||
574 | err = dma_declare_coherent_memory(&pdev->dev, res->start, | ||
575 | res->start, | ||
576 | (res->end - res->start) + 1, | ||
577 | DMA_MEMORY_MAP | | ||
578 | DMA_MEMORY_EXCLUSIVE); | ||
579 | if (!err) { | ||
580 | dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); | ||
581 | err = -ENXIO; | ||
582 | goto exit_iounmap; | ||
583 | } | ||
584 | |||
585 | pcdev->video_limit = (res->end - res->start) + 1; | ||
586 | } | ||
587 | |||
588 | /* request irq */ | ||
589 | err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, | ||
590 | pdev->dev.bus_id, pcdev); | ||
591 | if (err) { | ||
592 | dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); | ||
593 | goto exit_release_mem; | ||
594 | } | ||
595 | |||
596 | pcdev->ici.priv = pcdev; | ||
597 | pcdev->ici.dev.parent = &pdev->dev; | ||
598 | pcdev->ici.nr = pdev->id; | ||
599 | pcdev->ici.drv_name = pdev->dev.bus_id, | ||
600 | pcdev->ici.ops = &sh_mobile_ceu_host_ops, | ||
601 | |||
602 | err = soc_camera_host_register(&pcdev->ici); | ||
603 | if (err) | ||
604 | goto exit_free_irq; | ||
605 | |||
606 | return 0; | ||
607 | |||
608 | exit_free_irq: | ||
609 | free_irq(pcdev->irq, pcdev); | ||
610 | exit_release_mem: | ||
611 | if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) | ||
612 | dma_release_declared_memory(&pdev->dev); | ||
613 | exit_iounmap: | ||
614 | iounmap(base); | ||
615 | exit_kfree: | ||
616 | kfree(pcdev); | ||
617 | exit: | ||
618 | return err; | ||
619 | } | ||
620 | |||
621 | static int sh_mobile_ceu_remove(struct platform_device *pdev) | ||
622 | { | ||
623 | struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev); | ||
624 | |||
625 | soc_camera_host_unregister(&pcdev->ici); | ||
626 | free_irq(pcdev->irq, pcdev); | ||
627 | if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) | ||
628 | dma_release_declared_memory(&pdev->dev); | ||
629 | iounmap(pcdev->base); | ||
630 | kfree(pcdev); | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static struct platform_driver sh_mobile_ceu_driver = { | ||
635 | .driver = { | ||
636 | .name = "sh_mobile_ceu", | ||
637 | }, | ||
638 | .probe = sh_mobile_ceu_probe, | ||
639 | .remove = sh_mobile_ceu_remove, | ||
640 | }; | ||
641 | |||
642 | static int __init sh_mobile_ceu_init(void) | ||
643 | { | ||
644 | return platform_driver_register(&sh_mobile_ceu_driver); | ||
645 | } | ||
646 | |||
647 | static void __exit sh_mobile_ceu_exit(void) | ||
648 | { | ||
649 | return platform_driver_unregister(&sh_mobile_ceu_driver); | ||
650 | } | ||
651 | |||
652 | module_init(sh_mobile_ceu_init); | ||
653 | module_exit(sh_mobile_ceu_exit); | ||
654 | |||
655 | MODULE_DESCRIPTION("SuperH Mobile CEU driver"); | ||
656 | MODULE_AUTHOR("Magnus Damm"); | ||
657 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h new file mode 100644 index 000000000000..234a4711d2ec --- /dev/null +++ b/include/media/sh_mobile_ceu.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef __ASM_SH_MOBILE_CEU_H__ | ||
2 | #define __ASM_SH_MOBILE_CEU_H__ | ||
3 | |||
4 | #include <media/soc_camera.h> | ||
5 | |||
6 | struct sh_mobile_ceu_info { | ||
7 | unsigned long flags; /* SOCAM_... */ | ||
8 | void (*enable_camera)(void); | ||
9 | void (*disable_camera)(void); | ||
10 | }; | ||
11 | |||
12 | #endif /* __ASM_SH_MOBILE_CEU_H__ */ | ||