diff options
-rw-r--r-- | Documentation/sound/alsa/ALSA-Configuration.txt | 82 | ||||
-rw-r--r-- | include/sound/pcm.h | 1 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 14 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 2 | ||||
-rw-r--r-- | sound/drivers/Kconfig | 19 | ||||
-rw-r--r-- | sound/drivers/Makefile | 2 | ||||
-rw-r--r-- | sound/drivers/aloop.c | 1055 | ||||
-rw-r--r-- | sound/isa/Kconfig | 36 | ||||
-rw-r--r-- | sound/isa/Makefile | 4 | ||||
-rw-r--r-- | sound/isa/galaxy/Makefile | 10 | ||||
-rw-r--r-- | sound/isa/galaxy/azt1605.c | 91 | ||||
-rw-r--r-- | sound/isa/galaxy/azt2316.c | 111 | ||||
-rw-r--r-- | sound/isa/galaxy/galaxy.c | 652 | ||||
-rw-r--r-- | sound/isa/sgalaxy.c | 369 | ||||
-rw-r--r-- | sound/pci/ice1712/delta.c | 10 | ||||
-rw-r--r-- | sound/pci/ice1712/delta.h | 4 | ||||
-rw-r--r-- | sound/usb/card.c | 31 | ||||
-rw-r--r-- | sound/usb/endpoint.c | 2 | ||||
-rw-r--r-- | sound/usb/helper.c | 17 | ||||
-rw-r--r-- | sound/usb/midi.c | 9 | ||||
-rw-r--r-- | sound/usb/pcm.c | 4 | ||||
-rw-r--r-- | sound/usb/proc.c | 2 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 161 | ||||
-rw-r--r-- | sound/usb/urb.c | 2 |
24 files changed, 2260 insertions, 430 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 7f4dcebda9c6..d0eb696d32e8 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt | |||
@@ -300,6 +300,74 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
300 | control correctly. If you have problems regarding this, try | 300 | control correctly. If you have problems regarding this, try |
301 | another ALSA compliant mixer (alsamixer works). | 301 | another ALSA compliant mixer (alsamixer works). |
302 | 302 | ||
303 | Module snd-azt1605 | ||
304 | ------------------ | ||
305 | |||
306 | Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605 | ||
307 | chipset. | ||
308 | |||
309 | port - port # for BASE (0x220,0x240,0x260,0x280) | ||
310 | wss_port - port # for WSS (0x530,0x604,0xe80,0xf40) | ||
311 | irq - IRQ # for WSS (7,9,10,11) | ||
312 | dma1 - DMA # for WSS playback (0,1,3) | ||
313 | dma2 - DMA # for WSS capture (0,1), -1 = disabled (default) | ||
314 | mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default) | ||
315 | mpu_irq - IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default) | ||
316 | fm_port - port # for OPL3 (0x388), -1 = disabled (default) | ||
317 | |||
318 | This module supports multiple cards. It does not support autoprobe: port, | ||
319 | wss_port, irq and dma1 have to be specified. The other values are | ||
320 | optional. | ||
321 | |||
322 | "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240) | ||
323 | or the value stored in the card's EEPROM for cards that have an EEPROM and | ||
324 | their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can | ||
325 | be choosen freely from the options enumerated above. | ||
326 | |||
327 | If dma2 is specified and different from dma1, the card will operate in | ||
328 | full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to | ||
329 | enable capture since only channels 0 and 1 are available for capture. | ||
330 | |||
331 | Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0 | ||
332 | mpu_port=0x330 mpu_irq=9 fm_port=0x388". | ||
333 | |||
334 | Whatever IRQ and DMA channels you pick, be sure to reserve them for | ||
335 | legacy ISA in your BIOS. | ||
336 | |||
337 | Module snd-azt2316 | ||
338 | ------------------ | ||
339 | |||
340 | Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316 | ||
341 | chipset. | ||
342 | |||
343 | port - port # for BASE (0x220,0x240,0x260,0x280) | ||
344 | wss_port - port # for WSS (0x530,0x604,0xe80,0xf40) | ||
345 | irq - IRQ # for WSS (7,9,10,11) | ||
346 | dma1 - DMA # for WSS playback (0,1,3) | ||
347 | dma2 - DMA # for WSS capture (0,1), -1 = disabled (default) | ||
348 | mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default) | ||
349 | mpu_irq - IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default) | ||
350 | fm_port - port # for OPL3 (0x388), -1 = disabled (default) | ||
351 | |||
352 | This module supports multiple cards. It does not support autoprobe: port, | ||
353 | wss_port, irq and dma1 have to be specified. The other values are | ||
354 | optional. | ||
355 | |||
356 | "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240) | ||
357 | or the value stored in the card's EEPROM for cards that have an EEPROM and | ||
358 | their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can | ||
359 | be choosen freely from the options enumerated above. | ||
360 | |||
361 | If dma2 is specified and different from dma1, the card will operate in | ||
362 | full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to | ||
363 | enable capture since only channels 0 and 1 are available for capture. | ||
364 | |||
365 | Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0 | ||
366 | mpu_port=0x330 mpu_irq=9 fm_port=0x388". | ||
367 | |||
368 | Whatever IRQ and DMA channels you pick, be sure to reserve them for | ||
369 | legacy ISA in your BIOS. | ||
370 | |||
303 | Module snd-aw2 | 371 | Module snd-aw2 |
304 | -------------- | 372 | -------------- |
305 | 373 | ||
@@ -1641,20 +1709,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
1641 | 1709 | ||
1642 | This card is also known as Audio Excel DSP 16 or Zoltrix AV302. | 1710 | This card is also known as Audio Excel DSP 16 or Zoltrix AV302. |
1643 | 1711 | ||
1644 | Module snd-sgalaxy | ||
1645 | ------------------ | ||
1646 | |||
1647 | Module for Aztech Sound Galaxy sound card. | ||
1648 | |||
1649 | sbport - Port # for SB16 interface (0x220,0x240) | ||
1650 | wssport - Port # for WSS interface (0x530,0xe80,0xf40,0x604) | ||
1651 | irq - IRQ # (7,9,10,11) | ||
1652 | dma1 - DMA # | ||
1653 | |||
1654 | This module supports multiple cards. | ||
1655 | |||
1656 | The power-management is supported. | ||
1657 | |||
1658 | Module snd-sscape | 1712 | Module snd-sscape |
1659 | ----------------- | 1713 | ----------------- |
1660 | 1714 | ||
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 85f1c6bf8566..dfd9b76b1853 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
@@ -278,6 +278,7 @@ struct snd_pcm_runtime { | |||
278 | snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ | 278 | snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ |
279 | snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ | 279 | snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ |
280 | unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ | 280 | unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ |
281 | unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */ | ||
281 | snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ | 282 | snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ |
282 | 283 | ||
283 | /* -- HW params -- */ | 284 | /* -- HW params -- */ |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e23e0e7ab26f..a1707cca9c66 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -334,11 +334,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
334 | /* delta = "expected next hw_ptr" for in_interrupt != 0 */ | 334 | /* delta = "expected next hw_ptr" for in_interrupt != 0 */ |
335 | delta = runtime->hw_ptr_interrupt + runtime->period_size; | 335 | delta = runtime->hw_ptr_interrupt + runtime->period_size; |
336 | if (delta > new_hw_ptr) { | 336 | if (delta > new_hw_ptr) { |
337 | hw_base += runtime->buffer_size; | 337 | /* check for double acknowledged interrupts */ |
338 | if (hw_base >= runtime->boundary) | 338 | hdelta = jiffies - runtime->hw_ptr_jiffies; |
339 | hw_base = 0; | 339 | if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { |
340 | new_hw_ptr = hw_base + pos; | 340 | hw_base += runtime->buffer_size; |
341 | goto __delta; | 341 | if (hw_base >= runtime->boundary) |
342 | hw_base = 0; | ||
343 | new_hw_ptr = hw_base + pos; | ||
344 | goto __delta; | ||
345 | } | ||
342 | } | 346 | } |
343 | } | 347 | } |
344 | /* new_hw_ptr might be lower than old_hw_ptr in case when */ | 348 | /* new_hw_ptr might be lower than old_hw_ptr in case when */ |
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 134fc6c2e08d..e2e73895db12 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -864,6 +864,8 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) | |||
864 | struct snd_pcm_runtime *runtime = substream->runtime; | 864 | struct snd_pcm_runtime *runtime = substream->runtime; |
865 | snd_pcm_trigger_tstamp(substream); | 865 | snd_pcm_trigger_tstamp(substream); |
866 | runtime->hw_ptr_jiffies = jiffies; | 866 | runtime->hw_ptr_jiffies = jiffies; |
867 | runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / | ||
868 | runtime->rate; | ||
867 | runtime->status->state = state; | 869 | runtime->status->state = state; |
868 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 870 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
869 | runtime->silence_size > 0) | 871 | runtime->silence_size > 0) |
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 480c38623da8..c8961165277c 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig | |||
@@ -74,6 +74,25 @@ config SND_DUMMY | |||
74 | To compile this driver as a module, choose M here: the module | 74 | To compile this driver as a module, choose M here: the module |
75 | will be called snd-dummy. | 75 | will be called snd-dummy. |
76 | 76 | ||
77 | config SND_ALOOP | ||
78 | tristate "Generic loopback driver (PCM)" | ||
79 | select SND_PCM | ||
80 | help | ||
81 | Say 'Y' or 'M' to include support for the PCM loopback device. | ||
82 | This module returns played samples back to the user space using | ||
83 | the standard ALSA PCM device. The devices are routed 0->1 and | ||
84 | 1->0, where first number is the playback PCM device and second | ||
85 | number is the capture device. Module creates two PCM devices and | ||
86 | configured number of substreams (see the pcm_substreams module | ||
87 | parameter). | ||
88 | |||
89 | The looback device allow time sychronization with an external | ||
90 | timing source using the time shift universal control (+-20% | ||
91 | of system time). | ||
92 | |||
93 | To compile this driver as a module, choose M here: the module | ||
94 | will be called snd-aloop. | ||
95 | |||
77 | config SND_VIRMIDI | 96 | config SND_VIRMIDI |
78 | tristate "Virtual MIDI soundcard" | 97 | tristate "Virtual MIDI soundcard" |
79 | depends on SND_SEQUENCER | 98 | depends on SND_SEQUENCER |
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index d4a07f9ff2c7..1a8440c8b138 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile | |||
@@ -4,6 +4,7 @@ | |||
4 | # | 4 | # |
5 | 5 | ||
6 | snd-dummy-objs := dummy.o | 6 | snd-dummy-objs := dummy.o |
7 | snd-aloop-objs := aloop.o | ||
7 | snd-mtpav-objs := mtpav.o | 8 | snd-mtpav-objs := mtpav.o |
8 | snd-mts64-objs := mts64.o | 9 | snd-mts64-objs := mts64.o |
9 | snd-portman2x4-objs := portman2x4.o | 10 | snd-portman2x4-objs := portman2x4.o |
@@ -13,6 +14,7 @@ snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o | |||
13 | 14 | ||
14 | # Toplevel Module Dependency | 15 | # Toplevel Module Dependency |
15 | obj-$(CONFIG_SND_DUMMY) += snd-dummy.o | 16 | obj-$(CONFIG_SND_DUMMY) += snd-dummy.o |
17 | obj-$(CONFIG_SND_ALOOP) += snd-aloop.o | ||
16 | obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o | 18 | obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o |
17 | obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o | 19 | obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o |
18 | obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o | 20 | obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o |
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c new file mode 100644 index 000000000000..3123a15d23f6 --- /dev/null +++ b/sound/drivers/aloop.c | |||
@@ -0,0 +1,1055 @@ | |||
1 | /* | ||
2 | * Loopback soundcard | ||
3 | * | ||
4 | * Original code: | ||
5 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> | ||
6 | * | ||
7 | * More accurate positioning and full-duplex support: | ||
8 | * Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de> | ||
9 | * | ||
10 | * Major (almost complete) rewrite: | ||
11 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> | ||
12 | * | ||
13 | * A next major update in 2010 (separate timers for playback and capture): | ||
14 | * Copyright (c) Jaroslav Kysela <perex@perex.cz> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License as published by | ||
18 | * the Free Software Foundation; either version 2 of the License, or | ||
19 | * (at your option) any later version. | ||
20 | * | ||
21 | * This program is distributed in the hope that it will be useful, | ||
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
24 | * GNU General Public License for more details. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License | ||
27 | * along with this program; if not, write to the Free Software | ||
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/jiffies.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/time.h> | ||
36 | #include <linux/wait.h> | ||
37 | #include <linux/moduleparam.h> | ||
38 | #include <linux/platform_device.h> | ||
39 | #include <sound/core.h> | ||
40 | #include <sound/control.h> | ||
41 | #include <sound/pcm.h> | ||
42 | #include <sound/initval.h> | ||
43 | |||
44 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | ||
45 | MODULE_DESCRIPTION("A loopback soundcard"); | ||
46 | MODULE_LICENSE("GPL"); | ||
47 | MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); | ||
48 | |||
49 | #define MAX_PCM_SUBSTREAMS 8 | ||
50 | |||
51 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
52 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
53 | static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | ||
54 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | ||
55 | static int pcm_notify[SNDRV_CARDS]; | ||
56 | |||
57 | module_param_array(index, int, NULL, 0444); | ||
58 | MODULE_PARM_DESC(index, "Index value for loopback soundcard."); | ||
59 | module_param_array(id, charp, NULL, 0444); | ||
60 | MODULE_PARM_DESC(id, "ID string for loopback soundcard."); | ||
61 | module_param_array(enable, bool, NULL, 0444); | ||
62 | MODULE_PARM_DESC(enable, "Enable this loopback soundcard."); | ||
63 | module_param_array(pcm_substreams, int, NULL, 0444); | ||
64 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); | ||
65 | module_param_array(pcm_notify, int, NULL, 0444); | ||
66 | MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); | ||
67 | |||
68 | #define NO_PITCH 100000 | ||
69 | |||
70 | struct loopback_pcm; | ||
71 | |||
72 | struct loopback_cable { | ||
73 | spinlock_t lock; | ||
74 | struct loopback_pcm *streams[2]; | ||
75 | struct snd_pcm_hardware hw; | ||
76 | /* flags */ | ||
77 | unsigned int valid; | ||
78 | unsigned int running; | ||
79 | }; | ||
80 | |||
81 | struct loopback_setup { | ||
82 | unsigned int notify: 1; | ||
83 | unsigned int rate_shift; | ||
84 | unsigned int format; | ||
85 | unsigned int rate; | ||
86 | unsigned int channels; | ||
87 | struct snd_ctl_elem_id active_id; | ||
88 | struct snd_ctl_elem_id format_id; | ||
89 | struct snd_ctl_elem_id rate_id; | ||
90 | struct snd_ctl_elem_id channels_id; | ||
91 | }; | ||
92 | |||
93 | struct loopback { | ||
94 | struct snd_card *card; | ||
95 | struct mutex cable_lock; | ||
96 | struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2]; | ||
97 | struct snd_pcm *pcm[2]; | ||
98 | struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2]; | ||
99 | }; | ||
100 | |||
101 | struct loopback_pcm { | ||
102 | struct loopback *loopback; | ||
103 | struct snd_pcm_substream *substream; | ||
104 | struct loopback_cable *cable; | ||
105 | unsigned int pcm_buffer_size; | ||
106 | unsigned int buf_pos; /* position in buffer */ | ||
107 | unsigned int silent_size; | ||
108 | /* PCM parameters */ | ||
109 | unsigned int pcm_period_size; | ||
110 | unsigned int pcm_bps; /* bytes per second */ | ||
111 | unsigned int pcm_salign; /* bytes per sample * channels */ | ||
112 | unsigned int pcm_rate_shift; /* rate shift value */ | ||
113 | /* flags */ | ||
114 | unsigned int period_update_pending :1; | ||
115 | /* timer stuff */ | ||
116 | unsigned int irq_pos; /* fractional IRQ position */ | ||
117 | unsigned int period_size_frac; | ||
118 | unsigned long last_jiffies; | ||
119 | struct timer_list timer; | ||
120 | }; | ||
121 | |||
122 | static struct platform_device *devices[SNDRV_CARDS]; | ||
123 | |||
124 | static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x) | ||
125 | { | ||
126 | if (dpcm->pcm_rate_shift == NO_PITCH) { | ||
127 | x /= HZ; | ||
128 | } else { | ||
129 | x = div_u64(NO_PITCH * (unsigned long long)x, | ||
130 | HZ * (unsigned long long)dpcm->pcm_rate_shift); | ||
131 | } | ||
132 | return x - (x % dpcm->pcm_salign); | ||
133 | } | ||
134 | |||
135 | static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x) | ||
136 | { | ||
137 | if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */ | ||
138 | return x * HZ; | ||
139 | } else { | ||
140 | x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ, | ||
141 | NO_PITCH); | ||
142 | } | ||
143 | return x; | ||
144 | } | ||
145 | |||
146 | static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) | ||
147 | { | ||
148 | int device = dpcm->substream->pstr->pcm->device; | ||
149 | |||
150 | if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
151 | device ^= 1; | ||
152 | return &dpcm->loopback->setup[dpcm->substream->number][device]; | ||
153 | } | ||
154 | |||
155 | static inline unsigned int get_notify(struct loopback_pcm *dpcm) | ||
156 | { | ||
157 | return get_setup(dpcm)->notify; | ||
158 | } | ||
159 | |||
160 | static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) | ||
161 | { | ||
162 | return get_setup(dpcm)->rate_shift; | ||
163 | } | ||
164 | |||
165 | static void loopback_timer_start(struct loopback_pcm *dpcm) | ||
166 | { | ||
167 | unsigned long tick; | ||
168 | unsigned int rate_shift = get_rate_shift(dpcm); | ||
169 | |||
170 | if (rate_shift != dpcm->pcm_rate_shift) { | ||
171 | dpcm->pcm_rate_shift = rate_shift; | ||
172 | dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); | ||
173 | } | ||
174 | tick = dpcm->period_size_frac - dpcm->irq_pos; | ||
175 | tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; | ||
176 | dpcm->timer.expires = jiffies + tick; | ||
177 | add_timer(&dpcm->timer); | ||
178 | } | ||
179 | |||
180 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) | ||
181 | { | ||
182 | del_timer(&dpcm->timer); | ||
183 | } | ||
184 | |||
185 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) | ||
186 | #define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE) | ||
187 | #define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE) | ||
188 | |||
189 | static int loopback_check_format(struct loopback_cable *cable, int stream) | ||
190 | { | ||
191 | struct snd_pcm_runtime *runtime; | ||
192 | struct loopback_setup *setup; | ||
193 | struct snd_card *card; | ||
194 | int check; | ||
195 | |||
196 | if (cable->valid != CABLE_VALID_BOTH) { | ||
197 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
198 | goto __notify; | ||
199 | return 0; | ||
200 | } | ||
201 | runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> | ||
202 | substream->runtime; | ||
203 | check = cable->hw.formats != (1ULL << runtime->format) || | ||
204 | cable->hw.rate_min != runtime->rate || | ||
205 | cable->hw.rate_max != runtime->rate || | ||
206 | cable->hw.channels_min != runtime->channels || | ||
207 | cable->hw.channels_max != runtime->channels; | ||
208 | if (!check) | ||
209 | return 0; | ||
210 | if (stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
211 | return -EIO; | ||
212 | } else { | ||
213 | snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> | ||
214 | substream, SNDRV_PCM_STATE_DRAINING); | ||
215 | __notify: | ||
216 | runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> | ||
217 | substream->runtime; | ||
218 | setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); | ||
219 | card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; | ||
220 | if (setup->format != runtime->format) { | ||
221 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
222 | &setup->format_id); | ||
223 | setup->format = runtime->format; | ||
224 | } | ||
225 | if (setup->rate != runtime->rate) { | ||
226 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
227 | &setup->rate_id); | ||
228 | setup->rate = runtime->rate; | ||
229 | } | ||
230 | if (setup->channels != runtime->channels) { | ||
231 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
232 | &setup->channels_id); | ||
233 | setup->channels = runtime->channels; | ||
234 | } | ||
235 | } | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static void loopback_active_notify(struct loopback_pcm *dpcm) | ||
240 | { | ||
241 | snd_ctl_notify(dpcm->loopback->card, | ||
242 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
243 | &get_setup(dpcm)->active_id); | ||
244 | } | ||
245 | |||
246 | static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
247 | { | ||
248 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
249 | struct loopback_pcm *dpcm = runtime->private_data; | ||
250 | struct loopback_cable *cable = dpcm->cable; | ||
251 | int err; | ||
252 | |||
253 | switch (cmd) { | ||
254 | case SNDRV_PCM_TRIGGER_START: | ||
255 | err = loopback_check_format(cable, substream->stream); | ||
256 | if (err < 0) | ||
257 | return err; | ||
258 | dpcm->last_jiffies = jiffies; | ||
259 | dpcm->pcm_rate_shift = 0; | ||
260 | loopback_timer_start(dpcm); | ||
261 | cable->running |= (1 << substream->stream); | ||
262 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
263 | loopback_active_notify(dpcm); | ||
264 | break; | ||
265 | case SNDRV_PCM_TRIGGER_STOP: | ||
266 | cable->running &= ~(1 << substream->stream); | ||
267 | loopback_timer_stop(dpcm); | ||
268 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
269 | loopback_active_notify(dpcm); | ||
270 | break; | ||
271 | default: | ||
272 | return -EINVAL; | ||
273 | } | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int loopback_prepare(struct snd_pcm_substream *substream) | ||
278 | { | ||
279 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
280 | struct loopback_pcm *dpcm = runtime->private_data; | ||
281 | struct loopback_cable *cable = dpcm->cable; | ||
282 | unsigned int bps, salign; | ||
283 | |||
284 | salign = (snd_pcm_format_width(runtime->format) * | ||
285 | runtime->channels) / 8; | ||
286 | bps = salign * runtime->rate; | ||
287 | if (bps <= 0 || salign <= 0) | ||
288 | return -EINVAL; | ||
289 | |||
290 | dpcm->buf_pos = 0; | ||
291 | dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size); | ||
292 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
293 | /* clear capture buffer */ | ||
294 | dpcm->silent_size = dpcm->pcm_buffer_size; | ||
295 | snd_pcm_format_set_silence(runtime->format, runtime->dma_area, | ||
296 | runtime->buffer_size * runtime->channels); | ||
297 | } | ||
298 | |||
299 | dpcm->irq_pos = 0; | ||
300 | dpcm->period_update_pending = 0; | ||
301 | dpcm->pcm_bps = bps; | ||
302 | dpcm->pcm_salign = salign; | ||
303 | dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); | ||
304 | |||
305 | mutex_lock(&dpcm->loopback->cable_lock); | ||
306 | if (!(cable->valid & ~(1 << substream->stream))) { | ||
307 | cable->hw.formats = (1ULL << runtime->format); | ||
308 | cable->hw.rate_min = runtime->rate; | ||
309 | cable->hw.rate_max = runtime->rate; | ||
310 | cable->hw.channels_min = runtime->channels; | ||
311 | cable->hw.channels_max = runtime->channels; | ||
312 | } | ||
313 | cable->valid |= 1 << substream->stream; | ||
314 | mutex_unlock(&dpcm->loopback->cable_lock); | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes) | ||
320 | { | ||
321 | struct snd_pcm_runtime *runtime = dpcm->substream->runtime; | ||
322 | char *dst = runtime->dma_area; | ||
323 | unsigned int dst_off = dpcm->buf_pos; | ||
324 | |||
325 | if (dpcm->silent_size >= dpcm->pcm_buffer_size) | ||
326 | return; | ||
327 | if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size) | ||
328 | bytes = dpcm->pcm_buffer_size - dpcm->silent_size; | ||
329 | |||
330 | for (;;) { | ||
331 | unsigned int size = bytes; | ||
332 | if (dst_off + size > dpcm->pcm_buffer_size) | ||
333 | size = dpcm->pcm_buffer_size - dst_off; | ||
334 | snd_pcm_format_set_silence(runtime->format, dst + dst_off, | ||
335 | bytes_to_frames(runtime, size) * | ||
336 | runtime->channels); | ||
337 | dpcm->silent_size += size; | ||
338 | bytes -= size; | ||
339 | if (!bytes) | ||
340 | break; | ||
341 | dst_off = 0; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | static void copy_play_buf(struct loopback_pcm *play, | ||
346 | struct loopback_pcm *capt, | ||
347 | unsigned int bytes) | ||
348 | { | ||
349 | struct snd_pcm_runtime *runtime = play->substream->runtime; | ||
350 | char *src = play->substream->runtime->dma_area; | ||
351 | char *dst = capt->substream->runtime->dma_area; | ||
352 | unsigned int src_off = play->buf_pos; | ||
353 | unsigned int dst_off = capt->buf_pos; | ||
354 | unsigned int clear_bytes = 0; | ||
355 | |||
356 | /* check if playback is draining, trim the capture copy size | ||
357 | * when our pointer is at the end of playback ring buffer */ | ||
358 | if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && | ||
359 | snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { | ||
360 | snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; | ||
361 | appl_ptr = appl_ptr1 = runtime->control->appl_ptr; | ||
362 | appl_ptr1 -= appl_ptr1 % runtime->buffer_size; | ||
363 | appl_ptr1 += play->buf_pos / play->pcm_salign; | ||
364 | if (appl_ptr < appl_ptr1) | ||
365 | appl_ptr1 -= runtime->buffer_size; | ||
366 | diff = (appl_ptr - appl_ptr1) * play->pcm_salign; | ||
367 | if (diff < bytes) { | ||
368 | clear_bytes = bytes - diff; | ||
369 | bytes = diff; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | for (;;) { | ||
374 | unsigned int size = bytes; | ||
375 | if (src_off + size > play->pcm_buffer_size) | ||
376 | size = play->pcm_buffer_size - src_off; | ||
377 | if (dst_off + size > capt->pcm_buffer_size) | ||
378 | size = capt->pcm_buffer_size - dst_off; | ||
379 | memcpy(dst + dst_off, src + src_off, size); | ||
380 | capt->silent_size = 0; | ||
381 | bytes -= size; | ||
382 | if (!bytes) | ||
383 | break; | ||
384 | src_off = (src_off + size) % play->pcm_buffer_size; | ||
385 | dst_off = (dst_off + size) % capt->pcm_buffer_size; | ||
386 | } | ||
387 | |||
388 | if (clear_bytes > 0) | ||
389 | clear_capture_buf(capt, clear_bytes); | ||
390 | } | ||
391 | |||
392 | #define BYTEPOS_UPDATE_POSONLY 0 | ||
393 | #define BYTEPOS_UPDATE_CLEAR 1 | ||
394 | #define BYTEPOS_UPDATE_COPY 2 | ||
395 | |||
396 | static void loopback_bytepos_update(struct loopback_pcm *dpcm, | ||
397 | unsigned int delta, | ||
398 | unsigned int cmd) | ||
399 | { | ||
400 | unsigned int count; | ||
401 | unsigned long last_pos; | ||
402 | |||
403 | last_pos = byte_pos(dpcm, dpcm->irq_pos); | ||
404 | dpcm->irq_pos += delta * dpcm->pcm_bps; | ||
405 | count = byte_pos(dpcm, dpcm->irq_pos) - last_pos; | ||
406 | if (!count) | ||
407 | return; | ||
408 | if (cmd == BYTEPOS_UPDATE_CLEAR) | ||
409 | clear_capture_buf(dpcm, count); | ||
410 | else if (cmd == BYTEPOS_UPDATE_COPY) | ||
411 | copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK], | ||
412 | dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE], | ||
413 | count); | ||
414 | dpcm->buf_pos += count; | ||
415 | dpcm->buf_pos %= dpcm->pcm_buffer_size; | ||
416 | if (dpcm->irq_pos >= dpcm->period_size_frac) { | ||
417 | dpcm->irq_pos %= dpcm->period_size_frac; | ||
418 | dpcm->period_update_pending = 1; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | static void loopback_pos_update(struct loopback_cable *cable) | ||
423 | { | ||
424 | struct loopback_pcm *dpcm_play = | ||
425 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; | ||
426 | struct loopback_pcm *dpcm_capt = | ||
427 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; | ||
428 | unsigned long delta_play = 0, delta_capt = 0; | ||
429 | |||
430 | spin_lock(&cable->lock); | ||
431 | if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | ||
432 | delta_play = jiffies - dpcm_play->last_jiffies; | ||
433 | dpcm_play->last_jiffies += delta_play; | ||
434 | } | ||
435 | |||
436 | if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { | ||
437 | delta_capt = jiffies - dpcm_capt->last_jiffies; | ||
438 | dpcm_capt->last_jiffies += delta_capt; | ||
439 | } | ||
440 | |||
441 | if (delta_play == 0 && delta_capt == 0) { | ||
442 | spin_unlock(&cable->lock); | ||
443 | return; | ||
444 | } | ||
445 | |||
446 | if (delta_play > delta_capt) { | ||
447 | loopback_bytepos_update(dpcm_play, delta_play - delta_capt, | ||
448 | BYTEPOS_UPDATE_POSONLY); | ||
449 | delta_play = delta_capt; | ||
450 | } else if (delta_play < delta_capt) { | ||
451 | loopback_bytepos_update(dpcm_capt, delta_capt - delta_play, | ||
452 | BYTEPOS_UPDATE_CLEAR); | ||
453 | delta_capt = delta_play; | ||
454 | } | ||
455 | |||
456 | if (delta_play == 0 && delta_capt == 0) { | ||
457 | spin_unlock(&cable->lock); | ||
458 | return; | ||
459 | } | ||
460 | /* note delta_capt == delta_play at this moment */ | ||
461 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); | ||
462 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); | ||
463 | spin_unlock(&cable->lock); | ||
464 | } | ||
465 | |||
466 | static void loopback_timer_function(unsigned long data) | ||
467 | { | ||
468 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; | ||
469 | int stream; | ||
470 | |||
471 | loopback_pos_update(dpcm->cable); | ||
472 | stream = dpcm->substream->stream; | ||
473 | if (dpcm->cable->running & (1 << stream)) | ||
474 | loopback_timer_start(dpcm); | ||
475 | if (dpcm->period_update_pending) { | ||
476 | dpcm->period_update_pending = 0; | ||
477 | if (dpcm->cable->running & (1 << stream)) | ||
478 | snd_pcm_period_elapsed(dpcm->substream); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) | ||
483 | { | ||
484 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
485 | struct loopback_pcm *dpcm = runtime->private_data; | ||
486 | |||
487 | loopback_pos_update(dpcm->cable); | ||
488 | return bytes_to_frames(runtime, dpcm->buf_pos); | ||
489 | } | ||
490 | |||
491 | static struct snd_pcm_hardware loopback_pcm_hardware = | ||
492 | { | ||
493 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | | ||
494 | SNDRV_PCM_INFO_MMAP_VALID), | ||
495 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | ||
496 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | | ||
497 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), | ||
498 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, | ||
499 | .rate_min = 8000, | ||
500 | .rate_max = 192000, | ||
501 | .channels_min = 1, | ||
502 | .channels_max = 32, | ||
503 | .buffer_bytes_max = 2 * 1024 * 1024, | ||
504 | .period_bytes_min = 64, | ||
505 | .period_bytes_max = 2 * 1024 * 1024, | ||
506 | .periods_min = 1, | ||
507 | .periods_max = 1024, | ||
508 | .fifo_size = 0, | ||
509 | }; | ||
510 | |||
511 | static void loopback_runtime_free(struct snd_pcm_runtime *runtime) | ||
512 | { | ||
513 | struct loopback_pcm *dpcm = runtime->private_data; | ||
514 | kfree(dpcm); | ||
515 | } | ||
516 | |||
517 | static int loopback_hw_params(struct snd_pcm_substream *substream, | ||
518 | struct snd_pcm_hw_params *params) | ||
519 | { | ||
520 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | ||
521 | } | ||
522 | |||
523 | static int loopback_hw_free(struct snd_pcm_substream *substream) | ||
524 | { | ||
525 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
526 | struct loopback_pcm *dpcm = runtime->private_data; | ||
527 | struct loopback_cable *cable = dpcm->cable; | ||
528 | |||
529 | mutex_lock(&dpcm->loopback->cable_lock); | ||
530 | cable->valid &= ~(1 << substream->stream); | ||
531 | mutex_unlock(&dpcm->loopback->cable_lock); | ||
532 | return snd_pcm_lib_free_pages(substream); | ||
533 | } | ||
534 | |||
535 | static unsigned int get_cable_index(struct snd_pcm_substream *substream) | ||
536 | { | ||
537 | if (!substream->pcm->device) | ||
538 | return substream->stream; | ||
539 | else | ||
540 | return !substream->stream; | ||
541 | } | ||
542 | |||
543 | static int loopback_open(struct snd_pcm_substream *substream) | ||
544 | { | ||
545 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
546 | struct loopback *loopback = substream->private_data; | ||
547 | struct loopback_pcm *dpcm; | ||
548 | struct loopback_cable *cable; | ||
549 | int err = 0; | ||
550 | int dev = get_cable_index(substream); | ||
551 | |||
552 | mutex_lock(&loopback->cable_lock); | ||
553 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||
554 | if (!dpcm) { | ||
555 | err = -ENOMEM; | ||
556 | goto unlock; | ||
557 | } | ||
558 | dpcm->loopback = loopback; | ||
559 | dpcm->substream = substream; | ||
560 | setup_timer(&dpcm->timer, loopback_timer_function, | ||
561 | (unsigned long)dpcm); | ||
562 | |||
563 | cable = loopback->cables[substream->number][dev]; | ||
564 | if (!cable) { | ||
565 | cable = kzalloc(sizeof(*cable), GFP_KERNEL); | ||
566 | if (!cable) { | ||
567 | kfree(dpcm); | ||
568 | err = -ENOMEM; | ||
569 | goto unlock; | ||
570 | } | ||
571 | spin_lock_init(&cable->lock); | ||
572 | cable->hw = loopback_pcm_hardware; | ||
573 | loopback->cables[substream->number][dev] = cable; | ||
574 | } | ||
575 | dpcm->cable = cable; | ||
576 | cable->streams[substream->stream] = dpcm; | ||
577 | |||
578 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | ||
579 | |||
580 | runtime->private_data = dpcm; | ||
581 | runtime->private_free = loopback_runtime_free; | ||
582 | if (get_notify(dpcm) && | ||
583 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
584 | runtime->hw = loopback_pcm_hardware; | ||
585 | } else { | ||
586 | runtime->hw = cable->hw; | ||
587 | } | ||
588 | unlock: | ||
589 | mutex_unlock(&loopback->cable_lock); | ||
590 | return err; | ||
591 | } | ||
592 | |||
593 | static int loopback_close(struct snd_pcm_substream *substream) | ||
594 | { | ||
595 | struct loopback *loopback = substream->private_data; | ||
596 | struct loopback_pcm *dpcm = substream->runtime->private_data; | ||
597 | struct loopback_cable *cable; | ||
598 | int dev = get_cable_index(substream); | ||
599 | |||
600 | loopback_timer_stop(dpcm); | ||
601 | mutex_lock(&loopback->cable_lock); | ||
602 | cable = loopback->cables[substream->number][dev]; | ||
603 | if (cable->streams[!substream->stream]) { | ||
604 | /* other stream is still alive */ | ||
605 | cable->streams[substream->stream] = NULL; | ||
606 | } else { | ||
607 | /* free the cable */ | ||
608 | loopback->cables[substream->number][dev] = NULL; | ||
609 | kfree(cable); | ||
610 | } | ||
611 | mutex_unlock(&loopback->cable_lock); | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static struct snd_pcm_ops loopback_playback_ops = { | ||
616 | .open = loopback_open, | ||
617 | .close = loopback_close, | ||
618 | .ioctl = snd_pcm_lib_ioctl, | ||
619 | .hw_params = loopback_hw_params, | ||
620 | .hw_free = loopback_hw_free, | ||
621 | .prepare = loopback_prepare, | ||
622 | .trigger = loopback_trigger, | ||
623 | .pointer = loopback_pointer, | ||
624 | }; | ||
625 | |||
626 | static struct snd_pcm_ops loopback_capture_ops = { | ||
627 | .open = loopback_open, | ||
628 | .close = loopback_close, | ||
629 | .ioctl = snd_pcm_lib_ioctl, | ||
630 | .hw_params = loopback_hw_params, | ||
631 | .hw_free = loopback_hw_free, | ||
632 | .prepare = loopback_prepare, | ||
633 | .trigger = loopback_trigger, | ||
634 | .pointer = loopback_pointer, | ||
635 | }; | ||
636 | |||
637 | static int __devinit loopback_pcm_new(struct loopback *loopback, | ||
638 | int device, int substreams) | ||
639 | { | ||
640 | struct snd_pcm *pcm; | ||
641 | int err; | ||
642 | |||
643 | err = snd_pcm_new(loopback->card, "Loopback PCM", device, | ||
644 | substreams, substreams, &pcm); | ||
645 | if (err < 0) | ||
646 | return err; | ||
647 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops); | ||
648 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops); | ||
649 | |||
650 | pcm->private_data = loopback; | ||
651 | pcm->info_flags = 0; | ||
652 | strcpy(pcm->name, "Loopback PCM"); | ||
653 | |||
654 | loopback->pcm[device] = pcm; | ||
655 | |||
656 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
657 | snd_dma_continuous_data(GFP_KERNEL), | ||
658 | 0, 2 * 1024 * 1024); | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, | ||
663 | struct snd_ctl_elem_info *uinfo) | ||
664 | { | ||
665 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
666 | uinfo->count = 1; | ||
667 | uinfo->value.integer.min = 80000; | ||
668 | uinfo->value.integer.max = 120000; | ||
669 | uinfo->value.integer.step = 1; | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, | ||
674 | struct snd_ctl_elem_value *ucontrol) | ||
675 | { | ||
676 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
677 | |||
678 | ucontrol->value.integer.value[0] = | ||
679 | loopback->setup[kcontrol->id.subdevice] | ||
680 | [kcontrol->id.device].rate_shift; | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol, | ||
685 | struct snd_ctl_elem_value *ucontrol) | ||
686 | { | ||
687 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
688 | unsigned int val; | ||
689 | int change = 0; | ||
690 | |||
691 | val = ucontrol->value.integer.value[0]; | ||
692 | if (val < 80000) | ||
693 | val = 80000; | ||
694 | if (val > 120000) | ||
695 | val = 120000; | ||
696 | mutex_lock(&loopback->cable_lock); | ||
697 | if (val != loopback->setup[kcontrol->id.subdevice] | ||
698 | [kcontrol->id.device].rate_shift) { | ||
699 | loopback->setup[kcontrol->id.subdevice] | ||
700 | [kcontrol->id.device].rate_shift = val; | ||
701 | change = 1; | ||
702 | } | ||
703 | mutex_unlock(&loopback->cable_lock); | ||
704 | return change; | ||
705 | } | ||
706 | |||
707 | static int loopback_notify_get(struct snd_kcontrol *kcontrol, | ||
708 | struct snd_ctl_elem_value *ucontrol) | ||
709 | { | ||
710 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
711 | |||
712 | ucontrol->value.integer.value[0] = | ||
713 | loopback->setup[kcontrol->id.subdevice] | ||
714 | [kcontrol->id.device].notify; | ||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | static int loopback_notify_put(struct snd_kcontrol *kcontrol, | ||
719 | struct snd_ctl_elem_value *ucontrol) | ||
720 | { | ||
721 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
722 | unsigned int val; | ||
723 | int change = 0; | ||
724 | |||
725 | val = ucontrol->value.integer.value[0] ? 1 : 0; | ||
726 | if (val != loopback->setup[kcontrol->id.subdevice] | ||
727 | [kcontrol->id.device].notify) { | ||
728 | loopback->setup[kcontrol->id.subdevice] | ||
729 | [kcontrol->id.device].notify = val; | ||
730 | change = 1; | ||
731 | } | ||
732 | return change; | ||
733 | } | ||
734 | |||
735 | static int loopback_active_get(struct snd_kcontrol *kcontrol, | ||
736 | struct snd_ctl_elem_value *ucontrol) | ||
737 | { | ||
738 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
739 | struct loopback_cable *cable = loopback->cables | ||
740 | [kcontrol->id.subdevice][kcontrol->id.device]; | ||
741 | unsigned int val = 0; | ||
742 | |||
743 | if (cable != NULL) | ||
744 | val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? | ||
745 | 1 : 0; | ||
746 | ucontrol->value.integer.value[0] = val; | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static int loopback_format_info(struct snd_kcontrol *kcontrol, | ||
751 | struct snd_ctl_elem_info *uinfo) | ||
752 | { | ||
753 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
754 | uinfo->count = 1; | ||
755 | uinfo->value.integer.min = 0; | ||
756 | uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; | ||
757 | uinfo->value.integer.step = 1; | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | static int loopback_format_get(struct snd_kcontrol *kcontrol, | ||
762 | struct snd_ctl_elem_value *ucontrol) | ||
763 | { | ||
764 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
765 | |||
766 | ucontrol->value.integer.value[0] = | ||
767 | loopback->setup[kcontrol->id.subdevice] | ||
768 | [kcontrol->id.device].format; | ||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | static int loopback_rate_info(struct snd_kcontrol *kcontrol, | ||
773 | struct snd_ctl_elem_info *uinfo) | ||
774 | { | ||
775 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
776 | uinfo->count = 1; | ||
777 | uinfo->value.integer.min = 0; | ||
778 | uinfo->value.integer.max = 192000; | ||
779 | uinfo->value.integer.step = 1; | ||
780 | return 0; | ||
781 | } | ||
782 | |||
783 | static int loopback_rate_get(struct snd_kcontrol *kcontrol, | ||
784 | struct snd_ctl_elem_value *ucontrol) | ||
785 | { | ||
786 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
787 | |||
788 | ucontrol->value.integer.value[0] = | ||
789 | loopback->setup[kcontrol->id.subdevice] | ||
790 | [kcontrol->id.device].rate; | ||
791 | return 0; | ||
792 | } | ||
793 | |||
794 | static int loopback_channels_info(struct snd_kcontrol *kcontrol, | ||
795 | struct snd_ctl_elem_info *uinfo) | ||
796 | { | ||
797 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
798 | uinfo->count = 1; | ||
799 | uinfo->value.integer.min = 1; | ||
800 | uinfo->value.integer.max = 1024; | ||
801 | uinfo->value.integer.step = 1; | ||
802 | return 0; | ||
803 | } | ||
804 | |||
805 | static int loopback_channels_get(struct snd_kcontrol *kcontrol, | ||
806 | struct snd_ctl_elem_value *ucontrol) | ||
807 | { | ||
808 | struct loopback *loopback = snd_kcontrol_chip(kcontrol); | ||
809 | |||
810 | ucontrol->value.integer.value[0] = | ||
811 | loopback->setup[kcontrol->id.subdevice] | ||
812 | [kcontrol->id.device].rate; | ||
813 | return 0; | ||
814 | } | ||
815 | |||
816 | static struct snd_kcontrol_new loopback_controls[] __devinitdata = { | ||
817 | { | ||
818 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
819 | .name = "PCM Rate Shift 100000", | ||
820 | .info = loopback_rate_shift_info, | ||
821 | .get = loopback_rate_shift_get, | ||
822 | .put = loopback_rate_shift_put, | ||
823 | }, | ||
824 | { | ||
825 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
826 | .name = "PCM Notify", | ||
827 | .info = snd_ctl_boolean_mono_info, | ||
828 | .get = loopback_notify_get, | ||
829 | .put = loopback_notify_put, | ||
830 | }, | ||
831 | #define ACTIVE_IDX 2 | ||
832 | { | ||
833 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
834 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
835 | .name = "PCM Slave Active", | ||
836 | .info = snd_ctl_boolean_mono_info, | ||
837 | .get = loopback_active_get, | ||
838 | }, | ||
839 | #define FORMAT_IDX 3 | ||
840 | { | ||
841 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
842 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
843 | .name = "PCM Slave Format", | ||
844 | .info = loopback_format_info, | ||
845 | .get = loopback_format_get | ||
846 | }, | ||
847 | #define RATE_IDX 4 | ||
848 | { | ||
849 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
850 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
851 | .name = "PCM Slave Rate", | ||
852 | .info = loopback_rate_info, | ||
853 | .get = loopback_rate_get | ||
854 | }, | ||
855 | #define CHANNELS_IDX 5 | ||
856 | { | ||
857 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
858 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
859 | .name = "PCM Slave Channels", | ||
860 | .info = loopback_channels_info, | ||
861 | .get = loopback_channels_get | ||
862 | } | ||
863 | }; | ||
864 | |||
865 | static int __devinit loopback_mixer_new(struct loopback *loopback, int notify) | ||
866 | { | ||
867 | struct snd_card *card = loopback->card; | ||
868 | struct snd_pcm *pcm; | ||
869 | struct snd_kcontrol *kctl; | ||
870 | struct loopback_setup *setup; | ||
871 | int err, dev, substr, substr_count, idx; | ||
872 | |||
873 | strcpy(card->mixername, "Loopback Mixer"); | ||
874 | for (dev = 0; dev < 2; dev++) { | ||
875 | pcm = loopback->pcm[dev]; | ||
876 | substr_count = | ||
877 | pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; | ||
878 | for (substr = 0; substr < substr_count; substr++) { | ||
879 | setup = &loopback->setup[substr][dev]; | ||
880 | setup->notify = notify; | ||
881 | setup->rate_shift = NO_PITCH; | ||
882 | setup->format = SNDRV_PCM_FORMAT_S16_LE; | ||
883 | setup->rate = 48000; | ||
884 | setup->channels = 2; | ||
885 | for (idx = 0; idx < ARRAY_SIZE(loopback_controls); | ||
886 | idx++) { | ||
887 | kctl = snd_ctl_new1(&loopback_controls[idx], | ||
888 | loopback); | ||
889 | if (!kctl) | ||
890 | return -ENOMEM; | ||
891 | kctl->id.device = dev; | ||
892 | kctl->id.subdevice = substr; | ||
893 | switch (idx) { | ||
894 | case ACTIVE_IDX: | ||
895 | setup->active_id = kctl->id; | ||
896 | break; | ||
897 | case FORMAT_IDX: | ||
898 | setup->format_id = kctl->id; | ||
899 | break; | ||
900 | case RATE_IDX: | ||
901 | setup->rate_id = kctl->id; | ||
902 | break; | ||
903 | case CHANNELS_IDX: | ||
904 | setup->channels_id = kctl->id; | ||
905 | break; | ||
906 | default: | ||
907 | break; | ||
908 | } | ||
909 | err = snd_ctl_add(card, kctl); | ||
910 | if (err < 0) | ||
911 | return err; | ||
912 | } | ||
913 | } | ||
914 | } | ||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | static int __devinit loopback_probe(struct platform_device *devptr) | ||
919 | { | ||
920 | struct snd_card *card; | ||
921 | struct loopback *loopback; | ||
922 | int dev = devptr->id; | ||
923 | int err; | ||
924 | |||
925 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, | ||
926 | sizeof(struct loopback), &card); | ||
927 | if (err < 0) | ||
928 | return err; | ||
929 | loopback = card->private_data; | ||
930 | |||
931 | if (pcm_substreams[dev] < 1) | ||
932 | pcm_substreams[dev] = 1; | ||
933 | if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) | ||
934 | pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; | ||
935 | |||
936 | loopback->card = card; | ||
937 | mutex_init(&loopback->cable_lock); | ||
938 | |||
939 | err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]); | ||
940 | if (err < 0) | ||
941 | goto __nodev; | ||
942 | err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]); | ||
943 | if (err < 0) | ||
944 | goto __nodev; | ||
945 | err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); | ||
946 | if (err < 0) | ||
947 | goto __nodev; | ||
948 | strcpy(card->driver, "Loopback"); | ||
949 | strcpy(card->shortname, "Loopback"); | ||
950 | sprintf(card->longname, "Loopback %i", dev + 1); | ||
951 | err = snd_card_register(card); | ||
952 | if (!err) { | ||
953 | platform_set_drvdata(devptr, card); | ||
954 | return 0; | ||
955 | } | ||
956 | __nodev: | ||
957 | snd_card_free(card); | ||
958 | return err; | ||
959 | } | ||
960 | |||
961 | static int __devexit loopback_remove(struct platform_device *devptr) | ||
962 | { | ||
963 | snd_card_free(platform_get_drvdata(devptr)); | ||
964 | platform_set_drvdata(devptr, NULL); | ||
965 | return 0; | ||
966 | } | ||
967 | |||
968 | #ifdef CONFIG_PM | ||
969 | static int loopback_suspend(struct platform_device *pdev, | ||
970 | pm_message_t state) | ||
971 | { | ||
972 | struct snd_card *card = platform_get_drvdata(pdev); | ||
973 | struct loopback *loopback = card->private_data; | ||
974 | |||
975 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
976 | |||
977 | snd_pcm_suspend_all(loopback->pcm[0]); | ||
978 | snd_pcm_suspend_all(loopback->pcm[1]); | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static int loopback_resume(struct platform_device *pdev) | ||
983 | { | ||
984 | struct snd_card *card = platform_get_drvdata(pdev); | ||
985 | |||
986 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
987 | return 0; | ||
988 | } | ||
989 | #endif | ||
990 | |||
991 | #define SND_LOOPBACK_DRIVER "snd_aloop" | ||
992 | |||
993 | static struct platform_driver loopback_driver = { | ||
994 | .probe = loopback_probe, | ||
995 | .remove = __devexit_p(loopback_remove), | ||
996 | #ifdef CONFIG_PM | ||
997 | .suspend = loopback_suspend, | ||
998 | .resume = loopback_resume, | ||
999 | #endif | ||
1000 | .driver = { | ||
1001 | .name = SND_LOOPBACK_DRIVER | ||
1002 | }, | ||
1003 | }; | ||
1004 | |||
1005 | static void loopback_unregister_all(void) | ||
1006 | { | ||
1007 | int i; | ||
1008 | |||
1009 | for (i = 0; i < ARRAY_SIZE(devices); ++i) | ||
1010 | platform_device_unregister(devices[i]); | ||
1011 | platform_driver_unregister(&loopback_driver); | ||
1012 | } | ||
1013 | |||
1014 | static int __init alsa_card_loopback_init(void) | ||
1015 | { | ||
1016 | int i, err, cards; | ||
1017 | |||
1018 | err = platform_driver_register(&loopback_driver); | ||
1019 | if (err < 0) | ||
1020 | return err; | ||
1021 | |||
1022 | |||
1023 | cards = 0; | ||
1024 | for (i = 0; i < SNDRV_CARDS; i++) { | ||
1025 | struct platform_device *device; | ||
1026 | if (!enable[i]) | ||
1027 | continue; | ||
1028 | device = platform_device_register_simple(SND_LOOPBACK_DRIVER, | ||
1029 | i, NULL, 0); | ||
1030 | if (IS_ERR(device)) | ||
1031 | continue; | ||
1032 | if (!platform_get_drvdata(device)) { | ||
1033 | platform_device_unregister(device); | ||
1034 | continue; | ||
1035 | } | ||
1036 | devices[i] = device; | ||
1037 | cards++; | ||
1038 | } | ||
1039 | if (!cards) { | ||
1040 | #ifdef MODULE | ||
1041 | printk(KERN_ERR "aloop: No loopback enabled\n"); | ||
1042 | #endif | ||
1043 | loopback_unregister_all(); | ||
1044 | return -ENODEV; | ||
1045 | } | ||
1046 | return 0; | ||
1047 | } | ||
1048 | |||
1049 | static void __exit alsa_card_loopback_exit(void) | ||
1050 | { | ||
1051 | loopback_unregister_all(); | ||
1052 | } | ||
1053 | |||
1054 | module_init(alsa_card_loopback_init) | ||
1055 | module_exit(alsa_card_loopback_exit) | ||
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index c6990c680796..52064cfa91f3 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig | |||
@@ -77,6 +77,32 @@ config SND_ALS100 | |||
77 | To compile this driver as a module, choose M here: the module | 77 | To compile this driver as a module, choose M here: the module |
78 | will be called snd-als100. | 78 | will be called snd-als100. |
79 | 79 | ||
80 | config SND_AZT1605 | ||
81 | tristate "Aztech AZT1605 Driver" | ||
82 | depends on SND | ||
83 | select SND_WSS_LIB | ||
84 | select SND_MPU401_UART | ||
85 | select SND_OPL3_LIB | ||
86 | help | ||
87 | Say Y here to include support for Aztech Sound Galaxy cards | ||
88 | based on the AZT1605 chipset. | ||
89 | |||
90 | To compile this driver as a module, choose M here: the module | ||
91 | will be called snd-azt1605. | ||
92 | |||
93 | config SND_AZT2316 | ||
94 | tristate "Aztech AZT2316 Driver" | ||
95 | depends on SND | ||
96 | select SND_WSS_LIB | ||
97 | select SND_MPU401_UART | ||
98 | select SND_OPL3_LIB | ||
99 | help | ||
100 | Say Y here to include support for Aztech Sound Galaxy cards | ||
101 | based on the AZT2316 chipset. | ||
102 | |||
103 | To compile this driver as a module, choose M here: the module | ||
104 | will be called snd-azt2316. | ||
105 | |||
80 | config SND_AZT2320 | 106 | config SND_AZT2320 |
81 | tristate "Aztech Systems AZT2320" | 107 | tristate "Aztech Systems AZT2320" |
82 | depends on PNP | 108 | depends on PNP |
@@ -351,16 +377,6 @@ config SND_SB16_CSP | |||
351 | coprocessor can do variable tasks like various compression and | 377 | coprocessor can do variable tasks like various compression and |
352 | decompression algorithms. | 378 | decompression algorithms. |
353 | 379 | ||
354 | config SND_SGALAXY | ||
355 | tristate "Aztech Sound Galaxy" | ||
356 | select SND_WSS_LIB | ||
357 | help | ||
358 | Say Y here to include support for Aztech Sound Galaxy | ||
359 | soundcards. | ||
360 | |||
361 | To compile this driver as a module, choose M here: the module | ||
362 | will be called snd-sgalaxy. | ||
363 | |||
364 | config SND_SSCAPE | 380 | config SND_SSCAPE |
365 | tristate "Ensoniq SoundScape driver" | 381 | tristate "Ensoniq SoundScape driver" |
366 | select SND_MPU401_UART | 382 | select SND_MPU401_UART |
diff --git a/sound/isa/Makefile b/sound/isa/Makefile index c73d30c4f462..8d781e419e2e 100644 --- a/sound/isa/Makefile +++ b/sound/isa/Makefile | |||
@@ -10,7 +10,6 @@ snd-cmi8330-objs := cmi8330.o | |||
10 | snd-es18xx-objs := es18xx.o | 10 | snd-es18xx-objs := es18xx.o |
11 | snd-opl3sa2-objs := opl3sa2.o | 11 | snd-opl3sa2-objs := opl3sa2.o |
12 | snd-sc6000-objs := sc6000.o | 12 | snd-sc6000-objs := sc6000.o |
13 | snd-sgalaxy-objs := sgalaxy.o | ||
14 | snd-sscape-objs := sscape.o | 13 | snd-sscape-objs := sscape.o |
15 | 14 | ||
16 | # Toplevel Module Dependency | 15 | # Toplevel Module Dependency |
@@ -21,8 +20,7 @@ obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o | |||
21 | obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o | 20 | obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o |
22 | obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o | 21 | obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o |
23 | obj-$(CONFIG_SND_SC6000) += snd-sc6000.o | 22 | obj-$(CONFIG_SND_SC6000) += snd-sc6000.o |
24 | obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o | ||
25 | obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o | 23 | obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o |
26 | 24 | ||
27 | obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \ | 25 | obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \ |
28 | sb/ wavefront/ wss/ | 26 | sb/ wavefront/ wss/ |
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile new file mode 100644 index 000000000000..e307066d4315 --- /dev/null +++ b/sound/isa/galaxy/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-azt1605-objs := azt1605.o | ||
7 | snd-azt2316-objs := azt2316.o | ||
8 | |||
9 | obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o | ||
10 | obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o | ||
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c new file mode 100644 index 000000000000..9a97643cb713 --- /dev/null +++ b/sound/isa/galaxy/azt1605.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Aztech AZT1605 Driver | ||
3 | * Copyright (C) 2007,2010 Rene Herman | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #define AZT1605 | ||
21 | |||
22 | #define CRD_NAME "Aztech AZT1605" | ||
23 | #define DRV_NAME "AZT1605" | ||
24 | #define DEV_NAME "azt1605" | ||
25 | |||
26 | #define GALAXY_DSP_MAJOR 2 | ||
27 | #define GALAXY_DSP_MINOR 1 | ||
28 | |||
29 | #define GALAXY_CONFIG_SIZE 3 | ||
30 | |||
31 | /* | ||
32 | * 24-bit config register | ||
33 | */ | ||
34 | |||
35 | #define GALAXY_CONFIG_SBA_220 (0 << 0) | ||
36 | #define GALAXY_CONFIG_SBA_240 (1 << 0) | ||
37 | #define GALAXY_CONFIG_SBA_260 (2 << 0) | ||
38 | #define GALAXY_CONFIG_SBA_280 (3 << 0) | ||
39 | #define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 | ||
40 | |||
41 | #define GALAXY_CONFIG_MPUA_300 (0 << 2) | ||
42 | #define GALAXY_CONFIG_MPUA_330 (1 << 2) | ||
43 | |||
44 | #define GALAXY_CONFIG_MPU_ENABLE (1 << 3) | ||
45 | |||
46 | #define GALAXY_CONFIG_GAME_ENABLE (1 << 4) | ||
47 | |||
48 | #define GALAXY_CONFIG_CD_PANASONIC (1 << 5) | ||
49 | #define GALAXY_CONFIG_CD_MITSUMI (1 << 6) | ||
50 | #define GALAXY_CONFIG_CD_MASK (\ | ||
51 | GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI) | ||
52 | |||
53 | #define GALAXY_CONFIG_UNUSED (1 << 7) | ||
54 | #define GALAXY_CONFIG_UNUSED_MASK GALAXY_CONFIG_UNUSED | ||
55 | |||
56 | #define GALAXY_CONFIG_SBIRQ_2 (1 << 8) | ||
57 | #define GALAXY_CONFIG_SBIRQ_3 (1 << 9) | ||
58 | #define GALAXY_CONFIG_SBIRQ_5 (1 << 10) | ||
59 | #define GALAXY_CONFIG_SBIRQ_7 (1 << 11) | ||
60 | |||
61 | #define GALAXY_CONFIG_MPUIRQ_2 (1 << 12) | ||
62 | #define GALAXY_CONFIG_MPUIRQ_3 (1 << 13) | ||
63 | #define GALAXY_CONFIG_MPUIRQ_5 (1 << 14) | ||
64 | #define GALAXY_CONFIG_MPUIRQ_7 (1 << 15) | ||
65 | |||
66 | #define GALAXY_CONFIG_WSSA_530 (0 << 16) | ||
67 | #define GALAXY_CONFIG_WSSA_604 (1 << 16) | ||
68 | #define GALAXY_CONFIG_WSSA_E80 (2 << 16) | ||
69 | #define GALAXY_CONFIG_WSSA_F40 (3 << 16) | ||
70 | |||
71 | #define GALAXY_CONFIG_WSS_ENABLE (1 << 18) | ||
72 | |||
73 | #define GALAXY_CONFIG_CDIRQ_11 (1 << 19) | ||
74 | #define GALAXY_CONFIG_CDIRQ_12 (1 << 20) | ||
75 | #define GALAXY_CONFIG_CDIRQ_15 (1 << 21) | ||
76 | #define GALAXY_CONFIG_CDIRQ_MASK (\ | ||
77 | GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\ | ||
78 | GALAXY_CONFIG_CDIRQ_15) | ||
79 | |||
80 | #define GALAXY_CONFIG_CDDMA_DISABLE (0 << 22) | ||
81 | #define GALAXY_CONFIG_CDDMA_0 (1 << 22) | ||
82 | #define GALAXY_CONFIG_CDDMA_1 (2 << 22) | ||
83 | #define GALAXY_CONFIG_CDDMA_3 (3 << 22) | ||
84 | #define GALAXY_CONFIG_CDDMA_MASK GALAXY_CONFIG_CDDMA_3 | ||
85 | |||
86 | #define GALAXY_CONFIG_MASK (\ | ||
87 | GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\ | ||
88 | GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\ | ||
89 | GALAXY_CONFIG_CDDMA_MASK) | ||
90 | |||
91 | #include "galaxy.c" | ||
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c new file mode 100644 index 000000000000..189441141df6 --- /dev/null +++ b/sound/isa/galaxy/azt2316.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * Aztech AZT2316 Driver | ||
3 | * Copyright (C) 2007,2010 Rene Herman | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #define AZT2316 | ||
21 | |||
22 | #define CRD_NAME "Aztech AZT2316" | ||
23 | #define DRV_NAME "AZT2316" | ||
24 | #define DEV_NAME "azt2316" | ||
25 | |||
26 | #define GALAXY_DSP_MAJOR 3 | ||
27 | #define GALAXY_DSP_MINOR 1 | ||
28 | |||
29 | #define GALAXY_CONFIG_SIZE 4 | ||
30 | |||
31 | /* | ||
32 | * 32-bit config register | ||
33 | */ | ||
34 | |||
35 | #define GALAXY_CONFIG_SBA_220 (0 << 0) | ||
36 | #define GALAXY_CONFIG_SBA_240 (1 << 0) | ||
37 | #define GALAXY_CONFIG_SBA_260 (2 << 0) | ||
38 | #define GALAXY_CONFIG_SBA_280 (3 << 0) | ||
39 | #define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 | ||
40 | |||
41 | #define GALAXY_CONFIG_SBIRQ_2 (1 << 2) | ||
42 | #define GALAXY_CONFIG_SBIRQ_5 (1 << 3) | ||
43 | #define GALAXY_CONFIG_SBIRQ_7 (1 << 4) | ||
44 | #define GALAXY_CONFIG_SBIRQ_10 (1 << 5) | ||
45 | |||
46 | #define GALAXY_CONFIG_SBDMA_DISABLE (0 << 6) | ||
47 | #define GALAXY_CONFIG_SBDMA_0 (1 << 6) | ||
48 | #define GALAXY_CONFIG_SBDMA_1 (2 << 6) | ||
49 | #define GALAXY_CONFIG_SBDMA_3 (3 << 6) | ||
50 | |||
51 | #define GALAXY_CONFIG_WSSA_530 (0 << 8) | ||
52 | #define GALAXY_CONFIG_WSSA_604 (1 << 8) | ||
53 | #define GALAXY_CONFIG_WSSA_E80 (2 << 8) | ||
54 | #define GALAXY_CONFIG_WSSA_F40 (3 << 8) | ||
55 | |||
56 | #define GALAXY_CONFIG_WSS_ENABLE (1 << 10) | ||
57 | |||
58 | #define GALAXY_CONFIG_GAME_ENABLE (1 << 11) | ||
59 | |||
60 | #define GALAXY_CONFIG_MPUA_300 (0 << 12) | ||
61 | #define GALAXY_CONFIG_MPUA_330 (1 << 12) | ||
62 | |||
63 | #define GALAXY_CONFIG_MPU_ENABLE (1 << 13) | ||
64 | |||
65 | #define GALAXY_CONFIG_CDA_310 (0 << 14) | ||
66 | #define GALAXY_CONFIG_CDA_320 (1 << 14) | ||
67 | #define GALAXY_CONFIG_CDA_340 (2 << 14) | ||
68 | #define GALAXY_CONFIG_CDA_350 (3 << 14) | ||
69 | #define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350 | ||
70 | |||
71 | #define GALAXY_CONFIG_CD_DISABLE (0 << 16) | ||
72 | #define GALAXY_CONFIG_CD_PANASONIC (1 << 16) | ||
73 | #define GALAXY_CONFIG_CD_SONY (2 << 16) | ||
74 | #define GALAXY_CONFIG_CD_MITSUMI (3 << 16) | ||
75 | #define GALAXY_CONFIG_CD_AZTECH (4 << 16) | ||
76 | #define GALAXY_CONFIG_CD_UNUSED_5 (5 << 16) | ||
77 | #define GALAXY_CONFIG_CD_UNUSED_6 (6 << 16) | ||
78 | #define GALAXY_CONFIG_CD_UNUSED_7 (7 << 16) | ||
79 | #define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_7 | ||
80 | |||
81 | #define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 20) | ||
82 | #define GALAXY_CONFIG_CDDMA8_0 (1 << 20) | ||
83 | #define GALAXY_CONFIG_CDDMA8_1 (2 << 20) | ||
84 | #define GALAXY_CONFIG_CDDMA8_3 (3 << 20) | ||
85 | #define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3 | ||
86 | |||
87 | #define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 22) | ||
88 | #define GALAXY_CONFIG_CDDMA16_5 (1 << 22) | ||
89 | #define GALAXY_CONFIG_CDDMA16_6 (2 << 22) | ||
90 | #define GALAXY_CONFIG_CDDMA16_7 (3 << 22) | ||
91 | #define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7 | ||
92 | |||
93 | #define GALAXY_CONFIG_MPUIRQ_2 (1 << 24) | ||
94 | #define GALAXY_CONFIG_MPUIRQ_5 (1 << 25) | ||
95 | #define GALAXY_CONFIG_MPUIRQ_7 (1 << 26) | ||
96 | #define GALAXY_CONFIG_MPUIRQ_10 (1 << 27) | ||
97 | |||
98 | #define GALAXY_CONFIG_CDIRQ_5 (1 << 28) | ||
99 | #define GALAXY_CONFIG_CDIRQ_11 (1 << 29) | ||
100 | #define GALAXY_CONFIG_CDIRQ_12 (1 << 30) | ||
101 | #define GALAXY_CONFIG_CDIRQ_15 (1 << 31) | ||
102 | #define GALAXY_CONFIG_CDIRQ_MASK (\ | ||
103 | GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\ | ||
104 | GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15) | ||
105 | |||
106 | #define GALAXY_CONFIG_MASK (\ | ||
107 | GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\ | ||
108 | GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\ | ||
109 | GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK) | ||
110 | |||
111 | #include "galaxy.c" | ||
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c new file mode 100644 index 000000000000..ee54df082b9c --- /dev/null +++ b/sound/isa/galaxy/galaxy.c | |||
@@ -0,0 +1,652 @@ | |||
1 | /* | ||
2 | * Aztech AZT1605/AZT2316 Driver | ||
3 | * Copyright (C) 2007,2010 Rene Herman | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/isa.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <asm/processor.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/initval.h> | ||
28 | #include <sound/wss.h> | ||
29 | #include <sound/mpu401.h> | ||
30 | #include <sound/opl3.h> | ||
31 | |||
32 | MODULE_DESCRIPTION(CRD_NAME); | ||
33 | MODULE_AUTHOR("Rene Herman"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
37 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
38 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; | ||
39 | |||
40 | module_param_array(index, int, NULL, 0444); | ||
41 | MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); | ||
42 | module_param_array(id, charp, NULL, 0444); | ||
43 | MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); | ||
44 | module_param_array(enable, bool, NULL, 0444); | ||
45 | MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); | ||
46 | |||
47 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
48 | static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
49 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
50 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
51 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | ||
52 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | ||
53 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
54 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
55 | |||
56 | module_param_array(port, long, NULL, 0444); | ||
57 | MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); | ||
58 | module_param_array(wss_port, long, NULL, 0444); | ||
59 | MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver."); | ||
60 | module_param_array(mpu_port, long, NULL, 0444); | ||
61 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); | ||
62 | module_param_array(fm_port, long, NULL, 0444); | ||
63 | MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver."); | ||
64 | module_param_array(irq, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); | ||
66 | module_param_array(mpu_irq, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); | ||
68 | module_param_array(dma1, int, NULL, 0444); | ||
69 | MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver."); | ||
70 | module_param_array(dma2, int, NULL, 0444); | ||
71 | MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver."); | ||
72 | |||
73 | /* | ||
74 | * Generic SB DSP support routines | ||
75 | */ | ||
76 | |||
77 | #define DSP_PORT_RESET 0x6 | ||
78 | #define DSP_PORT_READ 0xa | ||
79 | #define DSP_PORT_COMMAND 0xc | ||
80 | #define DSP_PORT_STATUS 0xc | ||
81 | #define DSP_PORT_DATA_AVAIL 0xe | ||
82 | |||
83 | #define DSP_SIGNATURE 0xaa | ||
84 | |||
85 | #define DSP_COMMAND_GET_VERSION 0xe1 | ||
86 | |||
87 | static int __devinit dsp_get_byte(void __iomem *port, u8 *val) | ||
88 | { | ||
89 | int loops = 1000; | ||
90 | |||
91 | while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) { | ||
92 | if (!loops--) | ||
93 | return -EIO; | ||
94 | cpu_relax(); | ||
95 | } | ||
96 | *val = ioread8(port + DSP_PORT_READ); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int __devinit dsp_reset(void __iomem *port) | ||
101 | { | ||
102 | u8 val; | ||
103 | |||
104 | iowrite8(1, port + DSP_PORT_RESET); | ||
105 | udelay(10); | ||
106 | iowrite8(0, port + DSP_PORT_RESET); | ||
107 | |||
108 | if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE) | ||
109 | return -ENODEV; | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int __devinit dsp_command(void __iomem *port, u8 cmd) | ||
115 | { | ||
116 | int loops = 1000; | ||
117 | |||
118 | while (ioread8(port + DSP_PORT_STATUS) & 0x80) { | ||
119 | if (!loops--) | ||
120 | return -EIO; | ||
121 | cpu_relax(); | ||
122 | } | ||
123 | iowrite8(cmd, port + DSP_PORT_COMMAND); | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor) | ||
128 | { | ||
129 | int err; | ||
130 | |||
131 | err = dsp_command(port, DSP_COMMAND_GET_VERSION); | ||
132 | if (err < 0) | ||
133 | return err; | ||
134 | |||
135 | err = dsp_get_byte(port, major); | ||
136 | if (err < 0) | ||
137 | return err; | ||
138 | |||
139 | err = dsp_get_byte(port, minor); | ||
140 | if (err < 0) | ||
141 | return err; | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Generic WSS support routines | ||
148 | */ | ||
149 | |||
150 | #define WSS_CONFIG_DMA_0 (1 << 0) | ||
151 | #define WSS_CONFIG_DMA_1 (2 << 0) | ||
152 | #define WSS_CONFIG_DMA_3 (3 << 0) | ||
153 | #define WSS_CONFIG_DUPLEX (1 << 2) | ||
154 | #define WSS_CONFIG_IRQ_7 (1 << 3) | ||
155 | #define WSS_CONFIG_IRQ_9 (2 << 3) | ||
156 | #define WSS_CONFIG_IRQ_10 (3 << 3) | ||
157 | #define WSS_CONFIG_IRQ_11 (4 << 3) | ||
158 | |||
159 | #define WSS_PORT_CONFIG 0 | ||
160 | #define WSS_PORT_SIGNATURE 3 | ||
161 | |||
162 | #define WSS_SIGNATURE 4 | ||
163 | |||
164 | static int __devinit wss_detect(void __iomem *wss_port) | ||
165 | { | ||
166 | if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE) | ||
167 | return -ENODEV; | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static void wss_set_config(void __iomem *wss_port, u8 wss_config) | ||
173 | { | ||
174 | iowrite8(wss_config, wss_port + WSS_PORT_CONFIG); | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Aztech Sound Galaxy specifics | ||
179 | */ | ||
180 | |||
181 | #define GALAXY_PORT_CONFIG 1024 | ||
182 | #define CONFIG_PORT_SET 4 | ||
183 | |||
184 | #define DSP_COMMAND_GALAXY_8 8 | ||
185 | #define GALAXY_COMMAND_GET_TYPE 5 | ||
186 | |||
187 | #define DSP_COMMAND_GALAXY_9 9 | ||
188 | #define GALAXY_COMMAND_WSSMODE 0 | ||
189 | #define GALAXY_COMMAND_SB8MODE 1 | ||
190 | |||
191 | #define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE | ||
192 | #define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE | ||
193 | |||
194 | struct snd_galaxy { | ||
195 | void __iomem *port; | ||
196 | void __iomem *config_port; | ||
197 | void __iomem *wss_port; | ||
198 | u32 config; | ||
199 | struct resource *res_port; | ||
200 | struct resource *res_config_port; | ||
201 | struct resource *res_wss_port; | ||
202 | }; | ||
203 | |||
204 | static u32 config[SNDRV_CARDS]; | ||
205 | static u8 wss_config[SNDRV_CARDS]; | ||
206 | |||
207 | static int __devinit snd_galaxy_match(struct device *dev, unsigned int n) | ||
208 | { | ||
209 | if (!enable[n]) | ||
210 | return 0; | ||
211 | |||
212 | switch (port[n]) { | ||
213 | case SNDRV_AUTO_PORT: | ||
214 | dev_err(dev, "please specify port\n"); | ||
215 | return 0; | ||
216 | case 0x220: | ||
217 | config[n] |= GALAXY_CONFIG_SBA_220; | ||
218 | break; | ||
219 | case 0x240: | ||
220 | config[n] |= GALAXY_CONFIG_SBA_240; | ||
221 | break; | ||
222 | case 0x260: | ||
223 | config[n] |= GALAXY_CONFIG_SBA_260; | ||
224 | break; | ||
225 | case 0x280: | ||
226 | config[n] |= GALAXY_CONFIG_SBA_280; | ||
227 | break; | ||
228 | default: | ||
229 | dev_err(dev, "invalid port %#lx\n", port[n]); | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | switch (wss_port[n]) { | ||
234 | case SNDRV_AUTO_PORT: | ||
235 | dev_err(dev, "please specify wss_port\n"); | ||
236 | return 0; | ||
237 | case 0x530: | ||
238 | config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530; | ||
239 | break; | ||
240 | case 0x604: | ||
241 | config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604; | ||
242 | break; | ||
243 | case 0xe80: | ||
244 | config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80; | ||
245 | break; | ||
246 | case 0xf40: | ||
247 | config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40; | ||
248 | break; | ||
249 | default: | ||
250 | dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | switch (irq[n]) { | ||
255 | case SNDRV_AUTO_IRQ: | ||
256 | dev_err(dev, "please specify irq\n"); | ||
257 | return 0; | ||
258 | case 7: | ||
259 | wss_config[n] |= WSS_CONFIG_IRQ_7; | ||
260 | break; | ||
261 | case 2: | ||
262 | irq[n] = 9; | ||
263 | case 9: | ||
264 | wss_config[n] |= WSS_CONFIG_IRQ_9; | ||
265 | break; | ||
266 | case 10: | ||
267 | wss_config[n] |= WSS_CONFIG_IRQ_10; | ||
268 | break; | ||
269 | case 11: | ||
270 | wss_config[n] |= WSS_CONFIG_IRQ_11; | ||
271 | break; | ||
272 | default: | ||
273 | dev_err(dev, "invalid IRQ %d\n", irq[n]); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | switch (dma1[n]) { | ||
278 | case SNDRV_AUTO_DMA: | ||
279 | dev_err(dev, "please specify dma1\n"); | ||
280 | return 0; | ||
281 | case 0: | ||
282 | wss_config[n] |= WSS_CONFIG_DMA_0; | ||
283 | break; | ||
284 | case 1: | ||
285 | wss_config[n] |= WSS_CONFIG_DMA_1; | ||
286 | break; | ||
287 | case 3: | ||
288 | wss_config[n] |= WSS_CONFIG_DMA_3; | ||
289 | break; | ||
290 | default: | ||
291 | dev_err(dev, "invalid playback DMA %d\n", dma1[n]); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) { | ||
296 | dma2[n] = -1; | ||
297 | goto mpu; | ||
298 | } | ||
299 | |||
300 | wss_config[n] |= WSS_CONFIG_DUPLEX; | ||
301 | switch (dma2[n]) { | ||
302 | case 0: | ||
303 | break; | ||
304 | case 1: | ||
305 | if (dma1[n] == 0) | ||
306 | break; | ||
307 | default: | ||
308 | dev_err(dev, "invalid capture DMA %d\n", dma2[n]); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | mpu: | ||
313 | switch (mpu_port[n]) { | ||
314 | case SNDRV_AUTO_PORT: | ||
315 | dev_warn(dev, "mpu_port not specified; not using MPU-401\n"); | ||
316 | mpu_port[n] = -1; | ||
317 | goto fm; | ||
318 | case 0x300: | ||
319 | config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300; | ||
320 | break; | ||
321 | case 0x330: | ||
322 | config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330; | ||
323 | break; | ||
324 | default: | ||
325 | dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | switch (mpu_irq[n]) { | ||
330 | case SNDRV_AUTO_IRQ: | ||
331 | dev_warn(dev, "mpu_irq not specified: using polling mode\n"); | ||
332 | mpu_irq[n] = -1; | ||
333 | break; | ||
334 | case 2: | ||
335 | mpu_irq[n] = 9; | ||
336 | case 9: | ||
337 | config[n] |= GALAXY_CONFIG_MPUIRQ_2; | ||
338 | break; | ||
339 | #ifdef AZT1605 | ||
340 | case 3: | ||
341 | config[n] |= GALAXY_CONFIG_MPUIRQ_3; | ||
342 | break; | ||
343 | #endif | ||
344 | case 5: | ||
345 | config[n] |= GALAXY_CONFIG_MPUIRQ_5; | ||
346 | break; | ||
347 | case 7: | ||
348 | config[n] |= GALAXY_CONFIG_MPUIRQ_7; | ||
349 | break; | ||
350 | #ifdef AZT2316 | ||
351 | case 10: | ||
352 | config[n] |= GALAXY_CONFIG_MPUIRQ_10; | ||
353 | break; | ||
354 | #endif | ||
355 | default: | ||
356 | dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]); | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | if (mpu_irq[n] == irq[n]) { | ||
361 | dev_err(dev, "cannot share IRQ between WSS and MPU-401\n"); | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | fm: | ||
366 | switch (fm_port[n]) { | ||
367 | case SNDRV_AUTO_PORT: | ||
368 | dev_warn(dev, "fm_port not specified: not using OPL3\n"); | ||
369 | fm_port[n] = -1; | ||
370 | break; | ||
371 | case 0x388: | ||
372 | break; | ||
373 | default: | ||
374 | dev_err(dev, "illegal FM port %#lx\n", fm_port[n]); | ||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | config[n] |= GALAXY_CONFIG_GAME_ENABLE; | ||
379 | return 1; | ||
380 | } | ||
381 | |||
382 | static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type) | ||
383 | { | ||
384 | u8 major; | ||
385 | u8 minor; | ||
386 | int err; | ||
387 | |||
388 | err = dsp_reset(galaxy->port); | ||
389 | if (err < 0) | ||
390 | return err; | ||
391 | |||
392 | err = dsp_get_version(galaxy->port, &major, &minor); | ||
393 | if (err < 0) | ||
394 | return err; | ||
395 | |||
396 | if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR) | ||
397 | return -ENODEV; | ||
398 | |||
399 | err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8); | ||
400 | if (err < 0) | ||
401 | return err; | ||
402 | |||
403 | err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE); | ||
404 | if (err < 0) | ||
405 | return err; | ||
406 | |||
407 | err = dsp_get_byte(galaxy->port, type); | ||
408 | if (err < 0) | ||
409 | return err; | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode) | ||
415 | { | ||
416 | int err; | ||
417 | |||
418 | err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9); | ||
419 | if (err < 0) | ||
420 | return err; | ||
421 | |||
422 | err = dsp_command(galaxy->port, mode); | ||
423 | if (err < 0) | ||
424 | return err; | ||
425 | |||
426 | #ifdef AZT1605 | ||
427 | /* | ||
428 | * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again | ||
429 | */ | ||
430 | err = dsp_reset(galaxy->port); | ||
431 | if (err < 0) | ||
432 | return err; | ||
433 | #endif | ||
434 | |||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config) | ||
439 | { | ||
440 | u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET); | ||
441 | int i; | ||
442 | |||
443 | iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET); | ||
444 | for (i = 0; i < GALAXY_CONFIG_SIZE; i++) { | ||
445 | iowrite8(config, galaxy->config_port + i); | ||
446 | config >>= 8; | ||
447 | } | ||
448 | iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET); | ||
449 | msleep(10); | ||
450 | } | ||
451 | |||
452 | static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config) | ||
453 | { | ||
454 | int i; | ||
455 | |||
456 | for (i = GALAXY_CONFIG_SIZE; i; i--) { | ||
457 | u8 tmp = ioread8(galaxy->config_port + i - 1); | ||
458 | galaxy->config = (galaxy->config << 8) | tmp; | ||
459 | } | ||
460 | config |= galaxy->config & GALAXY_CONFIG_MASK; | ||
461 | galaxy_set_config(galaxy, config); | ||
462 | } | ||
463 | |||
464 | static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config) | ||
465 | { | ||
466 | int err; | ||
467 | |||
468 | err = wss_detect(galaxy->wss_port); | ||
469 | if (err < 0) | ||
470 | return err; | ||
471 | |||
472 | wss_set_config(galaxy->wss_port, wss_config); | ||
473 | |||
474 | err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS); | ||
475 | if (err < 0) | ||
476 | return err; | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static void snd_galaxy_free(struct snd_card *card) | ||
482 | { | ||
483 | struct snd_galaxy *galaxy = card->private_data; | ||
484 | |||
485 | if (galaxy->wss_port) { | ||
486 | wss_set_config(galaxy->wss_port, 0); | ||
487 | ioport_unmap(galaxy->wss_port); | ||
488 | release_and_free_resource(galaxy->res_wss_port); | ||
489 | } | ||
490 | if (galaxy->config_port) { | ||
491 | galaxy_set_config(galaxy, galaxy->config); | ||
492 | ioport_unmap(galaxy->config_port); | ||
493 | release_and_free_resource(galaxy->res_config_port); | ||
494 | } | ||
495 | if (galaxy->port) { | ||
496 | ioport_unmap(galaxy->port); | ||
497 | release_and_free_resource(galaxy->res_port); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n) | ||
502 | { | ||
503 | struct snd_galaxy *galaxy; | ||
504 | struct snd_wss *chip; | ||
505 | struct snd_card *card; | ||
506 | u8 type; | ||
507 | int err; | ||
508 | |||
509 | err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy, | ||
510 | &card); | ||
511 | if (err < 0) | ||
512 | return err; | ||
513 | |||
514 | snd_card_set_dev(card, dev); | ||
515 | |||
516 | card->private_free = snd_galaxy_free; | ||
517 | galaxy = card->private_data; | ||
518 | |||
519 | galaxy->res_port = request_region(port[n], 16, DRV_NAME); | ||
520 | if (!galaxy->res_port) { | ||
521 | dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n], | ||
522 | port[n] + 15); | ||
523 | err = -EBUSY; | ||
524 | goto error; | ||
525 | } | ||
526 | galaxy->port = ioport_map(port[n], 16); | ||
527 | |||
528 | err = galaxy_init(galaxy, &type); | ||
529 | if (err < 0) { | ||
530 | dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]); | ||
531 | goto error; | ||
532 | } | ||
533 | dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]); | ||
534 | |||
535 | galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG, | ||
536 | 16, DRV_NAME); | ||
537 | if (!galaxy->res_config_port) { | ||
538 | dev_err(dev, "could not grab ports %#lx-%#lx\n", | ||
539 | port[n] + GALAXY_PORT_CONFIG, | ||
540 | port[n] + GALAXY_PORT_CONFIG + 15); | ||
541 | err = -EBUSY; | ||
542 | goto error; | ||
543 | } | ||
544 | galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16); | ||
545 | |||
546 | galaxy_config(galaxy, config[n]); | ||
547 | |||
548 | galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME); | ||
549 | if (!galaxy->res_wss_port) { | ||
550 | dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n], | ||
551 | wss_port[n] + 3); | ||
552 | err = -EBUSY; | ||
553 | goto error; | ||
554 | } | ||
555 | galaxy->wss_port = ioport_map(wss_port[n], 4); | ||
556 | |||
557 | err = galaxy_wss_config(galaxy, wss_config[n]); | ||
558 | if (err < 0) { | ||
559 | dev_err(dev, "could not configure WSS\n"); | ||
560 | goto error; | ||
561 | } | ||
562 | |||
563 | strcpy(card->driver, DRV_NAME); | ||
564 | strcpy(card->shortname, DRV_NAME); | ||
565 | sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d", | ||
566 | card->shortname, port[n], wss_port[n], irq[n], dma1[n], | ||
567 | dma2[n]); | ||
568 | |||
569 | err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n], | ||
570 | dma2[n], WSS_HW_DETECT, 0, &chip); | ||
571 | if (err < 0) | ||
572 | goto error; | ||
573 | |||
574 | err = snd_wss_pcm(chip, 0, NULL); | ||
575 | if (err < 0) | ||
576 | goto error; | ||
577 | |||
578 | err = snd_wss_mixer(chip); | ||
579 | if (err < 0) | ||
580 | goto error; | ||
581 | |||
582 | err = snd_wss_timer(chip, 0, NULL); | ||
583 | if (err < 0) | ||
584 | goto error; | ||
585 | |||
586 | if (mpu_port[n] >= 0) { | ||
587 | err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, | ||
588 | mpu_port[n], 0, mpu_irq[n], | ||
589 | IRQF_DISABLED, NULL); | ||
590 | if (err < 0) | ||
591 | goto error; | ||
592 | } | ||
593 | |||
594 | if (fm_port[n] >= 0) { | ||
595 | struct snd_opl3 *opl3; | ||
596 | |||
597 | err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2, | ||
598 | OPL3_HW_AUTO, 0, &opl3); | ||
599 | if (err < 0) { | ||
600 | dev_err(dev, "no OPL device at %#lx\n", fm_port[n]); | ||
601 | goto error; | ||
602 | } | ||
603 | err = snd_opl3_timer_new(opl3, 1, 2); | ||
604 | if (err < 0) | ||
605 | goto error; | ||
606 | |||
607 | err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); | ||
608 | if (err < 0) | ||
609 | goto error; | ||
610 | } | ||
611 | |||
612 | err = snd_card_register(card); | ||
613 | if (err < 0) | ||
614 | goto error; | ||
615 | |||
616 | dev_set_drvdata(dev, card); | ||
617 | return 0; | ||
618 | |||
619 | error: | ||
620 | snd_card_free(card); | ||
621 | return err; | ||
622 | } | ||
623 | |||
624 | static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n) | ||
625 | { | ||
626 | snd_card_free(dev_get_drvdata(dev)); | ||
627 | dev_set_drvdata(dev, NULL); | ||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static struct isa_driver snd_galaxy_driver = { | ||
632 | .match = snd_galaxy_match, | ||
633 | .probe = snd_galaxy_probe, | ||
634 | .remove = __devexit_p(snd_galaxy_remove), | ||
635 | |||
636 | .driver = { | ||
637 | .name = DEV_NAME | ||
638 | } | ||
639 | }; | ||
640 | |||
641 | static int __init alsa_card_galaxy_init(void) | ||
642 | { | ||
643 | return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS); | ||
644 | } | ||
645 | |||
646 | static void __exit alsa_card_galaxy_exit(void) | ||
647 | { | ||
648 | isa_unregister_driver(&snd_galaxy_driver); | ||
649 | } | ||
650 | |||
651 | module_init(alsa_card_galaxy_init); | ||
652 | module_exit(alsa_card_galaxy_exit); | ||
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c deleted file mode 100644 index 6fe27b9d9440..000000000000 --- a/sound/isa/sgalaxy.c +++ /dev/null | |||
@@ -1,369 +0,0 @@ | |||
1 | /* | ||
2 | * Driver for Aztech Sound Galaxy cards | ||
3 | * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk. | ||
4 | * | ||
5 | * I don't have documentation for this card, I based this driver on the | ||
6 | * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/isa.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <asm/dma.h> | ||
32 | #include <sound/core.h> | ||
33 | #include <sound/sb.h> | ||
34 | #include <sound/wss.h> | ||
35 | #include <sound/control.h> | ||
36 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
37 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
38 | #include <sound/initval.h> | ||
39 | |||
40 | MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>"); | ||
41 | MODULE_DESCRIPTION("Aztech Sound Galaxy"); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}"); | ||
44 | |||
45 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
46 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
47 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
48 | static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ | ||
49 | static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ | ||
50 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ | ||
51 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
52 | |||
53 | module_param_array(index, int, NULL, 0444); | ||
54 | MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); | ||
55 | module_param_array(id, charp, NULL, 0444); | ||
56 | MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); | ||
57 | module_param_array(sbport, long, NULL, 0444); | ||
58 | MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); | ||
59 | module_param_array(wssport, long, NULL, 0444); | ||
60 | MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); | ||
61 | module_param_array(irq, int, NULL, 0444); | ||
62 | MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); | ||
63 | module_param_array(dma1, int, NULL, 0444); | ||
64 | MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); | ||
65 | |||
66 | #define SGALAXY_AUXC_LEFT 18 | ||
67 | #define SGALAXY_AUXC_RIGHT 19 | ||
68 | |||
69 | #define PFX "sgalaxy: " | ||
70 | |||
71 | /* | ||
72 | |||
73 | */ | ||
74 | |||
75 | #define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) | ||
76 | |||
77 | /* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */ | ||
78 | /* short time we actually need it.. */ | ||
79 | |||
80 | static int snd_sgalaxy_sbdsp_reset(unsigned long port) | ||
81 | { | ||
82 | int i; | ||
83 | |||
84 | outb(1, SBP1(port, RESET)); | ||
85 | udelay(10); | ||
86 | outb(0, SBP1(port, RESET)); | ||
87 | udelay(30); | ||
88 | for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); | ||
89 | if (inb(SBP1(port, READ)) != 0xaa) { | ||
90 | snd_printd("sb_reset: failed at 0x%lx!!!\n", port); | ||
91 | return -ENODEV; | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port, | ||
97 | unsigned char val) | ||
98 | { | ||
99 | int i; | ||
100 | |||
101 | for (i = 10000; i; i--) | ||
102 | if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { | ||
103 | outb(val, SBP1(port, COMMAND)); | ||
104 | return 1; | ||
105 | } | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id) | ||
111 | { | ||
112 | return IRQ_NONE; | ||
113 | } | ||
114 | |||
115 | static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) | ||
116 | { | ||
117 | static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, | ||
118 | 0x10, 0x18, 0x20, -1, -1, -1, -1}; | ||
119 | static int dma_bits[] = {1, 2, 0, 3}; | ||
120 | int tmp, tmp1; | ||
121 | |||
122 | if ((tmp = inb(port + 3)) == 0xff) | ||
123 | { | ||
124 | snd_printdd("I/O address dead (0x%lx)\n", port); | ||
125 | return 0; | ||
126 | } | ||
127 | #if 0 | ||
128 | snd_printdd("WSS signature = 0x%x\n", tmp); | ||
129 | #endif | ||
130 | |||
131 | if ((tmp & 0x3f) != 0x04 && | ||
132 | (tmp & 0x3f) != 0x0f && | ||
133 | (tmp & 0x3f) != 0x00) { | ||
134 | snd_printdd("No WSS signature detected on port 0x%lx\n", | ||
135 | port + 3); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | #if 0 | ||
140 | snd_printdd(PFX "setting up IRQ/DMA for WSS\n"); | ||
141 | #endif | ||
142 | |||
143 | /* initialize IRQ for WSS codec */ | ||
144 | tmp = interrupt_bits[irq % 16]; | ||
145 | if (tmp < 0) | ||
146 | return -EINVAL; | ||
147 | |||
148 | if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) { | ||
149 | snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq); | ||
150 | return -EIO; | ||
151 | } | ||
152 | |||
153 | outb(tmp | 0x40, port); | ||
154 | tmp1 = dma_bits[dma % 4]; | ||
155 | outb(tmp | tmp1, port); | ||
156 | |||
157 | free_irq(irq, NULL); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma) | ||
163 | { | ||
164 | #if 0 | ||
165 | snd_printdd(PFX "switching to WSS mode\n"); | ||
166 | #endif | ||
167 | |||
168 | /* switch to WSS mode */ | ||
169 | snd_sgalaxy_sbdsp_reset(sbport[dev]); | ||
170 | |||
171 | snd_sgalaxy_sbdsp_command(sbport[dev], 9); | ||
172 | snd_sgalaxy_sbdsp_command(sbport[dev], 0); | ||
173 | |||
174 | udelay(400); | ||
175 | return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); | ||
176 | } | ||
177 | |||
178 | static struct snd_kcontrol_new snd_sgalaxy_controls[] = { | ||
179 | WSS_DOUBLE("Aux Playback Switch", 0, | ||
180 | SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), | ||
181 | WSS_DOUBLE("Aux Playback Volume", 0, | ||
182 | SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) | ||
183 | }; | ||
184 | |||
185 | static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip) | ||
186 | { | ||
187 | struct snd_card *card = chip->card; | ||
188 | struct snd_ctl_elem_id id1, id2; | ||
189 | unsigned int idx; | ||
190 | int err; | ||
191 | |||
192 | memset(&id1, 0, sizeof(id1)); | ||
193 | memset(&id2, 0, sizeof(id2)); | ||
194 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
195 | /* reassign AUX0 to LINE */ | ||
196 | strcpy(id1.name, "Aux Playback Switch"); | ||
197 | strcpy(id2.name, "Line Playback Switch"); | ||
198 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
199 | return err; | ||
200 | strcpy(id1.name, "Aux Playback Volume"); | ||
201 | strcpy(id2.name, "Line Playback Volume"); | ||
202 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
203 | return err; | ||
204 | /* reassign AUX1 to FM */ | ||
205 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
206 | strcpy(id2.name, "FM Playback Switch"); | ||
207 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
208 | return err; | ||
209 | strcpy(id1.name, "Aux Playback Volume"); | ||
210 | strcpy(id2.name, "FM Playback Volume"); | ||
211 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
212 | return err; | ||
213 | /* build AUX2 input */ | ||
214 | for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { | ||
215 | err = snd_ctl_add(card, | ||
216 | snd_ctl_new1(&snd_sgalaxy_controls[idx], chip)); | ||
217 | if (err < 0) | ||
218 | return err; | ||
219 | } | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev) | ||
224 | { | ||
225 | if (!enable[dev]) | ||
226 | return 0; | ||
227 | if (sbport[dev] == SNDRV_AUTO_PORT) { | ||
228 | snd_printk(KERN_ERR PFX "specify SB port\n"); | ||
229 | return 0; | ||
230 | } | ||
231 | if (wssport[dev] == SNDRV_AUTO_PORT) { | ||
232 | snd_printk(KERN_ERR PFX "specify WSS port\n"); | ||
233 | return 0; | ||
234 | } | ||
235 | return 1; | ||
236 | } | ||
237 | |||
238 | static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev) | ||
239 | { | ||
240 | static int possible_irqs[] = {7, 9, 10, 11, -1}; | ||
241 | static int possible_dmas[] = {1, 3, 0, -1}; | ||
242 | int err, xirq, xdma1; | ||
243 | struct snd_card *card; | ||
244 | struct snd_wss *chip; | ||
245 | |||
246 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); | ||
247 | if (err < 0) | ||
248 | return err; | ||
249 | |||
250 | xirq = irq[dev]; | ||
251 | if (xirq == SNDRV_AUTO_IRQ) { | ||
252 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
253 | snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); | ||
254 | err = -EBUSY; | ||
255 | goto _err; | ||
256 | } | ||
257 | } | ||
258 | xdma1 = dma1[dev]; | ||
259 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
260 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
261 | snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); | ||
262 | err = -EBUSY; | ||
263 | goto _err; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0) | ||
268 | goto _err; | ||
269 | |||
270 | err = snd_wss_create(card, wssport[dev] + 4, -1, | ||
271 | xirq, xdma1, -1, | ||
272 | WSS_HW_DETECT, 0, &chip); | ||
273 | if (err < 0) | ||
274 | goto _err; | ||
275 | card->private_data = chip; | ||
276 | |||
277 | err = snd_wss_pcm(chip, 0, NULL); | ||
278 | if (err < 0) { | ||
279 | snd_printdd(PFX "error creating new WSS PCM device\n"); | ||
280 | goto _err; | ||
281 | } | ||
282 | err = snd_wss_mixer(chip); | ||
283 | if (err < 0) { | ||
284 | snd_printdd(PFX "error creating new WSS mixer\n"); | ||
285 | goto _err; | ||
286 | } | ||
287 | if ((err = snd_sgalaxy_mixer(chip)) < 0) { | ||
288 | snd_printdd(PFX "the mixer rewrite failed\n"); | ||
289 | goto _err; | ||
290 | } | ||
291 | |||
292 | strcpy(card->driver, "Sound Galaxy"); | ||
293 | strcpy(card->shortname, "Sound Galaxy"); | ||
294 | sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", | ||
295 | wssport[dev], xirq, xdma1); | ||
296 | |||
297 | snd_card_set_dev(card, devptr); | ||
298 | |||
299 | if ((err = snd_card_register(card)) < 0) | ||
300 | goto _err; | ||
301 | |||
302 | dev_set_drvdata(devptr, card); | ||
303 | return 0; | ||
304 | |||
305 | _err: | ||
306 | snd_card_free(card); | ||
307 | return err; | ||
308 | } | ||
309 | |||
310 | static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev) | ||
311 | { | ||
312 | snd_card_free(dev_get_drvdata(devptr)); | ||
313 | dev_set_drvdata(devptr, NULL); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | #ifdef CONFIG_PM | ||
318 | static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n, | ||
319 | pm_message_t state) | ||
320 | { | ||
321 | struct snd_card *card = dev_get_drvdata(pdev); | ||
322 | struct snd_wss *chip = card->private_data; | ||
323 | |||
324 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
325 | chip->suspend(chip); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int snd_sgalaxy_resume(struct device *pdev, unsigned int n) | ||
330 | { | ||
331 | struct snd_card *card = dev_get_drvdata(pdev); | ||
332 | struct snd_wss *chip = card->private_data; | ||
333 | |||
334 | chip->resume(chip); | ||
335 | snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]); | ||
336 | snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]); | ||
337 | |||
338 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
339 | return 0; | ||
340 | } | ||
341 | #endif | ||
342 | |||
343 | #define DEV_NAME "sgalaxy" | ||
344 | |||
345 | static struct isa_driver snd_sgalaxy_driver = { | ||
346 | .match = snd_sgalaxy_match, | ||
347 | .probe = snd_sgalaxy_probe, | ||
348 | .remove = __devexit_p(snd_sgalaxy_remove), | ||
349 | #ifdef CONFIG_PM | ||
350 | .suspend = snd_sgalaxy_suspend, | ||
351 | .resume = snd_sgalaxy_resume, | ||
352 | #endif | ||
353 | .driver = { | ||
354 | .name = DEV_NAME | ||
355 | }, | ||
356 | }; | ||
357 | |||
358 | static int __init alsa_card_sgalaxy_init(void) | ||
359 | { | ||
360 | return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS); | ||
361 | } | ||
362 | |||
363 | static void __exit alsa_card_sgalaxy_exit(void) | ||
364 | { | ||
365 | isa_unregister_driver(&snd_sgalaxy_driver); | ||
366 | } | ||
367 | |||
368 | module_init(alsa_card_sgalaxy_init) | ||
369 | module_exit(alsa_card_sgalaxy_exit) | ||
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index d216362626d0..712c1710f9a2 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c | |||
@@ -563,6 +563,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) | |||
563 | case ICE1712_SUBDEVICE_DELTA1010E: | 563 | case ICE1712_SUBDEVICE_DELTA1010E: |
564 | case ICE1712_SUBDEVICE_DELTA1010LT: | 564 | case ICE1712_SUBDEVICE_DELTA1010LT: |
565 | case ICE1712_SUBDEVICE_MEDIASTATION: | 565 | case ICE1712_SUBDEVICE_MEDIASTATION: |
566 | case ICE1712_SUBDEVICE_EDIROLDA2496: | ||
566 | ice->num_total_dacs = 8; | 567 | ice->num_total_dacs = 8; |
567 | ice->num_total_adcs = 8; | 568 | ice->num_total_adcs = 8; |
568 | break; | 569 | break; |
@@ -635,6 +636,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) | |||
635 | err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice); | 636 | err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice); |
636 | break; | 637 | break; |
637 | case ICE1712_SUBDEVICE_DELTA1010LT: | 638 | case ICE1712_SUBDEVICE_DELTA1010LT: |
639 | case ICE1712_SUBDEVICE_EDIROLDA2496: | ||
638 | err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice); | 640 | err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice); |
639 | break; | 641 | break; |
640 | case ICE1712_SUBDEVICE_DELTA66: | 642 | case ICE1712_SUBDEVICE_DELTA66: |
@@ -734,6 +736,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) | |||
734 | case ICE1712_SUBDEVICE_DELTA66: | 736 | case ICE1712_SUBDEVICE_DELTA66: |
735 | case ICE1712_SUBDEVICE_VX442: | 737 | case ICE1712_SUBDEVICE_VX442: |
736 | case ICE1712_SUBDEVICE_DELTA66E: | 738 | case ICE1712_SUBDEVICE_DELTA66E: |
739 | case ICE1712_SUBDEVICE_EDIROLDA2496: | ||
737 | err = snd_ice1712_akm4xxx_build_controls(ice); | 740 | err = snd_ice1712_akm4xxx_build_controls(ice); |
738 | if (err < 0) | 741 | if (err < 0) |
739 | return err; | 742 | return err; |
@@ -813,5 +816,12 @@ struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = { | |||
813 | .chip_init = snd_ice1712_delta_init, | 816 | .chip_init = snd_ice1712_delta_init, |
814 | .build_controls = snd_ice1712_delta_add_controls, | 817 | .build_controls = snd_ice1712_delta_add_controls, |
815 | }, | 818 | }, |
819 | { | ||
820 | .subvendor = ICE1712_SUBDEVICE_EDIROLDA2496, | ||
821 | .name = "Edirol DA2496", | ||
822 | .model = "da2496", | ||
823 | .chip_init = snd_ice1712_delta_init, | ||
824 | .build_controls = snd_ice1712_delta_add_controls, | ||
825 | }, | ||
816 | { } /* terminator */ | 826 | { } /* terminator */ |
817 | }; | 827 | }; |
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index f7f14df81f26..1a0ac6cd6501 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h | |||
@@ -34,7 +34,8 @@ | |||
34 | "{MidiMan M Audio,Delta 410},"\ | 34 | "{MidiMan M Audio,Delta 410},"\ |
35 | "{MidiMan M Audio,Audiophile 24/96},"\ | 35 | "{MidiMan M Audio,Audiophile 24/96},"\ |
36 | "{Digigram,VX442},"\ | 36 | "{Digigram,VX442},"\ |
37 | "{Lionstracs,Mediastation}," | 37 | "{Lionstracs,Mediastation},"\ |
38 | "{Edirol,DA2496}," | ||
38 | 39 | ||
39 | #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 | 40 | #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 |
40 | #define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6 | 41 | #define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6 |
@@ -47,6 +48,7 @@ | |||
47 | #define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6 | 48 | #define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6 |
48 | #define ICE1712_SUBDEVICE_VX442 0x12143cd6 | 49 | #define ICE1712_SUBDEVICE_VX442 0x12143cd6 |
49 | #define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100 | 50 | #define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100 |
51 | #define ICE1712_SUBDEVICE_EDIROLDA2496 0xce164010 | ||
50 | 52 | ||
51 | /* entry point */ | 53 | /* entry point */ |
52 | extern struct snd_ice1712_card_info snd_ice1712_delta_cards[]; | 54 | extern struct snd_ice1712_card_info snd_ice1712_delta_cards[]; |
diff --git a/sound/usb/card.c b/sound/usb/card.c index 32e4be8a187c..4aa4678e0a01 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c | |||
@@ -300,9 +300,13 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, | |||
300 | 300 | ||
301 | *rchip = NULL; | 301 | *rchip = NULL; |
302 | 302 | ||
303 | if (snd_usb_get_speed(dev) != USB_SPEED_LOW && | 303 | switch (snd_usb_get_speed(dev)) { |
304 | snd_usb_get_speed(dev) != USB_SPEED_FULL && | 304 | case USB_SPEED_LOW: |
305 | snd_usb_get_speed(dev) != USB_SPEED_HIGH) { | 305 | case USB_SPEED_FULL: |
306 | case USB_SPEED_HIGH: | ||
307 | case USB_SPEED_SUPER: | ||
308 | break; | ||
309 | default: | ||
306 | snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev)); | 310 | snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev)); |
307 | return -ENXIO; | 311 | return -ENXIO; |
308 | } | 312 | } |
@@ -378,11 +382,22 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, | |||
378 | if (len < sizeof(card->longname)) | 382 | if (len < sizeof(card->longname)) |
379 | usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); | 383 | usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); |
380 | 384 | ||
381 | strlcat(card->longname, | 385 | switch (snd_usb_get_speed(dev)) { |
382 | snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" : | 386 | case USB_SPEED_LOW: |
383 | snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" : | 387 | strlcat(card->longname, ", low speed", sizeof(card->longname)); |
384 | ", high speed", | 388 | break; |
385 | sizeof(card->longname)); | 389 | case USB_SPEED_FULL: |
390 | strlcat(card->longname, ", full speed", sizeof(card->longname)); | ||
391 | break; | ||
392 | case USB_SPEED_HIGH: | ||
393 | strlcat(card->longname, ", high speed", sizeof(card->longname)); | ||
394 | break; | ||
395 | case USB_SPEED_SUPER: | ||
396 | strlcat(card->longname, ", super speed", sizeof(card->longname)); | ||
397 | break; | ||
398 | default: | ||
399 | break; | ||
400 | } | ||
386 | 401 | ||
387 | snd_usb_audio_create_proc(chip); | 402 | snd_usb_audio_create_proc(chip); |
388 | 403 | ||
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index ef0a07e34844..b0ef9f501896 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c | |||
@@ -405,8 +405,6 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) | |||
405 | break; | 405 | break; |
406 | case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ | 406 | case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ |
407 | case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ | 407 | case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ |
408 | case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */ | ||
409 | case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ | ||
410 | /* doesn't set the sample rate attribute, but supports it */ | 408 | /* doesn't set the sample rate attribute, but supports it */ |
411 | fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; | 409 | fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; |
412 | break; | 410 | break; |
diff --git a/sound/usb/helper.c b/sound/usb/helper.c index d48d6f8f6ac9..f280c1903c25 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c | |||
@@ -103,11 +103,16 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, | |||
103 | unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, | 103 | unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, |
104 | struct usb_host_interface *alts) | 104 | struct usb_host_interface *alts) |
105 | { | 105 | { |
106 | if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH && | 106 | switch (snd_usb_get_speed(chip->dev)) { |
107 | get_endpoint(alts, 0)->bInterval >= 1 && | 107 | case USB_SPEED_HIGH: |
108 | get_endpoint(alts, 0)->bInterval <= 4) | 108 | case USB_SPEED_SUPER: |
109 | return get_endpoint(alts, 0)->bInterval - 1; | 109 | if (get_endpoint(alts, 0)->bInterval >= 1 && |
110 | else | 110 | get_endpoint(alts, 0)->bInterval <= 4) |
111 | return 0; | 111 | return get_endpoint(alts, 0)->bInterval - 1; |
112 | break; | ||
113 | default: | ||
114 | break; | ||
115 | } | ||
116 | return 0; | ||
112 | } | 117 | } |
113 | 118 | ||
diff --git a/sound/usb/midi.c b/sound/usb/midi.c index b9c2bc65f51a..156cd0716c42 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c | |||
@@ -834,7 +834,14 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, | |||
834 | 834 | ||
835 | if (!ep->ports[0].active) | 835 | if (!ep->ports[0].active) |
836 | return; | 836 | return; |
837 | count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2; | 837 | switch (snd_usb_get_speed(ep->umidi->dev)) { |
838 | case USB_SPEED_HIGH: | ||
839 | case USB_SPEED_SUPER: | ||
840 | count = 1; | ||
841 | break; | ||
842 | default: | ||
843 | count = 2; | ||
844 | } | ||
838 | count = snd_rawmidi_transmit(ep->ports[0].substream, | 845 | count = snd_rawmidi_transmit(ep->ports[0].substream, |
839 | urb->transfer_buffer, | 846 | urb->transfer_buffer, |
840 | count); | 847 | count); |
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 3b5135c93062..f49756c1b837 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c | |||
@@ -466,7 +466,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs, | |||
466 | return 0; | 466 | return 0; |
467 | } | 467 | } |
468 | /* check whether the period time is >= the data packet interval */ | 468 | /* check whether the period time is >= the data packet interval */ |
469 | if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) { | 469 | if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) { |
470 | ptime = 125 * (1 << fp->datainterval); | 470 | ptime = 125 * (1 << fp->datainterval); |
471 | if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { | 471 | if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { |
472 | hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); | 472 | hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); |
@@ -734,7 +734,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre | |||
734 | } | 734 | } |
735 | 735 | ||
736 | param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; | 736 | param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; |
737 | if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH) | 737 | if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) |
738 | /* full speed devices have fixed data packet interval */ | 738 | /* full speed devices have fixed data packet interval */ |
739 | ptmin = 1000; | 739 | ptmin = 1000; |
740 | if (ptmin == 1000) | 740 | if (ptmin == 1000) |
diff --git a/sound/usb/proc.c b/sound/usb/proc.c index f5e3f356b95f..3c650ab3c91d 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c | |||
@@ -107,7 +107,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s | |||
107 | } | 107 | } |
108 | snd_iprintf(buffer, "\n"); | 108 | snd_iprintf(buffer, "\n"); |
109 | } | 109 | } |
110 | if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) | 110 | if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) |
111 | snd_iprintf(buffer, " Data packet interval: %d us\n", | 111 | snd_iprintf(buffer, " Data packet interval: %d us\n", |
112 | 125 * (1 << fp->datainterval)); | 112 | 125 * (1 << fp->datainterval)); |
113 | // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); | 113 | // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); |
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 2e8003f98fca..c86c613e0b96 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h | |||
@@ -1136,11 +1136,34 @@ YAMAHA_DEVICE(0x7010, "UB99"), | |||
1136 | } | 1136 | } |
1137 | }, | 1137 | }, |
1138 | { | 1138 | { |
1139 | /* has ID 0x0066 when not in "Advanced Driver" mode */ | ||
1140 | USB_DEVICE(0x0582, 0x0064), | ||
1141 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { | ||
1142 | /* .vendor_name = "EDIROL", */ | ||
1143 | /* .product_name = "PCR-1", */ | ||
1144 | .ifnum = QUIRK_ANY_INTERFACE, | ||
1145 | .type = QUIRK_COMPOSITE, | ||
1146 | .data = (const struct snd_usb_audio_quirk[]) { | ||
1147 | { | ||
1148 | .ifnum = 1, | ||
1149 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | ||
1150 | }, | ||
1151 | { | ||
1152 | .ifnum = 2, | ||
1153 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | ||
1154 | }, | ||
1155 | { | ||
1156 | .ifnum = -1 | ||
1157 | } | ||
1158 | } | ||
1159 | } | ||
1160 | }, | ||
1161 | { | ||
1139 | /* has ID 0x0067 when not in "Advanced Driver" mode */ | 1162 | /* has ID 0x0067 when not in "Advanced Driver" mode */ |
1140 | USB_DEVICE(0x0582, 0x0065), | 1163 | USB_DEVICE(0x0582, 0x0065), |
1141 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { | 1164 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { |
1142 | .vendor_name = "EDIROL", | 1165 | /* .vendor_name = "EDIROL", */ |
1143 | .product_name = "PCR-1", | 1166 | /* .product_name = "PCR-1", */ |
1144 | .ifnum = 0, | 1167 | .ifnum = 0, |
1145 | .type = QUIRK_MIDI_FIXED_ENDPOINT, | 1168 | .type = QUIRK_MIDI_FIXED_ENDPOINT, |
1146 | .data = & (const struct snd_usb_midi_endpoint_info) { | 1169 | .data = & (const struct snd_usb_midi_endpoint_info) { |
@@ -1525,6 +1548,50 @@ YAMAHA_DEVICE(0x7010, "UB99"), | |||
1525 | } | 1548 | } |
1526 | } | 1549 | } |
1527 | }, | 1550 | }, |
1551 | { | ||
1552 | /* has ID 0x0110 when not in Advanced Driver mode */ | ||
1553 | USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f), | ||
1554 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { | ||
1555 | /* .vendor_name = "Roland", */ | ||
1556 | /* .product_name = "A-PRO", */ | ||
1557 | .ifnum = 1, | ||
1558 | .type = QUIRK_MIDI_FIXED_ENDPOINT, | ||
1559 | .data = & (const struct snd_usb_midi_endpoint_info) { | ||
1560 | .out_cables = 0x0003, | ||
1561 | .in_cables = 0x0007 | ||
1562 | } | ||
1563 | } | ||
1564 | }, | ||
1565 | { | ||
1566 | USB_DEVICE(0x0582, 0x0113), | ||
1567 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { | ||
1568 | /* .vendor_name = "BOSS", */ | ||
1569 | /* .product_name = "ME-25", */ | ||
1570 | .ifnum = QUIRK_ANY_INTERFACE, | ||
1571 | .type = QUIRK_COMPOSITE, | ||
1572 | .data = (const struct snd_usb_audio_quirk[]) { | ||
1573 | { | ||
1574 | .ifnum = 0, | ||
1575 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | ||
1576 | }, | ||
1577 | { | ||
1578 | .ifnum = 1, | ||
1579 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | ||
1580 | }, | ||
1581 | { | ||
1582 | .ifnum = 2, | ||
1583 | .type = QUIRK_MIDI_FIXED_ENDPOINT, | ||
1584 | .data = & (const struct snd_usb_midi_endpoint_info) { | ||
1585 | .out_cables = 0x0001, | ||
1586 | .in_cables = 0x0001 | ||
1587 | } | ||
1588 | }, | ||
1589 | { | ||
1590 | .ifnum = -1 | ||
1591 | } | ||
1592 | } | ||
1593 | } | ||
1594 | }, | ||
1528 | 1595 | ||
1529 | /* Guillemot devices */ | 1596 | /* Guillemot devices */ |
1530 | { | 1597 | { |
@@ -1830,7 +1897,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), | |||
1830 | USB_DEVICE(0x0763, 0x2080), | 1897 | USB_DEVICE(0x0763, 0x2080), |
1831 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { | 1898 | .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { |
1832 | /* .vendor_name = "M-Audio", */ | 1899 | /* .vendor_name = "M-Audio", */ |
1833 | /* .product_name = "Fast Track Ultra 8", */ | 1900 | /* .product_name = "Fast Track Ultra", */ |
1834 | .ifnum = QUIRK_ANY_INTERFACE, | 1901 | .ifnum = QUIRK_ANY_INTERFACE, |
1835 | .type = QUIRK_COMPOSITE, | 1902 | .type = QUIRK_COMPOSITE, |
1836 | .data = & (const struct snd_usb_audio_quirk[]) { | 1903 | .data = & (const struct snd_usb_audio_quirk[]) { |
@@ -1840,11 +1907,51 @@ YAMAHA_DEVICE(0x7010, "UB99"), | |||
1840 | }, | 1907 | }, |
1841 | { | 1908 | { |
1842 | .ifnum = 1, | 1909 | .ifnum = 1, |
1843 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | 1910 | .type = QUIRK_AUDIO_FIXED_ENDPOINT, |
1911 | .data = & (const struct audioformat) { | ||
1912 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
1913 | .channels = 8, | ||
1914 | .iface = 1, | ||
1915 | .altsetting = 1, | ||
1916 | .altset_idx = 1, | ||
1917 | .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, | ||
1918 | .endpoint = 0x01, | ||
1919 | .ep_attr = 0x09, | ||
1920 | .rates = SNDRV_PCM_RATE_44100 | | ||
1921 | SNDRV_PCM_RATE_48000 | | ||
1922 | SNDRV_PCM_RATE_88200 | | ||
1923 | SNDRV_PCM_RATE_96000, | ||
1924 | .rate_min = 44100, | ||
1925 | .rate_max = 96000, | ||
1926 | .nr_rates = 4, | ||
1927 | .rate_table = (unsigned int[]) { | ||
1928 | 44100, 48000, 88200, 96000 | ||
1929 | } | ||
1930 | } | ||
1844 | }, | 1931 | }, |
1845 | { | 1932 | { |
1846 | .ifnum = 2, | 1933 | .ifnum = 2, |
1847 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | 1934 | .type = QUIRK_AUDIO_FIXED_ENDPOINT, |
1935 | .data = & (const struct audioformat) { | ||
1936 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
1937 | .channels = 8, | ||
1938 | .iface = 2, | ||
1939 | .altsetting = 1, | ||
1940 | .altset_idx = 1, | ||
1941 | .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, | ||
1942 | .endpoint = 0x81, | ||
1943 | .ep_attr = 0x05, | ||
1944 | .rates = SNDRV_PCM_RATE_44100 | | ||
1945 | SNDRV_PCM_RATE_48000 | | ||
1946 | SNDRV_PCM_RATE_88200 | | ||
1947 | SNDRV_PCM_RATE_96000, | ||
1948 | .rate_min = 44100, | ||
1949 | .rate_max = 96000, | ||
1950 | .nr_rates = 4, | ||
1951 | .rate_table = (unsigned int[]) { | ||
1952 | 44100, 48000, 88200, 96000 | ||
1953 | } | ||
1954 | } | ||
1848 | }, | 1955 | }, |
1849 | /* interface 3 (MIDI) is standard compliant */ | 1956 | /* interface 3 (MIDI) is standard compliant */ |
1850 | { | 1957 | { |
@@ -1867,11 +1974,51 @@ YAMAHA_DEVICE(0x7010, "UB99"), | |||
1867 | }, | 1974 | }, |
1868 | { | 1975 | { |
1869 | .ifnum = 1, | 1976 | .ifnum = 1, |
1870 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | 1977 | .type = QUIRK_AUDIO_FIXED_ENDPOINT, |
1978 | .data = & (const struct audioformat) { | ||
1979 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
1980 | .channels = 8, | ||
1981 | .iface = 1, | ||
1982 | .altsetting = 1, | ||
1983 | .altset_idx = 1, | ||
1984 | .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, | ||
1985 | .endpoint = 0x01, | ||
1986 | .ep_attr = 0x09, | ||
1987 | .rates = SNDRV_PCM_RATE_44100 | | ||
1988 | SNDRV_PCM_RATE_48000 | | ||
1989 | SNDRV_PCM_RATE_88200 | | ||
1990 | SNDRV_PCM_RATE_96000, | ||
1991 | .rate_min = 44100, | ||
1992 | .rate_max = 96000, | ||
1993 | .nr_rates = 4, | ||
1994 | .rate_table = (unsigned int[]) { | ||
1995 | 44100, 48000, 88200, 96000 | ||
1996 | } | ||
1997 | } | ||
1871 | }, | 1998 | }, |
1872 | { | 1999 | { |
1873 | .ifnum = 2, | 2000 | .ifnum = 2, |
1874 | .type = QUIRK_AUDIO_STANDARD_INTERFACE | 2001 | .type = QUIRK_AUDIO_FIXED_ENDPOINT, |
2002 | .data = & (const struct audioformat) { | ||
2003 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, | ||
2004 | .channels = 8, | ||
2005 | .iface = 2, | ||
2006 | .altsetting = 1, | ||
2007 | .altset_idx = 1, | ||
2008 | .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, | ||
2009 | .endpoint = 0x81, | ||
2010 | .ep_attr = 0x05, | ||
2011 | .rates = SNDRV_PCM_RATE_44100 | | ||
2012 | SNDRV_PCM_RATE_48000 | | ||
2013 | SNDRV_PCM_RATE_88200 | | ||
2014 | SNDRV_PCM_RATE_96000, | ||
2015 | .rate_min = 44100, | ||
2016 | .rate_max = 96000, | ||
2017 | .nr_rates = 4, | ||
2018 | .rate_table = (unsigned int[]) { | ||
2019 | 44100, 48000, 88200, 96000 | ||
2020 | } | ||
2021 | } | ||
1875 | }, | 2022 | }, |
1876 | /* interface 3 (MIDI) is standard compliant */ | 2023 | /* interface 3 (MIDI) is standard compliant */ |
1877 | { | 2024 | { |
diff --git a/sound/usb/urb.c b/sound/usb/urb.c index de607d4411ac..8deeaad10f10 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c | |||
@@ -244,7 +244,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, | |||
244 | else | 244 | else |
245 | subs->curpacksize = maxsize; | 245 | subs->curpacksize = maxsize; |
246 | 246 | ||
247 | if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) | 247 | if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) |
248 | packs_per_ms = 8 >> subs->datainterval; | 248 | packs_per_ms = 8 >> subs->datainterval; |
249 | else | 249 | else |
250 | packs_per_ms = 1; | 250 | packs_per_ms = 1; |