Commit | Line | Data |
---|---|---|
e7332e3a C |
1 | /* |
2 | * vpif-display - VPIF display driver | |
3 | * Display driver for TI DaVinci VPIF | |
4 | * | |
5 | * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation version 2. | |
10 | * | |
11 | * This program is distributed .as is. WITHOUT ANY WARRANTY of any | |
12 | * kind, whether express or implied; without even the implied warranty | |
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/errno.h> | |
21 | #include <linux/fs.h> | |
22 | #include <linux/mm.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/workqueue.h> | |
25 | #include <linux/string.h> | |
26 | #include <linux/videodev2.h> | |
27 | #include <linux/wait.h> | |
28 | #include <linux/time.h> | |
29 | #include <linux/i2c.h> | |
30 | #include <linux/platform_device.h> | |
31 | #include <linux/io.h> | |
32 | #include <linux/version.h> | |
5a0e3ad6 | 33 | #include <linux/slab.h> |
e7332e3a C |
34 | |
35 | #include <asm/irq.h> | |
36 | #include <asm/page.h> | |
37 | ||
38 | #include <media/adv7343.h> | |
39 | #include <media/v4l2-device.h> | |
40 | #include <media/v4l2-ioctl.h> | |
41 | ||
42 | #include <mach/dm646x.h> | |
43 | ||
44 | #include "vpif_display.h" | |
45 | #include "vpif.h" | |
46 | ||
47 | MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); | |
48 | MODULE_LICENSE("GPL"); | |
49 | ||
50 | #define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50) | |
51 | ||
52 | #define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) | |
53 | #define vpif_dbg(level, debug, fmt, arg...) \ | |
54 | v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) | |
55 | ||
56 | static int debug = 1; | |
57 | static u32 ch2_numbuffers = 3; | |
58 | static u32 ch3_numbuffers = 3; | |
59 | static u32 ch2_bufsize = 1920 * 1080 * 2; | |
60 | static u32 ch3_bufsize = 720 * 576 * 2; | |
61 | ||
62 | module_param(debug, int, 0644); | |
63 | module_param(ch2_numbuffers, uint, S_IRUGO); | |
64 | module_param(ch3_numbuffers, uint, S_IRUGO); | |
65 | module_param(ch2_bufsize, uint, S_IRUGO); | |
66 | module_param(ch3_bufsize, uint, S_IRUGO); | |
67 | ||
68 | MODULE_PARM_DESC(debug, "Debug level 0-1"); | |
69 | MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); | |
70 | MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); | |
71 | MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); | |
72 | MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); | |
73 | ||
74 | static struct vpif_config_params config_params = { | |
75 | .min_numbuffers = 3, | |
76 | .numbuffers[0] = 3, | |
77 | .numbuffers[1] = 3, | |
78 | .min_bufsize[0] = 720 * 480 * 2, | |
79 | .min_bufsize[1] = 720 * 480 * 2, | |
80 | .channel_bufsize[0] = 1920 * 1080 * 2, | |
81 | .channel_bufsize[1] = 720 * 576 * 2, | |
82 | }; | |
83 | ||
84 | static struct vpif_device vpif_obj = { {NULL} }; | |
85 | static struct device *vpif_dev; | |
86 | ||
87 | static const struct vpif_channel_config_params ch_params[] = { | |
88 | { | |
89 | "NTSC", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, | |
90 | 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, | |
91 | }, | |
92 | { | |
93 | "PAL", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, | |
94 | 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, | |
95 | }, | |
96 | }; | |
97 | ||
98 | /* | |
99 | * vpif_uservirt_to_phys: This function is used to convert user | |
100 | * space virtual address to physical address. | |
101 | */ | |
102 | static u32 vpif_uservirt_to_phys(u32 virtp) | |
103 | { | |
104 | struct mm_struct *mm = current->mm; | |
105 | unsigned long physp = 0; | |
106 | struct vm_area_struct *vma; | |
107 | ||
108 | vma = find_vma(mm, virtp); | |
109 | ||
110 | /* For kernel direct-mapped memory, take the easy way */ | |
111 | if (virtp >= PAGE_OFFSET) { | |
112 | physp = virt_to_phys((void *)virtp); | |
113 | } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { | |
114 | /* this will catch, kernel-allocated, mmaped-to-usermode addr */ | |
115 | physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); | |
116 | } else { | |
117 | /* otherwise, use get_user_pages() for general userland pages */ | |
118 | int res, nr_pages = 1; | |
119 | struct page *pages; | |
120 | down_read(¤t->mm->mmap_sem); | |
121 | ||
122 | res = get_user_pages(current, current->mm, | |
123 | virtp, nr_pages, 1, 0, &pages, NULL); | |
124 | up_read(¤t->mm->mmap_sem); | |
125 | ||
126 | if (res == nr_pages) { | |
127 | physp = __pa(page_address(&pages[0]) + | |
128 | (virtp & ~PAGE_MASK)); | |
129 | } else { | |
130 | vpif_err("get_user_pages failed\n"); | |
131 | return 0; | |
132 | } | |
133 | } | |
134 | ||
135 | return physp; | |
136 | } | |
137 | ||
138 | /* | |
139 | * buffer_prepare: This is the callback function called from videobuf_qbuf() | |
140 | * function the buffer is prepared and user space virtual address is converted | |
141 | * into physical address | |
142 | */ | |
143 | static int vpif_buffer_prepare(struct videobuf_queue *q, | |
144 | struct videobuf_buffer *vb, | |
145 | enum v4l2_field field) | |
146 | { | |
147 | struct vpif_fh *fh = q->priv_data; | |
148 | struct common_obj *common; | |
149 | unsigned long addr; | |
150 | ||
151 | common = &fh->channel->common[VPIF_VIDEO_INDEX]; | |
152 | if (VIDEOBUF_NEEDS_INIT == vb->state) { | |
153 | vb->width = common->width; | |
154 | vb->height = common->height; | |
155 | vb->size = vb->width * vb->height; | |
156 | vb->field = field; | |
157 | } | |
158 | vb->state = VIDEOBUF_PREPARED; | |
159 | ||
160 | /* if user pointer memory mechanism is used, get the physical | |
161 | * address of the buffer */ | |
162 | if (V4L2_MEMORY_USERPTR == common->memory) { | |
163 | if (!vb->baddr) { | |
164 | vpif_err("buffer_address is 0\n"); | |
165 | return -EINVAL; | |
166 | } | |
167 | ||
168 | vb->boff = vpif_uservirt_to_phys(vb->baddr); | |
169 | if (!ISALIGNED(vb->boff)) | |
170 | goto buf_align_exit; | |
171 | } | |
172 | ||
173 | addr = vb->boff; | |
174 | if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { | |
175 | if (!ISALIGNED(addr + common->ytop_off) || | |
176 | !ISALIGNED(addr + common->ybtm_off) || | |
177 | !ISALIGNED(addr + common->ctop_off) || | |
178 | !ISALIGNED(addr + common->cbtm_off)) | |
179 | goto buf_align_exit; | |
180 | } | |
181 | return 0; | |
182 | ||
183 | buf_align_exit: | |
184 | vpif_err("buffer offset not aligned to 8 bytes\n"); | |
185 | return -EINVAL; | |
186 | } | |
187 | ||
188 | /* | |
189 | * vpif_buffer_setup: This function allocates memory for the buffers | |
190 | */ | |
191 | static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, | |
192 | unsigned int *size) | |
193 | { | |
194 | struct vpif_fh *fh = q->priv_data; | |
195 | struct channel_obj *ch = fh->channel; | |
196 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
197 | ||
198 | if (V4L2_MEMORY_MMAP != common->memory) | |
199 | return 0; | |
200 | ||
201 | *size = config_params.channel_bufsize[ch->channel_id]; | |
202 | if (*count < config_params.min_numbuffers) | |
203 | *count = config_params.min_numbuffers; | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | /* | |
209 | * vpif_buffer_queue: This function adds the buffer to DMA queue | |
210 | */ | |
211 | static void vpif_buffer_queue(struct videobuf_queue *q, | |
212 | struct videobuf_buffer *vb) | |
213 | { | |
214 | struct vpif_fh *fh = q->priv_data; | |
215 | struct common_obj *common; | |
216 | ||
217 | common = &fh->channel->common[VPIF_VIDEO_INDEX]; | |
218 | ||
219 | /* add the buffer to the DMA queue */ | |
220 | list_add_tail(&vb->queue, &common->dma_queue); | |
221 | vb->state = VIDEOBUF_QUEUED; | |
222 | } | |
223 | ||
224 | /* | |
225 | * vpif_buffer_release: This function is called from the videobuf layer to | |
226 | * free memory allocated to the buffers | |
227 | */ | |
228 | static void vpif_buffer_release(struct videobuf_queue *q, | |
229 | struct videobuf_buffer *vb) | |
230 | { | |
231 | struct vpif_fh *fh = q->priv_data; | |
232 | struct channel_obj *ch = fh->channel; | |
233 | struct common_obj *common; | |
234 | unsigned int buf_size = 0; | |
235 | ||
236 | common = &ch->common[VPIF_VIDEO_INDEX]; | |
237 | ||
238 | videobuf_dma_contig_free(q, vb); | |
239 | vb->state = VIDEOBUF_NEEDS_INIT; | |
240 | ||
241 | if (V4L2_MEMORY_MMAP != common->memory) | |
242 | return; | |
243 | ||
244 | buf_size = config_params.channel_bufsize[ch->channel_id]; | |
245 | } | |
246 | ||
247 | static struct videobuf_queue_ops video_qops = { | |
248 | .buf_setup = vpif_buffer_setup, | |
249 | .buf_prepare = vpif_buffer_prepare, | |
250 | .buf_queue = vpif_buffer_queue, | |
251 | .buf_release = vpif_buffer_release, | |
252 | }; | |
253 | static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; | |
254 | ||
255 | static void process_progressive_mode(struct common_obj *common) | |
256 | { | |
257 | unsigned long addr = 0; | |
258 | ||
259 | /* Get the next buffer from buffer queue */ | |
260 | common->next_frm = list_entry(common->dma_queue.next, | |
261 | struct videobuf_buffer, queue); | |
262 | /* Remove that buffer from the buffer queue */ | |
263 | list_del(&common->next_frm->queue); | |
264 | /* Mark status of the buffer as active */ | |
265 | common->next_frm->state = VIDEOBUF_ACTIVE; | |
266 | ||
267 | /* Set top and bottom field addrs in VPIF registers */ | |
268 | addr = videobuf_to_dma_contig(common->next_frm); | |
269 | common->set_addr(addr + common->ytop_off, | |
270 | addr + common->ybtm_off, | |
271 | addr + common->ctop_off, | |
272 | addr + common->cbtm_off); | |
273 | } | |
274 | ||
275 | static void process_interlaced_mode(int fid, struct common_obj *common) | |
276 | { | |
277 | /* device field id and local field id are in sync */ | |
278 | /* If this is even field */ | |
279 | if (0 == fid) { | |
280 | if (common->cur_frm == common->next_frm) | |
281 | return; | |
282 | ||
283 | /* one frame is displayed If next frame is | |
284 | * available, release cur_frm and move on */ | |
285 | /* Copy frame display time */ | |
286 | do_gettimeofday(&common->cur_frm->ts); | |
287 | /* Change status of the cur_frm */ | |
288 | common->cur_frm->state = VIDEOBUF_DONE; | |
289 | /* unlock semaphore on cur_frm */ | |
290 | wake_up_interruptible(&common->cur_frm->done); | |
291 | /* Make cur_frm pointing to next_frm */ | |
292 | common->cur_frm = common->next_frm; | |
293 | ||
294 | } else if (1 == fid) { /* odd field */ | |
295 | if (list_empty(&common->dma_queue) | |
296 | || (common->cur_frm != common->next_frm)) { | |
297 | return; | |
298 | } | |
299 | /* one field is displayed configure the next | |
300 | * frame if it is available else hold on current | |
301 | * frame */ | |
302 | /* Get next from the buffer queue */ | |
303 | process_progressive_mode(common); | |
304 | ||
305 | } | |
306 | } | |
307 | ||
308 | /* | |
309 | * vpif_channel_isr: It changes status of the displayed buffer, takes next | |
310 | * buffer from the queue and sets its address in VPIF registers | |
311 | */ | |
312 | static irqreturn_t vpif_channel_isr(int irq, void *dev_id) | |
313 | { | |
314 | struct vpif_device *dev = &vpif_obj; | |
315 | struct channel_obj *ch; | |
316 | struct common_obj *common; | |
317 | enum v4l2_field field; | |
318 | int fid = -1, i; | |
319 | int channel_id = 0; | |
320 | ||
321 | channel_id = *(int *)(dev_id); | |
322 | ch = dev->dev[channel_id]; | |
323 | field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; | |
324 | for (i = 0; i < VPIF_NUMOBJECTS; i++) { | |
325 | common = &ch->common[i]; | |
326 | /* If streaming is started in this channel */ | |
327 | if (0 == common->started) | |
328 | continue; | |
329 | ||
330 | if (1 == ch->vpifparams.std_info.frm_fmt) { | |
331 | if (list_empty(&common->dma_queue)) | |
332 | continue; | |
333 | ||
334 | /* Progressive mode */ | |
335 | if (!channel_first_int[i][channel_id]) { | |
336 | /* Mark status of the cur_frm to | |
337 | * done and unlock semaphore on it */ | |
338 | do_gettimeofday(&common->cur_frm->ts); | |
339 | common->cur_frm->state = VIDEOBUF_DONE; | |
340 | wake_up_interruptible(&common->cur_frm->done); | |
341 | /* Make cur_frm pointing to next_frm */ | |
342 | common->cur_frm = common->next_frm; | |
343 | } | |
344 | ||
345 | channel_first_int[i][channel_id] = 0; | |
346 | process_progressive_mode(common); | |
347 | } else { | |
348 | /* Interlaced mode */ | |
349 | /* If it is first interrupt, ignore it */ | |
350 | ||
351 | if (channel_first_int[i][channel_id]) { | |
352 | channel_first_int[i][channel_id] = 0; | |
353 | continue; | |
354 | } | |
355 | ||
356 | if (0 == i) { | |
357 | ch->field_id ^= 1; | |
358 | /* Get field id from VPIF registers */ | |
359 | fid = vpif_channel_getfid(ch->channel_id + 2); | |
360 | /* If fid does not match with stored field id */ | |
361 | if (fid != ch->field_id) { | |
362 | /* Make them in sync */ | |
363 | if (0 == fid) | |
364 | ch->field_id = fid; | |
365 | ||
366 | return IRQ_HANDLED; | |
367 | } | |
368 | } | |
369 | process_interlaced_mode(fid, common); | |
370 | } | |
371 | } | |
372 | ||
373 | return IRQ_HANDLED; | |
374 | } | |
375 | ||
376 | static int vpif_get_std_info(struct channel_obj *ch) | |
377 | { | |
378 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
379 | struct video_obj *vid_ch = &ch->video; | |
380 | struct vpif_params *vpifparams = &ch->vpifparams; | |
381 | struct vpif_channel_config_params *std_info = &vpifparams->std_info; | |
382 | const struct vpif_channel_config_params *config; | |
383 | ||
384 | int index; | |
385 | ||
386 | std_info->stdid = vid_ch->stdid; | |
387 | if (!std_info) | |
388 | return -1; | |
389 | ||
390 | for (index = 0; index < ARRAY_SIZE(ch_params); index++) { | |
391 | config = &ch_params[index]; | |
392 | if (config->stdid & std_info->stdid) { | |
393 | memcpy(std_info, config, sizeof(*config)); | |
394 | break; | |
395 | } | |
396 | } | |
397 | ||
398 | if (index == ARRAY_SIZE(ch_params)) | |
399 | return -1; | |
400 | ||
401 | common->fmt.fmt.pix.width = std_info->width; | |
402 | common->fmt.fmt.pix.height = std_info->height; | |
403 | vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", | |
404 | common->fmt.fmt.pix.width, common->fmt.fmt.pix.height); | |
405 | ||
406 | /* Set height and width paramateres */ | |
407 | ch->common[VPIF_VIDEO_INDEX].height = std_info->height; | |
408 | ch->common[VPIF_VIDEO_INDEX].width = std_info->width; | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | /* | |
414 | * vpif_calculate_offsets: This function calculates buffers offset for Y and C | |
415 | * in the top and bottom field | |
416 | */ | |
417 | static void vpif_calculate_offsets(struct channel_obj *ch) | |
418 | { | |
419 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
420 | struct vpif_params *vpifparams = &ch->vpifparams; | |
421 | enum v4l2_field field = common->fmt.fmt.pix.field; | |
422 | struct video_obj *vid_ch = &ch->video; | |
423 | unsigned int hpitch, vpitch, sizeimage; | |
424 | ||
425 | if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) { | |
426 | if (ch->vpifparams.std_info.frm_fmt) | |
427 | vid_ch->buf_field = V4L2_FIELD_NONE; | |
428 | else | |
429 | vid_ch->buf_field = V4L2_FIELD_INTERLACED; | |
430 | } else { | |
431 | vid_ch->buf_field = common->fmt.fmt.pix.field; | |
432 | } | |
433 | ||
434 | if (V4L2_MEMORY_USERPTR == common->memory) | |
435 | sizeimage = common->fmt.fmt.pix.sizeimage; | |
436 | else | |
437 | sizeimage = config_params.channel_bufsize[ch->channel_id]; | |
438 | ||
439 | hpitch = common->fmt.fmt.pix.bytesperline; | |
440 | vpitch = sizeimage / (hpitch * 2); | |
441 | if ((V4L2_FIELD_NONE == vid_ch->buf_field) || | |
442 | (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { | |
443 | common->ytop_off = 0; | |
444 | common->ybtm_off = hpitch; | |
445 | common->ctop_off = sizeimage / 2; | |
446 | common->cbtm_off = sizeimage / 2 + hpitch; | |
447 | } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { | |
448 | common->ytop_off = 0; | |
449 | common->ybtm_off = sizeimage / 4; | |
450 | common->ctop_off = sizeimage / 2; | |
451 | common->cbtm_off = common->ctop_off + sizeimage / 4; | |
452 | } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { | |
453 | common->ybtm_off = 0; | |
454 | common->ytop_off = sizeimage / 4; | |
455 | common->cbtm_off = sizeimage / 2; | |
456 | common->ctop_off = common->cbtm_off + sizeimage / 4; | |
457 | } | |
458 | ||
459 | if ((V4L2_FIELD_NONE == vid_ch->buf_field) || | |
460 | (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { | |
461 | vpifparams->video_params.storage_mode = 1; | |
462 | } else { | |
463 | vpifparams->video_params.storage_mode = 0; | |
464 | } | |
465 | ||
466 | if (ch->vpifparams.std_info.frm_fmt == 1) { | |
467 | vpifparams->video_params.hpitch = | |
468 | common->fmt.fmt.pix.bytesperline; | |
469 | } else { | |
470 | if ((field == V4L2_FIELD_ANY) || | |
471 | (field == V4L2_FIELD_INTERLACED)) | |
472 | vpifparams->video_params.hpitch = | |
473 | common->fmt.fmt.pix.bytesperline * 2; | |
474 | else | |
475 | vpifparams->video_params.hpitch = | |
476 | common->fmt.fmt.pix.bytesperline; | |
477 | } | |
478 | ||
479 | ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid; | |
480 | } | |
481 | ||
482 | static void vpif_config_format(struct channel_obj *ch) | |
483 | { | |
484 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
485 | ||
486 | common->fmt.fmt.pix.field = V4L2_FIELD_ANY; | |
487 | if (config_params.numbuffers[ch->channel_id] == 0) | |
488 | common->memory = V4L2_MEMORY_USERPTR; | |
489 | else | |
490 | common->memory = V4L2_MEMORY_MMAP; | |
491 | ||
492 | common->fmt.fmt.pix.sizeimage = | |
493 | config_params.channel_bufsize[ch->channel_id]; | |
494 | common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; | |
495 | common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | |
496 | } | |
497 | ||
498 | static int vpif_check_format(struct channel_obj *ch, | |
499 | struct v4l2_pix_format *pixfmt) | |
500 | { | |
501 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
502 | enum v4l2_field field = pixfmt->field; | |
503 | u32 sizeimage, hpitch, vpitch; | |
504 | ||
505 | if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) | |
506 | goto invalid_fmt_exit; | |
507 | ||
508 | if (!(VPIF_VALID_FIELD(field))) | |
509 | goto invalid_fmt_exit; | |
510 | ||
511 | if (pixfmt->bytesperline <= 0) | |
512 | goto invalid_pitch_exit; | |
513 | ||
514 | if (V4L2_MEMORY_USERPTR == common->memory) | |
515 | sizeimage = pixfmt->sizeimage; | |
516 | else | |
517 | sizeimage = config_params.channel_bufsize[ch->channel_id]; | |
518 | ||
519 | if (vpif_get_std_info(ch)) { | |
520 | vpif_err("Error getting the standard info\n"); | |
521 | return -EINVAL; | |
522 | } | |
523 | ||
524 | hpitch = pixfmt->bytesperline; | |
525 | vpitch = sizeimage / (hpitch * 2); | |
526 | ||
527 | /* Check for valid value of pitch */ | |
528 | if ((hpitch < ch->vpifparams.std_info.width) || | |
529 | (vpitch < ch->vpifparams.std_info.height)) | |
530 | goto invalid_pitch_exit; | |
531 | ||
532 | /* Check for 8 byte alignment */ | |
533 | if (!ISALIGNED(hpitch)) { | |
534 | vpif_err("invalid pitch alignment\n"); | |
535 | return -EINVAL; | |
536 | } | |
537 | pixfmt->width = common->fmt.fmt.pix.width; | |
538 | pixfmt->height = common->fmt.fmt.pix.height; | |
539 | ||
540 | return 0; | |
541 | ||
542 | invalid_fmt_exit: | |
543 | vpif_err("invalid field format\n"); | |
544 | return -EINVAL; | |
545 | ||
546 | invalid_pitch_exit: | |
547 | vpif_err("invalid pitch\n"); | |
548 | return -EINVAL; | |
549 | } | |
550 | ||
551 | static void vpif_config_addr(struct channel_obj *ch, int muxmode) | |
552 | { | |
553 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
554 | ||
555 | if (VPIF_CHANNEL3_VIDEO == ch->channel_id) { | |
556 | common->set_addr = ch3_set_videobuf_addr; | |
557 | } else { | |
558 | if (2 == muxmode) | |
559 | common->set_addr = ch2_set_videobuf_addr_yc_nmux; | |
560 | else | |
561 | common->set_addr = ch2_set_videobuf_addr; | |
562 | } | |
563 | } | |
564 | ||
565 | /* | |
566 | * vpif_mmap: It is used to map kernel space buffers into user spaces | |
567 | */ | |
568 | static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) | |
569 | { | |
570 | struct vpif_fh *fh = filep->private_data; | |
571 | struct common_obj *common = &fh->channel->common[VPIF_VIDEO_INDEX]; | |
572 | ||
573 | return videobuf_mmap_mapper(&common->buffer_queue, vma); | |
574 | } | |
575 | ||
576 | /* | |
577 | * vpif_poll: It is used for select/poll system call | |
578 | */ | |
579 | static unsigned int vpif_poll(struct file *filep, poll_table *wait) | |
580 | { | |
581 | struct vpif_fh *fh = filep->private_data; | |
582 | struct channel_obj *ch = fh->channel; | |
583 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
584 | ||
585 | if (common->started) | |
586 | return videobuf_poll_stream(filep, &common->buffer_queue, wait); | |
587 | ||
588 | return 0; | |
589 | } | |
590 | ||
591 | /* | |
592 | * vpif_open: It creates object of file handle structure and stores it in | |
593 | * private_data member of filepointer | |
594 | */ | |
595 | static int vpif_open(struct file *filep) | |
596 | { | |
597 | struct video_device *vdev = video_devdata(filep); | |
598 | struct channel_obj *ch = NULL; | |
599 | struct vpif_fh *fh = NULL; | |
600 | ||
601 | ch = video_get_drvdata(vdev); | |
602 | /* Allocate memory for the file handle object */ | |
603 | fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL); | |
604 | if (fh == NULL) { | |
605 | vpif_err("unable to allocate memory for file handle object\n"); | |
606 | return -ENOMEM; | |
607 | } | |
608 | ||
609 | /* store pointer to fh in private_data member of filep */ | |
610 | filep->private_data = fh; | |
611 | fh->channel = ch; | |
612 | fh->initialized = 0; | |
613 | if (!ch->initialized) { | |
614 | fh->initialized = 1; | |
615 | ch->initialized = 1; | |
616 | memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); | |
617 | } | |
618 | ||
619 | /* Increment channel usrs counter */ | |
620 | atomic_inc(&ch->usrs); | |
621 | /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ | |
622 | fh->io_allowed[VPIF_VIDEO_INDEX] = 0; | |
623 | /* Initialize priority of this instance to default priority */ | |
624 | fh->prio = V4L2_PRIORITY_UNSET; | |
625 | v4l2_prio_open(&ch->prio, &fh->prio); | |
626 | ||
627 | return 0; | |
628 | } | |
629 | ||
630 | /* | |
631 | * vpif_release: This function deletes buffer queue, frees the buffers and | |
632 | * the vpif file handle | |
633 | */ | |
634 | static int vpif_release(struct file *filep) | |
635 | { | |
636 | struct vpif_fh *fh = filep->private_data; | |
637 | struct channel_obj *ch = fh->channel; | |
638 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
639 | ||
13df4f6a C |
640 | if (mutex_lock_interruptible(&common->lock)) |
641 | return -ERESTARTSYS; | |
642 | ||
e7332e3a C |
643 | /* if this instance is doing IO */ |
644 | if (fh->io_allowed[VPIF_VIDEO_INDEX]) { | |
645 | /* Reset io_usrs member of channel object */ | |
646 | common->io_usrs = 0; | |
647 | /* Disable channel */ | |
648 | if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { | |
649 | enable_channel2(0); | |
650 | channel2_intr_enable(0); | |
651 | } | |
652 | if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || | |
653 | (2 == common->started)) { | |
654 | enable_channel3(0); | |
655 | channel3_intr_enable(0); | |
656 | } | |
657 | common->started = 0; | |
658 | /* Free buffers allocated */ | |
659 | videobuf_queue_cancel(&common->buffer_queue); | |
660 | videobuf_mmap_free(&common->buffer_queue); | |
661 | common->numbuffers = | |
662 | config_params.numbuffers[ch->channel_id]; | |
663 | } | |
664 | ||
665 | mutex_unlock(&common->lock); | |
666 | ||
667 | /* Decrement channel usrs counter */ | |
668 | atomic_dec(&ch->usrs); | |
669 | /* If this file handle has initialize encoder device, reset it */ | |
670 | if (fh->initialized) | |
671 | ch->initialized = 0; | |
672 | ||
673 | /* Close the priority */ | |
674 | v4l2_prio_close(&ch->prio, &fh->prio); | |
675 | filep->private_data = NULL; | |
676 | fh->initialized = 0; | |
677 | kfree(fh); | |
678 | ||
679 | return 0; | |
680 | } | |
681 | ||
682 | /* functions implementing ioctls */ | |
683 | ||
684 | static int vpif_querycap(struct file *file, void *priv, | |
685 | struct v4l2_capability *cap) | |
686 | { | |
317b2e2f | 687 | struct vpif_display_config *config = vpif_dev->platform_data; |
e7332e3a C |
688 | |
689 | cap->version = VPIF_DISPLAY_VERSION_CODE; | |
690 | cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; | |
691 | strlcpy(cap->driver, "vpif display", sizeof(cap->driver)); | |
692 | strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info)); | |
693 | strlcpy(cap->card, config->card_name, sizeof(cap->card)); | |
694 | ||
695 | return 0; | |
696 | } | |
697 | ||
698 | static int vpif_enum_fmt_vid_out(struct file *file, void *priv, | |
699 | struct v4l2_fmtdesc *fmt) | |
700 | { | |
701 | if (fmt->index != 0) { | |
702 | vpif_err("Invalid format index\n"); | |
703 | return -EINVAL; | |
704 | } | |
705 | ||
706 | /* Fill in the information about format */ | |
707 | fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | |
708 | strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); | |
709 | fmt->pixelformat = V4L2_PIX_FMT_YUV422P; | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
714 | static int vpif_g_fmt_vid_out(struct file *file, void *priv, | |
715 | struct v4l2_format *fmt) | |
716 | { | |
717 | struct vpif_fh *fh = priv; | |
718 | struct channel_obj *ch = fh->channel; | |
719 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
720 | ||
721 | /* Check the validity of the buffer type */ | |
722 | if (common->fmt.type != fmt->type) | |
723 | return -EINVAL; | |
724 | ||
725 | /* Fill in the information about format */ | |
13df4f6a C |
726 | if (mutex_lock_interruptible(&common->lock)) |
727 | return -ERESTARTSYS; | |
728 | ||
e7332e3a C |
729 | if (vpif_get_std_info(ch)) { |
730 | vpif_err("Error getting the standard info\n"); | |
731 | return -EINVAL; | |
732 | } | |
733 | ||
734 | *fmt = common->fmt; | |
735 | mutex_unlock(&common->lock); | |
736 | return 0; | |
737 | } | |
738 | ||
739 | static int vpif_s_fmt_vid_out(struct file *file, void *priv, | |
740 | struct v4l2_format *fmt) | |
741 | { | |
742 | struct vpif_fh *fh = priv; | |
743 | struct v4l2_pix_format *pixfmt; | |
744 | struct channel_obj *ch = fh->channel; | |
745 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
746 | int ret = 0; | |
747 | ||
748 | if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) | |
749 | || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { | |
750 | if (!fh->initialized) { | |
751 | vpif_dbg(1, debug, "Channel Busy\n"); | |
752 | return -EBUSY; | |
753 | } | |
754 | ||
755 | /* Check for the priority */ | |
756 | ret = v4l2_prio_check(&ch->prio, &fh->prio); | |
757 | if (0 != ret) | |
758 | return ret; | |
759 | fh->initialized = 1; | |
760 | } | |
761 | ||
762 | if (common->started) { | |
763 | vpif_dbg(1, debug, "Streaming in progress\n"); | |
764 | return -EBUSY; | |
765 | } | |
766 | ||
767 | pixfmt = &fmt->fmt.pix; | |
768 | /* Check for valid field format */ | |
769 | ret = vpif_check_format(ch, pixfmt); | |
770 | if (ret) | |
771 | return ret; | |
772 | ||
773 | /* store the pix format in the channel object */ | |
774 | common->fmt.fmt.pix = *pixfmt; | |
775 | /* store the format in the channel object */ | |
13df4f6a C |
776 | if (mutex_lock_interruptible(&common->lock)) |
777 | return -ERESTARTSYS; | |
778 | ||
e7332e3a C |
779 | common->fmt = *fmt; |
780 | mutex_unlock(&common->lock); | |
781 | ||
782 | return 0; | |
783 | } | |
784 | ||
785 | static int vpif_try_fmt_vid_out(struct file *file, void *priv, | |
786 | struct v4l2_format *fmt) | |
787 | { | |
788 | struct vpif_fh *fh = priv; | |
789 | struct channel_obj *ch = fh->channel; | |
790 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
791 | struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; | |
792 | int ret = 0; | |
793 | ||
794 | ret = vpif_check_format(ch, pixfmt); | |
795 | if (ret) { | |
796 | *pixfmt = common->fmt.fmt.pix; | |
797 | pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; | |
798 | } | |
799 | ||
800 | return ret; | |
801 | } | |
802 | ||
803 | static int vpif_reqbufs(struct file *file, void *priv, | |
804 | struct v4l2_requestbuffers *reqbuf) | |
805 | { | |
806 | struct vpif_fh *fh = priv; | |
807 | struct channel_obj *ch = fh->channel; | |
808 | struct common_obj *common; | |
809 | enum v4l2_field field; | |
810 | u8 index = 0; | |
811 | int ret = 0; | |
812 | ||
813 | /* This file handle has not initialized the channel, | |
814 | It is not allowed to do settings */ | |
815 | if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) | |
816 | || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { | |
817 | if (!fh->initialized) { | |
818 | vpif_err("Channel Busy\n"); | |
819 | return -EBUSY; | |
820 | } | |
821 | } | |
822 | ||
823 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) | |
824 | return -EINVAL; | |
825 | ||
826 | index = VPIF_VIDEO_INDEX; | |
827 | ||
828 | common = &ch->common[index]; | |
13df4f6a C |
829 | if (mutex_lock_interruptible(&common->lock)) |
830 | return -ERESTARTSYS; | |
831 | ||
e7332e3a C |
832 | if (common->fmt.type != reqbuf->type) { |
833 | ret = -EINVAL; | |
834 | goto reqbuf_exit; | |
835 | } | |
836 | ||
837 | if (0 != common->io_usrs) { | |
838 | ret = -EBUSY; | |
839 | goto reqbuf_exit; | |
840 | } | |
841 | ||
842 | if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { | |
843 | if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) | |
844 | field = V4L2_FIELD_INTERLACED; | |
845 | else | |
846 | field = common->fmt.fmt.pix.field; | |
847 | } else { | |
848 | field = V4L2_VBI_INTERLACED; | |
849 | } | |
850 | ||
851 | /* Initialize videobuf queue as per the buffer type */ | |
852 | videobuf_queue_dma_contig_init(&common->buffer_queue, | |
853 | &video_qops, NULL, | |
854 | &common->irqlock, | |
855 | reqbuf->type, field, | |
856 | sizeof(struct videobuf_buffer), fh); | |
857 | ||
858 | /* Set io allowed member of file handle to TRUE */ | |
859 | fh->io_allowed[index] = 1; | |
860 | /* Increment io usrs member of channel object to 1 */ | |
861 | common->io_usrs = 1; | |
862 | /* Store type of memory requested in channel object */ | |
863 | common->memory = reqbuf->memory; | |
864 | INIT_LIST_HEAD(&common->dma_queue); | |
865 | ||
866 | /* Allocate buffers */ | |
867 | ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); | |
868 | ||
869 | reqbuf_exit: | |
870 | mutex_unlock(&common->lock); | |
871 | return ret; | |
872 | } | |
873 | ||
874 | static int vpif_querybuf(struct file *file, void *priv, | |
875 | struct v4l2_buffer *tbuf) | |
876 | { | |
877 | struct vpif_fh *fh = priv; | |
878 | struct channel_obj *ch = fh->channel; | |
879 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
880 | ||
881 | if (common->fmt.type != tbuf->type) | |
882 | return -EINVAL; | |
883 | ||
884 | return videobuf_querybuf(&common->buffer_queue, tbuf); | |
885 | } | |
886 | ||
887 | static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) | |
888 | { | |
889 | ||
890 | struct vpif_fh *fh = priv; | |
891 | struct channel_obj *ch = fh->channel; | |
892 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
893 | struct v4l2_buffer tbuf = *buf; | |
894 | struct videobuf_buffer *buf1; | |
895 | unsigned long addr = 0; | |
896 | unsigned long flags; | |
897 | int ret = 0; | |
898 | ||
899 | if (common->fmt.type != tbuf.type) | |
900 | return -EINVAL; | |
901 | ||
902 | if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { | |
903 | vpif_err("fh->io_allowed\n"); | |
904 | return -EACCES; | |
905 | } | |
906 | ||
907 | if (!(list_empty(&common->dma_queue)) || | |
908 | (common->cur_frm != common->next_frm) || | |
909 | !(common->started) || | |
910 | (common->started && (0 == ch->field_id))) | |
911 | return videobuf_qbuf(&common->buffer_queue, buf); | |
912 | ||
913 | /* bufferqueue is empty store buffer address in VPIF registers */ | |
914 | mutex_lock(&common->buffer_queue.vb_lock); | |
915 | buf1 = common->buffer_queue.bufs[tbuf.index]; | |
916 | if (buf1->memory != tbuf.memory) { | |
917 | vpif_err("invalid buffer type\n"); | |
918 | goto qbuf_exit; | |
919 | } | |
920 | ||
921 | if ((buf1->state == VIDEOBUF_QUEUED) || | |
922 | (buf1->state == VIDEOBUF_ACTIVE)) { | |
923 | vpif_err("invalid state\n"); | |
924 | goto qbuf_exit; | |
925 | } | |
926 | ||
927 | switch (buf1->memory) { | |
928 | case V4L2_MEMORY_MMAP: | |
929 | if (buf1->baddr == 0) | |
930 | goto qbuf_exit; | |
931 | break; | |
932 | ||
933 | case V4L2_MEMORY_USERPTR: | |
934 | if (tbuf.length < buf1->bsize) | |
935 | goto qbuf_exit; | |
936 | ||
937 | if ((VIDEOBUF_NEEDS_INIT != buf1->state) | |
938 | && (buf1->baddr != tbuf.m.userptr)) | |
939 | vpif_buffer_release(&common->buffer_queue, buf1); | |
940 | buf1->baddr = tbuf.m.userptr; | |
941 | break; | |
942 | ||
943 | default: | |
944 | goto qbuf_exit; | |
945 | } | |
946 | ||
947 | local_irq_save(flags); | |
948 | ret = vpif_buffer_prepare(&common->buffer_queue, buf1, | |
949 | common->buffer_queue.field); | |
950 | if (ret < 0) { | |
951 | local_irq_restore(flags); | |
952 | goto qbuf_exit; | |
953 | } | |
954 | ||
955 | buf1->state = VIDEOBUF_ACTIVE; | |
956 | addr = buf1->boff; | |
957 | common->next_frm = buf1; | |
958 | if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { | |
959 | common->set_addr((addr + common->ytop_off), | |
960 | (addr + common->ybtm_off), | |
961 | (addr + common->ctop_off), | |
962 | (addr + common->cbtm_off)); | |
963 | } | |
964 | ||
965 | local_irq_restore(flags); | |
966 | list_add_tail(&buf1->stream, &common->buffer_queue.stream); | |
967 | mutex_unlock(&common->buffer_queue.vb_lock); | |
968 | return 0; | |
969 | ||
970 | qbuf_exit: | |
971 | mutex_unlock(&common->buffer_queue.vb_lock); | |
972 | return -EINVAL; | |
973 | } | |
974 | ||
975 | static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) | |
976 | { | |
977 | struct vpif_fh *fh = priv; | |
978 | struct channel_obj *ch = fh->channel; | |
979 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
980 | int ret = 0; | |
981 | ||
982 | if (!(*std_id & DM646X_V4L2_STD)) | |
983 | return -EINVAL; | |
984 | ||
985 | if (common->started) { | |
986 | vpif_err("streaming in progress\n"); | |
987 | return -EBUSY; | |
988 | } | |
989 | ||
990 | /* Call encoder subdevice function to set the standard */ | |
13df4f6a C |
991 | if (mutex_lock_interruptible(&common->lock)) |
992 | return -ERESTARTSYS; | |
e7332e3a C |
993 | |
994 | ch->video.stdid = *std_id; | |
995 | /* Get the information about the standard */ | |
996 | if (vpif_get_std_info(ch)) { | |
997 | vpif_err("Error getting the standard info\n"); | |
998 | return -EINVAL; | |
999 | } | |
1000 | ||
1001 | if ((ch->vpifparams.std_info.width * | |
1002 | ch->vpifparams.std_info.height * 2) > | |
1003 | config_params.channel_bufsize[ch->channel_id]) { | |
1004 | vpif_err("invalid std for this size\n"); | |
1005 | ret = -EINVAL; | |
1006 | goto s_std_exit; | |
1007 | } | |
1008 | ||
1009 | common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; | |
1010 | /* Configure the default format information */ | |
1011 | vpif_config_format(ch); | |
1012 | ||
1013 | ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, | |
1014 | s_std_output, *std_id); | |
1015 | if (ret < 0) { | |
1016 | vpif_err("Failed to set output standard\n"); | |
1017 | goto s_std_exit; | |
1018 | } | |
1019 | ||
1020 | ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, | |
1021 | s_std, *std_id); | |
1022 | if (ret < 0) | |
1023 | vpif_err("Failed to set standard for sub devices\n"); | |
1024 | ||
1025 | s_std_exit: | |
1026 | mutex_unlock(&common->lock); | |
1027 | return ret; | |
1028 | } | |
1029 | ||
1030 | static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) | |
1031 | { | |
1032 | struct vpif_fh *fh = priv; | |
1033 | struct channel_obj *ch = fh->channel; | |
1034 | ||
1035 | *std = ch->video.stdid; | |
1036 | return 0; | |
1037 | } | |
1038 | ||
1039 | static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) | |
1040 | { | |
1041 | struct vpif_fh *fh = priv; | |
1042 | struct channel_obj *ch = fh->channel; | |
1043 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
1044 | ||
1045 | return videobuf_dqbuf(&common->buffer_queue, p, | |
1046 | (file->f_flags & O_NONBLOCK)); | |
1047 | } | |
1048 | ||
1049 | static int vpif_streamon(struct file *file, void *priv, | |
1050 | enum v4l2_buf_type buftype) | |
1051 | { | |
1052 | struct vpif_fh *fh = priv; | |
1053 | struct channel_obj *ch = fh->channel; | |
1054 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
1055 | struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; | |
1056 | struct vpif_params *vpif = &ch->vpifparams; | |
317b2e2f | 1057 | struct vpif_display_config *vpif_config_data = |
e7332e3a C |
1058 | vpif_dev->platform_data; |
1059 | unsigned long addr = 0; | |
1060 | int ret = 0; | |
1061 | ||
1062 | if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { | |
1063 | vpif_err("buffer type not supported\n"); | |
1064 | return -EINVAL; | |
1065 | } | |
1066 | ||
1067 | if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { | |
1068 | vpif_err("fh->io_allowed\n"); | |
1069 | return -EACCES; | |
1070 | } | |
1071 | ||
1072 | /* If Streaming is already started, return error */ | |
1073 | if (common->started) { | |
1074 | vpif_err("channel->started\n"); | |
1075 | return -EBUSY; | |
1076 | } | |
1077 | ||
1078 | if ((ch->channel_id == VPIF_CHANNEL2_VIDEO | |
1079 | && oth_ch->common[VPIF_VIDEO_INDEX].started && | |
1080 | ch->vpifparams.std_info.ycmux_mode == 0) | |
1081 | || ((ch->channel_id == VPIF_CHANNEL3_VIDEO) | |
1082 | && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { | |
1083 | vpif_err("other channel is using\n"); | |
1084 | return -EBUSY; | |
1085 | } | |
1086 | ||
1087 | ret = vpif_check_format(ch, &common->fmt.fmt.pix); | |
1088 | if (ret < 0) | |
1089 | return ret; | |
1090 | ||
1091 | /* Call videobuf_streamon to start streaming in videobuf */ | |
1092 | ret = videobuf_streamon(&common->buffer_queue); | |
1093 | if (ret < 0) { | |
1094 | vpif_err("videobuf_streamon\n"); | |
1095 | return ret; | |
1096 | } | |
1097 | ||
13df4f6a C |
1098 | if (mutex_lock_interruptible(&common->lock)) |
1099 | return -ERESTARTSYS; | |
1100 | ||
e7332e3a C |
1101 | /* If buffer queue is empty, return error */ |
1102 | if (list_empty(&common->dma_queue)) { | |
1103 | vpif_err("buffer queue is empty\n"); | |
1104 | ret = -EIO; | |
1105 | goto streamon_exit; | |
1106 | } | |
1107 | ||
1108 | /* Get the next frame from the buffer queue */ | |
1109 | common->next_frm = common->cur_frm = | |
1110 | list_entry(common->dma_queue.next, | |
1111 | struct videobuf_buffer, queue); | |
1112 | ||
1113 | list_del(&common->cur_frm->queue); | |
1114 | /* Mark state of the current frame to active */ | |
1115 | common->cur_frm->state = VIDEOBUF_ACTIVE; | |
1116 | ||
1117 | /* Initialize field_id and started member */ | |
1118 | ch->field_id = 0; | |
1119 | common->started = 1; | |
1120 | if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { | |
1121 | addr = common->cur_frm->boff; | |
1122 | /* Calculate the offset for Y and C data in the buffer */ | |
1123 | vpif_calculate_offsets(ch); | |
1124 | ||
1125 | if ((ch->vpifparams.std_info.frm_fmt && | |
1126 | ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) | |
1127 | && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) | |
1128 | || (!ch->vpifparams.std_info.frm_fmt | |
1129 | && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { | |
1130 | vpif_err("conflict in field format and std format\n"); | |
1131 | ret = -EINVAL; | |
1132 | goto streamon_exit; | |
1133 | } | |
1134 | ||
1135 | /* clock settings */ | |
1136 | ret = | |
1137 | vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode, | |
1138 | ch->vpifparams.std_info.hd_sd); | |
1139 | if (ret < 0) { | |
1140 | vpif_err("can't set clock\n"); | |
1141 | goto streamon_exit; | |
1142 | } | |
1143 | ||
1144 | /* set the parameters and addresses */ | |
1145 | ret = vpif_set_video_params(vpif, ch->channel_id + 2); | |
1146 | if (ret < 0) | |
1147 | goto streamon_exit; | |
1148 | ||
1149 | common->started = ret; | |
1150 | vpif_config_addr(ch, ret); | |
1151 | common->set_addr((addr + common->ytop_off), | |
1152 | (addr + common->ybtm_off), | |
1153 | (addr + common->ctop_off), | |
1154 | (addr + common->cbtm_off)); | |
1155 | ||
1156 | /* Set interrupt for both the fields in VPIF | |
1157 | Register enable channel in VPIF register */ | |
1158 | if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { | |
1159 | channel2_intr_assert(); | |
1160 | channel2_intr_enable(1); | |
1161 | enable_channel2(1); | |
1162 | } | |
1163 | ||
1164 | if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) | |
1165 | || (common->started == 2)) { | |
1166 | channel3_intr_assert(); | |
1167 | channel3_intr_enable(1); | |
1168 | enable_channel3(1); | |
1169 | } | |
1170 | channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; | |
1171 | } | |
1172 | ||
1173 | streamon_exit: | |
1174 | mutex_unlock(&common->lock); | |
1175 | return ret; | |
1176 | } | |
1177 | ||
1178 | static int vpif_streamoff(struct file *file, void *priv, | |
1179 | enum v4l2_buf_type buftype) | |
1180 | { | |
1181 | struct vpif_fh *fh = priv; | |
1182 | struct channel_obj *ch = fh->channel; | |
1183 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
1184 | ||
1185 | if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { | |
1186 | vpif_err("buffer type not supported\n"); | |
1187 | return -EINVAL; | |
1188 | } | |
1189 | ||
1190 | if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { | |
1191 | vpif_err("fh->io_allowed\n"); | |
1192 | return -EACCES; | |
1193 | } | |
1194 | ||
1195 | if (!common->started) { | |
1196 | vpif_err("channel->started\n"); | |
1197 | return -EINVAL; | |
1198 | } | |
1199 | ||
13df4f6a C |
1200 | if (mutex_lock_interruptible(&common->lock)) |
1201 | return -ERESTARTSYS; | |
1202 | ||
e7332e3a C |
1203 | if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
1204 | /* disable channel */ | |
1205 | if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { | |
1206 | enable_channel2(0); | |
1207 | channel2_intr_enable(0); | |
1208 | } | |
1209 | if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || | |
1210 | (2 == common->started)) { | |
1211 | enable_channel3(0); | |
1212 | channel3_intr_enable(0); | |
1213 | } | |
1214 | } | |
1215 | ||
1216 | common->started = 0; | |
1217 | mutex_unlock(&common->lock); | |
1218 | ||
1219 | return videobuf_streamoff(&common->buffer_queue); | |
1220 | } | |
1221 | ||
1222 | static int vpif_cropcap(struct file *file, void *priv, | |
1223 | struct v4l2_cropcap *crop) | |
1224 | { | |
1225 | struct vpif_fh *fh = priv; | |
1226 | struct channel_obj *ch = fh->channel; | |
1227 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
1228 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) | |
1229 | return -EINVAL; | |
1230 | ||
1231 | crop->bounds.left = crop->bounds.top = 0; | |
1232 | crop->defrect.left = crop->defrect.top = 0; | |
1233 | crop->defrect.height = crop->bounds.height = common->height; | |
1234 | crop->defrect.width = crop->bounds.width = common->width; | |
1235 | ||
1236 | return 0; | |
1237 | } | |
1238 | ||
1239 | static int vpif_enum_output(struct file *file, void *fh, | |
1240 | struct v4l2_output *output) | |
1241 | { | |
1242 | ||
317b2e2f | 1243 | struct vpif_display_config *config = vpif_dev->platform_data; |
e7332e3a C |
1244 | |
1245 | if (output->index >= config->output_count) { | |
1246 | vpif_dbg(1, debug, "Invalid output index\n"); | |
1247 | return -EINVAL; | |
1248 | } | |
1249 | ||
1250 | strcpy(output->name, config->output[output->index]); | |
1251 | output->type = V4L2_OUTPUT_TYPE_ANALOG; | |
1252 | output->std = DM646X_V4L2_STD; | |
1253 | ||
1254 | return 0; | |
1255 | } | |
1256 | ||
1257 | static int vpif_s_output(struct file *file, void *priv, unsigned int i) | |
1258 | { | |
1259 | struct vpif_fh *fh = priv; | |
1260 | struct channel_obj *ch = fh->channel; | |
1261 | struct video_obj *vid_ch = &ch->video; | |
1262 | struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; | |
1263 | int ret = 0; | |
1264 | ||
13df4f6a C |
1265 | if (mutex_lock_interruptible(&common->lock)) |
1266 | return -ERESTARTSYS; | |
1267 | ||
e7332e3a C |
1268 | if (common->started) { |
1269 | vpif_err("Streaming in progress\n"); | |
1270 | ret = -EBUSY; | |
1271 | goto s_output_exit; | |
1272 | } | |
1273 | ||
1274 | ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, | |
1275 | s_routing, 0, i, 0); | |
1276 | ||
1277 | if (ret < 0) | |
1278 | vpif_err("Failed to set output standard\n"); | |
1279 | ||
1280 | vid_ch->output_id = i; | |
1281 | ||
1282 | s_output_exit: | |
1283 | mutex_unlock(&common->lock); | |
1284 | return ret; | |
1285 | } | |
1286 | ||
1287 | static int vpif_g_output(struct file *file, void *priv, unsigned int *i) | |
1288 | { | |
1289 | struct vpif_fh *fh = priv; | |
1290 | struct channel_obj *ch = fh->channel; | |
1291 | struct video_obj *vid_ch = &ch->video; | |
1292 | ||
1293 | *i = vid_ch->output_id; | |
1294 | ||
1295 | return 0; | |
1296 | } | |
1297 | ||
1298 | static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) | |
1299 | { | |
1300 | struct vpif_fh *fh = priv; | |
1301 | struct channel_obj *ch = fh->channel; | |
1302 | ||
1303 | *p = v4l2_prio_max(&ch->prio); | |
1304 | ||
1305 | return 0; | |
1306 | } | |
1307 | ||
1308 | static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) | |
1309 | { | |
1310 | struct vpif_fh *fh = priv; | |
1311 | struct channel_obj *ch = fh->channel; | |
1312 | ||
1313 | return v4l2_prio_change(&ch->prio, &fh->prio, p); | |
1314 | } | |
1315 | ||
1316 | /* vpif display ioctl operations */ | |
1317 | static const struct v4l2_ioctl_ops vpif_ioctl_ops = { | |
1318 | .vidioc_querycap = vpif_querycap, | |
1319 | .vidioc_g_priority = vpif_g_priority, | |
1320 | .vidioc_s_priority = vpif_s_priority, | |
1321 | .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out, | |
1322 | .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, | |
1323 | .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, | |
1324 | .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, | |
1325 | .vidioc_reqbufs = vpif_reqbufs, | |
1326 | .vidioc_querybuf = vpif_querybuf, | |
1327 | .vidioc_qbuf = vpif_qbuf, | |
1328 | .vidioc_dqbuf = vpif_dqbuf, | |
1329 | .vidioc_streamon = vpif_streamon, | |
1330 | .vidioc_streamoff = vpif_streamoff, | |
1331 | .vidioc_s_std = vpif_s_std, | |
1332 | .vidioc_g_std = vpif_g_std, | |
1333 | .vidioc_enum_output = vpif_enum_output, | |
1334 | .vidioc_s_output = vpif_s_output, | |
1335 | .vidioc_g_output = vpif_g_output, | |
1336 | .vidioc_cropcap = vpif_cropcap, | |
1337 | }; | |
1338 | ||
1339 | static const struct v4l2_file_operations vpif_fops = { | |
1340 | .owner = THIS_MODULE, | |
1341 | .open = vpif_open, | |
1342 | .release = vpif_release, | |
1343 | .ioctl = video_ioctl2, | |
1344 | .mmap = vpif_mmap, | |
1345 | .poll = vpif_poll | |
1346 | }; | |
1347 | ||
1348 | static struct video_device vpif_video_template = { | |
1349 | .name = "vpif", | |
1350 | .fops = &vpif_fops, | |
e7332e3a C |
1351 | .ioctl_ops = &vpif_ioctl_ops, |
1352 | .tvnorms = DM646X_V4L2_STD, | |
1353 | .current_norm = V4L2_STD_625_50, | |
1354 | ||
1355 | }; | |
1356 | ||
1357 | /*Configure the channels, buffer sizei, request irq */ | |
1358 | static int initialize_vpif(void) | |
1359 | { | |
1360 | int free_channel_objects_index; | |
1361 | int free_buffer_channel_index; | |
1362 | int free_buffer_index; | |
1363 | int err = 0, i, j; | |
1364 | ||
1365 | /* Default number of buffers should be 3 */ | |
1366 | if ((ch2_numbuffers > 0) && | |
1367 | (ch2_numbuffers < config_params.min_numbuffers)) | |
1368 | ch2_numbuffers = config_params.min_numbuffers; | |
1369 | if ((ch3_numbuffers > 0) && | |
1370 | (ch3_numbuffers < config_params.min_numbuffers)) | |
1371 | ch3_numbuffers = config_params.min_numbuffers; | |
1372 | ||
1373 | /* Set buffer size to min buffers size if invalid buffer size is | |
1374 | * given */ | |
1375 | if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) | |
1376 | ch2_bufsize = | |
1377 | config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; | |
1378 | if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) | |
1379 | ch3_bufsize = | |
1380 | config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; | |
1381 | ||
1382 | config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; | |
1383 | ||
1384 | if (ch2_numbuffers) { | |
1385 | config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = | |
1386 | ch2_bufsize; | |
1387 | } | |
1388 | config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; | |
1389 | ||
1390 | if (ch3_numbuffers) { | |
1391 | config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = | |
1392 | ch3_bufsize; | |
1393 | } | |
1394 | ||
1395 | /* Allocate memory for six channel objects */ | |
1396 | for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { | |
1397 | vpif_obj.dev[i] = | |
1398 | kmalloc(sizeof(struct channel_obj), GFP_KERNEL); | |
1399 | /* If memory allocation fails, return error */ | |
1400 | if (!vpif_obj.dev[i]) { | |
1401 | free_channel_objects_index = i; | |
1402 | err = -ENOMEM; | |
1403 | goto vpif_init_free_channel_objects; | |
1404 | } | |
1405 | } | |
1406 | ||
1407 | free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; | |
1408 | free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; | |
1409 | free_buffer_index = config_params.numbuffers[i - 1]; | |
1410 | ||
1411 | return 0; | |
1412 | ||
1413 | vpif_init_free_channel_objects: | |
1414 | for (j = 0; j < free_channel_objects_index; j++) | |
1415 | kfree(vpif_obj.dev[j]); | |
1416 | return err; | |
1417 | } | |
1418 | ||
1419 | /* | |
1420 | * vpif_probe: This function creates device entries by register itself to the | |
1421 | * V4L2 driver and initializes fields of each channel objects | |
1422 | */ | |
1423 | static __init int vpif_probe(struct platform_device *pdev) | |
1424 | { | |
317b2e2f MK |
1425 | struct vpif_subdev_info *subdevdata; |
1426 | struct vpif_display_config *config; | |
e7332e3a C |
1427 | int i, j = 0, k, q, m, err = 0; |
1428 | struct i2c_adapter *i2c_adap; | |
e7332e3a C |
1429 | struct common_obj *common; |
1430 | struct channel_obj *ch; | |
1431 | struct video_device *vfd; | |
1432 | struct resource *res; | |
1433 | int subdev_count; | |
1434 | ||
1435 | vpif_dev = &pdev->dev; | |
e7332e3a | 1436 | |
317b2e2f | 1437 | err = initialize_vpif(); |
e7332e3a | 1438 | |
317b2e2f MK |
1439 | if (err) { |
1440 | v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); | |
1441 | return err; | |
e7332e3a C |
1442 | } |
1443 | ||
e7332e3a C |
1444 | err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); |
1445 | if (err) { | |
1446 | v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); | |
1447 | return err; | |
1448 | } | |
1449 | ||
1450 | k = 0; | |
1451 | while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) { | |
1452 | for (i = res->start; i <= res->end; i++) { | |
1453 | if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, | |
1454 | "DM646x_Display", | |
1455 | (void *)(&vpif_obj.dev[k]->channel_id))) { | |
1456 | err = -EBUSY; | |
1457 | goto vpif_int_err; | |
1458 | } | |
1459 | } | |
1460 | k++; | |
1461 | } | |
1462 | ||
1463 | for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { | |
1464 | ||
1465 | /* Get the pointer to the channel object */ | |
1466 | ch = vpif_obj.dev[i]; | |
1467 | ||
1468 | /* Allocate memory for video device */ | |
1469 | vfd = video_device_alloc(); | |
1470 | if (vfd == NULL) { | |
1471 | for (j = 0; j < i; j++) { | |
1472 | ch = vpif_obj.dev[j]; | |
1473 | video_device_release(ch->video_dev); | |
1474 | } | |
1475 | err = -ENOMEM; | |
317b2e2f | 1476 | goto vpif_int_err; |
e7332e3a C |
1477 | } |
1478 | ||
1479 | /* Initialize field of video device */ | |
1480 | *vfd = vpif_video_template; | |
1481 | vfd->v4l2_dev = &vpif_obj.v4l2_dev; | |
1482 | vfd->release = video_device_release; | |
1483 | snprintf(vfd->name, sizeof(vfd->name), | |
1484 | "DM646x_VPIFDisplay_DRIVER_V%d.%d.%d", | |
1485 | (VPIF_DISPLAY_VERSION_CODE >> 16) & 0xff, | |
1486 | (VPIF_DISPLAY_VERSION_CODE >> 8) & 0xff, | |
1487 | (VPIF_DISPLAY_VERSION_CODE) & 0xff); | |
1488 | ||
1489 | /* Set video_dev to the video device */ | |
1490 | ch->video_dev = vfd; | |
1491 | } | |
1492 | ||
1493 | for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { | |
1494 | ch = vpif_obj.dev[j]; | |
1495 | /* Initialize field of the channel objects */ | |
1496 | atomic_set(&ch->usrs, 0); | |
1497 | for (k = 0; k < VPIF_NUMOBJECTS; k++) { | |
1498 | ch->common[k].numbuffers = 0; | |
1499 | common = &ch->common[k]; | |
1500 | common->io_usrs = 0; | |
1501 | common->started = 0; | |
1502 | spin_lock_init(&common->irqlock); | |
1503 | mutex_init(&common->lock); | |
1504 | common->numbuffers = 0; | |
1505 | common->set_addr = NULL; | |
1506 | common->ytop_off = common->ybtm_off = 0; | |
1507 | common->ctop_off = common->cbtm_off = 0; | |
1508 | common->cur_frm = common->next_frm = NULL; | |
1509 | memset(&common->fmt, 0, sizeof(common->fmt)); | |
1510 | common->numbuffers = config_params.numbuffers[k]; | |
1511 | ||
1512 | } | |
1513 | ch->initialized = 0; | |
1514 | ch->channel_id = j; | |
1515 | if (j < 2) | |
1516 | ch->common[VPIF_VIDEO_INDEX].numbuffers = | |
1517 | config_params.numbuffers[ch->channel_id]; | |
1518 | else | |
1519 | ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; | |
1520 | ||
1521 | memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); | |
1522 | ||
1523 | /* Initialize prio member of channel object */ | |
1524 | v4l2_prio_init(&ch->prio); | |
1525 | ch->common[VPIF_VIDEO_INDEX].fmt.type = | |
1526 | V4L2_BUF_TYPE_VIDEO_OUTPUT; | |
1527 | ||
1528 | /* register video device */ | |
1529 | vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", | |
1530 | (int)ch, (int)&ch->video_dev); | |
1531 | ||
1532 | err = video_register_device(ch->video_dev, | |
1533 | VFL_TYPE_GRABBER, (j ? 3 : 2)); | |
1534 | if (err < 0) | |
1535 | goto probe_out; | |
1536 | ||
1537 | video_set_drvdata(ch->video_dev, ch); | |
1538 | } | |
1539 | ||
1540 | i2c_adap = i2c_get_adapter(1); | |
1541 | config = pdev->dev.platform_data; | |
1542 | subdev_count = config->subdev_count; | |
1543 | subdevdata = config->subdevinfo; | |
1544 | vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count, | |
1545 | GFP_KERNEL); | |
1546 | if (vpif_obj.sd == NULL) { | |
1547 | vpif_err("unable to allocate memory for subdevice pointers\n"); | |
1548 | err = -ENOMEM; | |
1549 | goto probe_out; | |
1550 | } | |
1551 | ||
1552 | for (i = 0; i < subdev_count; i++) { | |
317b2e2f | 1553 | vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, |
e7332e3a | 1554 | i2c_adap, subdevdata[i].name, |
317b2e2f MK |
1555 | &subdevdata[i].board_info, |
1556 | NULL); | |
e7332e3a C |
1557 | if (!vpif_obj.sd[i]) { |
1558 | vpif_err("Error registering v4l2 subdevice\n"); | |
1559 | goto probe_subdev_out; | |
1560 | } | |
1561 | ||
1562 | if (vpif_obj.sd[i]) | |
1563 | vpif_obj.sd[i]->grp_id = 1 << i; | |
1564 | } | |
1565 | ||
1566 | return 0; | |
1567 | ||
1568 | probe_subdev_out: | |
1569 | kfree(vpif_obj.sd); | |
1570 | probe_out: | |
1571 | for (k = 0; k < j; k++) { | |
1572 | ch = vpif_obj.dev[k]; | |
1573 | video_unregister_device(ch->video_dev); | |
1574 | video_device_release(ch->video_dev); | |
1575 | ch->video_dev = NULL; | |
1576 | } | |
1577 | vpif_int_err: | |
1578 | v4l2_device_unregister(&vpif_obj.v4l2_dev); | |
1579 | vpif_err("VPIF IRQ request failed\n"); | |
1580 | for (q = k; k >= 0; k--) { | |
1581 | for (m = i; m >= res->start; m--) | |
1582 | free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id)); | |
1583 | res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1); | |
1584 | m = res->end; | |
1585 | } | |
e7332e3a C |
1586 | |
1587 | return err; | |
1588 | } | |
1589 | ||
1590 | /* | |
1591 | * vpif_remove: It un-register channels from V4L2 driver | |
1592 | */ | |
1593 | static int vpif_remove(struct platform_device *device) | |
1594 | { | |
1595 | struct channel_obj *ch; | |
1596 | int i; | |
1597 | ||
1598 | v4l2_device_unregister(&vpif_obj.v4l2_dev); | |
1599 | ||
1600 | /* un-register device */ | |
1601 | for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { | |
1602 | /* Get the pointer to the channel object */ | |
1603 | ch = vpif_obj.dev[i]; | |
1604 | /* Unregister video device */ | |
1605 | video_unregister_device(ch->video_dev); | |
1606 | ||
1607 | ch->video_dev = NULL; | |
1608 | } | |
1609 | ||
1610 | return 0; | |
1611 | } | |
1612 | ||
1613 | static struct platform_driver vpif_driver = { | |
1614 | .driver = { | |
1615 | .name = "vpif_display", | |
1616 | .owner = THIS_MODULE, | |
1617 | }, | |
1618 | .probe = vpif_probe, | |
1619 | .remove = vpif_remove, | |
1620 | }; | |
1621 | ||
1622 | static __init int vpif_init(void) | |
1623 | { | |
1624 | return platform_driver_register(&vpif_driver); | |
1625 | } | |
1626 | ||
1627 | /* | |
1628 | * vpif_cleanup: This function un-registers device and driver to the kernel, | |
1629 | * frees requested irq handler and de-allocates memory allocated for channel | |
1630 | * objects. | |
1631 | */ | |
1632 | static void vpif_cleanup(void) | |
1633 | { | |
1634 | struct platform_device *pdev; | |
1635 | struct resource *res; | |
1636 | int irq_num; | |
1637 | int i = 0; | |
1638 | ||
1639 | pdev = container_of(vpif_dev, struct platform_device, dev); | |
1640 | ||
1641 | while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { | |
1642 | for (irq_num = res->start; irq_num <= res->end; irq_num++) | |
1643 | free_irq(irq_num, | |
1644 | (void *)(&vpif_obj.dev[i]->channel_id)); | |
1645 | i++; | |
1646 | } | |
1647 | ||
e7332e3a C |
1648 | platform_driver_unregister(&vpif_driver); |
1649 | kfree(vpif_obj.sd); | |
1650 | for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) | |
1651 | kfree(vpif_obj.dev[i]); | |
1652 | } | |
1653 | ||
1654 | module_init(vpif_init); | |
1655 | module_exit(vpif_cleanup); |