diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/saa7134/saa7134-oss.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/saa7134/saa7134-oss.c')
-rw-r--r-- | drivers/media/video/saa7134/saa7134-oss.c | 857 |
1 files changed, 857 insertions, 0 deletions
diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c new file mode 100644 index 000000000000..6b6a643bf1cd --- /dev/null +++ b/drivers/media/video/saa7134/saa7134-oss.c | |||
@@ -0,0 +1,857 @@ | |||
1 | /* | ||
2 | * $Id: saa7134-oss.c,v 1.13 2004/12/10 12:33:39 kraxel Exp $ | ||
3 | * | ||
4 | * device driver for philips saa7134 based TV cards | ||
5 | * oss dsp interface | ||
6 | * | ||
7 | * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
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 as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/soundcard.h> | ||
31 | |||
32 | #include "saa7134-reg.h" | ||
33 | #include "saa7134.h" | ||
34 | |||
35 | /* ------------------------------------------------------------------ */ | ||
36 | |||
37 | static unsigned int oss_debug = 0; | ||
38 | module_param(oss_debug, int, 0644); | ||
39 | MODULE_PARM_DESC(oss_debug,"enable debug messages [oss]"); | ||
40 | |||
41 | static unsigned int oss_rate = 0; | ||
42 | module_param(oss_rate, int, 0444); | ||
43 | MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)"); | ||
44 | |||
45 | #define dprintk(fmt, arg...) if (oss_debug) \ | ||
46 | printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg) | ||
47 | |||
48 | /* ------------------------------------------------------------------ */ | ||
49 | |||
50 | static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) | ||
51 | { | ||
52 | blksize &= ~0xff; | ||
53 | if (blksize < 0x100) | ||
54 | blksize = 0x100; | ||
55 | if (blksize > 0x10000) | ||
56 | blksize = 0x10000; | ||
57 | |||
58 | if (blocks < 2) | ||
59 | blocks = 2; | ||
60 | while ((blksize * blocks) & ~PAGE_MASK) | ||
61 | blocks++; | ||
62 | if ((blksize * blocks) > 1024*1024) | ||
63 | blocks = 1024*1024 / blksize; | ||
64 | |||
65 | dev->oss.blocks = blocks; | ||
66 | dev->oss.blksize = blksize; | ||
67 | dev->oss.bufsize = blksize * blocks; | ||
68 | |||
69 | dprintk("buffer config: %d blocks / %d bytes, %d kB total\n", | ||
70 | blocks,blksize,blksize * blocks / 1024); | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static int dsp_buffer_init(struct saa7134_dev *dev) | ||
75 | { | ||
76 | int err; | ||
77 | |||
78 | if (!dev->oss.bufsize) | ||
79 | BUG(); | ||
80 | videobuf_dma_init(&dev->oss.dma); | ||
81 | err = videobuf_dma_init_kernel(&dev->oss.dma, PCI_DMA_FROMDEVICE, | ||
82 | dev->oss.bufsize >> PAGE_SHIFT); | ||
83 | if (0 != err) | ||
84 | return err; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int dsp_buffer_free(struct saa7134_dev *dev) | ||
89 | { | ||
90 | if (!dev->oss.blksize) | ||
91 | BUG(); | ||
92 | videobuf_dma_free(&dev->oss.dma); | ||
93 | dev->oss.blocks = 0; | ||
94 | dev->oss.blksize = 0; | ||
95 | dev->oss.bufsize = 0; | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void dsp_dma_start(struct saa7134_dev *dev) | ||
100 | { | ||
101 | dev->oss.dma_blk = 0; | ||
102 | dev->oss.dma_running = 1; | ||
103 | saa7134_set_dmabits(dev); | ||
104 | } | ||
105 | |||
106 | static void dsp_dma_stop(struct saa7134_dev *dev) | ||
107 | { | ||
108 | dev->oss.dma_blk = -1; | ||
109 | dev->oss.dma_running = 0; | ||
110 | saa7134_set_dmabits(dev); | ||
111 | } | ||
112 | |||
113 | static int dsp_rec_start(struct saa7134_dev *dev) | ||
114 | { | ||
115 | int err, bswap, sign; | ||
116 | u32 fmt, control; | ||
117 | unsigned long flags; | ||
118 | |||
119 | /* prepare buffer */ | ||
120 | if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->oss.dma))) | ||
121 | return err; | ||
122 | if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->oss.pt))) | ||
123 | goto fail1; | ||
124 | if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->oss.pt, | ||
125 | dev->oss.dma.sglist, | ||
126 | dev->oss.dma.sglen, | ||
127 | 0))) | ||
128 | goto fail2; | ||
129 | |||
130 | /* sample format */ | ||
131 | switch (dev->oss.afmt) { | ||
132 | case AFMT_U8: | ||
133 | case AFMT_S8: fmt = 0x00; break; | ||
134 | case AFMT_U16_LE: | ||
135 | case AFMT_U16_BE: | ||
136 | case AFMT_S16_LE: | ||
137 | case AFMT_S16_BE: fmt = 0x01; break; | ||
138 | default: | ||
139 | err = -EINVAL; | ||
140 | goto fail2; | ||
141 | } | ||
142 | |||
143 | switch (dev->oss.afmt) { | ||
144 | case AFMT_S8: | ||
145 | case AFMT_S16_LE: | ||
146 | case AFMT_S16_BE: sign = 1; break; | ||
147 | default: sign = 0; break; | ||
148 | } | ||
149 | |||
150 | switch (dev->oss.afmt) { | ||
151 | case AFMT_U16_BE: | ||
152 | case AFMT_S16_BE: bswap = 1; break; | ||
153 | default: bswap = 0; break; | ||
154 | } | ||
155 | |||
156 | switch (dev->pci->device) { | ||
157 | case PCI_DEVICE_ID_PHILIPS_SAA7134: | ||
158 | if (1 == dev->oss.channels) | ||
159 | fmt |= (1 << 3); | ||
160 | if (2 == dev->oss.channels) | ||
161 | fmt |= (3 << 3); | ||
162 | if (sign) | ||
163 | fmt |= 0x04; | ||
164 | fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80; | ||
165 | |||
166 | saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff)); | ||
167 | saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >> 8); | ||
168 | saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16); | ||
169 | saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); | ||
170 | break; | ||
171 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | ||
172 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | ||
173 | if (1 == dev->oss.channels) | ||
174 | fmt |= (1 << 4); | ||
175 | if (2 == dev->oss.channels) | ||
176 | fmt |= (2 << 4); | ||
177 | if (!sign) | ||
178 | fmt |= 0x04; | ||
179 | saa_writel(0x588 >> 2, dev->oss.blksize -4); | ||
180 | saa_writel(0x58c >> 2, 0x543210 | (fmt << 24)); | ||
181 | break; | ||
182 | } | ||
183 | dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", | ||
184 | dev->oss.afmt, dev->oss.channels, fmt, | ||
185 | bswap ? 'b' : '-'); | ||
186 | |||
187 | /* dma: setup channel 6 (= AUDIO) */ | ||
188 | control = SAA7134_RS_CONTROL_BURST_16 | | ||
189 | SAA7134_RS_CONTROL_ME | | ||
190 | (dev->oss.pt.dma >> 12); | ||
191 | if (bswap) | ||
192 | control |= SAA7134_RS_CONTROL_BSWAP; | ||
193 | saa_writel(SAA7134_RS_BA1(6),0); | ||
194 | saa_writel(SAA7134_RS_BA2(6),dev->oss.blksize); | ||
195 | saa_writel(SAA7134_RS_PITCH(6),0); | ||
196 | saa_writel(SAA7134_RS_CONTROL(6),control); | ||
197 | |||
198 | /* start dma */ | ||
199 | dev->oss.recording_on = 1; | ||
200 | spin_lock_irqsave(&dev->slock,flags); | ||
201 | dsp_dma_start(dev); | ||
202 | spin_unlock_irqrestore(&dev->slock,flags); | ||
203 | return 0; | ||
204 | |||
205 | fail2: | ||
206 | saa7134_pgtable_free(dev->pci,&dev->oss.pt); | ||
207 | fail1: | ||
208 | videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma); | ||
209 | return err; | ||
210 | } | ||
211 | |||
212 | static int dsp_rec_stop(struct saa7134_dev *dev) | ||
213 | { | ||
214 | unsigned long flags; | ||
215 | |||
216 | dprintk("rec_stop dma_blk=%d\n",dev->oss.dma_blk); | ||
217 | |||
218 | /* stop dma */ | ||
219 | dev->oss.recording_on = 0; | ||
220 | spin_lock_irqsave(&dev->slock,flags); | ||
221 | dsp_dma_stop(dev); | ||
222 | spin_unlock_irqrestore(&dev->slock,flags); | ||
223 | |||
224 | /* unlock buffer */ | ||
225 | saa7134_pgtable_free(dev->pci,&dev->oss.pt); | ||
226 | videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | /* ------------------------------------------------------------------ */ | ||
231 | |||
232 | static int dsp_open(struct inode *inode, struct file *file) | ||
233 | { | ||
234 | int minor = iminor(inode); | ||
235 | struct saa7134_dev *h,*dev = NULL; | ||
236 | struct list_head *list; | ||
237 | int err; | ||
238 | |||
239 | list_for_each(list,&saa7134_devlist) { | ||
240 | h = list_entry(list, struct saa7134_dev, devlist); | ||
241 | if (h->oss.minor_dsp == minor) | ||
242 | dev = h; | ||
243 | } | ||
244 | if (NULL == dev) | ||
245 | return -ENODEV; | ||
246 | |||
247 | down(&dev->oss.lock); | ||
248 | err = -EBUSY; | ||
249 | if (dev->oss.users_dsp) | ||
250 | goto fail1; | ||
251 | dev->oss.users_dsp++; | ||
252 | file->private_data = dev; | ||
253 | |||
254 | dev->oss.afmt = AFMT_U8; | ||
255 | dev->oss.channels = 1; | ||
256 | dev->oss.read_count = 0; | ||
257 | dev->oss.read_offset = 0; | ||
258 | dsp_buffer_conf(dev,PAGE_SIZE,64); | ||
259 | err = dsp_buffer_init(dev); | ||
260 | if (0 != err) | ||
261 | goto fail2; | ||
262 | |||
263 | up(&dev->oss.lock); | ||
264 | return 0; | ||
265 | |||
266 | fail2: | ||
267 | dev->oss.users_dsp--; | ||
268 | fail1: | ||
269 | up(&dev->oss.lock); | ||
270 | return err; | ||
271 | } | ||
272 | |||
273 | static int dsp_release(struct inode *inode, struct file *file) | ||
274 | { | ||
275 | struct saa7134_dev *dev = file->private_data; | ||
276 | |||
277 | down(&dev->oss.lock); | ||
278 | if (dev->oss.recording_on) | ||
279 | dsp_rec_stop(dev); | ||
280 | dsp_buffer_free(dev); | ||
281 | dev->oss.users_dsp--; | ||
282 | file->private_data = NULL; | ||
283 | up(&dev->oss.lock); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static ssize_t dsp_read(struct file *file, char __user *buffer, | ||
288 | size_t count, loff_t *ppos) | ||
289 | { | ||
290 | struct saa7134_dev *dev = file->private_data; | ||
291 | DECLARE_WAITQUEUE(wait, current); | ||
292 | unsigned int bytes; | ||
293 | unsigned long flags; | ||
294 | int err,ret = 0; | ||
295 | |||
296 | add_wait_queue(&dev->oss.wq, &wait); | ||
297 | down(&dev->oss.lock); | ||
298 | while (count > 0) { | ||
299 | /* wait for data if needed */ | ||
300 | if (0 == dev->oss.read_count) { | ||
301 | if (!dev->oss.recording_on) { | ||
302 | err = dsp_rec_start(dev); | ||
303 | if (err < 0) { | ||
304 | if (0 == ret) | ||
305 | ret = err; | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | if (dev->oss.recording_on && | ||
310 | !dev->oss.dma_running) { | ||
311 | /* recover from overruns */ | ||
312 | spin_lock_irqsave(&dev->slock,flags); | ||
313 | dsp_dma_start(dev); | ||
314 | spin_unlock_irqrestore(&dev->slock,flags); | ||
315 | } | ||
316 | if (file->f_flags & O_NONBLOCK) { | ||
317 | if (0 == ret) | ||
318 | ret = -EAGAIN; | ||
319 | break; | ||
320 | } | ||
321 | up(&dev->oss.lock); | ||
322 | set_current_state(TASK_INTERRUPTIBLE); | ||
323 | if (0 == dev->oss.read_count) | ||
324 | schedule(); | ||
325 | set_current_state(TASK_RUNNING); | ||
326 | down(&dev->oss.lock); | ||
327 | if (signal_pending(current)) { | ||
328 | if (0 == ret) | ||
329 | ret = -EINTR; | ||
330 | break; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | /* copy data to userspace */ | ||
335 | bytes = count; | ||
336 | if (bytes > dev->oss.read_count) | ||
337 | bytes = dev->oss.read_count; | ||
338 | if (bytes > dev->oss.bufsize - dev->oss.read_offset) | ||
339 | bytes = dev->oss.bufsize - dev->oss.read_offset; | ||
340 | if (copy_to_user(buffer + ret, | ||
341 | dev->oss.dma.vmalloc + dev->oss.read_offset, | ||
342 | bytes)) { | ||
343 | if (0 == ret) | ||
344 | ret = -EFAULT; | ||
345 | break; | ||
346 | } | ||
347 | |||
348 | ret += bytes; | ||
349 | count -= bytes; | ||
350 | dev->oss.read_count -= bytes; | ||
351 | dev->oss.read_offset += bytes; | ||
352 | if (dev->oss.read_offset == dev->oss.bufsize) | ||
353 | dev->oss.read_offset = 0; | ||
354 | } | ||
355 | up(&dev->oss.lock); | ||
356 | remove_wait_queue(&dev->oss.wq, &wait); | ||
357 | return ret; | ||
358 | } | ||
359 | |||
360 | static ssize_t dsp_write(struct file *file, const char __user *buffer, | ||
361 | size_t count, loff_t *ppos) | ||
362 | { | ||
363 | return -EINVAL; | ||
364 | } | ||
365 | |||
366 | static int dsp_ioctl(struct inode *inode, struct file *file, | ||
367 | unsigned int cmd, unsigned long arg) | ||
368 | { | ||
369 | struct saa7134_dev *dev = file->private_data; | ||
370 | void __user *argp = (void __user *) arg; | ||
371 | int __user *p = argp; | ||
372 | int val = 0; | ||
373 | |||
374 | if (oss_debug > 1) | ||
375 | saa7134_print_ioctl(dev->name,cmd); | ||
376 | switch (cmd) { | ||
377 | case OSS_GETVERSION: | ||
378 | return put_user(SOUND_VERSION, p); | ||
379 | case SNDCTL_DSP_GETCAPS: | ||
380 | return 0; | ||
381 | |||
382 | case SNDCTL_DSP_SPEED: | ||
383 | if (get_user(val, p)) | ||
384 | return -EFAULT; | ||
385 | /* fall through */ | ||
386 | case SOUND_PCM_READ_RATE: | ||
387 | return put_user(dev->oss.rate, p); | ||
388 | |||
389 | case SNDCTL_DSP_STEREO: | ||
390 | if (get_user(val, p)) | ||
391 | return -EFAULT; | ||
392 | down(&dev->oss.lock); | ||
393 | dev->oss.channels = val ? 2 : 1; | ||
394 | if (dev->oss.recording_on) { | ||
395 | dsp_rec_stop(dev); | ||
396 | dsp_rec_start(dev); | ||
397 | } | ||
398 | up(&dev->oss.lock); | ||
399 | return put_user(dev->oss.channels-1, p); | ||
400 | |||
401 | case SNDCTL_DSP_CHANNELS: | ||
402 | if (get_user(val, p)) | ||
403 | return -EFAULT; | ||
404 | if (val != 1 && val != 2) | ||
405 | return -EINVAL; | ||
406 | down(&dev->oss.lock); | ||
407 | dev->oss.channels = val; | ||
408 | if (dev->oss.recording_on) { | ||
409 | dsp_rec_stop(dev); | ||
410 | dsp_rec_start(dev); | ||
411 | } | ||
412 | up(&dev->oss.lock); | ||
413 | /* fall through */ | ||
414 | case SOUND_PCM_READ_CHANNELS: | ||
415 | return put_user(dev->oss.channels, p); | ||
416 | |||
417 | case SNDCTL_DSP_GETFMTS: /* Returns a mask */ | ||
418 | return put_user(AFMT_U8 | AFMT_S8 | | ||
419 | AFMT_U16_LE | AFMT_U16_BE | | ||
420 | AFMT_S16_LE | AFMT_S16_BE, p); | ||
421 | |||
422 | case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ | ||
423 | if (get_user(val, p)) | ||
424 | return -EFAULT; | ||
425 | switch (val) { | ||
426 | case AFMT_QUERY: | ||
427 | /* nothing to do */ | ||
428 | break; | ||
429 | case AFMT_U8: | ||
430 | case AFMT_S8: | ||
431 | case AFMT_U16_LE: | ||
432 | case AFMT_U16_BE: | ||
433 | case AFMT_S16_LE: | ||
434 | case AFMT_S16_BE: | ||
435 | down(&dev->oss.lock); | ||
436 | dev->oss.afmt = val; | ||
437 | if (dev->oss.recording_on) { | ||
438 | dsp_rec_stop(dev); | ||
439 | dsp_rec_start(dev); | ||
440 | } | ||
441 | up(&dev->oss.lock); | ||
442 | return put_user(dev->oss.afmt, p); | ||
443 | default: | ||
444 | return -EINVAL; | ||
445 | } | ||
446 | |||
447 | case SOUND_PCM_READ_BITS: | ||
448 | switch (dev->oss.afmt) { | ||
449 | case AFMT_U8: | ||
450 | case AFMT_S8: | ||
451 | return put_user(8, p); | ||
452 | case AFMT_U16_LE: | ||
453 | case AFMT_U16_BE: | ||
454 | case AFMT_S16_LE: | ||
455 | case AFMT_S16_BE: | ||
456 | return put_user(16, p); | ||
457 | default: | ||
458 | return -EINVAL; | ||
459 | } | ||
460 | |||
461 | case SNDCTL_DSP_NONBLOCK: | ||
462 | file->f_flags |= O_NONBLOCK; | ||
463 | return 0; | ||
464 | |||
465 | case SNDCTL_DSP_RESET: | ||
466 | down(&dev->oss.lock); | ||
467 | if (dev->oss.recording_on) | ||
468 | dsp_rec_stop(dev); | ||
469 | up(&dev->oss.lock); | ||
470 | return 0; | ||
471 | case SNDCTL_DSP_GETBLKSIZE: | ||
472 | return put_user(dev->oss.blksize, p); | ||
473 | |||
474 | case SNDCTL_DSP_SETFRAGMENT: | ||
475 | if (get_user(val, p)) | ||
476 | return -EFAULT; | ||
477 | if (dev->oss.recording_on) | ||
478 | return -EBUSY; | ||
479 | dsp_buffer_free(dev); | ||
480 | /* used to be arg >> 16 instead of val >> 16; fixed */ | ||
481 | dsp_buffer_conf(dev,1 << (val & 0xffff), (val >> 16) & 0xffff); | ||
482 | dsp_buffer_init(dev); | ||
483 | return 0; | ||
484 | |||
485 | case SNDCTL_DSP_SYNC: | ||
486 | /* NOP */ | ||
487 | return 0; | ||
488 | |||
489 | case SNDCTL_DSP_GETISPACE: | ||
490 | { | ||
491 | audio_buf_info info; | ||
492 | info.fragsize = dev->oss.blksize; | ||
493 | info.fragstotal = dev->oss.blocks; | ||
494 | info.bytes = dev->oss.read_count; | ||
495 | info.fragments = info.bytes / info.fragsize; | ||
496 | if (copy_to_user(argp, &info, sizeof(info))) | ||
497 | return -EFAULT; | ||
498 | return 0; | ||
499 | } | ||
500 | default: | ||
501 | return -EINVAL; | ||
502 | } | ||
503 | } | ||
504 | |||
505 | static unsigned int dsp_poll(struct file *file, struct poll_table_struct *wait) | ||
506 | { | ||
507 | struct saa7134_dev *dev = file->private_data; | ||
508 | unsigned int mask = 0; | ||
509 | |||
510 | poll_wait(file, &dev->oss.wq, wait); | ||
511 | |||
512 | if (0 == dev->oss.read_count) { | ||
513 | down(&dev->oss.lock); | ||
514 | if (!dev->oss.recording_on) | ||
515 | dsp_rec_start(dev); | ||
516 | up(&dev->oss.lock); | ||
517 | } else | ||
518 | mask |= (POLLIN | POLLRDNORM); | ||
519 | return mask; | ||
520 | } | ||
521 | |||
522 | struct file_operations saa7134_dsp_fops = { | ||
523 | .owner = THIS_MODULE, | ||
524 | .open = dsp_open, | ||
525 | .release = dsp_release, | ||
526 | .read = dsp_read, | ||
527 | .write = dsp_write, | ||
528 | .ioctl = dsp_ioctl, | ||
529 | .poll = dsp_poll, | ||
530 | .llseek = no_llseek, | ||
531 | }; | ||
532 | |||
533 | /* ------------------------------------------------------------------ */ | ||
534 | |||
535 | static int | ||
536 | mixer_recsrc_7134(struct saa7134_dev *dev) | ||
537 | { | ||
538 | int analog_io,rate; | ||
539 | |||
540 | switch (dev->oss.input) { | ||
541 | case TV: | ||
542 | saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0); | ||
543 | saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); | ||
544 | break; | ||
545 | case LINE1: | ||
546 | case LINE2: | ||
547 | case LINE2_LEFT: | ||
548 | analog_io = (LINE1 == dev->oss.input) ? 0x00 : 0x08; | ||
549 | rate = (32000 == dev->oss.rate) ? 0x01 : 0x03; | ||
550 | saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io); | ||
551 | saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80); | ||
552 | saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); | ||
553 | break; | ||
554 | } | ||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static int | ||
559 | mixer_recsrc_7133(struct saa7134_dev *dev) | ||
560 | { | ||
561 | u32 value = 0xbbbbbb; | ||
562 | |||
563 | switch (dev->oss.input) { | ||
564 | case TV: | ||
565 | value = 0xbbbb10; /* MAIN */ | ||
566 | break; | ||
567 | case LINE1: | ||
568 | value = 0xbbbb32; /* AUX1 */ | ||
569 | break; | ||
570 | case LINE2: | ||
571 | case LINE2_LEFT: | ||
572 | value = 0xbbbb54; /* AUX2 */ | ||
573 | break; | ||
574 | } | ||
575 | saa_dsp_writel(dev, 0x46c >> 2, value); | ||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | static int | ||
580 | mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src) | ||
581 | { | ||
582 | static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" }; | ||
583 | |||
584 | dev->oss.count++; | ||
585 | dev->oss.input = src; | ||
586 | dprintk("mixer input = %s\n",iname[dev->oss.input]); | ||
587 | |||
588 | switch (dev->pci->device) { | ||
589 | case PCI_DEVICE_ID_PHILIPS_SAA7134: | ||
590 | mixer_recsrc_7134(dev); | ||
591 | break; | ||
592 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | ||
593 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | ||
594 | mixer_recsrc_7133(dev); | ||
595 | break; | ||
596 | } | ||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | static int | ||
601 | mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level) | ||
602 | { | ||
603 | switch (dev->pci->device) { | ||
604 | case PCI_DEVICE_ID_PHILIPS_SAA7134: | ||
605 | switch (src) { | ||
606 | case TV: | ||
607 | /* nothing */ | ||
608 | break; | ||
609 | case LINE1: | ||
610 | saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, | ||
611 | (100 == level) ? 0x00 : 0x10); | ||
612 | break; | ||
613 | case LINE2: | ||
614 | case LINE2_LEFT: | ||
615 | saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, | ||
616 | (100 == level) ? 0x00 : 0x20); | ||
617 | break; | ||
618 | } | ||
619 | break; | ||
620 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | ||
621 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | ||
622 | /* nothing */ | ||
623 | break; | ||
624 | } | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /* ------------------------------------------------------------------ */ | ||
629 | |||
630 | static int mixer_open(struct inode *inode, struct file *file) | ||
631 | { | ||
632 | int minor = iminor(inode); | ||
633 | struct saa7134_dev *h,*dev = NULL; | ||
634 | struct list_head *list; | ||
635 | |||
636 | list_for_each(list,&saa7134_devlist) { | ||
637 | h = list_entry(list, struct saa7134_dev, devlist); | ||
638 | if (h->oss.minor_mixer == minor) | ||
639 | dev = h; | ||
640 | } | ||
641 | if (NULL == dev) | ||
642 | return -ENODEV; | ||
643 | |||
644 | file->private_data = dev; | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int mixer_release(struct inode *inode, struct file *file) | ||
649 | { | ||
650 | file->private_data = NULL; | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static int mixer_ioctl(struct inode *inode, struct file *file, | ||
655 | unsigned int cmd, unsigned long arg) | ||
656 | { | ||
657 | struct saa7134_dev *dev = file->private_data; | ||
658 | enum saa7134_audio_in input; | ||
659 | int val,ret; | ||
660 | void __user *argp = (void __user *) arg; | ||
661 | int __user *p = argp; | ||
662 | |||
663 | if (oss_debug > 1) | ||
664 | saa7134_print_ioctl(dev->name,cmd); | ||
665 | switch (cmd) { | ||
666 | case OSS_GETVERSION: | ||
667 | return put_user(SOUND_VERSION, p); | ||
668 | case SOUND_MIXER_INFO: | ||
669 | { | ||
670 | mixer_info info; | ||
671 | memset(&info,0,sizeof(info)); | ||
672 | strlcpy(info.id, "TV audio", sizeof(info.id)); | ||
673 | strlcpy(info.name, dev->name, sizeof(info.name)); | ||
674 | info.modify_counter = dev->oss.count; | ||
675 | if (copy_to_user(argp, &info, sizeof(info))) | ||
676 | return -EFAULT; | ||
677 | return 0; | ||
678 | } | ||
679 | case SOUND_OLD_MIXER_INFO: | ||
680 | { | ||
681 | _old_mixer_info info; | ||
682 | memset(&info,0,sizeof(info)); | ||
683 | strlcpy(info.id, "TV audio", sizeof(info.id)); | ||
684 | strlcpy(info.name, dev->name, sizeof(info.name)); | ||
685 | if (copy_to_user(argp, &info, sizeof(info))) | ||
686 | return -EFAULT; | ||
687 | return 0; | ||
688 | } | ||
689 | case MIXER_READ(SOUND_MIXER_CAPS): | ||
690 | return put_user(SOUND_CAP_EXCL_INPUT, p); | ||
691 | case MIXER_READ(SOUND_MIXER_STEREODEVS): | ||
692 | return put_user(0, p); | ||
693 | case MIXER_READ(SOUND_MIXER_RECMASK): | ||
694 | case MIXER_READ(SOUND_MIXER_DEVMASK): | ||
695 | val = SOUND_MASK_LINE1 | SOUND_MASK_LINE2; | ||
696 | if (32000 == dev->oss.rate) | ||
697 | val |= SOUND_MASK_VIDEO; | ||
698 | return put_user(val, p); | ||
699 | |||
700 | case MIXER_WRITE(SOUND_MIXER_RECSRC): | ||
701 | if (get_user(val, p)) | ||
702 | return -EFAULT; | ||
703 | input = dev->oss.input; | ||
704 | if (32000 == dev->oss.rate && | ||
705 | val & SOUND_MASK_VIDEO && dev->oss.input != TV) | ||
706 | input = TV; | ||
707 | if (val & SOUND_MASK_LINE1 && dev->oss.input != LINE1) | ||
708 | input = LINE1; | ||
709 | if (val & SOUND_MASK_LINE2 && dev->oss.input != LINE2) | ||
710 | input = LINE2; | ||
711 | if (input != dev->oss.input) | ||
712 | mixer_recsrc(dev,input); | ||
713 | /* fall throuth */ | ||
714 | case MIXER_READ(SOUND_MIXER_RECSRC): | ||
715 | switch (dev->oss.input) { | ||
716 | case TV: ret = SOUND_MASK_VIDEO; break; | ||
717 | case LINE1: ret = SOUND_MASK_LINE1; break; | ||
718 | case LINE2: ret = SOUND_MASK_LINE2; break; | ||
719 | default: ret = 0; | ||
720 | } | ||
721 | return put_user(ret, p); | ||
722 | |||
723 | case MIXER_WRITE(SOUND_MIXER_VIDEO): | ||
724 | case MIXER_READ(SOUND_MIXER_VIDEO): | ||
725 | if (32000 != dev->oss.rate) | ||
726 | return -EINVAL; | ||
727 | return put_user(100 | 100 << 8, p); | ||
728 | |||
729 | case MIXER_WRITE(SOUND_MIXER_LINE1): | ||
730 | if (get_user(val, p)) | ||
731 | return -EFAULT; | ||
732 | val &= 0xff; | ||
733 | val = (val <= 50) ? 50 : 100; | ||
734 | dev->oss.line1 = val; | ||
735 | mixer_level(dev,LINE1,dev->oss.line1); | ||
736 | /* fall throuth */ | ||
737 | case MIXER_READ(SOUND_MIXER_LINE1): | ||
738 | return put_user(dev->oss.line1 | dev->oss.line1 << 8, p); | ||
739 | |||
740 | case MIXER_WRITE(SOUND_MIXER_LINE2): | ||
741 | if (get_user(val, p)) | ||
742 | return -EFAULT; | ||
743 | val &= 0xff; | ||
744 | val = (val <= 50) ? 50 : 100; | ||
745 | dev->oss.line2 = val; | ||
746 | mixer_level(dev,LINE2,dev->oss.line2); | ||
747 | /* fall throuth */ | ||
748 | case MIXER_READ(SOUND_MIXER_LINE2): | ||
749 | return put_user(dev->oss.line2 | dev->oss.line2 << 8, p); | ||
750 | |||
751 | default: | ||
752 | return -EINVAL; | ||
753 | } | ||
754 | } | ||
755 | |||
756 | struct file_operations saa7134_mixer_fops = { | ||
757 | .owner = THIS_MODULE, | ||
758 | .open = mixer_open, | ||
759 | .release = mixer_release, | ||
760 | .ioctl = mixer_ioctl, | ||
761 | .llseek = no_llseek, | ||
762 | }; | ||
763 | |||
764 | /* ------------------------------------------------------------------ */ | ||
765 | |||
766 | int saa7134_oss_init1(struct saa7134_dev *dev) | ||
767 | { | ||
768 | /* general */ | ||
769 | init_MUTEX(&dev->oss.lock); | ||
770 | init_waitqueue_head(&dev->oss.wq); | ||
771 | |||
772 | switch (dev->pci->device) { | ||
773 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | ||
774 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | ||
775 | saa_writel(0x588 >> 2, 0x00000fff); | ||
776 | saa_writel(0x58c >> 2, 0x00543210); | ||
777 | saa_dsp_writel(dev, 0x46c >> 2, 0xbbbbbb); | ||
778 | break; | ||
779 | } | ||
780 | |||
781 | /* dsp */ | ||
782 | dev->oss.rate = 32000; | ||
783 | if (oss_rate) | ||
784 | dev->oss.rate = oss_rate; | ||
785 | dev->oss.rate = (dev->oss.rate > 40000) ? 48000 : 32000; | ||
786 | |||
787 | /* mixer */ | ||
788 | dev->oss.line1 = 50; | ||
789 | dev->oss.line2 = 50; | ||
790 | mixer_level(dev,LINE1,dev->oss.line1); | ||
791 | mixer_level(dev,LINE2,dev->oss.line2); | ||
792 | mixer_recsrc(dev, (dev->oss.rate == 32000) ? TV : LINE2); | ||
793 | |||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | int saa7134_oss_fini(struct saa7134_dev *dev) | ||
798 | { | ||
799 | /* nothing */ | ||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status) | ||
804 | { | ||
805 | int next_blk, reg = 0; | ||
806 | |||
807 | spin_lock(&dev->slock); | ||
808 | if (UNSET == dev->oss.dma_blk) { | ||
809 | dprintk("irq: recording stopped\n"); | ||
810 | goto done; | ||
811 | } | ||
812 | if (0 != (status & 0x0f000000)) | ||
813 | dprintk("irq: lost %ld\n", (status >> 24) & 0x0f); | ||
814 | if (0 == (status & 0x10000000)) { | ||
815 | /* odd */ | ||
816 | if (0 == (dev->oss.dma_blk & 0x01)) | ||
817 | reg = SAA7134_RS_BA1(6); | ||
818 | } else { | ||
819 | /* even */ | ||
820 | if (0 == (dev->oss.dma_blk & 0x00)) | ||
821 | reg = SAA7134_RS_BA2(6); | ||
822 | } | ||
823 | if (0 == reg) { | ||
824 | dprintk("irq: field oops [%s]\n", | ||
825 | (status & 0x10000000) ? "even" : "odd"); | ||
826 | goto done; | ||
827 | } | ||
828 | if (dev->oss.read_count >= dev->oss.blksize * (dev->oss.blocks-2)) { | ||
829 | dprintk("irq: overrun [full=%d/%d]\n",dev->oss.read_count, | ||
830 | dev->oss.bufsize); | ||
831 | dsp_dma_stop(dev); | ||
832 | goto done; | ||
833 | } | ||
834 | |||
835 | /* next block addr */ | ||
836 | next_blk = (dev->oss.dma_blk + 2) % dev->oss.blocks; | ||
837 | saa_writel(reg,next_blk * dev->oss.blksize); | ||
838 | if (oss_debug > 2) | ||
839 | dprintk("irq: ok, %s, next_blk=%d, addr=%x\n", | ||
840 | (status & 0x10000000) ? "even" : "odd ", next_blk, | ||
841 | next_blk * dev->oss.blksize); | ||
842 | |||
843 | /* update status & wake waiting readers */ | ||
844 | dev->oss.dma_blk = (dev->oss.dma_blk + 1) % dev->oss.blocks; | ||
845 | dev->oss.read_count += dev->oss.blksize; | ||
846 | wake_up(&dev->oss.wq); | ||
847 | |||
848 | done: | ||
849 | spin_unlock(&dev->slock); | ||
850 | } | ||
851 | |||
852 | /* ----------------------------------------------------------- */ | ||
853 | /* | ||
854 | * Local variables: | ||
855 | * c-basic-offset: 8 | ||
856 | * End: | ||
857 | */ | ||