diff options
Diffstat (limited to 'drivers/sbus/char/vfc_dev.c')
-rw-r--r-- | drivers/sbus/char/vfc_dev.c | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c new file mode 100644 index 000000000000..86ce54130954 --- /dev/null +++ b/drivers/sbus/char/vfc_dev.c | |||
@@ -0,0 +1,742 @@ | |||
1 | /* | ||
2 | * drivers/sbus/char/vfc_dev.c | ||
3 | * | ||
4 | * Driver for the Videopix Frame Grabber. | ||
5 | * | ||
6 | * In order to use the VFC you need to program the video controller | ||
7 | * chip. This chip is the Phillips SAA9051. You need to call their | ||
8 | * documentation ordering line to get the docs. | ||
9 | * | ||
10 | * There is very little documentation on the VFC itself. There is | ||
11 | * some useful info that can be found in the manuals that come with | ||
12 | * the card. I will hopefully write some better docs at a later date. | ||
13 | * | ||
14 | * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) | ||
15 | * */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | #include <linux/mm.h> | ||
28 | |||
29 | #include <asm/openprom.h> | ||
30 | #include <asm/oplib.h> | ||
31 | #include <asm/io.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/sbus.h> | ||
34 | #include <asm/page.h> | ||
35 | #include <asm/pgtable.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #define VFC_MAJOR (60) | ||
39 | |||
40 | #if 0 | ||
41 | #define VFC_IOCTL_DEBUG | ||
42 | #endif | ||
43 | |||
44 | #include "vfc.h" | ||
45 | #include <asm/vfc_ioctls.h> | ||
46 | |||
47 | static struct file_operations vfc_fops; | ||
48 | struct vfc_dev **vfc_dev_lst; | ||
49 | static char vfcstr[]="vfc"; | ||
50 | static unsigned char saa9051_init_array[VFC_SAA9051_NR] = { | ||
51 | 0x00, 0x64, 0x72, 0x52, | ||
52 | 0x36, 0x18, 0xff, 0x20, | ||
53 | 0xfc, 0x77, 0xe3, 0x50, | ||
54 | 0x3e | ||
55 | }; | ||
56 | |||
57 | void vfc_lock_device(struct vfc_dev *dev) | ||
58 | { | ||
59 | down(&dev->device_lock_sem); | ||
60 | } | ||
61 | |||
62 | void vfc_unlock_device(struct vfc_dev *dev) | ||
63 | { | ||
64 | up(&dev->device_lock_sem); | ||
65 | } | ||
66 | |||
67 | |||
68 | void vfc_captstat_reset(struct vfc_dev *dev) | ||
69 | { | ||
70 | dev->control_reg |= VFC_CONTROL_CAPTRESET; | ||
71 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
72 | dev->control_reg &= ~VFC_CONTROL_CAPTRESET; | ||
73 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
74 | dev->control_reg |= VFC_CONTROL_CAPTRESET; | ||
75 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
76 | } | ||
77 | |||
78 | void vfc_memptr_reset(struct vfc_dev *dev) | ||
79 | { | ||
80 | dev->control_reg |= VFC_CONTROL_MEMPTR; | ||
81 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
82 | dev->control_reg &= ~VFC_CONTROL_MEMPTR; | ||
83 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
84 | dev->control_reg |= VFC_CONTROL_MEMPTR; | ||
85 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
86 | } | ||
87 | |||
88 | int vfc_csr_init(struct vfc_dev *dev) | ||
89 | { | ||
90 | dev->control_reg = 0x80000000; | ||
91 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
92 | udelay(200); | ||
93 | dev->control_reg &= ~0x80000000; | ||
94 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
95 | udelay(100); | ||
96 | sbus_writel(0x0f000000, &dev->regs->i2c_magic2); | ||
97 | |||
98 | vfc_memptr_reset(dev); | ||
99 | |||
100 | dev->control_reg &= ~VFC_CONTROL_DIAGMODE; | ||
101 | dev->control_reg &= ~VFC_CONTROL_CAPTURE; | ||
102 | dev->control_reg |= 0x40000000; | ||
103 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
104 | |||
105 | vfc_captstat_reset(dev); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | int vfc_saa9051_init(struct vfc_dev *dev) | ||
111 | { | ||
112 | int i; | ||
113 | |||
114 | for (i = 0; i < VFC_SAA9051_NR; i++) | ||
115 | dev->saa9051_state_array[i] = saa9051_init_array[i]; | ||
116 | |||
117 | vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR, | ||
118 | dev->saa9051_state_array, VFC_SAA9051_NR); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | int init_vfc_hw(struct vfc_dev *dev) | ||
123 | { | ||
124 | vfc_lock_device(dev); | ||
125 | vfc_csr_init(dev); | ||
126 | |||
127 | vfc_pcf8584_init(dev); | ||
128 | vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic | ||
129 | sun code above*/ | ||
130 | vfc_saa9051_init(dev); | ||
131 | vfc_unlock_device(dev); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | int init_vfc_devstruct(struct vfc_dev *dev, int instance) | ||
136 | { | ||
137 | dev->instance=instance; | ||
138 | init_MUTEX(&dev->device_lock_sem); | ||
139 | dev->control_reg=0; | ||
140 | init_waitqueue_head(&dev->poll_wait); | ||
141 | dev->busy=0; | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) | ||
146 | { | ||
147 | if(dev == NULL) { | ||
148 | printk(KERN_ERR "VFC: Bogus pointer passed\n"); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | printk("Initializing vfc%d\n",instance); | ||
152 | dev->regs = NULL; | ||
153 | dev->regs = (volatile struct vfc_regs *) | ||
154 | sbus_ioremap(&sdev->resource[0], 0, | ||
155 | sizeof(struct vfc_regs), vfcstr); | ||
156 | dev->which_io = sdev->reg_addrs[0].which_io; | ||
157 | dev->phys_regs = (struct vfc_regs *) sdev->reg_addrs[0].phys_addr; | ||
158 | if (dev->regs == NULL) | ||
159 | return -EIO; | ||
160 | |||
161 | printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n", | ||
162 | instance,(unsigned long)sdev->reg_addrs[0].phys_addr,(unsigned long)dev->regs); | ||
163 | |||
164 | if (init_vfc_devstruct(dev, instance)) | ||
165 | return -EINVAL; | ||
166 | if (init_vfc_hw(dev)) | ||
167 | return -EIO; | ||
168 | |||
169 | devfs_mk_cdev(MKDEV(VFC_MAJOR, instance), | ||
170 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
171 | "vfc/%d", instance); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | |||
176 | struct vfc_dev *vfc_get_dev_ptr(int instance) | ||
177 | { | ||
178 | return vfc_dev_lst[instance]; | ||
179 | } | ||
180 | |||
181 | static DEFINE_SPINLOCK(vfc_dev_lock); | ||
182 | |||
183 | static int vfc_open(struct inode *inode, struct file *file) | ||
184 | { | ||
185 | struct vfc_dev *dev; | ||
186 | |||
187 | spin_lock(&vfc_dev_lock); | ||
188 | dev = vfc_get_dev_ptr(iminor(inode)); | ||
189 | if (dev == NULL) { | ||
190 | spin_unlock(&vfc_dev_lock); | ||
191 | return -ENODEV; | ||
192 | } | ||
193 | if (dev->busy) { | ||
194 | spin_unlock(&vfc_dev_lock); | ||
195 | return -EBUSY; | ||
196 | } | ||
197 | |||
198 | dev->busy = 1; | ||
199 | spin_unlock(&vfc_dev_lock); | ||
200 | |||
201 | vfc_lock_device(dev); | ||
202 | |||
203 | vfc_csr_init(dev); | ||
204 | vfc_pcf8584_init(dev); | ||
205 | vfc_init_i2c_bus(dev); | ||
206 | vfc_saa9051_init(dev); | ||
207 | vfc_memptr_reset(dev); | ||
208 | vfc_captstat_reset(dev); | ||
209 | |||
210 | vfc_unlock_device(dev); | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int vfc_release(struct inode *inode,struct file *file) | ||
215 | { | ||
216 | struct vfc_dev *dev; | ||
217 | |||
218 | spin_lock(&vfc_dev_lock); | ||
219 | dev = vfc_get_dev_ptr(iminor(inode)); | ||
220 | if (!dev || !dev->busy) { | ||
221 | spin_unlock(&vfc_dev_lock); | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | dev->busy = 0; | ||
225 | spin_unlock(&vfc_dev_lock); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp) | ||
230 | { | ||
231 | struct vfc_debug_inout inout; | ||
232 | unsigned char *buffer; | ||
233 | |||
234 | if (!capable(CAP_SYS_ADMIN)) | ||
235 | return -EPERM; | ||
236 | |||
237 | switch(cmd) { | ||
238 | case VFC_I2C_SEND: | ||
239 | if(copy_from_user(&inout, argp, sizeof(inout))) | ||
240 | return -EFAULT; | ||
241 | |||
242 | buffer = kmalloc(inout.len, GFP_KERNEL); | ||
243 | if (buffer == NULL) | ||
244 | return -ENOMEM; | ||
245 | |||
246 | if(copy_from_user(buffer, inout.buffer, inout.len)) { | ||
247 | kfree(buffer); | ||
248 | return -EFAULT; | ||
249 | } | ||
250 | |||
251 | |||
252 | vfc_lock_device(dev); | ||
253 | inout.ret= | ||
254 | vfc_i2c_sendbuf(dev,inout.addr & 0xff, | ||
255 | buffer,inout.len); | ||
256 | |||
257 | if (copy_to_user(argp,&inout,sizeof(inout))) { | ||
258 | kfree(buffer); | ||
259 | return -EFAULT; | ||
260 | } | ||
261 | vfc_unlock_device(dev); | ||
262 | |||
263 | break; | ||
264 | case VFC_I2C_RECV: | ||
265 | if (copy_from_user(&inout, argp, sizeof(inout))) | ||
266 | return -EFAULT; | ||
267 | |||
268 | buffer = kmalloc(inout.len, GFP_KERNEL); | ||
269 | if (buffer == NULL) | ||
270 | return -ENOMEM; | ||
271 | |||
272 | memset(buffer,0,inout.len); | ||
273 | vfc_lock_device(dev); | ||
274 | inout.ret= | ||
275 | vfc_i2c_recvbuf(dev,inout.addr & 0xff | ||
276 | ,buffer,inout.len); | ||
277 | vfc_unlock_device(dev); | ||
278 | |||
279 | if (copy_to_user(inout.buffer, buffer, inout.len)) { | ||
280 | kfree(buffer); | ||
281 | return -EFAULT; | ||
282 | } | ||
283 | if (copy_to_user(argp,&inout,sizeof(inout))) { | ||
284 | kfree(buffer); | ||
285 | return -EFAULT; | ||
286 | } | ||
287 | kfree(buffer); | ||
288 | break; | ||
289 | default: | ||
290 | return -EINVAL; | ||
291 | }; | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | int vfc_capture_start(struct vfc_dev *dev) | ||
297 | { | ||
298 | vfc_captstat_reset(dev); | ||
299 | dev->control_reg = sbus_readl(&dev->regs->control); | ||
300 | if((dev->control_reg & VFC_STATUS_CAPTURE)) { | ||
301 | printk(KERN_ERR "vfc%d: vfc capture status not reset\n", | ||
302 | dev->instance); | ||
303 | return -EIO; | ||
304 | } | ||
305 | |||
306 | vfc_lock_device(dev); | ||
307 | dev->control_reg &= ~VFC_CONTROL_CAPTURE; | ||
308 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
309 | dev->control_reg |= VFC_CONTROL_CAPTURE; | ||
310 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
311 | dev->control_reg &= ~VFC_CONTROL_CAPTURE; | ||
312 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
313 | vfc_unlock_device(dev); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | int vfc_capture_poll(struct vfc_dev *dev) | ||
319 | { | ||
320 | int timeout = 1000; | ||
321 | |||
322 | while (!timeout--) { | ||
323 | if (dev->regs->control & VFC_STATUS_CAPTURE) | ||
324 | break; | ||
325 | vfc_i2c_delay_no_busy(dev, 100); | ||
326 | } | ||
327 | if(!timeout) { | ||
328 | printk(KERN_WARNING "vfc%d: capture timed out\n", | ||
329 | dev->instance); | ||
330 | return -ETIMEDOUT; | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | |||
336 | |||
337 | static int vfc_set_control_ioctl(struct inode *inode, struct file *file, | ||
338 | struct vfc_dev *dev, unsigned long arg) | ||
339 | { | ||
340 | int setcmd, ret = 0; | ||
341 | |||
342 | if (copy_from_user(&setcmd,(void __user *)arg,sizeof(unsigned int))) | ||
343 | return -EFAULT; | ||
344 | |||
345 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n", | ||
346 | dev->instance,setcmd)); | ||
347 | |||
348 | switch(setcmd) { | ||
349 | case MEMPRST: | ||
350 | vfc_lock_device(dev); | ||
351 | vfc_memptr_reset(dev); | ||
352 | vfc_unlock_device(dev); | ||
353 | ret=0; | ||
354 | break; | ||
355 | case CAPTRCMD: | ||
356 | vfc_capture_start(dev); | ||
357 | vfc_capture_poll(dev); | ||
358 | break; | ||
359 | case DIAGMODE: | ||
360 | if(capable(CAP_SYS_ADMIN)) { | ||
361 | vfc_lock_device(dev); | ||
362 | dev->control_reg |= VFC_CONTROL_DIAGMODE; | ||
363 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
364 | vfc_unlock_device(dev); | ||
365 | ret = 0; | ||
366 | } else { | ||
367 | ret = -EPERM; | ||
368 | } | ||
369 | break; | ||
370 | case NORMMODE: | ||
371 | vfc_lock_device(dev); | ||
372 | dev->control_reg &= ~VFC_CONTROL_DIAGMODE; | ||
373 | sbus_writel(dev->control_reg, &dev->regs->control); | ||
374 | vfc_unlock_device(dev); | ||
375 | ret = 0; | ||
376 | break; | ||
377 | case CAPTRSTR: | ||
378 | vfc_capture_start(dev); | ||
379 | ret = 0; | ||
380 | break; | ||
381 | case CAPTRWAIT: | ||
382 | vfc_capture_poll(dev); | ||
383 | ret = 0; | ||
384 | break; | ||
385 | default: | ||
386 | ret = -EINVAL; | ||
387 | break; | ||
388 | }; | ||
389 | |||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | |||
394 | int vfc_port_change_ioctl(struct inode *inode, struct file *file, | ||
395 | struct vfc_dev *dev, unsigned long arg) | ||
396 | { | ||
397 | int ret = 0; | ||
398 | int cmd; | ||
399 | |||
400 | if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) { | ||
401 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " | ||
402 | "vfc_port_change_ioctl\n", | ||
403 | dev->instance)); | ||
404 | return -EFAULT; | ||
405 | } | ||
406 | |||
407 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n", | ||
408 | dev->instance, cmd)); | ||
409 | |||
410 | switch(cmd) { | ||
411 | case 1: | ||
412 | case 2: | ||
413 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72; | ||
414 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52; | ||
415 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36; | ||
416 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18; | ||
417 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2; | ||
418 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3; | ||
419 | VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e; | ||
420 | break; | ||
421 | case 3: | ||
422 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a; | ||
423 | VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17; | ||
424 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa; | ||
425 | VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde; | ||
426 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = | ||
427 | VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2; | ||
428 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC; | ||
429 | VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0; | ||
430 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= | ||
431 | ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); | ||
432 | break; | ||
433 | default: | ||
434 | ret = -EINVAL; | ||
435 | return ret; | ||
436 | break; | ||
437 | } | ||
438 | |||
439 | switch(cmd) { | ||
440 | case 1: | ||
441 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= | ||
442 | (VFC_SAA9051_SS0 | VFC_SAA9051_SS1); | ||
443 | break; | ||
444 | case 2: | ||
445 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= | ||
446 | ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); | ||
447 | VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0; | ||
448 | break; | ||
449 | case 3: | ||
450 | break; | ||
451 | default: | ||
452 | ret = -EINVAL; | ||
453 | return ret; | ||
454 | break; | ||
455 | } | ||
456 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2); | ||
457 | ret=vfc_update_saa9051(dev); | ||
458 | udelay(500); | ||
459 | VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2); | ||
460 | ret=vfc_update_saa9051(dev); | ||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | int vfc_set_video_ioctl(struct inode *inode, struct file *file, | ||
465 | struct vfc_dev *dev, unsigned long arg) | ||
466 | { | ||
467 | int ret = 0; | ||
468 | int cmd; | ||
469 | |||
470 | if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) { | ||
471 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " | ||
472 | "vfc_set_video_ioctl\n", | ||
473 | dev->instance)); | ||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n", | ||
478 | dev->instance, cmd)); | ||
479 | switch(cmd) { | ||
480 | case STD_NTSC: | ||
481 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT; | ||
482 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN | | ||
483 | VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS; | ||
484 | ret = vfc_update_saa9051(dev); | ||
485 | break; | ||
486 | case STD_PAL: | ||
487 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN | | ||
488 | VFC_SAA9051_CCFR1 | | ||
489 | VFC_SAA9051_CCFR0 | | ||
490 | VFC_SAA9051_FS); | ||
491 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT; | ||
492 | ret = vfc_update_saa9051(dev); | ||
493 | break; | ||
494 | |||
495 | case COLOR_ON: | ||
496 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO; | ||
497 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &= | ||
498 | ~(VFC_SAA9051_BY | VFC_SAA9051_PF); | ||
499 | ret = vfc_update_saa9051(dev); | ||
500 | break; | ||
501 | case MONO: | ||
502 | VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO); | ||
503 | VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |= | ||
504 | (VFC_SAA9051_BY | VFC_SAA9051_PF); | ||
505 | ret = vfc_update_saa9051(dev); | ||
506 | break; | ||
507 | default: | ||
508 | ret = -EINVAL; | ||
509 | break; | ||
510 | }; | ||
511 | |||
512 | return ret; | ||
513 | } | ||
514 | |||
515 | int vfc_get_video_ioctl(struct inode *inode, struct file *file, | ||
516 | struct vfc_dev *dev, unsigned long arg) | ||
517 | { | ||
518 | int ret = 0; | ||
519 | unsigned int status = NO_LOCK; | ||
520 | unsigned char buf[1]; | ||
521 | |||
522 | if(vfc_i2c_recvbuf(dev, VFC_SAA9051_ADDR, buf, 1)) { | ||
523 | printk(KERN_ERR "vfc%d: Unable to get status\n", | ||
524 | dev->instance); | ||
525 | return -EIO; | ||
526 | } | ||
527 | |||
528 | if(buf[0] & VFC_SAA9051_HLOCK) { | ||
529 | status = NO_LOCK; | ||
530 | } else if(buf[0] & VFC_SAA9051_FD) { | ||
531 | if(buf[0] & VFC_SAA9051_CD) | ||
532 | status = NTSC_COLOR; | ||
533 | else | ||
534 | status = NTSC_NOCOLOR; | ||
535 | } else { | ||
536 | if(buf[0] & VFC_SAA9051_CD) | ||
537 | status = PAL_COLOR; | ||
538 | else | ||
539 | status = PAL_NOCOLOR; | ||
540 | } | ||
541 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; " | ||
542 | "buf[0]=%x\n", dev->instance, status, buf[0])); | ||
543 | |||
544 | if (copy_to_user((void __user *)arg,&status,sizeof(unsigned int))) { | ||
545 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " | ||
546 | "vfc_get_video_ioctl\n", | ||
547 | dev->instance)); | ||
548 | return ret; | ||
549 | } | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
554 | unsigned long arg) | ||
555 | { | ||
556 | int ret = 0; | ||
557 | unsigned int tmp; | ||
558 | struct vfc_dev *dev; | ||
559 | void __user *argp = (void __user *)arg; | ||
560 | |||
561 | dev = vfc_get_dev_ptr(iminor(inode)); | ||
562 | if(dev == NULL) | ||
563 | return -ENODEV; | ||
564 | |||
565 | switch(cmd & 0x0000ffff) { | ||
566 | case VFCGCTRL: | ||
567 | #if 0 | ||
568 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n", dev->instance)); | ||
569 | #endif | ||
570 | tmp = sbus_readl(&dev->regs->control); | ||
571 | if(copy_to_user(argp, &tmp, sizeof(unsigned int))) { | ||
572 | ret = -EFAULT; | ||
573 | break; | ||
574 | } | ||
575 | ret = 0; | ||
576 | break; | ||
577 | case VFCSCTRL: | ||
578 | ret = vfc_set_control_ioctl(inode, file, dev, arg); | ||
579 | break; | ||
580 | case VFCGVID: | ||
581 | ret = vfc_get_video_ioctl(inode, file, dev, arg); | ||
582 | break; | ||
583 | case VFCSVID: | ||
584 | ret = vfc_set_video_ioctl(inode, file, dev, arg); | ||
585 | break; | ||
586 | case VFCHUE: | ||
587 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n", dev->instance)); | ||
588 | if(copy_from_user(&tmp,argp,sizeof(unsigned int))) { | ||
589 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer " | ||
590 | "to IOCTL(VFCHUE)", dev->instance)); | ||
591 | ret = -EFAULT; | ||
592 | } else { | ||
593 | VFC_SAA9051_SA(dev,VFC_SAA9051_HUE) = tmp; | ||
594 | vfc_update_saa9051(dev); | ||
595 | ret = 0; | ||
596 | } | ||
597 | break; | ||
598 | case VFCPORTCHG: | ||
599 | ret = vfc_port_change_ioctl(inode, file, dev, arg); | ||
600 | break; | ||
601 | case VFCRDINFO: | ||
602 | ret = -EINVAL; | ||
603 | VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n", dev->instance)); | ||
604 | break; | ||
605 | default: | ||
606 | ret = vfc_debug(vfc_get_dev_ptr(iminor(inode)), cmd, argp); | ||
607 | break; | ||
608 | }; | ||
609 | |||
610 | return ret; | ||
611 | } | ||
612 | |||
613 | static int vfc_mmap(struct file *file, struct vm_area_struct *vma) | ||
614 | { | ||
615 | unsigned int map_size, ret, map_offset; | ||
616 | struct vfc_dev *dev; | ||
617 | |||
618 | dev = vfc_get_dev_ptr(iminor(file->f_dentry->d_inode)); | ||
619 | if(dev == NULL) | ||
620 | return -ENODEV; | ||
621 | |||
622 | map_size = vma->vm_end - vma->vm_start; | ||
623 | if(map_size > sizeof(struct vfc_regs)) | ||
624 | map_size = sizeof(struct vfc_regs); | ||
625 | |||
626 | vma->vm_flags |= | ||
627 | (VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE); | ||
628 | map_offset = (unsigned int) (long)dev->phys_regs; | ||
629 | ret = io_remap_pfn_range(vma, vma->vm_start, | ||
630 | MK_IOSPACE_PFN(dev->which_io, | ||
631 | map_offset >> PAGE_SHIFT), | ||
632 | map_size, vma->vm_page_prot); | ||
633 | |||
634 | if(ret) | ||
635 | return -EAGAIN; | ||
636 | |||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | |||
641 | static struct file_operations vfc_fops = { | ||
642 | .owner = THIS_MODULE, | ||
643 | .llseek = no_llseek, | ||
644 | .ioctl = vfc_ioctl, | ||
645 | .mmap = vfc_mmap, | ||
646 | .open = vfc_open, | ||
647 | .release = vfc_release, | ||
648 | }; | ||
649 | |||
650 | static int vfc_probe(void) | ||
651 | { | ||
652 | struct sbus_bus *sbus; | ||
653 | struct sbus_dev *sdev = NULL; | ||
654 | int ret; | ||
655 | int instance = 0, cards = 0; | ||
656 | |||
657 | for_all_sbusdev(sdev, sbus) { | ||
658 | if (strcmp(sdev->prom_name, "vfc") == 0) { | ||
659 | cards++; | ||
660 | continue; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | if (!cards) | ||
665 | return -ENODEV; | ||
666 | |||
667 | vfc_dev_lst = (struct vfc_dev **)kmalloc(sizeof(struct vfc_dev *) * | ||
668 | (cards+1), | ||
669 | GFP_KERNEL); | ||
670 | if (vfc_dev_lst == NULL) | ||
671 | return -ENOMEM; | ||
672 | memset(vfc_dev_lst, 0, sizeof(struct vfc_dev *) * (cards + 1)); | ||
673 | vfc_dev_lst[cards] = NULL; | ||
674 | |||
675 | ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops); | ||
676 | if(ret) { | ||
677 | printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR); | ||
678 | kfree(vfc_dev_lst); | ||
679 | return -EIO; | ||
680 | } | ||
681 | devfs_mk_dir("vfc"); | ||
682 | instance = 0; | ||
683 | for_all_sbusdev(sdev, sbus) { | ||
684 | if (strcmp(sdev->prom_name, "vfc") == 0) { | ||
685 | vfc_dev_lst[instance]=(struct vfc_dev *) | ||
686 | kmalloc(sizeof(struct vfc_dev), GFP_KERNEL); | ||
687 | if (vfc_dev_lst[instance] == NULL) | ||
688 | return -ENOMEM; | ||
689 | ret = init_vfc_device(sdev, | ||
690 | vfc_dev_lst[instance], | ||
691 | instance); | ||
692 | if(ret) { | ||
693 | printk(KERN_ERR "Unable to initialize" | ||
694 | " vfc%d device\n", | ||
695 | instance); | ||
696 | } else { | ||
697 | } | ||
698 | |||
699 | instance++; | ||
700 | continue; | ||
701 | } | ||
702 | } | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | #ifdef MODULE | ||
708 | int init_module(void) | ||
709 | #else | ||
710 | int vfc_init(void) | ||
711 | #endif | ||
712 | { | ||
713 | return vfc_probe(); | ||
714 | } | ||
715 | |||
716 | #ifdef MODULE | ||
717 | static void deinit_vfc_device(struct vfc_dev *dev) | ||
718 | { | ||
719 | if(dev == NULL) | ||
720 | return; | ||
721 | devfs_remove("vfc/%d", dev->instance); | ||
722 | sbus_iounmap((unsigned long)dev->regs, sizeof(struct vfc_regs)); | ||
723 | kfree(dev); | ||
724 | } | ||
725 | |||
726 | void cleanup_module(void) | ||
727 | { | ||
728 | struct vfc_dev **devp; | ||
729 | |||
730 | unregister_chrdev(VFC_MAJOR,vfcstr); | ||
731 | |||
732 | for (devp = vfc_dev_lst; *devp; devp++) | ||
733 | deinit_vfc_device(*devp); | ||
734 | |||
735 | devfs_remove("vfc"); | ||
736 | kfree(vfc_dev_lst); | ||
737 | return; | ||
738 | } | ||
739 | #endif | ||
740 | |||
741 | MODULE_LICENSE("GPL"); | ||
742 | |||