Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * |
3 | * device driver for philips saa7134 based TV cards | |
4 | * oss dsp interface | |
5 | * | |
6 | * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | |
4aabf633 RC |
7 | * 2005 conversion to standalone module: |
8 | * Ricardo Cerqueira <v4l@cerqueira.org> | |
1da177e4 LT |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | */ | |
24 | ||
25 | #include <linux/init.h> | |
26 | #include <linux/list.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/moduleparam.h> | |
29 | #include <linux/kernel.h> | |
4aabf633 | 30 | #include <linux/interrupt.h> |
1da177e4 | 31 | #include <linux/slab.h> |
4aabf633 | 32 | #include <linux/sound.h> |
1da177e4 LT |
33 | #include <linux/soundcard.h> |
34 | ||
35 | #include "saa7134-reg.h" | |
36 | #include "saa7134.h" | |
37 | ||
38 | /* ------------------------------------------------------------------ */ | |
39 | ||
4aabf633 RC |
40 | static unsigned int debug = 0; |
41 | module_param(debug, int, 0644); | |
42 | MODULE_PARM_DESC(debug,"enable debug messages [oss]"); | |
1da177e4 | 43 | |
4aabf633 RC |
44 | static unsigned int rate = 0; |
45 | module_param(rate, int, 0444); | |
46 | MODULE_PARM_DESC(rate,"sample rate (valid are: 32000,48000)"); | |
1da177e4 | 47 | |
4aabf633 RC |
48 | static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; |
49 | MODULE_PARM_DESC(dsp_nr, "device numbers for SAA7134 capture interface(s)."); | |
50 | module_param_array(dsp_nr, int, NULL, 0444); | |
51 | ||
52 | static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; | |
53 | MODULE_PARM_DESC(mixer_nr, "mixer numbers for SAA7134 capture interface(s)."); | |
54 | module_param_array(mixer_nr, int, NULL, 0444); | |
55 | ||
56 | #define dprintk(fmt, arg...) if (debug) \ | |
1da177e4 LT |
57 | printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg) |
58 | ||
bd15eba3 | 59 | |
1da177e4 LT |
60 | /* ------------------------------------------------------------------ */ |
61 | ||
62 | static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) | |
63 | { | |
1da177e4 LT |
64 | if (blksize < 0x100) |
65 | blksize = 0x100; | |
66 | if (blksize > 0x10000) | |
67 | blksize = 0x10000; | |
68 | ||
69 | if (blocks < 2) | |
70 | blocks = 2; | |
1da177e4 LT |
71 | if ((blksize * blocks) > 1024*1024) |
72 | blocks = 1024*1024 / blksize; | |
73 | ||
b54134be RC |
74 | dev->dmasound.blocks = blocks; |
75 | dev->dmasound.blksize = blksize; | |
76 | dev->dmasound.bufsize = blksize * blocks; | |
1da177e4 LT |
77 | |
78 | dprintk("buffer config: %d blocks / %d bytes, %d kB total\n", | |
4ac97914 | 79 | blocks,blksize,blksize * blocks / 1024); |
1da177e4 LT |
80 | return 0; |
81 | } | |
82 | ||
83 | static int dsp_buffer_init(struct saa7134_dev *dev) | |
84 | { | |
85 | int err; | |
86 | ||
ae24601b | 87 | BUG_ON(!dev->dmasound.bufsize); |
b54134be RC |
88 | videobuf_dma_init(&dev->dmasound.dma); |
89 | err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE, | |
90 | (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); | |
1da177e4 LT |
91 | if (0 != err) |
92 | return err; | |
93 | return 0; | |
94 | } | |
95 | ||
96 | static int dsp_buffer_free(struct saa7134_dev *dev) | |
97 | { | |
ae24601b | 98 | BUG_ON(!dev->dmasound.blksize); |
b54134be RC |
99 | videobuf_dma_free(&dev->dmasound.dma); |
100 | dev->dmasound.blocks = 0; | |
101 | dev->dmasound.blksize = 0; | |
102 | dev->dmasound.bufsize = 0; | |
1da177e4 LT |
103 | return 0; |
104 | } | |
105 | ||
106 | static void dsp_dma_start(struct saa7134_dev *dev) | |
107 | { | |
b54134be RC |
108 | dev->dmasound.dma_blk = 0; |
109 | dev->dmasound.dma_running = 1; | |
1da177e4 LT |
110 | saa7134_set_dmabits(dev); |
111 | } | |
112 | ||
113 | static void dsp_dma_stop(struct saa7134_dev *dev) | |
114 | { | |
b54134be RC |
115 | dev->dmasound.dma_blk = -1; |
116 | dev->dmasound.dma_running = 0; | |
1da177e4 LT |
117 | saa7134_set_dmabits(dev); |
118 | } | |
119 | ||
120 | static int dsp_rec_start(struct saa7134_dev *dev) | |
121 | { | |
122 | int err, bswap, sign; | |
123 | u32 fmt, control; | |
124 | unsigned long flags; | |
125 | ||
126 | /* prepare buffer */ | |
c7b0ac05 | 127 | if (0 != (err = videobuf_pci_dma_map(dev->pci,&dev->dmasound.dma))) |
1da177e4 | 128 | return err; |
b54134be | 129 | if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) |
1da177e4 | 130 | goto fail1; |
b54134be RC |
131 | if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt, |
132 | dev->dmasound.dma.sglist, | |
133 | dev->dmasound.dma.sglen, | |
1da177e4 LT |
134 | 0))) |
135 | goto fail2; | |
136 | ||
137 | /* sample format */ | |
b54134be | 138 | switch (dev->dmasound.afmt) { |
1da177e4 LT |
139 | case AFMT_U8: |
140 | case AFMT_S8: fmt = 0x00; break; | |
141 | case AFMT_U16_LE: | |
142 | case AFMT_U16_BE: | |
143 | case AFMT_S16_LE: | |
144 | case AFMT_S16_BE: fmt = 0x01; break; | |
145 | default: | |
146 | err = -EINVAL; | |
147 | goto fail2; | |
148 | } | |
149 | ||
b54134be | 150 | switch (dev->dmasound.afmt) { |
1da177e4 LT |
151 | case AFMT_S8: |
152 | case AFMT_S16_LE: | |
153 | case AFMT_S16_BE: sign = 1; break; | |
154 | default: sign = 0; break; | |
155 | } | |
156 | ||
b54134be | 157 | switch (dev->dmasound.afmt) { |
1da177e4 LT |
158 | case AFMT_U16_BE: |
159 | case AFMT_S16_BE: bswap = 1; break; | |
160 | default: bswap = 0; break; | |
161 | } | |
162 | ||
163 | switch (dev->pci->device) { | |
164 | case PCI_DEVICE_ID_PHILIPS_SAA7134: | |
b54134be | 165 | if (1 == dev->dmasound.channels) |
1da177e4 | 166 | fmt |= (1 << 3); |
b54134be | 167 | if (2 == dev->dmasound.channels) |
1da177e4 LT |
168 | fmt |= (3 << 3); |
169 | if (sign) | |
170 | fmt |= 0x04; | |
b54134be | 171 | fmt |= (TV == dev->dmasound.input) ? 0xc0 : 0x80; |
1da177e4 | 172 | |
b54134be RC |
173 | saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff)); |
174 | saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >> 8); | |
175 | saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16); | |
1da177e4 | 176 | saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); |
ac19ecc6 | 177 | |
1da177e4 LT |
178 | break; |
179 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | |
180 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | |
b54134be | 181 | if (1 == dev->dmasound.channels) |
1da177e4 | 182 | fmt |= (1 << 4); |
b54134be | 183 | if (2 == dev->dmasound.channels) |
1da177e4 LT |
184 | fmt |= (2 << 4); |
185 | if (!sign) | |
186 | fmt |= 0x04; | |
b54134be | 187 | saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -4); |
bd15eba3 | 188 | saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24)); |
1da177e4 LT |
189 | break; |
190 | } | |
191 | dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", | |
b54134be | 192 | dev->dmasound.afmt, dev->dmasound.channels, fmt, |
1da177e4 LT |
193 | bswap ? 'b' : '-'); |
194 | ||
195 | /* dma: setup channel 6 (= AUDIO) */ | |
196 | control = SAA7134_RS_CONTROL_BURST_16 | | |
197 | SAA7134_RS_CONTROL_ME | | |
b54134be | 198 | (dev->dmasound.pt.dma >> 12); |
1da177e4 LT |
199 | if (bswap) |
200 | control |= SAA7134_RS_CONTROL_BSWAP; | |
201 | saa_writel(SAA7134_RS_BA1(6),0); | |
b54134be | 202 | saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize); |
1da177e4 LT |
203 | saa_writel(SAA7134_RS_PITCH(6),0); |
204 | saa_writel(SAA7134_RS_CONTROL(6),control); | |
205 | ||
206 | /* start dma */ | |
b54134be | 207 | dev->dmasound.recording_on = 1; |
1da177e4 LT |
208 | spin_lock_irqsave(&dev->slock,flags); |
209 | dsp_dma_start(dev); | |
210 | spin_unlock_irqrestore(&dev->slock,flags); | |
211 | return 0; | |
212 | ||
213 | fail2: | |
b54134be | 214 | saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); |
1da177e4 | 215 | fail1: |
c7b0ac05 | 216 | videobuf_pci_dma_unmap(dev->pci,&dev->dmasound.dma); |
1da177e4 LT |
217 | return err; |
218 | } | |
219 | ||
220 | static int dsp_rec_stop(struct saa7134_dev *dev) | |
221 | { | |
222 | unsigned long flags; | |
223 | ||
b54134be | 224 | dprintk("rec_stop dma_blk=%d\n",dev->dmasound.dma_blk); |
1da177e4 LT |
225 | |
226 | /* stop dma */ | |
b54134be | 227 | dev->dmasound.recording_on = 0; |
1da177e4 LT |
228 | spin_lock_irqsave(&dev->slock,flags); |
229 | dsp_dma_stop(dev); | |
230 | spin_unlock_irqrestore(&dev->slock,flags); | |
231 | ||
232 | /* unlock buffer */ | |
b54134be | 233 | saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); |
c7b0ac05 | 234 | videobuf_pci_dma_unmap(dev->pci,&dev->dmasound.dma); |
1da177e4 LT |
235 | return 0; |
236 | } | |
237 | ||
238 | /* ------------------------------------------------------------------ */ | |
239 | ||
240 | static int dsp_open(struct inode *inode, struct file *file) | |
241 | { | |
242 | int minor = iminor(inode); | |
243 | struct saa7134_dev *h,*dev = NULL; | |
244 | struct list_head *list; | |
245 | int err; | |
246 | ||
247 | list_for_each(list,&saa7134_devlist) { | |
248 | h = list_entry(list, struct saa7134_dev, devlist); | |
b54134be | 249 | if (h->dmasound.minor_dsp == minor) |
1da177e4 LT |
250 | dev = h; |
251 | } | |
252 | if (NULL == dev) | |
253 | return -ENODEV; | |
254 | ||
3593cab5 | 255 | mutex_lock(&dev->dmasound.lock); |
1da177e4 | 256 | err = -EBUSY; |
b54134be | 257 | if (dev->dmasound.users_dsp) |
1da177e4 | 258 | goto fail1; |
b54134be | 259 | dev->dmasound.users_dsp++; |
1da177e4 LT |
260 | file->private_data = dev; |
261 | ||
b54134be RC |
262 | dev->dmasound.afmt = AFMT_U8; |
263 | dev->dmasound.channels = 1; | |
264 | dev->dmasound.read_count = 0; | |
265 | dev->dmasound.read_offset = 0; | |
1da177e4 LT |
266 | dsp_buffer_conf(dev,PAGE_SIZE,64); |
267 | err = dsp_buffer_init(dev); | |
268 | if (0 != err) | |
269 | goto fail2; | |
270 | ||
3593cab5 | 271 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 LT |
272 | return 0; |
273 | ||
274 | fail2: | |
b54134be | 275 | dev->dmasound.users_dsp--; |
1da177e4 | 276 | fail1: |
3593cab5 | 277 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 LT |
278 | return err; |
279 | } | |
280 | ||
281 | static int dsp_release(struct inode *inode, struct file *file) | |
282 | { | |
283 | struct saa7134_dev *dev = file->private_data; | |
284 | ||
3593cab5 | 285 | mutex_lock(&dev->dmasound.lock); |
b54134be | 286 | if (dev->dmasound.recording_on) |
1da177e4 LT |
287 | dsp_rec_stop(dev); |
288 | dsp_buffer_free(dev); | |
b54134be | 289 | dev->dmasound.users_dsp--; |
1da177e4 | 290 | file->private_data = NULL; |
3593cab5 | 291 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 LT |
292 | return 0; |
293 | } | |
294 | ||
295 | static ssize_t dsp_read(struct file *file, char __user *buffer, | |
296 | size_t count, loff_t *ppos) | |
297 | { | |
298 | struct saa7134_dev *dev = file->private_data; | |
299 | DECLARE_WAITQUEUE(wait, current); | |
300 | unsigned int bytes; | |
301 | unsigned long flags; | |
302 | int err,ret = 0; | |
303 | ||
b54134be | 304 | add_wait_queue(&dev->dmasound.wq, &wait); |
3593cab5 | 305 | mutex_lock(&dev->dmasound.lock); |
1da177e4 LT |
306 | while (count > 0) { |
307 | /* wait for data if needed */ | |
b54134be RC |
308 | if (0 == dev->dmasound.read_count) { |
309 | if (!dev->dmasound.recording_on) { | |
1da177e4 LT |
310 | err = dsp_rec_start(dev); |
311 | if (err < 0) { | |
312 | if (0 == ret) | |
313 | ret = err; | |
314 | break; | |
315 | } | |
316 | } | |
b54134be RC |
317 | if (dev->dmasound.recording_on && |
318 | !dev->dmasound.dma_running) { | |
1da177e4 LT |
319 | /* recover from overruns */ |
320 | spin_lock_irqsave(&dev->slock,flags); | |
321 | dsp_dma_start(dev); | |
322 | spin_unlock_irqrestore(&dev->slock,flags); | |
323 | } | |
324 | if (file->f_flags & O_NONBLOCK) { | |
325 | if (0 == ret) | |
326 | ret = -EAGAIN; | |
327 | break; | |
328 | } | |
3593cab5 | 329 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 | 330 | set_current_state(TASK_INTERRUPTIBLE); |
b54134be | 331 | if (0 == dev->dmasound.read_count) |
1da177e4 LT |
332 | schedule(); |
333 | set_current_state(TASK_RUNNING); | |
3593cab5 | 334 | mutex_lock(&dev->dmasound.lock); |
1da177e4 LT |
335 | if (signal_pending(current)) { |
336 | if (0 == ret) | |
337 | ret = -EINTR; | |
338 | break; | |
339 | } | |
340 | } | |
341 | ||
342 | /* copy data to userspace */ | |
343 | bytes = count; | |
b54134be RC |
344 | if (bytes > dev->dmasound.read_count) |
345 | bytes = dev->dmasound.read_count; | |
346 | if (bytes > dev->dmasound.bufsize - dev->dmasound.read_offset) | |
347 | bytes = dev->dmasound.bufsize - dev->dmasound.read_offset; | |
1da177e4 | 348 | if (copy_to_user(buffer + ret, |
b54134be | 349 | dev->dmasound.dma.vmalloc + dev->dmasound.read_offset, |
1da177e4 LT |
350 | bytes)) { |
351 | if (0 == ret) | |
352 | ret = -EFAULT; | |
353 | break; | |
354 | } | |
355 | ||
356 | ret += bytes; | |
357 | count -= bytes; | |
b54134be RC |
358 | dev->dmasound.read_count -= bytes; |
359 | dev->dmasound.read_offset += bytes; | |
360 | if (dev->dmasound.read_offset == dev->dmasound.bufsize) | |
361 | dev->dmasound.read_offset = 0; | |
1da177e4 | 362 | } |
3593cab5 | 363 | mutex_unlock(&dev->dmasound.lock); |
b54134be | 364 | remove_wait_queue(&dev->dmasound.wq, &wait); |
1da177e4 LT |
365 | return ret; |
366 | } | |
367 | ||
368 | static ssize_t dsp_write(struct file *file, const char __user *buffer, | |
369 | size_t count, loff_t *ppos) | |
370 | { | |
371 | return -EINVAL; | |
372 | } | |
373 | ||
5e453dc7 MK |
374 | static const char *osspcm_ioctls[] = { |
375 | "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT", | |
376 | "CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS", | |
377 | "GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER", | |
378 | "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO", | |
379 | "SETDUPLEX", "GETODELAY" | |
380 | }; | |
381 | #define OSSPCM_IOCTLS ARRAY_SIZE(osspcm_ioctls) | |
382 | ||
383 | static void saa7134_oss_print_ioctl(char *name, unsigned int cmd) | |
384 | { | |
385 | char *dir; | |
386 | ||
387 | switch (_IOC_DIR(cmd)) { | |
388 | case _IOC_NONE: dir = "--"; break; | |
389 | case _IOC_READ: dir = "r-"; break; | |
390 | case _IOC_WRITE: dir = "-w"; break; | |
391 | case _IOC_READ | _IOC_WRITE: dir = "rw"; break; | |
392 | default: dir = "??"; break; | |
393 | } | |
394 | switch (_IOC_TYPE(cmd)) { | |
395 | case 'P': | |
396 | printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n", | |
397 | name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ? | |
398 | osspcm_ioctls[_IOC_NR(cmd)] : "???"); | |
399 | break; | |
400 | case 'M': | |
401 | printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n", | |
402 | name, cmd, dir, _IOC_NR(cmd)); | |
403 | break; | |
404 | default: | |
405 | printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", | |
406 | name, cmd, dir, _IOC_NR(cmd)); | |
407 | } | |
408 | } | |
409 | ||
1da177e4 LT |
410 | static int dsp_ioctl(struct inode *inode, struct file *file, |
411 | unsigned int cmd, unsigned long arg) | |
412 | { | |
413 | struct saa7134_dev *dev = file->private_data; | |
414 | void __user *argp = (void __user *) arg; | |
415 | int __user *p = argp; | |
416 | int val = 0; | |
417 | ||
4aabf633 | 418 | if (debug > 1) |
5e453dc7 | 419 | saa7134_oss_print_ioctl(dev->name,cmd); |
4ac97914 MCC |
420 | switch (cmd) { |
421 | case OSS_GETVERSION: | |
422 | return put_user(SOUND_VERSION, p); | |
423 | case SNDCTL_DSP_GETCAPS: | |
1da177e4 LT |
424 | return 0; |
425 | ||
4ac97914 | 426 | case SNDCTL_DSP_SPEED: |
1da177e4 LT |
427 | if (get_user(val, p)) |
428 | return -EFAULT; | |
429 | /* fall through */ | |
4ac97914 | 430 | case SOUND_PCM_READ_RATE: |
b54134be | 431 | return put_user(dev->dmasound.rate, p); |
1da177e4 | 432 | |
4ac97914 | 433 | case SNDCTL_DSP_STEREO: |
1da177e4 LT |
434 | if (get_user(val, p)) |
435 | return -EFAULT; | |
3593cab5 | 436 | mutex_lock(&dev->dmasound.lock); |
b54134be RC |
437 | dev->dmasound.channels = val ? 2 : 1; |
438 | if (dev->dmasound.recording_on) { | |
1da177e4 LT |
439 | dsp_rec_stop(dev); |
440 | dsp_rec_start(dev); | |
441 | } | |
3593cab5 | 442 | mutex_unlock(&dev->dmasound.lock); |
b54134be | 443 | return put_user(dev->dmasound.channels-1, p); |
1da177e4 | 444 | |
4ac97914 | 445 | case SNDCTL_DSP_CHANNELS: |
1da177e4 LT |
446 | if (get_user(val, p)) |
447 | return -EFAULT; | |
448 | if (val != 1 && val != 2) | |
449 | return -EINVAL; | |
3593cab5 | 450 | mutex_lock(&dev->dmasound.lock); |
b54134be RC |
451 | dev->dmasound.channels = val; |
452 | if (dev->dmasound.recording_on) { | |
1da177e4 LT |
453 | dsp_rec_stop(dev); |
454 | dsp_rec_start(dev); | |
455 | } | |
3593cab5 | 456 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 | 457 | /* fall through */ |
4ac97914 | 458 | case SOUND_PCM_READ_CHANNELS: |
b54134be | 459 | return put_user(dev->dmasound.channels, p); |
1da177e4 | 460 | |
4ac97914 | 461 | case SNDCTL_DSP_GETFMTS: /* Returns a mask */ |
1da177e4 LT |
462 | return put_user(AFMT_U8 | AFMT_S8 | |
463 | AFMT_U16_LE | AFMT_U16_BE | | |
464 | AFMT_S16_LE | AFMT_S16_BE, p); | |
465 | ||
4ac97914 | 466 | case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ |
1da177e4 LT |
467 | if (get_user(val, p)) |
468 | return -EFAULT; | |
469 | switch (val) { | |
470 | case AFMT_QUERY: | |
471 | /* nothing to do */ | |
472 | break; | |
473 | case AFMT_U8: | |
474 | case AFMT_S8: | |
475 | case AFMT_U16_LE: | |
476 | case AFMT_U16_BE: | |
477 | case AFMT_S16_LE: | |
478 | case AFMT_S16_BE: | |
3593cab5 | 479 | mutex_lock(&dev->dmasound.lock); |
b54134be RC |
480 | dev->dmasound.afmt = val; |
481 | if (dev->dmasound.recording_on) { | |
1da177e4 LT |
482 | dsp_rec_stop(dev); |
483 | dsp_rec_start(dev); | |
484 | } | |
3593cab5 | 485 | mutex_unlock(&dev->dmasound.lock); |
b54134be | 486 | return put_user(dev->dmasound.afmt, p); |
1da177e4 LT |
487 | default: |
488 | return -EINVAL; | |
489 | } | |
490 | ||
4ac97914 | 491 | case SOUND_PCM_READ_BITS: |
b54134be | 492 | switch (dev->dmasound.afmt) { |
1da177e4 LT |
493 | case AFMT_U8: |
494 | case AFMT_S8: | |
495 | return put_user(8, p); | |
496 | case AFMT_U16_LE: | |
497 | case AFMT_U16_BE: | |
498 | case AFMT_S16_LE: | |
499 | case AFMT_S16_BE: | |
500 | return put_user(16, p); | |
501 | default: | |
502 | return -EINVAL; | |
503 | } | |
504 | ||
4ac97914 MCC |
505 | case SNDCTL_DSP_NONBLOCK: |
506 | file->f_flags |= O_NONBLOCK; | |
507 | return 0; | |
1da177e4 | 508 | |
4ac97914 | 509 | case SNDCTL_DSP_RESET: |
3593cab5 | 510 | mutex_lock(&dev->dmasound.lock); |
b54134be | 511 | if (dev->dmasound.recording_on) |
1da177e4 | 512 | dsp_rec_stop(dev); |
3593cab5 | 513 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 | 514 | return 0; |
4ac97914 | 515 | case SNDCTL_DSP_GETBLKSIZE: |
b54134be | 516 | return put_user(dev->dmasound.blksize, p); |
1da177e4 | 517 | |
4ac97914 | 518 | case SNDCTL_DSP_SETFRAGMENT: |
1da177e4 LT |
519 | if (get_user(val, p)) |
520 | return -EFAULT; | |
b54134be | 521 | if (dev->dmasound.recording_on) |
1da177e4 LT |
522 | return -EBUSY; |
523 | dsp_buffer_free(dev); | |
524 | /* used to be arg >> 16 instead of val >> 16; fixed */ | |
525 | dsp_buffer_conf(dev,1 << (val & 0xffff), (val >> 16) & 0xffff); | |
526 | dsp_buffer_init(dev); | |
527 | return 0; | |
528 | ||
4ac97914 | 529 | case SNDCTL_DSP_SYNC: |
1da177e4 LT |
530 | /* NOP */ |
531 | return 0; | |
532 | ||
533 | case SNDCTL_DSP_GETISPACE: | |
534 | { | |
535 | audio_buf_info info; | |
b54134be RC |
536 | info.fragsize = dev->dmasound.blksize; |
537 | info.fragstotal = dev->dmasound.blocks; | |
538 | info.bytes = dev->dmasound.read_count; | |
1da177e4 LT |
539 | info.fragments = info.bytes / info.fragsize; |
540 | if (copy_to_user(argp, &info, sizeof(info))) | |
541 | return -EFAULT; | |
542 | return 0; | |
543 | } | |
544 | default: | |
545 | return -EINVAL; | |
546 | } | |
547 | } | |
548 | ||
549 | static unsigned int dsp_poll(struct file *file, struct poll_table_struct *wait) | |
550 | { | |
551 | struct saa7134_dev *dev = file->private_data; | |
552 | unsigned int mask = 0; | |
553 | ||
b54134be | 554 | poll_wait(file, &dev->dmasound.wq, wait); |
1da177e4 | 555 | |
b54134be | 556 | if (0 == dev->dmasound.read_count) { |
3593cab5 | 557 | mutex_lock(&dev->dmasound.lock); |
b54134be | 558 | if (!dev->dmasound.recording_on) |
1da177e4 | 559 | dsp_rec_start(dev); |
3593cab5 | 560 | mutex_unlock(&dev->dmasound.lock); |
1da177e4 LT |
561 | } else |
562 | mask |= (POLLIN | POLLRDNORM); | |
563 | return mask; | |
564 | } | |
565 | ||
566 | struct file_operations saa7134_dsp_fops = { | |
567 | .owner = THIS_MODULE, | |
568 | .open = dsp_open, | |
569 | .release = dsp_release, | |
570 | .read = dsp_read, | |
571 | .write = dsp_write, | |
572 | .ioctl = dsp_ioctl, | |
573 | .poll = dsp_poll, | |
574 | .llseek = no_llseek, | |
575 | }; | |
576 | ||
577 | /* ------------------------------------------------------------------ */ | |
578 | ||
579 | static int | |
580 | mixer_recsrc_7134(struct saa7134_dev *dev) | |
581 | { | |
582 | int analog_io,rate; | |
583 | ||
b54134be | 584 | switch (dev->dmasound.input) { |
1da177e4 LT |
585 | case TV: |
586 | saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0); | |
587 | saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); | |
588 | break; | |
589 | case LINE1: | |
590 | case LINE2: | |
591 | case LINE2_LEFT: | |
b54134be RC |
592 | analog_io = (LINE1 == dev->dmasound.input) ? 0x00 : 0x08; |
593 | rate = (32000 == dev->dmasound.rate) ? 0x01 : 0x03; | |
1da177e4 LT |
594 | saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io); |
595 | saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80); | |
596 | saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); | |
597 | break; | |
598 | } | |
599 | return 0; | |
600 | } | |
601 | ||
602 | static int | |
603 | mixer_recsrc_7133(struct saa7134_dev *dev) | |
604 | { | |
330a115a | 605 | u32 anabar, xbarin; |
1da177e4 | 606 | |
330a115a MCC |
607 | xbarin = 0x03; // adc |
608 | anabar = 0; | |
b54134be | 609 | switch (dev->dmasound.input) { |
1da177e4 | 610 | case TV: |
330a115a | 611 | xbarin = 0; // Demodulator |
4ac97914 | 612 | anabar = 2; // DACs |
1da177e4 LT |
613 | break; |
614 | case LINE1: | |
330a115a | 615 | anabar = 0; // aux1, aux1 |
1da177e4 LT |
616 | break; |
617 | case LINE2: | |
618 | case LINE2_LEFT: | |
330a115a | 619 | anabar = 9; // aux2, aux2 |
1da177e4 LT |
620 | break; |
621 | } | |
330a115a MCC |
622 | /* output xbar always main channel */ |
623 | saa_dsp_writel(dev, 0x46c >> 2, 0xbbbb10); | |
624 | saa_dsp_writel(dev, 0x464 >> 2, xbarin); | |
625 | saa_writel(0x594 >> 2, anabar); | |
626 | ||
1da177e4 LT |
627 | return 0; |
628 | } | |
629 | ||
630 | static int | |
631 | mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src) | |
632 | { | |
633 | static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" }; | |
634 | ||
b54134be RC |
635 | dev->dmasound.count++; |
636 | dev->dmasound.input = src; | |
637 | dprintk("mixer input = %s\n",iname[dev->dmasound.input]); | |
1da177e4 LT |
638 | |
639 | switch (dev->pci->device) { | |
640 | case PCI_DEVICE_ID_PHILIPS_SAA7134: | |
641 | mixer_recsrc_7134(dev); | |
642 | break; | |
643 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | |
644 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | |
645 | mixer_recsrc_7133(dev); | |
646 | break; | |
647 | } | |
648 | return 0; | |
649 | } | |
650 | ||
651 | static int | |
652 | mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level) | |
653 | { | |
654 | switch (dev->pci->device) { | |
655 | case PCI_DEVICE_ID_PHILIPS_SAA7134: | |
656 | switch (src) { | |
657 | case TV: | |
658 | /* nothing */ | |
659 | break; | |
660 | case LINE1: | |
661 | saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, | |
662 | (100 == level) ? 0x00 : 0x10); | |
663 | break; | |
664 | case LINE2: | |
665 | case LINE2_LEFT: | |
666 | saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, | |
667 | (100 == level) ? 0x00 : 0x20); | |
668 | break; | |
669 | } | |
670 | break; | |
671 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | |
672 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | |
673 | /* nothing */ | |
674 | break; | |
675 | } | |
676 | return 0; | |
677 | } | |
678 | ||
679 | /* ------------------------------------------------------------------ */ | |
680 | ||
681 | static int mixer_open(struct inode *inode, struct file *file) | |
682 | { | |
683 | int minor = iminor(inode); | |
684 | struct saa7134_dev *h,*dev = NULL; | |
685 | struct list_head *list; | |
686 | ||
687 | list_for_each(list,&saa7134_devlist) { | |
688 | h = list_entry(list, struct saa7134_dev, devlist); | |
b54134be | 689 | if (h->dmasound.minor_mixer == minor) |
1da177e4 LT |
690 | dev = h; |
691 | } | |
692 | if (NULL == dev) | |
693 | return -ENODEV; | |
694 | ||
695 | file->private_data = dev; | |
696 | return 0; | |
697 | } | |
698 | ||
699 | static int mixer_release(struct inode *inode, struct file *file) | |
700 | { | |
701 | file->private_data = NULL; | |
702 | return 0; | |
703 | } | |
704 | ||
705 | static int mixer_ioctl(struct inode *inode, struct file *file, | |
706 | unsigned int cmd, unsigned long arg) | |
707 | { | |
708 | struct saa7134_dev *dev = file->private_data; | |
709 | enum saa7134_audio_in input; | |
710 | int val,ret; | |
711 | void __user *argp = (void __user *) arg; | |
712 | int __user *p = argp; | |
713 | ||
4aabf633 | 714 | if (debug > 1) |
5e453dc7 | 715 | saa7134_oss_print_ioctl(dev->name,cmd); |
4ac97914 MCC |
716 | switch (cmd) { |
717 | case OSS_GETVERSION: | |
718 | return put_user(SOUND_VERSION, p); | |
1da177e4 LT |
719 | case SOUND_MIXER_INFO: |
720 | { | |
721 | mixer_info info; | |
722 | memset(&info,0,sizeof(info)); | |
4ac97914 MCC |
723 | strlcpy(info.id, "TV audio", sizeof(info.id)); |
724 | strlcpy(info.name, dev->name, sizeof(info.name)); | |
b54134be | 725 | info.modify_counter = dev->dmasound.count; |
4ac97914 MCC |
726 | if (copy_to_user(argp, &info, sizeof(info))) |
727 | return -EFAULT; | |
1da177e4 LT |
728 | return 0; |
729 | } | |
730 | case SOUND_OLD_MIXER_INFO: | |
731 | { | |
732 | _old_mixer_info info; | |
733 | memset(&info,0,sizeof(info)); | |
4ac97914 MCC |
734 | strlcpy(info.id, "TV audio", sizeof(info.id)); |
735 | strlcpy(info.name, dev->name, sizeof(info.name)); | |
736 | if (copy_to_user(argp, &info, sizeof(info))) | |
737 | return -EFAULT; | |
1da177e4 LT |
738 | return 0; |
739 | } | |
740 | case MIXER_READ(SOUND_MIXER_CAPS): | |
741 | return put_user(SOUND_CAP_EXCL_INPUT, p); | |
742 | case MIXER_READ(SOUND_MIXER_STEREODEVS): | |
743 | return put_user(0, p); | |
744 | case MIXER_READ(SOUND_MIXER_RECMASK): | |
745 | case MIXER_READ(SOUND_MIXER_DEVMASK): | |
746 | val = SOUND_MASK_LINE1 | SOUND_MASK_LINE2; | |
b54134be | 747 | if (32000 == dev->dmasound.rate) |
1da177e4 LT |
748 | val |= SOUND_MASK_VIDEO; |
749 | return put_user(val, p); | |
750 | ||
751 | case MIXER_WRITE(SOUND_MIXER_RECSRC): | |
752 | if (get_user(val, p)) | |
753 | return -EFAULT; | |
b54134be RC |
754 | input = dev->dmasound.input; |
755 | if (32000 == dev->dmasound.rate && | |
756 | val & SOUND_MASK_VIDEO && dev->dmasound.input != TV) | |
1da177e4 | 757 | input = TV; |
b54134be | 758 | if (val & SOUND_MASK_LINE1 && dev->dmasound.input != LINE1) |
1da177e4 | 759 | input = LINE1; |
b54134be | 760 | if (val & SOUND_MASK_LINE2 && dev->dmasound.input != LINE2) |
1da177e4 | 761 | input = LINE2; |
b54134be | 762 | if (input != dev->dmasound.input) |
1da177e4 LT |
763 | mixer_recsrc(dev,input); |
764 | /* fall throuth */ | |
765 | case MIXER_READ(SOUND_MIXER_RECSRC): | |
b54134be | 766 | switch (dev->dmasound.input) { |
1da177e4 LT |
767 | case TV: ret = SOUND_MASK_VIDEO; break; |
768 | case LINE1: ret = SOUND_MASK_LINE1; break; | |
769 | case LINE2: ret = SOUND_MASK_LINE2; break; | |
770 | default: ret = 0; | |
771 | } | |
772 | return put_user(ret, p); | |
773 | ||
774 | case MIXER_WRITE(SOUND_MIXER_VIDEO): | |
775 | case MIXER_READ(SOUND_MIXER_VIDEO): | |
b54134be | 776 | if (32000 != dev->dmasound.rate) |
1da177e4 LT |
777 | return -EINVAL; |
778 | return put_user(100 | 100 << 8, p); | |
779 | ||
780 | case MIXER_WRITE(SOUND_MIXER_LINE1): | |
781 | if (get_user(val, p)) | |
782 | return -EFAULT; | |
783 | val &= 0xff; | |
784 | val = (val <= 50) ? 50 : 100; | |
b54134be RC |
785 | dev->dmasound.line1 = val; |
786 | mixer_level(dev,LINE1,dev->dmasound.line1); | |
1da177e4 LT |
787 | /* fall throuth */ |
788 | case MIXER_READ(SOUND_MIXER_LINE1): | |
b54134be | 789 | return put_user(dev->dmasound.line1 | dev->dmasound.line1 << 8, p); |
1da177e4 LT |
790 | |
791 | case MIXER_WRITE(SOUND_MIXER_LINE2): | |
792 | if (get_user(val, p)) | |
793 | return -EFAULT; | |
794 | val &= 0xff; | |
795 | val = (val <= 50) ? 50 : 100; | |
b54134be RC |
796 | dev->dmasound.line2 = val; |
797 | mixer_level(dev,LINE2,dev->dmasound.line2); | |
1da177e4 LT |
798 | /* fall throuth */ |
799 | case MIXER_READ(SOUND_MIXER_LINE2): | |
b54134be | 800 | return put_user(dev->dmasound.line2 | dev->dmasound.line2 << 8, p); |
1da177e4 LT |
801 | |
802 | default: | |
803 | return -EINVAL; | |
804 | } | |
805 | } | |
806 | ||
807 | struct file_operations saa7134_mixer_fops = { | |
808 | .owner = THIS_MODULE, | |
809 | .open = mixer_open, | |
810 | .release = mixer_release, | |
811 | .ioctl = mixer_ioctl, | |
812 | .llseek = no_llseek, | |
813 | }; | |
814 | ||
815 | /* ------------------------------------------------------------------ */ | |
816 | ||
7d12e780 | 817 | static irqreturn_t saa7134_oss_irq(int irq, void *dev_id) |
4aabf633 | 818 | { |
afd1a0c9 MCC |
819 | struct saa7134_dmasound *dmasound = dev_id; |
820 | struct saa7134_dev *dev = dmasound->priv_data; | |
821 | unsigned long report, status; | |
822 | int loop, handled = 0; | |
823 | ||
824 | for (loop = 0; loop < 10; loop++) { | |
825 | report = saa_readl(SAA7134_IRQ_REPORT); | |
826 | status = saa_readl(SAA7134_IRQ_STATUS); | |
827 | ||
828 | if (report & SAA7134_IRQ_REPORT_DONE_RA3) { | |
674434c6 MCC |
829 | handled = 1; |
830 | saa_writel(SAA7134_IRQ_REPORT,report); | |
831 | saa7134_irq_oss_done(dev, status); | |
afd1a0c9 | 832 | } else { |
674434c6 | 833 | goto out; |
afd1a0c9 MCC |
834 | } |
835 | } | |
836 | ||
837 | if (loop == 10) { | |
838 | dprintk("error! looping IRQ!"); | |
839 | } | |
4aabf633 | 840 | out: |
afd1a0c9 | 841 | return IRQ_RETVAL(handled); |
4aabf633 RC |
842 | } |
843 | ||
1da177e4 LT |
844 | int saa7134_oss_init1(struct saa7134_dev *dev) |
845 | { | |
4aabf633 | 846 | |
afd1a0c9 | 847 | if ((request_irq(dev->pci->irq, saa7134_oss_irq, |
8076fe32 | 848 | IRQF_SHARED | IRQF_DISABLED, dev->name, |
4aabf633 RC |
849 | (void*) &dev->dmasound)) < 0) |
850 | return -1; | |
851 | ||
1da177e4 | 852 | /* general */ |
3593cab5 | 853 | mutex_init(&dev->dmasound.lock); |
b54134be | 854 | init_waitqueue_head(&dev->dmasound.wq); |
1da177e4 LT |
855 | |
856 | switch (dev->pci->device) { | |
857 | case PCI_DEVICE_ID_PHILIPS_SAA7133: | |
858 | case PCI_DEVICE_ID_PHILIPS_SAA7135: | |
859 | saa_writel(0x588 >> 2, 0x00000fff); | |
860 | saa_writel(0x58c >> 2, 0x00543210); | |
861 | saa_dsp_writel(dev, 0x46c >> 2, 0xbbbbbb); | |
862 | break; | |
863 | } | |
864 | ||
865 | /* dsp */ | |
b54134be | 866 | dev->dmasound.rate = 32000; |
4aabf633 RC |
867 | if (rate) |
868 | dev->dmasound.rate = rate; | |
b54134be | 869 | dev->dmasound.rate = (dev->dmasound.rate > 40000) ? 48000 : 32000; |
1da177e4 LT |
870 | |
871 | /* mixer */ | |
b54134be RC |
872 | dev->dmasound.line1 = 50; |
873 | dev->dmasound.line2 = 50; | |
874 | mixer_level(dev,LINE1,dev->dmasound.line1); | |
875 | mixer_level(dev,LINE2,dev->dmasound.line2); | |
876 | mixer_recsrc(dev, (dev->dmasound.rate == 32000) ? TV : LINE2); | |
1da177e4 LT |
877 | |
878 | return 0; | |
879 | } | |
880 | ||
881 | int saa7134_oss_fini(struct saa7134_dev *dev) | |
882 | { | |
883 | /* nothing */ | |
884 | return 0; | |
885 | } | |
886 | ||
887 | void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status) | |
888 | { | |
889 | int next_blk, reg = 0; | |
890 | ||
891 | spin_lock(&dev->slock); | |
b54134be | 892 | if (UNSET == dev->dmasound.dma_blk) { |
1da177e4 LT |
893 | dprintk("irq: recording stopped\n"); |
894 | goto done; | |
895 | } | |
896 | if (0 != (status & 0x0f000000)) | |
897 | dprintk("irq: lost %ld\n", (status >> 24) & 0x0f); | |
898 | if (0 == (status & 0x10000000)) { | |
899 | /* odd */ | |
b54134be | 900 | if (0 == (dev->dmasound.dma_blk & 0x01)) |
1da177e4 LT |
901 | reg = SAA7134_RS_BA1(6); |
902 | } else { | |
903 | /* even */ | |
b54134be | 904 | if (1 == (dev->dmasound.dma_blk & 0x01)) |
1da177e4 LT |
905 | reg = SAA7134_RS_BA2(6); |
906 | } | |
907 | if (0 == reg) { | |
908 | dprintk("irq: field oops [%s]\n", | |
909 | (status & 0x10000000) ? "even" : "odd"); | |
910 | goto done; | |
911 | } | |
b54134be RC |
912 | if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) { |
913 | dprintk("irq: overrun [full=%d/%d]\n",dev->dmasound.read_count, | |
914 | dev->dmasound.bufsize); | |
1da177e4 LT |
915 | dsp_dma_stop(dev); |
916 | goto done; | |
917 | } | |
918 | ||
919 | /* next block addr */ | |
b54134be RC |
920 | next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks; |
921 | saa_writel(reg,next_blk * dev->dmasound.blksize); | |
4aabf633 | 922 | if (debug > 2) |
1da177e4 LT |
923 | dprintk("irq: ok, %s, next_blk=%d, addr=%x\n", |
924 | (status & 0x10000000) ? "even" : "odd ", next_blk, | |
b54134be | 925 | next_blk * dev->dmasound.blksize); |
1da177e4 LT |
926 | |
927 | /* update status & wake waiting readers */ | |
b54134be RC |
928 | dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks; |
929 | dev->dmasound.read_count += dev->dmasound.blksize; | |
930 | wake_up(&dev->dmasound.wq); | |
1da177e4 LT |
931 | |
932 | done: | |
933 | spin_unlock(&dev->slock); | |
934 | } | |
935 | ||
943a4902 | 936 | static int saa7134_dsp_create(struct saa7134_dev *dev) |
4aabf633 RC |
937 | { |
938 | int err; | |
939 | ||
f5b974cb RC |
940 | err = dev->dmasound.minor_dsp = |
941 | register_sound_dsp(&saa7134_dsp_fops, | |
674434c6 | 942 | dsp_nr[dev->nr]); |
f5b974cb RC |
943 | if (err < 0) { |
944 | goto fail; | |
945 | } | |
946 | printk(KERN_INFO "%s: registered device dsp%d\n", | |
674434c6 | 947 | dev->name,dev->dmasound.minor_dsp >> 4); |
f5b974cb RC |
948 | |
949 | err = dev->dmasound.minor_mixer = | |
950 | register_sound_mixer(&saa7134_mixer_fops, | |
674434c6 | 951 | mixer_nr[dev->nr]); |
f5b974cb RC |
952 | if (err < 0) |
953 | goto fail; | |
954 | printk(KERN_INFO "%s: registered device mixer%d\n", | |
674434c6 | 955 | dev->name,dev->dmasound.minor_mixer >> 4); |
4aabf633 RC |
956 | |
957 | return 0; | |
958 | ||
959 | fail: | |
afd1a0c9 | 960 | unregister_sound_dsp(dev->dmasound.minor_dsp); |
4aabf633 RC |
961 | return 0; |
962 | ||
963 | ||
964 | } | |
965 | ||
f5b974cb RC |
966 | static int oss_device_init(struct saa7134_dev *dev) |
967 | { | |
968 | dev->dmasound.priv_data = dev; | |
969 | saa7134_oss_init1(dev); | |
970 | saa7134_dsp_create(dev); | |
971 | return 1; | |
972 | } | |
973 | ||
974 | static int oss_device_exit(struct saa7134_dev *dev) | |
975 | { | |
976 | ||
977 | unregister_sound_mixer(dev->dmasound.minor_mixer); | |
978 | unregister_sound_dsp(dev->dmasound.minor_dsp); | |
979 | ||
980 | saa7134_oss_fini(dev); | |
981 | ||
982 | if (dev->pci->irq > 0) { | |
983 | synchronize_irq(dev->pci->irq); | |
984 | free_irq(dev->pci->irq,&dev->dmasound); | |
985 | } | |
986 | ||
987 | dev->dmasound.priv_data = NULL; | |
988 | return 1; | |
989 | } | |
990 | ||
4aabf633 RC |
991 | static int saa7134_oss_init(void) |
992 | { | |
afd1a0c9 MCC |
993 | struct saa7134_dev *dev = NULL; |
994 | struct list_head *list; | |
4aabf633 | 995 | |
78457018 AB |
996 | if (!saa7134_dmasound_init && !saa7134_dmasound_exit) { |
997 | saa7134_dmasound_init = oss_device_init; | |
998 | saa7134_dmasound_exit = oss_device_exit; | |
7bb95296 RC |
999 | } else { |
1000 | printk(KERN_WARNING "saa7134 OSS: can't load, DMA sound handler already assigned (probably to ALSA)\n"); | |
1001 | return -EBUSY; | |
1002 | } | |
1003 | ||
afd1a0c9 | 1004 | printk(KERN_INFO "saa7134 OSS driver for DMA sound loaded\n"); |
4aabf633 | 1005 | |
7bb95296 | 1006 | |
afd1a0c9 MCC |
1007 | list_for_each(list,&saa7134_devlist) { |
1008 | dev = list_entry(list, struct saa7134_dev, devlist); | |
4aabf633 | 1009 | if (dev->dmasound.priv_data == NULL) { |
f5b974cb | 1010 | oss_device_init(dev); |
4aabf633 | 1011 | } else { |
afd1a0c9 | 1012 | printk(KERN_ERR "saa7134 OSS: DMA sound is being handled by ALSA, ignoring %s\n",dev->name); |
4aabf633 RC |
1013 | return -EBUSY; |
1014 | } | |
afd1a0c9 | 1015 | } |
4aabf633 | 1016 | |
afd1a0c9 MCC |
1017 | if (dev == NULL) |
1018 | printk(KERN_INFO "saa7134 OSS: no saa7134 cards found\n"); | |
4aabf633 | 1019 | |
afd1a0c9 | 1020 | return 0; |
4aabf633 RC |
1021 | |
1022 | } | |
1023 | ||
943a4902 | 1024 | static void saa7134_oss_exit(void) |
4aabf633 | 1025 | { |
afd1a0c9 MCC |
1026 | struct saa7134_dev *dev = NULL; |
1027 | struct list_head *list; | |
4aabf633 | 1028 | |
afd1a0c9 MCC |
1029 | list_for_each(list,&saa7134_devlist) { |
1030 | dev = list_entry(list, struct saa7134_dev, devlist); | |
4aabf633 RC |
1031 | |
1032 | /* Device isn't registered by OSS, probably ALSA's */ | |
1033 | if (!dev->dmasound.minor_dsp) | |
1034 | continue; | |
1035 | ||
f5b974cb | 1036 | oss_device_exit(dev); |
674434c6 | 1037 | |
afd1a0c9 | 1038 | } |
4aabf633 | 1039 | |
78457018 AB |
1040 | saa7134_dmasound_init = NULL; |
1041 | saa7134_dmasound_exit = NULL; | |
7bb95296 | 1042 | |
afd1a0c9 | 1043 | printk(KERN_INFO "saa7134 OSS driver for DMA sound unloaded\n"); |
4aabf633 | 1044 | |
afd1a0c9 | 1045 | return; |
4aabf633 RC |
1046 | } |
1047 | ||
c7ac6b42 LT |
1048 | /* We initialize this late, to make sure the sound system is up and running */ |
1049 | late_initcall(saa7134_oss_init); | |
4aabf633 RC |
1050 | module_exit(saa7134_oss_exit); |
1051 | MODULE_LICENSE("GPL"); | |
1052 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | |
1053 | ||
1da177e4 LT |
1054 | /* ----------------------------------------------------------- */ |
1055 | /* | |
1056 | * Local variables: | |
1057 | * c-basic-offset: 8 | |
1058 | * End: | |
1059 | */ |