diff options
author | Guennadi Liakhovetski <lg@denx.de> | 2009-02-23 10:13:24 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:42:53 -0400 |
commit | 4f67130ad35d6760c27984cf94b13a8cb85e4034 (patch) | |
tree | 926d5f4f5acb4ea49d97b96eed2296ad97e79840 /drivers | |
parent | 70e1d353e5d70551c5f69d464c4d194afae62f63 (diff) |
V4L/DVB (10674): soc-camera: camera host driver for i.MX3x SoCs
Tested with 8 bit Bayer and 8 bit monochrome video.
create mode 100644 arch/arm/plat-mxc/include/mach/mx3_camera.h
create mode 100644 drivers/media/video/mx3_camera.c
Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 5 | ||||
-rw-r--r-- | drivers/media/video/mx3_camera.c | 1183 |
3 files changed, 1193 insertions, 2 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 19cf3b8f67c4..3e4ce4aade3e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -779,6 +779,13 @@ config SOC_CAMERA_OV772X | |||
779 | help | 779 | help |
780 | This is a ov772x camera driver | 780 | This is a ov772x camera driver |
781 | 781 | ||
782 | config VIDEO_MX3 | ||
783 | tristate "i.MX3x Camera Sensor Interface driver" | ||
784 | depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA | ||
785 | select VIDEOBUF_DMA_CONTIG | ||
786 | ---help--- | ||
787 | This is a v4l2 driver for the i.MX3x Camera Sensor Interface | ||
788 | |||
782 | config VIDEO_PXA27x | 789 | config VIDEO_PXA27x |
783 | tristate "PXA27x Quick Capture Interface driver" | 790 | tristate "PXA27x Quick Capture Interface driver" |
784 | depends on VIDEO_DEV && PXA27x && SOC_CAMERA | 791 | depends on VIDEO_DEV && PXA27x && SOC_CAMERA |
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 72f6d03d2d8f..6e2c69569f97 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -134,10 +134,11 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/ | |||
134 | obj-$(CONFIG_VIDEO_VIVI) += vivi.o | 134 | obj-$(CONFIG_VIDEO_VIVI) += vivi.o |
135 | obj-$(CONFIG_VIDEO_CX23885) += cx23885/ | 135 | obj-$(CONFIG_VIDEO_CX23885) += cx23885/ |
136 | 136 | ||
137 | obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o | 137 | obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o |
138 | obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o | ||
138 | obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o | 139 | obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o |
139 | obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o | 140 | obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o |
140 | obj-$(CONFIG_SOC_CAMERA) += soc_camera.o | 141 | obj-$(CONFIG_SOC_CAMERA) += soc_camera.o |
141 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o | 142 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o |
142 | obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o | 143 | obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o |
143 | obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o | 144 | obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o |
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c new file mode 100644 index 000000000000..f525dc48f6ca --- /dev/null +++ b/drivers/media/video/mx3_camera.c | |||
@@ -0,0 +1,1183 @@ | |||
1 | /* | ||
2 | * V4L2 Driver for i.MX3x camera host | ||
3 | * | ||
4 | * Copyright (C) 2008 | ||
5 | * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/version.h> | ||
15 | #include <linux/videodev2.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | |||
21 | #include <media/v4l2-common.h> | ||
22 | #include <media/v4l2-dev.h> | ||
23 | #include <media/videobuf-dma-contig.h> | ||
24 | #include <media/soc_camera.h> | ||
25 | |||
26 | #include <mach/ipu.h> | ||
27 | #include <mach/mx3_camera.h> | ||
28 | |||
29 | #define MX3_CAM_DRV_NAME "mx3-camera" | ||
30 | |||
31 | /* CMOS Sensor Interface Registers */ | ||
32 | #define CSI_REG_START 0x60 | ||
33 | |||
34 | #define CSI_SENS_CONF (0x60 - CSI_REG_START) | ||
35 | #define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START) | ||
36 | #define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START) | ||
37 | #define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START) | ||
38 | #define CSI_TST_CTRL (0x70 - CSI_REG_START) | ||
39 | #define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START) | ||
40 | #define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START) | ||
41 | #define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START) | ||
42 | #define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START) | ||
43 | #define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START) | ||
44 | |||
45 | #define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 | ||
46 | #define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 | ||
47 | #define CSI_SENS_CONF_DATA_POL_SHIFT 2 | ||
48 | #define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 | ||
49 | #define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 | ||
50 | #define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7 | ||
51 | #define CSI_SENS_CONF_DATA_FMT_SHIFT 8 | ||
52 | #define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10 | ||
53 | #define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 | ||
54 | #define CSI_SENS_CONF_DIVRATIO_SHIFT 16 | ||
55 | |||
56 | #define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT) | ||
57 | #define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT) | ||
58 | #define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT) | ||
59 | |||
60 | #define MAX_VIDEO_MEM 16 | ||
61 | |||
62 | struct mx3_camera_buffer { | ||
63 | /* common v4l buffer stuff -- must be first */ | ||
64 | struct videobuf_buffer vb; | ||
65 | const struct soc_camera_data_format *fmt; | ||
66 | |||
67 | /* One descriptot per scatterlist (per frame) */ | ||
68 | struct dma_async_tx_descriptor *txd; | ||
69 | |||
70 | /* We have to "build" a scatterlist ourselves - one element per frame */ | ||
71 | struct scatterlist sg; | ||
72 | }; | ||
73 | |||
74 | /** | ||
75 | * struct mx3_camera_dev - i.MX3x camera (CSI) object | ||
76 | * @dev: camera device, to which the coherent buffer is attached | ||
77 | * @icd: currently attached camera sensor | ||
78 | * @clk: pointer to clock | ||
79 | * @base: remapped register base address | ||
80 | * @pdata: platform data | ||
81 | * @platform_flags: platform flags | ||
82 | * @mclk: master clock frequency in Hz | ||
83 | * @capture: list of capture videobuffers | ||
84 | * @lock: protects video buffer lists | ||
85 | * @active: active video buffer | ||
86 | * @idmac_channel: array of pointers to IPU DMAC DMA channels | ||
87 | * @soc_host: embedded soc_host object | ||
88 | */ | ||
89 | struct mx3_camera_dev { | ||
90 | struct device *dev; | ||
91 | /* | ||
92 | * i.MX3x is only supposed to handle one camera on its Camera Sensor | ||
93 | * Interface. If anyone ever builds hardware to enable more than one | ||
94 | * camera _simultaneously_, they will have to modify this driver too | ||
95 | */ | ||
96 | struct soc_camera_device *icd; | ||
97 | struct clk *clk; | ||
98 | |||
99 | void __iomem *base; | ||
100 | |||
101 | struct mx3_camera_pdata *pdata; | ||
102 | |||
103 | unsigned long platform_flags; | ||
104 | unsigned long mclk; | ||
105 | |||
106 | struct list_head capture; | ||
107 | spinlock_t lock; /* Protects video buffer lists */ | ||
108 | struct mx3_camera_buffer *active; | ||
109 | |||
110 | /* IDMAC / dmaengine interface */ | ||
111 | struct idmac_channel *idmac_channel[1]; /* We need one channel */ | ||
112 | |||
113 | struct soc_camera_host soc_host; | ||
114 | }; | ||
115 | |||
116 | struct dma_chan_request { | ||
117 | struct mx3_camera_dev *mx3_cam; | ||
118 | enum ipu_channel id; | ||
119 | }; | ||
120 | |||
121 | static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt); | ||
122 | |||
123 | static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg) | ||
124 | { | ||
125 | return __raw_readl(mx3->base + reg); | ||
126 | } | ||
127 | |||
128 | static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) | ||
129 | { | ||
130 | __raw_writel(value, mx3->base + reg); | ||
131 | } | ||
132 | |||
133 | /* Called from the IPU IDMAC ISR */ | ||
134 | static void mx3_cam_dma_done(void *arg) | ||
135 | { | ||
136 | struct idmac_tx_desc *desc = to_tx_desc(arg); | ||
137 | struct dma_chan *chan = desc->txd.chan; | ||
138 | struct idmac_channel *ichannel = to_idmac_chan(chan); | ||
139 | struct mx3_camera_dev *mx3_cam = ichannel->client; | ||
140 | struct videobuf_buffer *vb; | ||
141 | |||
142 | dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n", | ||
143 | desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0); | ||
144 | |||
145 | spin_lock(&mx3_cam->lock); | ||
146 | if (mx3_cam->active) { | ||
147 | vb = &mx3_cam->active->vb; | ||
148 | |||
149 | list_del_init(&vb->queue); | ||
150 | vb->state = VIDEOBUF_DONE; | ||
151 | do_gettimeofday(&vb->ts); | ||
152 | vb->field_count++; | ||
153 | wake_up(&vb->done); | ||
154 | } | ||
155 | |||
156 | if (list_empty(&mx3_cam->capture)) { | ||
157 | mx3_cam->active = NULL; | ||
158 | spin_unlock(&mx3_cam->lock); | ||
159 | |||
160 | /* | ||
161 | * stop capture - without further buffers IPU_CHA_BUF0_RDY will | ||
162 | * not get updated | ||
163 | */ | ||
164 | return; | ||
165 | } | ||
166 | |||
167 | mx3_cam->active = list_entry(mx3_cam->capture.next, | ||
168 | struct mx3_camera_buffer, vb.queue); | ||
169 | mx3_cam->active->vb.state = VIDEOBUF_ACTIVE; | ||
170 | spin_unlock(&mx3_cam->lock); | ||
171 | } | ||
172 | |||
173 | static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf) | ||
174 | { | ||
175 | struct soc_camera_device *icd = vq->priv_data; | ||
176 | struct videobuf_buffer *vb = &buf->vb; | ||
177 | struct dma_async_tx_descriptor *txd = buf->txd; | ||
178 | struct idmac_channel *ichan; | ||
179 | |||
180 | BUG_ON(in_interrupt()); | ||
181 | |||
182 | dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | ||
183 | vb, vb->baddr, vb->bsize); | ||
184 | |||
185 | /* | ||
186 | * This waits until this buffer is out of danger, i.e., until it is no | ||
187 | * longer in STATE_QUEUED or STATE_ACTIVE | ||
188 | */ | ||
189 | videobuf_waiton(vb, 0, 0); | ||
190 | if (txd) { | ||
191 | ichan = to_idmac_chan(txd->chan); | ||
192 | async_tx_ack(txd); | ||
193 | } | ||
194 | videobuf_dma_contig_free(vq, vb); | ||
195 | buf->txd = NULL; | ||
196 | |||
197 | vb->state = VIDEOBUF_NEEDS_INIT; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * Videobuf operations | ||
202 | */ | ||
203 | |||
204 | /* | ||
205 | * Calculate the __buffer__ (not data) size and number of buffers. | ||
206 | * Called with .vb_lock held | ||
207 | */ | ||
208 | static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, | ||
209 | unsigned int *size) | ||
210 | { | ||
211 | struct soc_camera_device *icd = vq->priv_data; | ||
212 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
213 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
214 | /* | ||
215 | * bits-per-pixel (depth) as specified in camera's pixel format does | ||
216 | * not necessarily match what the camera interface writes to RAM, but | ||
217 | * it should be good enough for now. | ||
218 | */ | ||
219 | unsigned int bpp = DIV_ROUND_UP(icd->current_fmt->depth, 8); | ||
220 | |||
221 | if (!mx3_cam->idmac_channel[0]) | ||
222 | return -EINVAL; | ||
223 | |||
224 | *size = icd->width * icd->height * bpp; | ||
225 | |||
226 | if (!*count) | ||
227 | *count = 32; | ||
228 | |||
229 | if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) | ||
230 | *count = MAX_VIDEO_MEM * 1024 * 1024 / *size; | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | /* Called with .vb_lock held */ | ||
236 | static int mx3_videobuf_prepare(struct videobuf_queue *vq, | ||
237 | struct videobuf_buffer *vb, enum v4l2_field field) | ||
238 | { | ||
239 | struct soc_camera_device *icd = vq->priv_data; | ||
240 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
241 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
242 | struct mx3_camera_buffer *buf = | ||
243 | container_of(vb, struct mx3_camera_buffer, vb); | ||
244 | /* current_fmt _must_ always be set */ | ||
245 | size_t new_size = icd->width * icd->height * | ||
246 | ((icd->current_fmt->depth + 7) >> 3); | ||
247 | int ret; | ||
248 | |||
249 | /* | ||
250 | * I think, in buf_prepare you only have to protect global data, | ||
251 | * the actual buffer is yours | ||
252 | */ | ||
253 | |||
254 | if (buf->fmt != icd->current_fmt || | ||
255 | vb->width != icd->width || | ||
256 | vb->height != icd->height || | ||
257 | vb->field != field) { | ||
258 | buf->fmt = icd->current_fmt; | ||
259 | vb->width = icd->width; | ||
260 | vb->height = icd->height; | ||
261 | vb->field = field; | ||
262 | if (vb->state != VIDEOBUF_NEEDS_INIT) | ||
263 | free_buffer(vq, buf); | ||
264 | } | ||
265 | |||
266 | if (vb->baddr && vb->bsize < new_size) { | ||
267 | /* User provided buffer, but it is too small */ | ||
268 | ret = -ENOMEM; | ||
269 | goto out; | ||
270 | } | ||
271 | |||
272 | if (vb->state == VIDEOBUF_NEEDS_INIT) { | ||
273 | struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; | ||
274 | struct scatterlist *sg = &buf->sg; | ||
275 | |||
276 | /* | ||
277 | * The total size of video-buffers that will be allocated / mapped. | ||
278 | * *size that we calculated in videobuf_setup gets assigned to | ||
279 | * vb->bsize, and now we use the same calculation to get vb->size. | ||
280 | */ | ||
281 | vb->size = new_size; | ||
282 | |||
283 | /* This actually (allocates and) maps buffers */ | ||
284 | ret = videobuf_iolock(vq, vb, NULL); | ||
285 | if (ret) | ||
286 | goto fail; | ||
287 | |||
288 | /* | ||
289 | * We will have to configure the IDMAC channel. It has two slots | ||
290 | * for DMA buffers, we shall enter the first two buffers there, | ||
291 | * and then submit new buffers in DMA-ready interrupts | ||
292 | */ | ||
293 | sg_init_table(sg, 1); | ||
294 | sg_dma_address(sg) = videobuf_to_dma_contig(vb); | ||
295 | sg_dma_len(sg) = vb->size; | ||
296 | |||
297 | buf->txd = ichan->dma_chan.device->device_prep_slave_sg( | ||
298 | &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, | ||
299 | DMA_PREP_INTERRUPT); | ||
300 | if (!buf->txd) { | ||
301 | ret = -EIO; | ||
302 | goto fail; | ||
303 | } | ||
304 | |||
305 | buf->txd->callback_param = buf->txd; | ||
306 | buf->txd->callback = mx3_cam_dma_done; | ||
307 | |||
308 | vb->state = VIDEOBUF_PREPARED; | ||
309 | } | ||
310 | |||
311 | return 0; | ||
312 | |||
313 | fail: | ||
314 | free_buffer(vq, buf); | ||
315 | out: | ||
316 | return ret; | ||
317 | } | ||
318 | |||
319 | static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) | ||
320 | { | ||
321 | /* Add more formats as need arises and test possibilities appear... */ | ||
322 | switch (fourcc) { | ||
323 | case V4L2_PIX_FMT_RGB565: | ||
324 | return IPU_PIX_FMT_RGB565; | ||
325 | case V4L2_PIX_FMT_RGB24: | ||
326 | return IPU_PIX_FMT_RGB24; | ||
327 | case V4L2_PIX_FMT_RGB332: | ||
328 | return IPU_PIX_FMT_RGB332; | ||
329 | case V4L2_PIX_FMT_YUV422P: | ||
330 | return IPU_PIX_FMT_YVU422P; | ||
331 | default: | ||
332 | return IPU_PIX_FMT_GENERIC; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | /* Called with .vb_lock held */ | ||
337 | static void mx3_videobuf_queue(struct videobuf_queue *vq, | ||
338 | struct videobuf_buffer *vb) | ||
339 | { | ||
340 | struct soc_camera_device *icd = vq->priv_data; | ||
341 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
342 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
343 | struct mx3_camera_buffer *buf = | ||
344 | container_of(vb, struct mx3_camera_buffer, vb); | ||
345 | struct dma_async_tx_descriptor *txd = buf->txd; | ||
346 | struct idmac_channel *ichan = to_idmac_chan(txd->chan); | ||
347 | struct idmac_video_param *video = &ichan->params.video; | ||
348 | const struct soc_camera_data_format *data_fmt = icd->current_fmt; | ||
349 | dma_cookie_t cookie; | ||
350 | unsigned long flags; | ||
351 | |||
352 | /* This is the configuration of one sg-element */ | ||
353 | video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc); | ||
354 | video->out_width = icd->width; | ||
355 | video->out_height = icd->height; | ||
356 | video->out_stride = icd->width; | ||
357 | |||
358 | #ifdef DEBUG | ||
359 | /* helps to see what DMA actually has written */ | ||
360 | memset((void *)vb->baddr, 0xaa, vb->bsize); | ||
361 | #endif | ||
362 | |||
363 | spin_lock_irqsave(&mx3_cam->lock, flags); | ||
364 | |||
365 | list_add_tail(&vb->queue, &mx3_cam->capture); | ||
366 | |||
367 | if (!mx3_cam->active) { | ||
368 | mx3_cam->active = buf; | ||
369 | vb->state = VIDEOBUF_ACTIVE; | ||
370 | } else { | ||
371 | vb->state = VIDEOBUF_QUEUED; | ||
372 | } | ||
373 | |||
374 | spin_unlock_irqrestore(&mx3_cam->lock, flags); | ||
375 | |||
376 | cookie = txd->tx_submit(txd); | ||
377 | dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); | ||
378 | if (cookie >= 0) | ||
379 | return; | ||
380 | |||
381 | /* Submit error */ | ||
382 | vb->state = VIDEOBUF_PREPARED; | ||
383 | |||
384 | spin_lock_irqsave(&mx3_cam->lock, flags); | ||
385 | |||
386 | list_del_init(&vb->queue); | ||
387 | |||
388 | if (mx3_cam->active == buf) | ||
389 | mx3_cam->active = NULL; | ||
390 | |||
391 | spin_unlock_irqrestore(&mx3_cam->lock, flags); | ||
392 | } | ||
393 | |||
394 | /* Called with .vb_lock held */ | ||
395 | static void mx3_videobuf_release(struct videobuf_queue *vq, | ||
396 | struct videobuf_buffer *vb) | ||
397 | { | ||
398 | struct soc_camera_device *icd = vq->priv_data; | ||
399 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
400 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
401 | struct mx3_camera_buffer *buf = | ||
402 | container_of(vb, struct mx3_camera_buffer, vb); | ||
403 | unsigned long flags; | ||
404 | |||
405 | dev_dbg(&icd->dev, "Release%s DMA 0x%08x (state %d), queue %sempty\n", | ||
406 | mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), | ||
407 | vb->state, list_empty(&vb->queue) ? "" : "not "); | ||
408 | spin_lock_irqsave(&mx3_cam->lock, flags); | ||
409 | if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && | ||
410 | !list_empty(&vb->queue)) { | ||
411 | vb->state = VIDEOBUF_ERROR; | ||
412 | |||
413 | list_del_init(&vb->queue); | ||
414 | if (mx3_cam->active == buf) | ||
415 | mx3_cam->active = NULL; | ||
416 | } | ||
417 | spin_unlock_irqrestore(&mx3_cam->lock, flags); | ||
418 | free_buffer(vq, buf); | ||
419 | } | ||
420 | |||
421 | static struct videobuf_queue_ops mx3_videobuf_ops = { | ||
422 | .buf_setup = mx3_videobuf_setup, | ||
423 | .buf_prepare = mx3_videobuf_prepare, | ||
424 | .buf_queue = mx3_videobuf_queue, | ||
425 | .buf_release = mx3_videobuf_release, | ||
426 | }; | ||
427 | |||
428 | static void mx3_camera_init_videobuf(struct videobuf_queue *q, | ||
429 | struct soc_camera_device *icd) | ||
430 | { | ||
431 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
432 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
433 | |||
434 | videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, mx3_cam->dev, | ||
435 | &mx3_cam->lock, | ||
436 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
437 | V4L2_FIELD_NONE, | ||
438 | sizeof(struct mx3_camera_buffer), icd); | ||
439 | } | ||
440 | |||
441 | /* First part of ipu_csi_init_interface() */ | ||
442 | static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, | ||
443 | struct soc_camera_device *icd) | ||
444 | { | ||
445 | u32 conf; | ||
446 | long rate; | ||
447 | |||
448 | /* Set default size: ipu_csi_set_window_size() */ | ||
449 | csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE); | ||
450 | /* ...and position to 0:0: ipu_csi_set_window_pos() */ | ||
451 | conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; | ||
452 | csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL); | ||
453 | |||
454 | /* We use only gated clock synchronisation mode so far */ | ||
455 | conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT; | ||
456 | |||
457 | /* Set generic data, platform-biggest bus-width */ | ||
458 | conf |= CSI_SENS_CONF_DATA_FMT_BAYER; | ||
459 | |||
460 | if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) | ||
461 | conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
462 | else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) | ||
463 | conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
464 | else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) | ||
465 | conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
466 | else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/ | ||
467 | conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
468 | |||
469 | if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC) | ||
470 | conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT; | ||
471 | if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC) | ||
472 | conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT; | ||
473 | if (mx3_cam->platform_flags & MX3_CAMERA_DP) | ||
474 | conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; | ||
475 | if (mx3_cam->platform_flags & MX3_CAMERA_PCP) | ||
476 | conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; | ||
477 | if (mx3_cam->platform_flags & MX3_CAMERA_HSP) | ||
478 | conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; | ||
479 | if (mx3_cam->platform_flags & MX3_CAMERA_VSP) | ||
480 | conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; | ||
481 | |||
482 | /* ipu_csi_init_interface() */ | ||
483 | csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); | ||
484 | |||
485 | clk_enable(mx3_cam->clk); | ||
486 | rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); | ||
487 | dev_dbg(&icd->dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); | ||
488 | if (rate) | ||
489 | clk_set_rate(mx3_cam->clk, rate); | ||
490 | } | ||
491 | |||
492 | /* Called with .video_lock held */ | ||
493 | static int mx3_camera_add_device(struct soc_camera_device *icd) | ||
494 | { | ||
495 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
496 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
497 | int ret; | ||
498 | |||
499 | if (mx3_cam->icd) { | ||
500 | ret = -EBUSY; | ||
501 | goto ebusy; | ||
502 | } | ||
503 | |||
504 | mx3_camera_activate(mx3_cam, icd); | ||
505 | ret = icd->ops->init(icd); | ||
506 | if (ret < 0) { | ||
507 | clk_disable(mx3_cam->clk); | ||
508 | goto einit; | ||
509 | } | ||
510 | |||
511 | mx3_cam->icd = icd; | ||
512 | |||
513 | einit: | ||
514 | ebusy: | ||
515 | if (!ret) | ||
516 | dev_info(&icd->dev, "MX3 Camera driver attached to camera %d\n", | ||
517 | icd->devnum); | ||
518 | |||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | /* Called with .video_lock held */ | ||
523 | static void mx3_camera_remove_device(struct soc_camera_device *icd) | ||
524 | { | ||
525 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
526 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
527 | struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; | ||
528 | |||
529 | BUG_ON(icd != mx3_cam->icd); | ||
530 | |||
531 | if (*ichan) { | ||
532 | dma_release_channel(&(*ichan)->dma_chan); | ||
533 | *ichan = NULL; | ||
534 | } | ||
535 | |||
536 | icd->ops->release(icd); | ||
537 | |||
538 | clk_disable(mx3_cam->clk); | ||
539 | |||
540 | mx3_cam->icd = NULL; | ||
541 | |||
542 | dev_info(&icd->dev, "MX3 Camera driver detached from camera %d\n", | ||
543 | icd->devnum); | ||
544 | } | ||
545 | |||
546 | static bool channel_change_requested(struct soc_camera_device *icd, | ||
547 | const struct soc_camera_format_xlate *xlate, | ||
548 | __u32 pixfmt, struct v4l2_rect *rect) | ||
549 | { | ||
550 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
551 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
552 | struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; | ||
553 | |||
554 | /* So far only one configuration is supported */ | ||
555 | return pixfmt || (ichan && rect->width * rect->height > | ||
556 | icd->width * icd->height); | ||
557 | } | ||
558 | |||
559 | static int test_platform_param(struct mx3_camera_dev *mx3_cam, | ||
560 | unsigned char buswidth, unsigned long *flags) | ||
561 | { | ||
562 | /* | ||
563 | * Platform specified synchronization and pixel clock polarities are | ||
564 | * only a recommendation and are only used during probing. MX3x | ||
565 | * camera interface only works in master mode, i.e., uses HSYNC and | ||
566 | * VSYNC signals from the sensor | ||
567 | */ | ||
568 | *flags = SOCAM_MASTER | | ||
569 | SOCAM_HSYNC_ACTIVE_HIGH | | ||
570 | SOCAM_HSYNC_ACTIVE_LOW | | ||
571 | SOCAM_VSYNC_ACTIVE_HIGH | | ||
572 | SOCAM_VSYNC_ACTIVE_LOW | | ||
573 | SOCAM_PCLK_SAMPLE_RISING | | ||
574 | SOCAM_PCLK_SAMPLE_FALLING | | ||
575 | SOCAM_DATA_ACTIVE_HIGH | | ||
576 | SOCAM_DATA_ACTIVE_LOW; | ||
577 | |||
578 | /* If requested data width is supported by the platform, use it or any | ||
579 | * possible lower value - i.MX31 is smart enough to schift bits */ | ||
580 | switch (buswidth) { | ||
581 | case 15: | ||
582 | if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)) | ||
583 | return -EINVAL; | ||
584 | *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 | | ||
585 | SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; | ||
586 | break; | ||
587 | case 10: | ||
588 | if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)) | ||
589 | return -EINVAL; | ||
590 | *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 | | ||
591 | SOCAM_DATAWIDTH_4; | ||
592 | break; | ||
593 | case 8: | ||
594 | if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)) | ||
595 | return -EINVAL; | ||
596 | *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; | ||
597 | break; | ||
598 | case 4: | ||
599 | if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)) | ||
600 | return -EINVAL; | ||
601 | *flags |= SOCAM_DATAWIDTH_4; | ||
602 | break; | ||
603 | default: | ||
604 | dev_info(mx3_cam->dev, "Unsupported bus width %d\n", buswidth); | ||
605 | return -EINVAL; | ||
606 | } | ||
607 | |||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | static int mx3_camera_try_bus_param(struct soc_camera_device *icd, | ||
612 | const unsigned int depth) | ||
613 | { | ||
614 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
615 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
616 | unsigned long bus_flags, camera_flags; | ||
617 | int ret = test_platform_param(mx3_cam, depth, &bus_flags); | ||
618 | |||
619 | dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", depth, ret); | ||
620 | |||
621 | if (ret < 0) | ||
622 | return ret; | ||
623 | |||
624 | camera_flags = icd->ops->query_bus_param(icd); | ||
625 | |||
626 | ret = soc_camera_bus_param_compatible(camera_flags, bus_flags); | ||
627 | if (ret < 0) | ||
628 | dev_warn(&icd->dev, "Flags incompatible: camera %lx, host %lx\n", | ||
629 | camera_flags, bus_flags); | ||
630 | |||
631 | return ret; | ||
632 | } | ||
633 | |||
634 | static bool chan_filter(struct dma_chan *chan, void *arg) | ||
635 | { | ||
636 | struct dma_chan_request *rq = arg; | ||
637 | struct mx3_camera_pdata *pdata; | ||
638 | |||
639 | if (!rq) | ||
640 | return false; | ||
641 | |||
642 | pdata = rq->mx3_cam->dev->platform_data; | ||
643 | |||
644 | return rq->id == chan->chan_id && | ||
645 | pdata->dma_dev == chan->device->dev; | ||
646 | } | ||
647 | |||
648 | static const struct soc_camera_data_format mx3_camera_formats[] = { | ||
649 | { | ||
650 | .name = "Bayer (sRGB) 8 bit", | ||
651 | .depth = 8, | ||
652 | .fourcc = V4L2_PIX_FMT_SBGGR8, | ||
653 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
654 | }, { | ||
655 | .name = "Monochrome 8 bit", | ||
656 | .depth = 8, | ||
657 | .fourcc = V4L2_PIX_FMT_GREY, | ||
658 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
659 | }, | ||
660 | }; | ||
661 | |||
662 | static bool buswidth_supported(struct soc_camera_host *ici, int depth) | ||
663 | { | ||
664 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
665 | |||
666 | switch (depth) { | ||
667 | case 4: | ||
668 | return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4); | ||
669 | case 8: | ||
670 | return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8); | ||
671 | case 10: | ||
672 | return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10); | ||
673 | case 15: | ||
674 | return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15); | ||
675 | } | ||
676 | return false; | ||
677 | } | ||
678 | |||
679 | static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, | ||
680 | struct soc_camera_format_xlate *xlate) | ||
681 | { | ||
682 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
683 | int formats = 0, buswidth, ret; | ||
684 | |||
685 | buswidth = icd->formats[idx].depth; | ||
686 | |||
687 | if (!buswidth_supported(ici, buswidth)) | ||
688 | return 0; | ||
689 | |||
690 | ret = mx3_camera_try_bus_param(icd, buswidth); | ||
691 | if (ret < 0) | ||
692 | return 0; | ||
693 | |||
694 | switch (icd->formats[idx].fourcc) { | ||
695 | case V4L2_PIX_FMT_SGRBG10: | ||
696 | formats++; | ||
697 | if (xlate) { | ||
698 | xlate->host_fmt = &mx3_camera_formats[0]; | ||
699 | xlate->cam_fmt = icd->formats + idx; | ||
700 | xlate->buswidth = buswidth; | ||
701 | xlate++; | ||
702 | dev_dbg(&ici->dev, "Providing format %s using %s\n", | ||
703 | mx3_camera_formats[0].name, | ||
704 | icd->formats[idx].name); | ||
705 | } | ||
706 | goto passthrough; | ||
707 | case V4L2_PIX_FMT_Y16: | ||
708 | formats++; | ||
709 | if (xlate) { | ||
710 | xlate->host_fmt = &mx3_camera_formats[1]; | ||
711 | xlate->cam_fmt = icd->formats + idx; | ||
712 | xlate->buswidth = buswidth; | ||
713 | xlate++; | ||
714 | dev_dbg(&ici->dev, "Providing format %s using %s\n", | ||
715 | mx3_camera_formats[0].name, | ||
716 | icd->formats[idx].name); | ||
717 | } | ||
718 | default: | ||
719 | passthrough: | ||
720 | /* Generic pass-through */ | ||
721 | formats++; | ||
722 | if (xlate) { | ||
723 | xlate->host_fmt = icd->formats + idx; | ||
724 | xlate->cam_fmt = icd->formats + idx; | ||
725 | xlate->buswidth = buswidth; | ||
726 | xlate++; | ||
727 | dev_dbg(&ici->dev, | ||
728 | "Providing format %s in pass-through mode\n", | ||
729 | icd->formats[idx].name); | ||
730 | } | ||
731 | } | ||
732 | |||
733 | return formats; | ||
734 | } | ||
735 | |||
736 | static int mx3_camera_set_fmt(struct soc_camera_device *icd, | ||
737 | __u32 pixfmt, struct v4l2_rect *rect) | ||
738 | { | ||
739 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
740 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
741 | const struct soc_camera_format_xlate *xlate; | ||
742 | u32 ctrl, width_field, height_field; | ||
743 | int ret; | ||
744 | |||
745 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | ||
746 | if (pixfmt && !xlate) { | ||
747 | dev_warn(&ici->dev, "Format %x not found\n", pixfmt); | ||
748 | return -EINVAL; | ||
749 | } | ||
750 | |||
751 | /* | ||
752 | * We now know pixel formats and can decide upon DMA-channel(s) | ||
753 | * So far only direct camera-to-memory is supported | ||
754 | */ | ||
755 | if (channel_change_requested(icd, xlate, pixfmt, rect)) { | ||
756 | dma_cap_mask_t mask; | ||
757 | struct dma_chan *chan; | ||
758 | struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; | ||
759 | /* We have to use IDMAC_IC_7 for Bayer / generic data */ | ||
760 | struct dma_chan_request rq = {.mx3_cam = mx3_cam, | ||
761 | .id = IDMAC_IC_7}; | ||
762 | |||
763 | if (*ichan) { | ||
764 | struct videobuf_buffer *vb, *_vb; | ||
765 | dma_release_channel(&(*ichan)->dma_chan); | ||
766 | *ichan = NULL; | ||
767 | mx3_cam->active = NULL; | ||
768 | list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) { | ||
769 | list_del_init(&vb->queue); | ||
770 | vb->state = VIDEOBUF_ERROR; | ||
771 | wake_up(&vb->done); | ||
772 | } | ||
773 | } | ||
774 | |||
775 | dma_cap_zero(mask); | ||
776 | dma_cap_set(DMA_SLAVE, mask); | ||
777 | dma_cap_set(DMA_PRIVATE, mask); | ||
778 | chan = dma_request_channel(mask, chan_filter, &rq); | ||
779 | if (!chan) | ||
780 | return -EBUSY; | ||
781 | |||
782 | *ichan = to_idmac_chan(chan); | ||
783 | (*ichan)->client = mx3_cam; | ||
784 | } | ||
785 | |||
786 | /* | ||
787 | * Might have to perform a complete interface initialisation like in | ||
788 | * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider | ||
789 | * mxc_v4l2_s_fmt() | ||
790 | */ | ||
791 | |||
792 | /* Setup frame size - this cannot be changed on-the-fly... */ | ||
793 | width_field = rect->width - 1; | ||
794 | height_field = rect->height - 1; | ||
795 | csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); | ||
796 | |||
797 | csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); | ||
798 | csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2); | ||
799 | |||
800 | csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE); | ||
801 | |||
802 | /* ...and position */ | ||
803 | ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; | ||
804 | /* Sensor does the cropping */ | ||
805 | csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); | ||
806 | |||
807 | /* | ||
808 | * No need to free resources here if we fail, we'll see if we need to | ||
809 | * do this next time we are called | ||
810 | */ | ||
811 | |||
812 | ret = icd->ops->set_fmt(icd, pixfmt ? xlate->cam_fmt->fourcc : 0, rect); | ||
813 | if (pixfmt && !ret) { | ||
814 | icd->buswidth = xlate->buswidth; | ||
815 | icd->current_fmt = xlate->host_fmt; | ||
816 | } | ||
817 | |||
818 | return ret; | ||
819 | } | ||
820 | |||
821 | static int mx3_camera_try_fmt(struct soc_camera_device *icd, | ||
822 | struct v4l2_format *f) | ||
823 | { | ||
824 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
825 | const struct soc_camera_format_xlate *xlate; | ||
826 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
827 | __u32 pixfmt = pix->pixelformat; | ||
828 | enum v4l2_field field; | ||
829 | int ret; | ||
830 | |||
831 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | ||
832 | if (pixfmt && !xlate) { | ||
833 | dev_warn(&ici->dev, "Format %x not found\n", pixfmt); | ||
834 | return -EINVAL; | ||
835 | } | ||
836 | |||
837 | /* limit to MX3 hardware capabilities */ | ||
838 | if (pix->height > 4096) | ||
839 | pix->height = 4096; | ||
840 | if (pix->width > 4096) | ||
841 | pix->width = 4096; | ||
842 | |||
843 | pix->bytesperline = pix->width * | ||
844 | DIV_ROUND_UP(xlate->host_fmt->depth, 8); | ||
845 | pix->sizeimage = pix->height * pix->bytesperline; | ||
846 | |||
847 | /* camera has to see its format, but the user the original one */ | ||
848 | pix->pixelformat = xlate->cam_fmt->fourcc; | ||
849 | /* limit to sensor capabilities */ | ||
850 | ret = icd->ops->try_fmt(icd, f); | ||
851 | pix->pixelformat = xlate->host_fmt->fourcc; | ||
852 | |||
853 | field = pix->field; | ||
854 | |||
855 | if (field == V4L2_FIELD_ANY) { | ||
856 | pix->field = V4L2_FIELD_NONE; | ||
857 | } else if (field != V4L2_FIELD_NONE) { | ||
858 | dev_err(&icd->dev, "Field type %d unsupported.\n", field); | ||
859 | return -EINVAL; | ||
860 | } | ||
861 | |||
862 | return ret; | ||
863 | } | ||
864 | |||
865 | static int mx3_camera_reqbufs(struct soc_camera_file *icf, | ||
866 | struct v4l2_requestbuffers *p) | ||
867 | { | ||
868 | return 0; | ||
869 | } | ||
870 | |||
871 | static unsigned int mx3_camera_poll(struct file *file, poll_table *pt) | ||
872 | { | ||
873 | struct soc_camera_file *icf = file->private_data; | ||
874 | |||
875 | return videobuf_poll_stream(file, &icf->vb_vidq, pt); | ||
876 | } | ||
877 | |||
878 | static int mx3_camera_querycap(struct soc_camera_host *ici, | ||
879 | struct v4l2_capability *cap) | ||
880 | { | ||
881 | /* cap->name is set by the firendly caller:-> */ | ||
882 | strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); | ||
883 | cap->version = KERNEL_VERSION(0, 2, 2); | ||
884 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
885 | |||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) | ||
890 | { | ||
891 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
892 | struct mx3_camera_dev *mx3_cam = ici->priv; | ||
893 | unsigned long bus_flags, camera_flags, common_flags; | ||
894 | u32 dw, sens_conf; | ||
895 | int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags); | ||
896 | const struct soc_camera_format_xlate *xlate; | ||
897 | |||
898 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | ||
899 | if (!xlate) { | ||
900 | dev_warn(&ici->dev, "Format %x not found\n", pixfmt); | ||
901 | return -EINVAL; | ||
902 | } | ||
903 | |||
904 | dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", | ||
905 | icd->buswidth, ret); | ||
906 | |||
907 | if (ret < 0) | ||
908 | return ret; | ||
909 | |||
910 | camera_flags = icd->ops->query_bus_param(icd); | ||
911 | |||
912 | common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); | ||
913 | if (!common_flags) { | ||
914 | dev_dbg(&ici->dev, "no common flags: camera %lx, host %lx\n", | ||
915 | camera_flags, bus_flags); | ||
916 | return -EINVAL; | ||
917 | } | ||
918 | |||
919 | /* Make choices, based on platform preferences */ | ||
920 | if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && | ||
921 | (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { | ||
922 | if (mx3_cam->platform_flags & MX3_CAMERA_HSP) | ||
923 | common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; | ||
924 | else | ||
925 | common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; | ||
926 | } | ||
927 | |||
928 | if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && | ||
929 | (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { | ||
930 | if (mx3_cam->platform_flags & MX3_CAMERA_VSP) | ||
931 | common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; | ||
932 | else | ||
933 | common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; | ||
934 | } | ||
935 | |||
936 | if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && | ||
937 | (common_flags & SOCAM_DATA_ACTIVE_LOW)) { | ||
938 | if (mx3_cam->platform_flags & MX3_CAMERA_DP) | ||
939 | common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; | ||
940 | else | ||
941 | common_flags &= ~SOCAM_DATA_ACTIVE_LOW; | ||
942 | } | ||
943 | |||
944 | if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && | ||
945 | (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { | ||
946 | if (mx3_cam->platform_flags & MX3_CAMERA_PCP) | ||
947 | common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; | ||
948 | else | ||
949 | common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; | ||
950 | } | ||
951 | |||
952 | /* Make the camera work in widest common mode, we'll take care of | ||
953 | * the rest */ | ||
954 | if (common_flags & SOCAM_DATAWIDTH_15) | ||
955 | common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | | ||
956 | SOCAM_DATAWIDTH_15; | ||
957 | else if (common_flags & SOCAM_DATAWIDTH_10) | ||
958 | common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | | ||
959 | SOCAM_DATAWIDTH_10; | ||
960 | else if (common_flags & SOCAM_DATAWIDTH_8) | ||
961 | common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | | ||
962 | SOCAM_DATAWIDTH_8; | ||
963 | else | ||
964 | common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | | ||
965 | SOCAM_DATAWIDTH_4; | ||
966 | |||
967 | ret = icd->ops->set_bus_param(icd, common_flags); | ||
968 | if (ret < 0) | ||
969 | return ret; | ||
970 | |||
971 | /* | ||
972 | * So far only gated clock mode is supported. Add a line | ||
973 | * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) | | ||
974 | * below and select the required mode when supporting other | ||
975 | * synchronisation protocols. | ||
976 | */ | ||
977 | sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) & | ||
978 | ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) | | ||
979 | (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) | | ||
980 | (1 << CSI_SENS_CONF_DATA_POL_SHIFT) | | ||
981 | (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) | | ||
982 | (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) | | ||
983 | (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT)); | ||
984 | |||
985 | /* TODO: Support RGB and YUV formats */ | ||
986 | |||
987 | /* This has been set in mx3_camera_activate(), but we clear it above */ | ||
988 | sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; | ||
989 | |||
990 | if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) | ||
991 | sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; | ||
992 | if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) | ||
993 | sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; | ||
994 | if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) | ||
995 | sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; | ||
996 | if (common_flags & SOCAM_DATA_ACTIVE_LOW) | ||
997 | sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; | ||
998 | |||
999 | /* Just do what we're asked to do */ | ||
1000 | switch (xlate->host_fmt->depth) { | ||
1001 | case 4: | ||
1002 | dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
1003 | break; | ||
1004 | case 8: | ||
1005 | dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
1006 | break; | ||
1007 | case 10: | ||
1008 | dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
1009 | break; | ||
1010 | default: | ||
1011 | /* | ||
1012 | * Actually it can only be 15 now, default is just to silence | ||
1013 | * compiler warnings | ||
1014 | */ | ||
1015 | case 15: | ||
1016 | dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; | ||
1017 | } | ||
1018 | |||
1019 | csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); | ||
1020 | |||
1021 | dev_dbg(&ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | static struct soc_camera_host_ops mx3_soc_camera_host_ops = { | ||
1027 | .owner = THIS_MODULE, | ||
1028 | .add = mx3_camera_add_device, | ||
1029 | .remove = mx3_camera_remove_device, | ||
1030 | #ifdef CONFIG_PM | ||
1031 | .suspend = mx3_camera_suspend, | ||
1032 | .resume = mx3_camera_resume, | ||
1033 | #endif | ||
1034 | .set_fmt = mx3_camera_set_fmt, | ||
1035 | .try_fmt = mx3_camera_try_fmt, | ||
1036 | .get_formats = mx3_camera_get_formats, | ||
1037 | .init_videobuf = mx3_camera_init_videobuf, | ||
1038 | .reqbufs = mx3_camera_reqbufs, | ||
1039 | .poll = mx3_camera_poll, | ||
1040 | .querycap = mx3_camera_querycap, | ||
1041 | .set_bus_param = mx3_camera_set_bus_param, | ||
1042 | }; | ||
1043 | |||
1044 | static int mx3_camera_probe(struct platform_device *pdev) | ||
1045 | { | ||
1046 | struct mx3_camera_dev *mx3_cam; | ||
1047 | struct resource *res; | ||
1048 | void __iomem *base; | ||
1049 | int err = 0; | ||
1050 | struct soc_camera_host *soc_host; | ||
1051 | |||
1052 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1053 | if (!res) { | ||
1054 | err = -ENODEV; | ||
1055 | goto egetres; | ||
1056 | } | ||
1057 | |||
1058 | mx3_cam = vmalloc(sizeof(*mx3_cam)); | ||
1059 | if (!mx3_cam) { | ||
1060 | dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); | ||
1061 | err = -ENOMEM; | ||
1062 | goto ealloc; | ||
1063 | } | ||
1064 | memset(mx3_cam, 0, sizeof(*mx3_cam)); | ||
1065 | |||
1066 | mx3_cam->clk = clk_get(&pdev->dev, "csi_clk"); | ||
1067 | if (IS_ERR(mx3_cam->clk)) { | ||
1068 | err = PTR_ERR(mx3_cam->clk); | ||
1069 | goto eclkget; | ||
1070 | } | ||
1071 | |||
1072 | dev_set_drvdata(&pdev->dev, mx3_cam); | ||
1073 | |||
1074 | mx3_cam->pdata = pdev->dev.platform_data; | ||
1075 | mx3_cam->platform_flags = mx3_cam->pdata->flags; | ||
1076 | if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 | | ||
1077 | MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 | | ||
1078 | MX3_CAMERA_DATAWIDTH_15))) { | ||
1079 | /* Platform hasn't set available data widths. This is bad. | ||
1080 | * Warn and use a default. */ | ||
1081 | dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " | ||
1082 | "data widths, using default 8 bit\n"); | ||
1083 | mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; | ||
1084 | } | ||
1085 | |||
1086 | mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000; | ||
1087 | if (!mx3_cam->mclk) { | ||
1088 | dev_warn(&pdev->dev, | ||
1089 | "mclk_10khz == 0! Please, fix your platform data. " | ||
1090 | "Using default 20MHz\n"); | ||
1091 | mx3_cam->mclk = 20000000; | ||
1092 | } | ||
1093 | |||
1094 | /* list of video-buffers */ | ||
1095 | INIT_LIST_HEAD(&mx3_cam->capture); | ||
1096 | spin_lock_init(&mx3_cam->lock); | ||
1097 | |||
1098 | base = ioremap(res->start, res->end - res->start + 1); | ||
1099 | if (!base) { | ||
1100 | err = -ENOMEM; | ||
1101 | goto eioremap; | ||
1102 | } | ||
1103 | |||
1104 | mx3_cam->base = base; | ||
1105 | mx3_cam->dev = &pdev->dev; | ||
1106 | |||
1107 | soc_host = &mx3_cam->soc_host; | ||
1108 | soc_host->drv_name = MX3_CAM_DRV_NAME; | ||
1109 | soc_host->ops = &mx3_soc_camera_host_ops; | ||
1110 | soc_host->priv = mx3_cam; | ||
1111 | soc_host->dev.parent = &pdev->dev; | ||
1112 | soc_host->nr = pdev->id; | ||
1113 | err = soc_camera_host_register(soc_host); | ||
1114 | if (err) | ||
1115 | goto ecamhostreg; | ||
1116 | |||
1117 | /* IDMAC interface */ | ||
1118 | dmaengine_get(); | ||
1119 | |||
1120 | return 0; | ||
1121 | |||
1122 | ecamhostreg: | ||
1123 | iounmap(base); | ||
1124 | eioremap: | ||
1125 | clk_put(mx3_cam->clk); | ||
1126 | eclkget: | ||
1127 | vfree(mx3_cam); | ||
1128 | ealloc: | ||
1129 | egetres: | ||
1130 | return err; | ||
1131 | } | ||
1132 | |||
1133 | static int __devexit mx3_camera_remove(struct platform_device *pdev) | ||
1134 | { | ||
1135 | struct mx3_camera_dev *mx3_cam = platform_get_drvdata(pdev); | ||
1136 | |||
1137 | clk_put(mx3_cam->clk); | ||
1138 | |||
1139 | soc_camera_host_unregister(&mx3_cam->soc_host); | ||
1140 | |||
1141 | iounmap(mx3_cam->base); | ||
1142 | |||
1143 | /* | ||
1144 | * The channel has either not been allocated, | ||
1145 | * or should have been released | ||
1146 | */ | ||
1147 | if (WARN_ON(mx3_cam->idmac_channel[0])) | ||
1148 | dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); | ||
1149 | |||
1150 | vfree(mx3_cam); | ||
1151 | |||
1152 | dmaengine_put(); | ||
1153 | |||
1154 | dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n"); | ||
1155 | |||
1156 | return 0; | ||
1157 | } | ||
1158 | |||
1159 | static struct platform_driver mx3_camera_driver = { | ||
1160 | .driver = { | ||
1161 | .name = MX3_CAM_DRV_NAME, | ||
1162 | }, | ||
1163 | .probe = mx3_camera_probe, | ||
1164 | .remove = __exit_p(mx3_camera_remove), | ||
1165 | }; | ||
1166 | |||
1167 | |||
1168 | static int __devinit mx3_camera_init(void) | ||
1169 | { | ||
1170 | return platform_driver_register(&mx3_camera_driver); | ||
1171 | } | ||
1172 | |||
1173 | static void __exit mx3_camera_exit(void) | ||
1174 | { | ||
1175 | platform_driver_unregister(&mx3_camera_driver); | ||
1176 | } | ||
1177 | |||
1178 | module_init(mx3_camera_init); | ||
1179 | module_exit(mx3_camera_exit); | ||
1180 | |||
1181 | MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); | ||
1182 | MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); | ||
1183 | MODULE_LICENSE("GPL v2"); | ||