]>
Commit | Line | Data |
---|---|---|
195ebc43 JW |
1 | /* |
2 | * Copyright (c) 2011 Atmel Corporation | |
3 | * Josh Wu, <josh.wu@atmel.com> | |
4 | * | |
5 | * Based on previous work by Lars Haring, <lars.haring@atmel.com> | |
6 | * and Sedji Gaouaou | |
7 | * Based on the bttv driver for Bt848 with respective copyright holders | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/clk.h> | |
15 | #include <linux/completion.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/fs.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/platform_device.h> | |
f3745a3a | 23 | #include <linux/pm_runtime.h> |
195ebc43 JW |
24 | #include <linux/slab.h> |
25 | ||
195ebc43 JW |
26 | #include <media/soc_camera.h> |
27 | #include <media/soc_mediabus.h> | |
8ff19bc4 | 28 | #include <media/v4l2-of.h> |
195ebc43 JW |
29 | #include <media/videobuf2-dma-contig.h> |
30 | ||
40a78f36 LP |
31 | #include "atmel-isi.h" |
32 | ||
195ebc43 JW |
33 | #define MAX_BUFFER_NUM 32 |
34 | #define MAX_SUPPORT_WIDTH 2048 | |
35 | #define MAX_SUPPORT_HEIGHT 2048 | |
36 | #define VID_LIMIT_BYTES (16 * 1024 * 1024) | |
37 | #define MIN_FRAME_RATE 15 | |
38 | #define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) | |
39 | ||
195ebc43 JW |
40 | /* Frame buffer descriptor */ |
41 | struct fbd { | |
42 | /* Physical address of the frame buffer */ | |
43 | u32 fb_address; | |
44 | /* DMA Control Register(only in HISI2) */ | |
45 | u32 dma_ctrl; | |
46 | /* Physical address of the next fbd */ | |
47 | u32 next_fbd_address; | |
48 | }; | |
49 | ||
50 | static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl) | |
51 | { | |
52 | fb_desc->dma_ctrl = ctrl; | |
53 | } | |
54 | ||
55 | struct isi_dma_desc { | |
56 | struct list_head list; | |
57 | struct fbd *p_fbd; | |
8f05232f | 58 | dma_addr_t fbd_phys; |
195ebc43 JW |
59 | }; |
60 | ||
61 | /* Frame buffer data */ | |
62 | struct frame_buffer { | |
63 | struct vb2_buffer vb; | |
64 | struct isi_dma_desc *p_dma_desc; | |
65 | struct list_head list; | |
66 | }; | |
67 | ||
68 | struct atmel_isi { | |
69 | /* Protects the access of variables shared with the ISR */ | |
70 | spinlock_t lock; | |
71 | void __iomem *regs; | |
72 | ||
73 | int sequence; | |
195ebc43 JW |
74 | |
75 | struct vb2_alloc_ctx *alloc_ctx; | |
76 | ||
77 | /* Allocate descriptors for dma buffer use */ | |
78 | struct fbd *p_fb_descriptors; | |
8f05232f | 79 | dma_addr_t fb_descriptors_phys; |
195ebc43 JW |
80 | struct list_head dma_desc_head; |
81 | struct isi_dma_desc dma_desc[MAX_BUFFER_NUM]; | |
82 | ||
83 | struct completion complete; | |
d8ec0961 | 84 | /* ISI peripherial clock */ |
195ebc43 JW |
85 | struct clk *pclk; |
86 | unsigned int irq; | |
87 | ||
833e1063 | 88 | struct isi_platform_data pdata; |
a4e9f10b | 89 | u16 width_flags; /* max 12 bits */ |
195ebc43 JW |
90 | |
91 | struct list_head video_buffer_list; | |
92 | struct frame_buffer *active; | |
93 | ||
195ebc43 JW |
94 | struct soc_camera_host soc_host; |
95 | }; | |
96 | ||
97 | static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val) | |
98 | { | |
99 | writel(val, isi->regs + reg); | |
100 | } | |
101 | static u32 isi_readl(struct atmel_isi *isi, u32 reg) | |
102 | { | |
103 | return readl(isi->regs + reg); | |
104 | } | |
105 | ||
9626d03e | 106 | static void configure_geometry(struct atmel_isi *isi, u32 width, |
27ffaeb0 | 107 | u32 height, u32 code) |
195ebc43 | 108 | { |
ad5f9d19 | 109 | u32 cfg2; |
195ebc43 | 110 | |
ad5f9d19 | 111 | /* According to sensor's output format to set cfg2 */ |
195ebc43 | 112 | switch (code) { |
9626d03e JW |
113 | default: |
114 | /* Grey */ | |
27ffaeb0 | 115 | case MEDIA_BUS_FMT_Y8_1X8: |
ad5f9d19 | 116 | cfg2 = ISI_CFG2_GRAYSCALE | ISI_CFG2_COL_SPACE_YCbCr; |
195ebc43 | 117 | break; |
9626d03e | 118 | /* YUV */ |
27ffaeb0 | 119 | case MEDIA_BUS_FMT_VYUY8_2X8: |
ad5f9d19 | 120 | cfg2 = ISI_CFG2_YCC_SWAP_MODE_3 | ISI_CFG2_COL_SPACE_YCbCr; |
195ebc43 | 121 | break; |
27ffaeb0 | 122 | case MEDIA_BUS_FMT_UYVY8_2X8: |
ad5f9d19 | 123 | cfg2 = ISI_CFG2_YCC_SWAP_MODE_2 | ISI_CFG2_COL_SPACE_YCbCr; |
195ebc43 | 124 | break; |
27ffaeb0 | 125 | case MEDIA_BUS_FMT_YVYU8_2X8: |
ad5f9d19 | 126 | cfg2 = ISI_CFG2_YCC_SWAP_MODE_1 | ISI_CFG2_COL_SPACE_YCbCr; |
195ebc43 | 127 | break; |
27ffaeb0 | 128 | case MEDIA_BUS_FMT_YUYV8_2X8: |
ad5f9d19 | 129 | cfg2 = ISI_CFG2_YCC_SWAP_DEFAULT | ISI_CFG2_COL_SPACE_YCbCr; |
195ebc43 JW |
130 | break; |
131 | /* RGB, TODO */ | |
195ebc43 JW |
132 | } |
133 | ||
134 | isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); | |
195ebc43 | 135 | /* Set width */ |
195ebc43 JW |
136 | cfg2 |= ((width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & |
137 | ISI_CFG2_IM_HSIZE_MASK; | |
138 | /* Set height */ | |
195ebc43 JW |
139 | cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) |
140 | & ISI_CFG2_IM_VSIZE_MASK; | |
141 | isi_writel(isi, ISI_CFG2, cfg2); | |
9626d03e | 142 | } |
195ebc43 | 143 | |
9626d03e JW |
144 | static bool is_supported(struct soc_camera_device *icd, |
145 | const u32 pixformat) | |
146 | { | |
147 | switch (pixformat) { | |
148 | /* YUV, including grey */ | |
149 | case V4L2_PIX_FMT_GREY: | |
150 | case V4L2_PIX_FMT_YUYV: | |
151 | case V4L2_PIX_FMT_UYVY: | |
152 | case V4L2_PIX_FMT_YVYU: | |
153 | case V4L2_PIX_FMT_VYUY: | |
154 | return true; | |
155 | /* RGB, TODO */ | |
156 | default: | |
157 | return false; | |
158 | } | |
195ebc43 JW |
159 | } |
160 | ||
161 | static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) | |
162 | { | |
163 | if (isi->active) { | |
164 | struct vb2_buffer *vb = &isi->active->vb; | |
165 | struct frame_buffer *buf = isi->active; | |
166 | ||
167 | list_del_init(&buf->list); | |
8e6057b5 | 168 | v4l2_get_timestamp(&vb->v4l2_buf.timestamp); |
195ebc43 JW |
169 | vb->v4l2_buf.sequence = isi->sequence++; |
170 | vb2_buffer_done(vb, VB2_BUF_STATE_DONE); | |
171 | } | |
172 | ||
173 | if (list_empty(&isi->video_buffer_list)) { | |
174 | isi->active = NULL; | |
175 | } else { | |
176 | /* start next dma frame. */ | |
177 | isi->active = list_entry(isi->video_buffer_list.next, | |
178 | struct frame_buffer, list); | |
179 | isi_writel(isi, ISI_DMA_C_DSCR, | |
8f05232f | 180 | (u32)isi->active->p_dma_desc->fbd_phys); |
195ebc43 JW |
181 | isi_writel(isi, ISI_DMA_C_CTRL, |
182 | ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); | |
183 | isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); | |
184 | } | |
185 | return IRQ_HANDLED; | |
186 | } | |
187 | ||
188 | /* ISI interrupt service routine */ | |
189 | static irqreturn_t isi_interrupt(int irq, void *dev_id) | |
190 | { | |
191 | struct atmel_isi *isi = dev_id; | |
192 | u32 status, mask, pending; | |
193 | irqreturn_t ret = IRQ_NONE; | |
194 | ||
195 | spin_lock(&isi->lock); | |
196 | ||
197 | status = isi_readl(isi, ISI_STATUS); | |
198 | mask = isi_readl(isi, ISI_INTMASK); | |
199 | pending = status & mask; | |
200 | ||
201 | if (pending & ISI_CTRL_SRST) { | |
202 | complete(&isi->complete); | |
203 | isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST); | |
204 | ret = IRQ_HANDLED; | |
205 | } else if (pending & ISI_CTRL_DIS) { | |
206 | complete(&isi->complete); | |
207 | isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS); | |
208 | ret = IRQ_HANDLED; | |
209 | } else { | |
195ebc43 JW |
210 | if (likely(pending & ISI_SR_CXFR_DONE)) |
211 | ret = atmel_isi_handle_streaming(isi); | |
212 | } | |
213 | ||
214 | spin_unlock(&isi->lock); | |
215 | return ret; | |
216 | } | |
217 | ||
218 | #define WAIT_ISI_RESET 1 | |
219 | #define WAIT_ISI_DISABLE 0 | |
220 | static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) | |
221 | { | |
222 | unsigned long timeout; | |
223 | /* | |
224 | * The reset or disable will only succeed if we have a | |
225 | * pixel clock from the camera. | |
226 | */ | |
227 | init_completion(&isi->complete); | |
228 | ||
229 | if (wait_reset) { | |
230 | isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST); | |
231 | isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST); | |
232 | } else { | |
233 | isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS); | |
234 | isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); | |
235 | } | |
236 | ||
237 | timeout = wait_for_completion_timeout(&isi->complete, | |
d67b4882 | 238 | msecs_to_jiffies(500)); |
195ebc43 JW |
239 | if (timeout == 0) |
240 | return -ETIMEDOUT; | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | /* ------------------------------------------------------------------ | |
246 | Videobuf operations | |
247 | ------------------------------------------------------------------*/ | |
fc714e70 GL |
248 | static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, |
249 | unsigned int *nbuffers, unsigned int *nplanes, | |
250 | unsigned int sizes[], void *alloc_ctxs[]) | |
195ebc43 JW |
251 | { |
252 | struct soc_camera_device *icd = soc_camera_from_vb2q(vq); | |
7dfff953 | 253 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 JW |
254 | struct atmel_isi *isi = ici->priv; |
255 | unsigned long size; | |
195ebc43 | 256 | |
2b61d46e | 257 | size = icd->sizeimage; |
195ebc43 JW |
258 | |
259 | if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM) | |
260 | *nbuffers = MAX_BUFFER_NUM; | |
261 | ||
262 | if (size * *nbuffers > VID_LIMIT_BYTES) | |
263 | *nbuffers = VID_LIMIT_BYTES / size; | |
264 | ||
265 | *nplanes = 1; | |
266 | sizes[0] = size; | |
267 | alloc_ctxs[0] = isi->alloc_ctx; | |
268 | ||
269 | isi->sequence = 0; | |
270 | isi->active = NULL; | |
271 | ||
7dfff953 | 272 | dev_dbg(icd->parent, "%s, count=%d, size=%ld\n", __func__, |
195ebc43 JW |
273 | *nbuffers, size); |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
278 | static int buffer_init(struct vb2_buffer *vb) | |
279 | { | |
280 | struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); | |
281 | ||
282 | buf->p_dma_desc = NULL; | |
283 | INIT_LIST_HEAD(&buf->list); | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
288 | static int buffer_prepare(struct vb2_buffer *vb) | |
289 | { | |
290 | struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); | |
291 | struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); | |
7dfff953 | 292 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 JW |
293 | struct atmel_isi *isi = ici->priv; |
294 | unsigned long size; | |
295 | struct isi_dma_desc *desc; | |
195ebc43 | 296 | |
2b61d46e | 297 | size = icd->sizeimage; |
195ebc43 JW |
298 | |
299 | if (vb2_plane_size(vb, 0) < size) { | |
7dfff953 | 300 | dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n", |
195ebc43 JW |
301 | __func__, vb2_plane_size(vb, 0), size); |
302 | return -EINVAL; | |
303 | } | |
304 | ||
305 | vb2_set_plane_payload(&buf->vb, 0, size); | |
306 | ||
307 | if (!buf->p_dma_desc) { | |
308 | if (list_empty(&isi->dma_desc_head)) { | |
7dfff953 | 309 | dev_err(icd->parent, "Not enough dma descriptors.\n"); |
195ebc43 JW |
310 | return -EINVAL; |
311 | } else { | |
312 | /* Get an available descriptor */ | |
313 | desc = list_entry(isi->dma_desc_head.next, | |
314 | struct isi_dma_desc, list); | |
315 | /* Delete the descriptor since now it is used */ | |
316 | list_del_init(&desc->list); | |
317 | ||
318 | /* Initialize the dma descriptor */ | |
319 | desc->p_fbd->fb_address = | |
ba7fcb0c | 320 | vb2_dma_contig_plane_dma_addr(vb, 0); |
195ebc43 JW |
321 | desc->p_fbd->next_fbd_address = 0; |
322 | set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB); | |
323 | ||
324 | buf->p_dma_desc = desc; | |
325 | } | |
326 | } | |
327 | return 0; | |
328 | } | |
329 | ||
330 | static void buffer_cleanup(struct vb2_buffer *vb) | |
331 | { | |
332 | struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); | |
7dfff953 | 333 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 JW |
334 | struct atmel_isi *isi = ici->priv; |
335 | struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); | |
336 | ||
337 | /* This descriptor is available now and we add to head list */ | |
338 | if (buf->p_dma_desc) | |
339 | list_add(&buf->p_dma_desc->list, &isi->dma_desc_head); | |
340 | } | |
341 | ||
342 | static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) | |
343 | { | |
344 | u32 ctrl, cfg1; | |
345 | ||
346 | cfg1 = isi_readl(isi, ISI_CFG1); | |
347 | /* Enable irq: cxfr for the codec path, pxfr for the preview path */ | |
348 | isi_writel(isi, ISI_INTEN, | |
349 | ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); | |
350 | ||
351 | /* Check if already in a frame */ | |
352 | if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { | |
f7f6ce2d | 353 | dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n"); |
195ebc43 JW |
354 | return; |
355 | } | |
356 | ||
8f05232f | 357 | isi_writel(isi, ISI_DMA_C_DSCR, (u32)buffer->p_dma_desc->fbd_phys); |
195ebc43 JW |
358 | isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); |
359 | isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); | |
360 | ||
bd6f2745 | 361 | cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK; |
195ebc43 | 362 | /* Enable linked list */ |
833e1063 | 363 | cfg1 |= isi->pdata.frate | ISI_CFG1_DISCR; |
195ebc43 JW |
364 | |
365 | /* Enable codec path and ISI */ | |
366 | ctrl = ISI_CTRL_CDC | ISI_CTRL_EN; | |
367 | isi_writel(isi, ISI_CTRL, ctrl); | |
368 | isi_writel(isi, ISI_CFG1, cfg1); | |
369 | } | |
370 | ||
371 | static void buffer_queue(struct vb2_buffer *vb) | |
372 | { | |
373 | struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); | |
7dfff953 | 374 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 JW |
375 | struct atmel_isi *isi = ici->priv; |
376 | struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb); | |
377 | unsigned long flags = 0; | |
378 | ||
379 | spin_lock_irqsave(&isi->lock, flags); | |
380 | list_add_tail(&buf->list, &isi->video_buffer_list); | |
381 | ||
382 | if (isi->active == NULL) { | |
383 | isi->active = buf; | |
bd323e28 MS |
384 | if (vb2_is_streaming(vb->vb2_queue)) |
385 | start_dma(isi, buf); | |
195ebc43 JW |
386 | } |
387 | spin_unlock_irqrestore(&isi->lock, flags); | |
388 | } | |
389 | ||
bd323e28 | 390 | static int start_streaming(struct vb2_queue *vq, unsigned int count) |
195ebc43 JW |
391 | { |
392 | struct soc_camera_device *icd = soc_camera_from_vb2q(vq); | |
7dfff953 | 393 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 | 394 | struct atmel_isi *isi = ici->priv; |
c7686264 LP |
395 | int ret; |
396 | ||
f3745a3a JW |
397 | pm_runtime_get_sync(ici->v4l2_dev.dev); |
398 | ||
c7686264 LP |
399 | /* Reset ISI */ |
400 | ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); | |
401 | if (ret < 0) { | |
402 | dev_err(icd->parent, "Reset ISI timed out\n"); | |
f3745a3a | 403 | pm_runtime_put(ici->v4l2_dev.dev); |
c7686264 LP |
404 | return ret; |
405 | } | |
406 | /* Disable all interrupts */ | |
9842a417 | 407 | isi_writel(isi, ISI_INTDIS, (u32)~0UL); |
195ebc43 | 408 | |
9626d03e | 409 | configure_geometry(isi, icd->user_width, icd->user_height, |
f653da34 | 410 | icd->current_fmt->code); |
f653da34 | 411 | |
195ebc43 | 412 | spin_lock_irq(&isi->lock); |
1426f61b | 413 | /* Clear any pending interrupt */ |
b91677ad | 414 | isi_readl(isi, ISI_STATUS); |
195ebc43 | 415 | |
bd323e28 MS |
416 | if (count) |
417 | start_dma(isi, isi->active); | |
195ebc43 JW |
418 | spin_unlock_irq(&isi->lock); |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
423 | /* abort streaming and wait for last buffer */ | |
e37559b2 | 424 | static void stop_streaming(struct vb2_queue *vq) |
195ebc43 JW |
425 | { |
426 | struct soc_camera_device *icd = soc_camera_from_vb2q(vq); | |
7dfff953 | 427 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 JW |
428 | struct atmel_isi *isi = ici->priv; |
429 | struct frame_buffer *buf, *node; | |
430 | int ret = 0; | |
431 | unsigned long timeout; | |
432 | ||
433 | spin_lock_irq(&isi->lock); | |
434 | isi->active = NULL; | |
435 | /* Release all active buffers */ | |
436 | list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { | |
437 | list_del_init(&buf->list); | |
438 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | |
439 | } | |
440 | spin_unlock_irq(&isi->lock); | |
441 | ||
442 | timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ; | |
443 | /* Wait until the end of the current frame. */ | |
444 | while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) && | |
445 | time_before(jiffies, timeout)) | |
446 | msleep(1); | |
447 | ||
8c037835 | 448 | if (time_after(jiffies, timeout)) |
7dfff953 | 449 | dev_err(icd->parent, |
195ebc43 | 450 | "Timeout waiting for finishing codec request\n"); |
195ebc43 JW |
451 | |
452 | /* Disable interrupts */ | |
453 | isi_writel(isi, ISI_INTDIS, | |
454 | ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); | |
455 | ||
456 | /* Disable ISI and wait for it is done */ | |
457 | ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); | |
458 | if (ret < 0) | |
7dfff953 | 459 | dev_err(icd->parent, "Disable ISI timed out\n"); |
f3745a3a JW |
460 | |
461 | pm_runtime_put(ici->v4l2_dev.dev); | |
195ebc43 JW |
462 | } |
463 | ||
464 | static struct vb2_ops isi_video_qops = { | |
465 | .queue_setup = queue_setup, | |
466 | .buf_init = buffer_init, | |
467 | .buf_prepare = buffer_prepare, | |
468 | .buf_cleanup = buffer_cleanup, | |
469 | .buf_queue = buffer_queue, | |
470 | .start_streaming = start_streaming, | |
471 | .stop_streaming = stop_streaming, | |
976036df LP |
472 | .wait_prepare = vb2_ops_wait_prepare, |
473 | .wait_finish = vb2_ops_wait_finish, | |
195ebc43 JW |
474 | }; |
475 | ||
476 | /* ------------------------------------------------------------------ | |
477 | SOC camera operations for the device | |
478 | ------------------------------------------------------------------*/ | |
479 | static int isi_camera_init_videobuf(struct vb2_queue *q, | |
480 | struct soc_camera_device *icd) | |
481 | { | |
976036df LP |
482 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
483 | ||
195ebc43 JW |
484 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
485 | q->io_modes = VB2_MMAP; | |
486 | q->drv_priv = icd; | |
487 | q->buf_struct_size = sizeof(struct frame_buffer); | |
488 | q->ops = &isi_video_qops; | |
489 | q->mem_ops = &vb2_dma_contig_memops; | |
ade48681 | 490 | q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
976036df | 491 | q->lock = &ici->host_lock; |
195ebc43 JW |
492 | |
493 | return vb2_queue_init(q); | |
494 | } | |
495 | ||
496 | static int isi_camera_set_fmt(struct soc_camera_device *icd, | |
497 | struct v4l2_format *f) | |
498 | { | |
195ebc43 JW |
499 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); |
500 | const struct soc_camera_format_xlate *xlate; | |
501 | struct v4l2_pix_format *pix = &f->fmt.pix; | |
ebf984bb HV |
502 | struct v4l2_subdev_format format = { |
503 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
504 | }; | |
505 | struct v4l2_mbus_framefmt *mf = &format.format; | |
195ebc43 JW |
506 | int ret; |
507 | ||
9626d03e JW |
508 | /* check with atmel-isi support format, if not support use YUYV */ |
509 | if (!is_supported(icd, pix->pixelformat)) | |
510 | pix->pixelformat = V4L2_PIX_FMT_YUYV; | |
511 | ||
195ebc43 JW |
512 | xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); |
513 | if (!xlate) { | |
7dfff953 | 514 | dev_warn(icd->parent, "Format %x not found\n", |
195ebc43 JW |
515 | pix->pixelformat); |
516 | return -EINVAL; | |
517 | } | |
518 | ||
7dfff953 | 519 | dev_dbg(icd->parent, "Plan to set format %dx%d\n", |
195ebc43 JW |
520 | pix->width, pix->height); |
521 | ||
ebf984bb HV |
522 | mf->width = pix->width; |
523 | mf->height = pix->height; | |
524 | mf->field = pix->field; | |
525 | mf->colorspace = pix->colorspace; | |
526 | mf->code = xlate->code; | |
195ebc43 | 527 | |
ebf984bb | 528 | ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); |
195ebc43 JW |
529 | if (ret < 0) |
530 | return ret; | |
531 | ||
ebf984bb | 532 | if (mf->code != xlate->code) |
195ebc43 JW |
533 | return -EINVAL; |
534 | ||
ebf984bb HV |
535 | pix->width = mf->width; |
536 | pix->height = mf->height; | |
537 | pix->field = mf->field; | |
538 | pix->colorspace = mf->colorspace; | |
195ebc43 JW |
539 | icd->current_fmt = xlate; |
540 | ||
7dfff953 | 541 | dev_dbg(icd->parent, "Finally set format %dx%d\n", |
195ebc43 JW |
542 | pix->width, pix->height); |
543 | ||
544 | return ret; | |
545 | } | |
546 | ||
547 | static int isi_camera_try_fmt(struct soc_camera_device *icd, | |
548 | struct v4l2_format *f) | |
549 | { | |
550 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | |
551 | const struct soc_camera_format_xlate *xlate; | |
552 | struct v4l2_pix_format *pix = &f->fmt.pix; | |
5eab4983 HV |
553 | struct v4l2_subdev_pad_config pad_cfg; |
554 | struct v4l2_subdev_format format = { | |
555 | .which = V4L2_SUBDEV_FORMAT_TRY, | |
556 | }; | |
557 | struct v4l2_mbus_framefmt *mf = &format.format; | |
195ebc43 JW |
558 | u32 pixfmt = pix->pixelformat; |
559 | int ret; | |
560 | ||
9626d03e JW |
561 | /* check with atmel-isi support format, if not support use YUYV */ |
562 | if (!is_supported(icd, pix->pixelformat)) | |
563 | pix->pixelformat = V4L2_PIX_FMT_YUYV; | |
564 | ||
195ebc43 JW |
565 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); |
566 | if (pixfmt && !xlate) { | |
7dfff953 | 567 | dev_warn(icd->parent, "Format %x not found\n", pixfmt); |
195ebc43 JW |
568 | return -EINVAL; |
569 | } | |
570 | ||
571 | /* limit to Atmel ISI hardware capabilities */ | |
572 | if (pix->height > MAX_SUPPORT_HEIGHT) | |
573 | pix->height = MAX_SUPPORT_HEIGHT; | |
574 | if (pix->width > MAX_SUPPORT_WIDTH) | |
575 | pix->width = MAX_SUPPORT_WIDTH; | |
576 | ||
577 | /* limit to sensor capabilities */ | |
5eab4983 HV |
578 | mf->width = pix->width; |
579 | mf->height = pix->height; | |
580 | mf->field = pix->field; | |
581 | mf->colorspace = pix->colorspace; | |
582 | mf->code = xlate->code; | |
195ebc43 | 583 | |
5eab4983 | 584 | ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); |
195ebc43 JW |
585 | if (ret < 0) |
586 | return ret; | |
587 | ||
5eab4983 HV |
588 | pix->width = mf->width; |
589 | pix->height = mf->height; | |
590 | pix->colorspace = mf->colorspace; | |
195ebc43 | 591 | |
5eab4983 | 592 | switch (mf->field) { |
195ebc43 JW |
593 | case V4L2_FIELD_ANY: |
594 | pix->field = V4L2_FIELD_NONE; | |
595 | break; | |
596 | case V4L2_FIELD_NONE: | |
597 | break; | |
598 | default: | |
7dfff953 | 599 | dev_err(icd->parent, "Field type %d unsupported.\n", |
5eab4983 | 600 | mf->field); |
195ebc43 JW |
601 | ret = -EINVAL; |
602 | } | |
603 | ||
604 | return ret; | |
605 | } | |
606 | ||
607 | static const struct soc_mbus_pixelfmt isi_camera_formats[] = { | |
608 | { | |
609 | .fourcc = V4L2_PIX_FMT_YUYV, | |
610 | .name = "Packed YUV422 16 bit", | |
611 | .bits_per_sample = 8, | |
612 | .packing = SOC_MBUS_PACKING_2X8_PADHI, | |
613 | .order = SOC_MBUS_ORDER_LE, | |
ad3b81fa | 614 | .layout = SOC_MBUS_LAYOUT_PACKED, |
195ebc43 JW |
615 | }, |
616 | }; | |
617 | ||
618 | /* This will be corrected as we get more formats */ | |
619 | static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) | |
620 | { | |
621 | return fmt->packing == SOC_MBUS_PACKING_NONE || | |
622 | (fmt->bits_per_sample == 8 && | |
623 | fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || | |
624 | (fmt->bits_per_sample > 8 && | |
625 | fmt->packing == SOC_MBUS_PACKING_EXTEND16); | |
626 | } | |
627 | ||
a4e9f10b GL |
628 | #define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \ |
629 | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ | |
630 | V4L2_MBUS_HSYNC_ACTIVE_LOW | \ | |
631 | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ | |
632 | V4L2_MBUS_VSYNC_ACTIVE_LOW | \ | |
633 | V4L2_MBUS_PCLK_SAMPLE_RISING | \ | |
634 | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ | |
635 | V4L2_MBUS_DATA_ACTIVE_HIGH) | |
195ebc43 JW |
636 | |
637 | static int isi_camera_try_bus_param(struct soc_camera_device *icd, | |
638 | unsigned char buswidth) | |
639 | { | |
a4e9f10b | 640 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); |
7dfff953 | 641 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 | 642 | struct atmel_isi *isi = ici->priv; |
a4e9f10b GL |
643 | struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; |
644 | unsigned long common_flags; | |
195ebc43 JW |
645 | int ret; |
646 | ||
a4e9f10b GL |
647 | ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); |
648 | if (!ret) { | |
649 | common_flags = soc_mbus_config_compatible(&cfg, | |
650 | ISI_BUS_PARAM); | |
651 | if (!common_flags) { | |
652 | dev_warn(icd->parent, | |
653 | "Flags incompatible: camera 0x%x, host 0x%x\n", | |
654 | cfg.flags, ISI_BUS_PARAM); | |
655 | return -EINVAL; | |
656 | } | |
657 | } else if (ret != -ENOIOCTLCMD) { | |
658 | return ret; | |
659 | } | |
660 | ||
661 | if ((1 << (buswidth - 1)) & isi->width_flags) | |
662 | return 0; | |
663 | return -EINVAL; | |
195ebc43 JW |
664 | } |
665 | ||
666 | ||
667 | static int isi_camera_get_formats(struct soc_camera_device *icd, | |
668 | unsigned int idx, | |
669 | struct soc_camera_format_xlate *xlate) | |
670 | { | |
671 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | |
672 | int formats = 0, ret; | |
673 | /* sensor format */ | |
ebcff5fc HV |
674 | struct v4l2_subdev_mbus_code_enum code = { |
675 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
676 | .index = idx, | |
677 | }; | |
195ebc43 JW |
678 | /* soc camera host format */ |
679 | const struct soc_mbus_pixelfmt *fmt; | |
680 | ||
ebcff5fc | 681 | ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); |
195ebc43 JW |
682 | if (ret < 0) |
683 | /* No more formats */ | |
684 | return 0; | |
685 | ||
ebcff5fc | 686 | fmt = soc_mbus_get_fmtdesc(code.code); |
195ebc43 | 687 | if (!fmt) { |
7dfff953 | 688 | dev_err(icd->parent, |
ebcff5fc | 689 | "Invalid format code #%u: %d\n", idx, code.code); |
195ebc43 JW |
690 | return 0; |
691 | } | |
692 | ||
693 | /* This also checks support for the requested bits-per-sample */ | |
694 | ret = isi_camera_try_bus_param(icd, fmt->bits_per_sample); | |
695 | if (ret < 0) { | |
7dfff953 | 696 | dev_err(icd->parent, |
195ebc43 JW |
697 | "Fail to try the bus parameters.\n"); |
698 | return 0; | |
699 | } | |
700 | ||
ebcff5fc | 701 | switch (code.code) { |
27ffaeb0 BB |
702 | case MEDIA_BUS_FMT_UYVY8_2X8: |
703 | case MEDIA_BUS_FMT_VYUY8_2X8: | |
704 | case MEDIA_BUS_FMT_YUYV8_2X8: | |
705 | case MEDIA_BUS_FMT_YVYU8_2X8: | |
195ebc43 JW |
706 | formats++; |
707 | if (xlate) { | |
708 | xlate->host_fmt = &isi_camera_formats[0]; | |
ebcff5fc | 709 | xlate->code = code.code; |
195ebc43 | 710 | xlate++; |
7dfff953 | 711 | dev_dbg(icd->parent, "Providing format %s using code %d\n", |
ebcff5fc | 712 | isi_camera_formats[0].name, code.code); |
195ebc43 JW |
713 | } |
714 | break; | |
715 | default: | |
716 | if (!isi_camera_packing_supported(fmt)) | |
717 | return 0; | |
718 | if (xlate) | |
7dfff953 | 719 | dev_dbg(icd->parent, |
195ebc43 JW |
720 | "Providing format %s in pass-through mode\n", |
721 | fmt->name); | |
722 | } | |
723 | ||
724 | /* Generic pass-through */ | |
725 | formats++; | |
726 | if (xlate) { | |
727 | xlate->host_fmt = fmt; | |
ebcff5fc | 728 | xlate->code = code.code; |
195ebc43 JW |
729 | xlate++; |
730 | } | |
731 | ||
732 | return formats; | |
733 | } | |
734 | ||
195ebc43 JW |
735 | static int isi_camera_add_device(struct soc_camera_device *icd) |
736 | { | |
ffebad79 GL |
737 | dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n", |
738 | icd->devnum); | |
739 | ||
740 | return 0; | |
741 | } | |
742 | ||
743 | static void isi_camera_remove_device(struct soc_camera_device *icd) | |
744 | { | |
745 | dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n", | |
746 | icd->devnum); | |
747 | } | |
748 | ||
195ebc43 JW |
749 | static unsigned int isi_camera_poll(struct file *file, poll_table *pt) |
750 | { | |
751 | struct soc_camera_device *icd = file->private_data; | |
752 | ||
753 | return vb2_poll(&icd->vb2_vidq, file, pt); | |
754 | } | |
755 | ||
756 | static int isi_camera_querycap(struct soc_camera_host *ici, | |
757 | struct v4l2_capability *cap) | |
758 | { | |
759 | strcpy(cap->driver, "atmel-isi"); | |
760 | strcpy(cap->card, "Atmel Image Sensor Interface"); | |
7d96c3e4 GL |
761 | cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; |
762 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; | |
763 | ||
195ebc43 JW |
764 | return 0; |
765 | } | |
766 | ||
8843d119 | 767 | static int isi_camera_set_bus_param(struct soc_camera_device *icd) |
195ebc43 | 768 | { |
a4e9f10b | 769 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); |
7dfff953 | 770 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); |
195ebc43 | 771 | struct atmel_isi *isi = ici->priv; |
a4e9f10b GL |
772 | struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; |
773 | unsigned long common_flags; | |
195ebc43 JW |
774 | int ret; |
775 | u32 cfg1 = 0; | |
776 | ||
a4e9f10b GL |
777 | ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); |
778 | if (!ret) { | |
779 | common_flags = soc_mbus_config_compatible(&cfg, | |
780 | ISI_BUS_PARAM); | |
781 | if (!common_flags) { | |
782 | dev_warn(icd->parent, | |
783 | "Flags incompatible: camera 0x%x, host 0x%x\n", | |
784 | cfg.flags, ISI_BUS_PARAM); | |
785 | return -EINVAL; | |
786 | } | |
787 | } else if (ret != -ENOIOCTLCMD) { | |
788 | return ret; | |
789 | } else { | |
790 | common_flags = ISI_BUS_PARAM; | |
791 | } | |
792 | dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n", | |
793 | cfg.flags, ISI_BUS_PARAM, common_flags); | |
195ebc43 JW |
794 | |
795 | /* Make choises, based on platform preferences */ | |
a4e9f10b GL |
796 | if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && |
797 | (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { | |
833e1063 | 798 | if (isi->pdata.hsync_act_low) |
a4e9f10b | 799 | common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; |
195ebc43 | 800 | else |
a4e9f10b | 801 | common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; |
195ebc43 JW |
802 | } |
803 | ||
a4e9f10b GL |
804 | if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && |
805 | (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { | |
833e1063 | 806 | if (isi->pdata.vsync_act_low) |
a4e9f10b | 807 | common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; |
195ebc43 | 808 | else |
a4e9f10b | 809 | common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; |
195ebc43 JW |
810 | } |
811 | ||
a4e9f10b GL |
812 | if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && |
813 | (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { | |
833e1063 | 814 | if (isi->pdata.pclk_act_falling) |
a4e9f10b | 815 | common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; |
195ebc43 | 816 | else |
a4e9f10b | 817 | common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; |
195ebc43 JW |
818 | } |
819 | ||
a4e9f10b GL |
820 | cfg.flags = common_flags; |
821 | ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); | |
822 | if (ret < 0 && ret != -ENOIOCTLCMD) { | |
823 | dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", | |
195ebc43 JW |
824 | common_flags, ret); |
825 | return ret; | |
826 | } | |
827 | ||
828 | /* set bus param for ISI */ | |
a4e9f10b | 829 | if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) |
195ebc43 | 830 | cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW; |
a4e9f10b | 831 | if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) |
195ebc43 | 832 | cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW; |
a4e9f10b | 833 | if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) |
195ebc43 JW |
834 | cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; |
835 | ||
833e1063 | 836 | if (isi->pdata.has_emb_sync) |
195ebc43 | 837 | cfg1 |= ISI_CFG1_EMB_SYNC; |
833e1063 | 838 | if (isi->pdata.full_mode) |
195ebc43 JW |
839 | cfg1 |= ISI_CFG1_FULL_MODE; |
840 | ||
ce037f19 JW |
841 | cfg1 |= ISI_CFG1_THMASK_BEATS_16; |
842 | ||
f3745a3a JW |
843 | /* Enable PM and peripheral clock before operate isi registers */ |
844 | pm_runtime_get_sync(ici->v4l2_dev.dev); | |
845 | ||
195ebc43 JW |
846 | isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); |
847 | isi_writel(isi, ISI_CFG1, cfg1); | |
848 | ||
f3745a3a JW |
849 | pm_runtime_put(ici->v4l2_dev.dev); |
850 | ||
195ebc43 JW |
851 | return 0; |
852 | } | |
853 | ||
854 | static struct soc_camera_host_ops isi_soc_camera_host_ops = { | |
855 | .owner = THIS_MODULE, | |
856 | .add = isi_camera_add_device, | |
857 | .remove = isi_camera_remove_device, | |
858 | .set_fmt = isi_camera_set_fmt, | |
859 | .try_fmt = isi_camera_try_fmt, | |
860 | .get_formats = isi_camera_get_formats, | |
861 | .init_videobuf2 = isi_camera_init_videobuf, | |
862 | .poll = isi_camera_poll, | |
863 | .querycap = isi_camera_querycap, | |
864 | .set_bus_param = isi_camera_set_bus_param, | |
865 | }; | |
866 | ||
867 | /* -----------------------------------------------------------------------*/ | |
4c62e976 | 868 | static int atmel_isi_remove(struct platform_device *pdev) |
195ebc43 JW |
869 | { |
870 | struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); | |
871 | struct atmel_isi *isi = container_of(soc_host, | |
872 | struct atmel_isi, soc_host); | |
873 | ||
195ebc43 JW |
874 | soc_camera_host_unregister(soc_host); |
875 | vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); | |
876 | dma_free_coherent(&pdev->dev, | |
877 | sizeof(struct fbd) * MAX_BUFFER_NUM, | |
878 | isi->p_fb_descriptors, | |
879 | isi->fb_descriptors_phys); | |
f3745a3a | 880 | pm_runtime_disable(&pdev->dev); |
195ebc43 | 881 | |
195ebc43 JW |
882 | return 0; |
883 | } | |
884 | ||
40a78f36 | 885 | static int atmel_isi_parse_dt(struct atmel_isi *isi, |
8ff19bc4 JW |
886 | struct platform_device *pdev) |
887 | { | |
888 | struct device_node *np= pdev->dev.of_node; | |
889 | struct v4l2_of_endpoint ep; | |
890 | int err; | |
891 | ||
892 | /* Default settings for ISI */ | |
893 | isi->pdata.full_mode = 1; | |
8ff19bc4 JW |
894 | isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; |
895 | ||
896 | np = of_graph_get_next_endpoint(np, NULL); | |
897 | if (!np) { | |
898 | dev_err(&pdev->dev, "Could not find the endpoint\n"); | |
899 | return -EINVAL; | |
900 | } | |
901 | ||
902 | err = v4l2_of_parse_endpoint(np, &ep); | |
37512397 | 903 | of_node_put(np); |
8ff19bc4 JW |
904 | if (err) { |
905 | dev_err(&pdev->dev, "Could not parse the endpoint\n"); | |
37512397 | 906 | return err; |
8ff19bc4 JW |
907 | } |
908 | ||
909 | switch (ep.bus.parallel.bus_width) { | |
910 | case 8: | |
911 | isi->pdata.data_width_flags = ISI_DATAWIDTH_8; | |
912 | break; | |
913 | case 10: | |
914 | isi->pdata.data_width_flags = | |
915 | ISI_DATAWIDTH_8 | ISI_DATAWIDTH_10; | |
916 | break; | |
917 | default: | |
918 | dev_err(&pdev->dev, "Unsupported bus width: %d\n", | |
919 | ep.bus.parallel.bus_width); | |
37512397 | 920 | return -EINVAL; |
8ff19bc4 JW |
921 | } |
922 | ||
37512397 | 923 | return 0; |
8ff19bc4 JW |
924 | } |
925 | ||
4c62e976 | 926 | static int atmel_isi_probe(struct platform_device *pdev) |
195ebc43 JW |
927 | { |
928 | unsigned int irq; | |
929 | struct atmel_isi *isi; | |
195ebc43 JW |
930 | struct resource *regs; |
931 | int ret, i; | |
195ebc43 | 932 | struct soc_camera_host *soc_host; |
195ebc43 | 933 | |
c52c0cbf | 934 | isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); |
195ebc43 | 935 | if (!isi) { |
195ebc43 | 936 | dev_err(&pdev->dev, "Can't allocate interface!\n"); |
c52c0cbf | 937 | return -ENOMEM; |
195ebc43 JW |
938 | } |
939 | ||
c52c0cbf LP |
940 | isi->pclk = devm_clk_get(&pdev->dev, "isi_clk"); |
941 | if (IS_ERR(isi->pclk)) | |
942 | return PTR_ERR(isi->pclk); | |
943 | ||
40a78f36 LP |
944 | ret = atmel_isi_parse_dt(isi, pdev); |
945 | if (ret) | |
946 | return ret; | |
8ff19bc4 | 947 | |
195ebc43 JW |
948 | isi->active = NULL; |
949 | spin_lock_init(&isi->lock); | |
195ebc43 JW |
950 | INIT_LIST_HEAD(&isi->video_buffer_list); |
951 | INIT_LIST_HEAD(&isi->dma_desc_head); | |
952 | ||
953 | isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, | |
954 | sizeof(struct fbd) * MAX_BUFFER_NUM, | |
955 | &isi->fb_descriptors_phys, | |
956 | GFP_KERNEL); | |
957 | if (!isi->p_fb_descriptors) { | |
195ebc43 | 958 | dev_err(&pdev->dev, "Can't allocate descriptors!\n"); |
c01d568e | 959 | return -ENOMEM; |
195ebc43 JW |
960 | } |
961 | ||
962 | for (i = 0; i < MAX_BUFFER_NUM; i++) { | |
963 | isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i; | |
964 | isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys + | |
965 | i * sizeof(struct fbd); | |
966 | list_add(&isi->dma_desc[i].list, &isi->dma_desc_head); | |
967 | } | |
968 | ||
969 | isi->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); | |
970 | if (IS_ERR(isi->alloc_ctx)) { | |
971 | ret = PTR_ERR(isi->alloc_ctx); | |
972 | goto err_alloc_ctx; | |
973 | } | |
974 | ||
c52c0cbf LP |
975 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
976 | isi->regs = devm_ioremap_resource(&pdev->dev, regs); | |
977 | if (IS_ERR(isi->regs)) { | |
978 | ret = PTR_ERR(isi->regs); | |
195ebc43 JW |
979 | goto err_ioremap; |
980 | } | |
981 | ||
833e1063 | 982 | if (isi->pdata.data_width_flags & ISI_DATAWIDTH_8) |
a4e9f10b | 983 | isi->width_flags = 1 << 7; |
833e1063 | 984 | if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) |
a4e9f10b GL |
985 | isi->width_flags |= 1 << 9; |
986 | ||
195ebc43 | 987 | irq = platform_get_irq(pdev, 0); |
e35abd44 | 988 | if (IS_ERR_VALUE(irq)) { |
195ebc43 JW |
989 | ret = irq; |
990 | goto err_req_irq; | |
991 | } | |
992 | ||
c52c0cbf | 993 | ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi); |
195ebc43 JW |
994 | if (ret) { |
995 | dev_err(&pdev->dev, "Unable to request irq %d\n", irq); | |
996 | goto err_req_irq; | |
997 | } | |
998 | isi->irq = irq; | |
999 | ||
1000 | soc_host = &isi->soc_host; | |
1001 | soc_host->drv_name = "isi-camera"; | |
1002 | soc_host->ops = &isi_soc_camera_host_ops; | |
1003 | soc_host->priv = isi; | |
1004 | soc_host->v4l2_dev.dev = &pdev->dev; | |
1005 | soc_host->nr = pdev->id; | |
1006 | ||
f3745a3a JW |
1007 | pm_suspend_ignore_children(&pdev->dev, true); |
1008 | pm_runtime_enable(&pdev->dev); | |
1009 | ||
195ebc43 JW |
1010 | ret = soc_camera_host_register(soc_host); |
1011 | if (ret) { | |
1012 | dev_err(&pdev->dev, "Unable to register soc camera host\n"); | |
1013 | goto err_register_soc_camera_host; | |
1014 | } | |
1015 | return 0; | |
1016 | ||
1017 | err_register_soc_camera_host: | |
f3745a3a | 1018 | pm_runtime_disable(&pdev->dev); |
195ebc43 | 1019 | err_req_irq: |
195ebc43 JW |
1020 | err_ioremap: |
1021 | vb2_dma_contig_cleanup_ctx(isi->alloc_ctx); | |
1022 | err_alloc_ctx: | |
1023 | dma_free_coherent(&pdev->dev, | |
1024 | sizeof(struct fbd) * MAX_BUFFER_NUM, | |
1025 | isi->p_fb_descriptors, | |
1026 | isi->fb_descriptors_phys); | |
195ebc43 JW |
1027 | |
1028 | return ret; | |
1029 | } | |
1030 | ||
18baba64 | 1031 | #ifdef CONFIG_PM |
f3745a3a JW |
1032 | static int atmel_isi_runtime_suspend(struct device *dev) |
1033 | { | |
1034 | struct soc_camera_host *soc_host = to_soc_camera_host(dev); | |
1035 | struct atmel_isi *isi = container_of(soc_host, | |
1036 | struct atmel_isi, soc_host); | |
1037 | ||
1038 | clk_disable_unprepare(isi->pclk); | |
1039 | ||
1040 | return 0; | |
1041 | } | |
1042 | static int atmel_isi_runtime_resume(struct device *dev) | |
1043 | { | |
1044 | struct soc_camera_host *soc_host = to_soc_camera_host(dev); | |
1045 | struct atmel_isi *isi = container_of(soc_host, | |
1046 | struct atmel_isi, soc_host); | |
1047 | ||
1048 | return clk_prepare_enable(isi->pclk); | |
1049 | } | |
18baba64 | 1050 | #endif /* CONFIG_PM */ |
f3745a3a JW |
1051 | |
1052 | static const struct dev_pm_ops atmel_isi_dev_pm_ops = { | |
1053 | SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend, | |
1054 | atmel_isi_runtime_resume, NULL) | |
1055 | }; | |
1056 | ||
8ff19bc4 JW |
1057 | static const struct of_device_id atmel_isi_of_match[] = { |
1058 | { .compatible = "atmel,at91sam9g45-isi" }, | |
1059 | { } | |
1060 | }; | |
1061 | MODULE_DEVICE_TABLE(of, atmel_isi_of_match); | |
1062 | ||
195ebc43 | 1063 | static struct platform_driver atmel_isi_driver = { |
4c62e976 | 1064 | .remove = atmel_isi_remove, |
195ebc43 JW |
1065 | .driver = { |
1066 | .name = "atmel_isi", | |
8ff19bc4 | 1067 | .of_match_table = of_match_ptr(atmel_isi_of_match), |
f3745a3a | 1068 | .pm = &atmel_isi_dev_pm_ops, |
195ebc43 JW |
1069 | }, |
1070 | }; | |
1071 | ||
8c31aeca | 1072 | module_platform_driver_probe(atmel_isi_driver, atmel_isi_probe); |
195ebc43 JW |
1073 | |
1074 | MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>"); | |
1075 | MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux"); | |
1076 | MODULE_LICENSE("GPL"); | |
1077 | MODULE_SUPPORTED_DEVICE("video"); |