diff options
Diffstat (limited to 'sound/drivers')
33 files changed, 13017 insertions, 0 deletions
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig new file mode 100644 index 000000000000..3b2bee19e2c0 --- /dev/null +++ b/sound/drivers/Kconfig | |||
@@ -0,0 +1,98 @@ | |||
1 | # ALSA generic drivers | ||
2 | |||
3 | menu "Generic devices" | ||
4 | depends on SND!=n | ||
5 | |||
6 | |||
7 | config SND_MPU401_UART | ||
8 | tristate | ||
9 | select SND_TIMER | ||
10 | select SND_RAWMIDI | ||
11 | |||
12 | config SND_OPL3_LIB | ||
13 | tristate | ||
14 | select SND_TIMER | ||
15 | select SND_HWDEP | ||
16 | |||
17 | config SND_OPL4_LIB | ||
18 | tristate | ||
19 | select SND_TIMER | ||
20 | select SND_HWDEP | ||
21 | |||
22 | config SND_VX_LIB | ||
23 | tristate | ||
24 | select SND_HWDEP | ||
25 | select SND_PCM | ||
26 | |||
27 | |||
28 | config SND_DUMMY | ||
29 | tristate "Dummy (/dev/null) soundcard" | ||
30 | depends on SND | ||
31 | select SND_PCM | ||
32 | help | ||
33 | Say Y here to include the dummy driver. This driver does | ||
34 | nothing, but emulates various mixer controls and PCM devices. | ||
35 | |||
36 | You don't need this unless you're testing the hardware support | ||
37 | of programs using the ALSA API. | ||
38 | |||
39 | To compile this driver as a module, choose M here: the module | ||
40 | will be called snd-dummy. | ||
41 | |||
42 | config SND_VIRMIDI | ||
43 | tristate "Virtual MIDI soundcard" | ||
44 | depends on SND_SEQUENCER | ||
45 | select SND_TIMER | ||
46 | select SND_RAWMIDI | ||
47 | help | ||
48 | Say Y here to include the virtual MIDI driver. This driver | ||
49 | allows to connect applications using raw MIDI devices to | ||
50 | sequencer clients. | ||
51 | |||
52 | If you don't know what MIDI is, say N here. | ||
53 | |||
54 | To compile this driver as a module, choose M here: the module | ||
55 | will be called snd-virmidi. | ||
56 | |||
57 | config SND_MTPAV | ||
58 | tristate "MOTU MidiTimePiece AV multiport MIDI" | ||
59 | depends on SND | ||
60 | select SND_TIMER | ||
61 | select SND_RAWMIDI | ||
62 | help | ||
63 | To use a MOTU MidiTimePiece AV multiport MIDI adapter | ||
64 | connected to the parallel port, say Y here and make sure that | ||
65 | the standard parallel port driver isn't used for the port. | ||
66 | |||
67 | To compile this driver as a module, choose M here: the module | ||
68 | will be called snd-mtpav. | ||
69 | |||
70 | config SND_SERIAL_U16550 | ||
71 | tristate "UART16550 serial MIDI driver" | ||
72 | depends on SND | ||
73 | select SND_TIMER | ||
74 | select SND_RAWMIDI | ||
75 | help | ||
76 | To include support for MIDI serial port interfaces, say Y here | ||
77 | and read <file:Documentation/sound/alsa/serial-u16550.txt>. | ||
78 | This driver works with serial UARTs 16550 and better. | ||
79 | |||
80 | This driver accesses the serial port hardware directly, so | ||
81 | make sure that the standard serial driver isn't used or | ||
82 | deactivated with setserial before loading this driver. | ||
83 | |||
84 | To compile this driver as a module, choose M here: the module | ||
85 | will be called snd-serial-u16550. | ||
86 | |||
87 | config SND_MPU401 | ||
88 | tristate "Generic MPU-401 UART driver" | ||
89 | depends on SND | ||
90 | select SND_MPU401_UART | ||
91 | help | ||
92 | Say Y here to include support for MIDI ports compatible with | ||
93 | the Roland MPU-401 interface in UART mode. | ||
94 | |||
95 | To compile this driver as a module, choose M here: the module | ||
96 | will be called snd-mpu401. | ||
97 | |||
98 | endmenu | ||
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile new file mode 100644 index 000000000000..cb98c3d662be --- /dev/null +++ b/sound/drivers/Makefile | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-dummy-objs := dummy.o | ||
7 | snd-mtpav-objs := mtpav.o | ||
8 | snd-serial-u16550-objs := serial-u16550.o | ||
9 | snd-virmidi-objs := virmidi.o | ||
10 | |||
11 | # Toplevel Module Dependency | ||
12 | obj-$(CONFIG_SND_DUMMY) += snd-dummy.o | ||
13 | obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o | ||
14 | obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o | ||
15 | obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o | ||
16 | |||
17 | obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ | ||
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c new file mode 100644 index 000000000000..a61640cf7ae7 --- /dev/null +++ b/sound/drivers/dummy.c | |||
@@ -0,0 +1,643 @@ | |||
1 | /* | ||
2 | * Dummy soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
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, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/control.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/rawmidi.h> | ||
32 | #include <sound/initval.h> | ||
33 | |||
34 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
35 | MODULE_DESCRIPTION("Dummy soundcard (/dev/null)"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); | ||
38 | |||
39 | #define MAX_PCM_DEVICES 4 | ||
40 | #define MAX_PCM_SUBSTREAMS 16 | ||
41 | #define MAX_MIDI_DEVICES 2 | ||
42 | |||
43 | #if 0 /* emu10k1 emulation */ | ||
44 | #define MAX_BUFFER_SIZE (128 * 1024) | ||
45 | static int emu10k1_playback_constraints(snd_pcm_runtime_t *runtime) | ||
46 | { | ||
47 | int err; | ||
48 | if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) | ||
49 | return err; | ||
50 | if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) | ||
51 | return err; | ||
52 | return 0; | ||
53 | } | ||
54 | #define add_playback_constraints emu10k1_playback_constraints | ||
55 | #endif | ||
56 | |||
57 | #if 0 /* RME9652 emulation */ | ||
58 | #define MAX_BUFFER_SIZE (26 * 64 * 1024) | ||
59 | #define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE | ||
60 | #define USE_CHANNELS_MIN 26 | ||
61 | #define USE_CHANNELS_MAX 26 | ||
62 | #define USE_PERIODS_MIN 2 | ||
63 | #define USE_PERIODS_MAX 2 | ||
64 | #endif | ||
65 | |||
66 | #if 0 /* ICE1712 emulation */ | ||
67 | #define MAX_BUFFER_SIZE (256 * 1024) | ||
68 | #define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE | ||
69 | #define USE_CHANNELS_MIN 10 | ||
70 | #define USE_CHANNELS_MAX 10 | ||
71 | #define USE_PERIODS_MIN 1 | ||
72 | #define USE_PERIODS_MAX 1024 | ||
73 | #endif | ||
74 | |||
75 | #if 0 /* UDA1341 emulation */ | ||
76 | #define MAX_BUFFER_SIZE (16380) | ||
77 | #define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE | ||
78 | #define USE_CHANNELS_MIN 2 | ||
79 | #define USE_CHANNELS_MAX 2 | ||
80 | #define USE_PERIODS_MIN 2 | ||
81 | #define USE_PERIODS_MAX 255 | ||
82 | #endif | ||
83 | |||
84 | #if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */ | ||
85 | #define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE | ||
86 | #define USE_CHANNELS_MIN 2 | ||
87 | #define USE_CHANNELS_MAX 2 | ||
88 | #define USE_RATE SNDRV_PCM_RATE_48000 | ||
89 | #define USE_RATE_MIN 48000 | ||
90 | #define USE_RATE_MAX 48000 | ||
91 | #endif | ||
92 | |||
93 | |||
94 | /* defaults */ | ||
95 | #ifndef MAX_BUFFER_SIZE | ||
96 | #define MAX_BUFFER_SIZE (64*1024) | ||
97 | #endif | ||
98 | #ifndef USE_FORMATS | ||
99 | #define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) | ||
100 | #endif | ||
101 | #ifndef USE_RATE | ||
102 | #define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000 | ||
103 | #define USE_RATE_MIN 5500 | ||
104 | #define USE_RATE_MAX 48000 | ||
105 | #endif | ||
106 | #ifndef USE_CHANNELS_MIN | ||
107 | #define USE_CHANNELS_MIN 1 | ||
108 | #endif | ||
109 | #ifndef USE_CHANNELS_MAX | ||
110 | #define USE_CHANNELS_MAX 2 | ||
111 | #endif | ||
112 | #ifndef USE_PERIODS_MIN | ||
113 | #define USE_PERIODS_MIN 1 | ||
114 | #endif | ||
115 | #ifndef USE_PERIODS_MAX | ||
116 | #define USE_PERIODS_MAX 1024 | ||
117 | #endif | ||
118 | #ifndef add_playback_constraints | ||
119 | #define add_playback_constraints(x) 0 | ||
120 | #endif | ||
121 | #ifndef add_capture_constraints | ||
122 | #define add_capture_constraints(x) 0 | ||
123 | #endif | ||
124 | |||
125 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
126 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
127 | static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | ||
128 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
129 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | ||
130 | //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
131 | |||
132 | module_param_array(index, int, NULL, 0444); | ||
133 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); | ||
134 | module_param_array(id, charp, NULL, 0444); | ||
135 | MODULE_PARM_DESC(id, "ID string for dummy soundcard."); | ||
136 | module_param_array(enable, bool, NULL, 0444); | ||
137 | MODULE_PARM_DESC(enable, "Enable this dummy soundcard."); | ||
138 | module_param_array(pcm_devs, int, NULL, 0444); | ||
139 | MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver."); | ||
140 | module_param_array(pcm_substreams, int, NULL, 0444); | ||
141 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); | ||
142 | //module_param_array(midi_devs, int, NULL, 0444); | ||
143 | //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); | ||
144 | |||
145 | #define MIXER_ADDR_MASTER 0 | ||
146 | #define MIXER_ADDR_LINE 1 | ||
147 | #define MIXER_ADDR_MIC 2 | ||
148 | #define MIXER_ADDR_SYNTH 3 | ||
149 | #define MIXER_ADDR_CD 4 | ||
150 | #define MIXER_ADDR_LAST 4 | ||
151 | |||
152 | typedef struct snd_card_dummy { | ||
153 | snd_card_t *card; | ||
154 | spinlock_t mixer_lock; | ||
155 | int mixer_volume[MIXER_ADDR_LAST+1][2]; | ||
156 | int capture_source[MIXER_ADDR_LAST+1][2]; | ||
157 | } snd_card_dummy_t; | ||
158 | |||
159 | typedef struct snd_card_dummy_pcm { | ||
160 | snd_card_dummy_t *dummy; | ||
161 | spinlock_t lock; | ||
162 | struct timer_list timer; | ||
163 | unsigned int pcm_size; | ||
164 | unsigned int pcm_count; | ||
165 | unsigned int pcm_bps; /* bytes per second */ | ||
166 | unsigned int pcm_jiffie; /* bytes per one jiffie */ | ||
167 | unsigned int pcm_irq_pos; /* IRQ position */ | ||
168 | unsigned int pcm_buf_pos; /* position in buffer */ | ||
169 | snd_pcm_substream_t *substream; | ||
170 | } snd_card_dummy_pcm_t; | ||
171 | |||
172 | static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
173 | |||
174 | |||
175 | static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream) | ||
176 | { | ||
177 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
178 | snd_card_dummy_pcm_t *dpcm = runtime->private_data; | ||
179 | |||
180 | dpcm->timer.expires = 1 + jiffies; | ||
181 | add_timer(&dpcm->timer); | ||
182 | } | ||
183 | |||
184 | static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream) | ||
185 | { | ||
186 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
187 | snd_card_dummy_pcm_t *dpcm = runtime->private_data; | ||
188 | |||
189 | del_timer(&dpcm->timer); | ||
190 | } | ||
191 | |||
192 | static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream, | ||
193 | int cmd) | ||
194 | { | ||
195 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
196 | snd_card_dummy_pcm_timer_start(substream); | ||
197 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
198 | snd_card_dummy_pcm_timer_stop(substream); | ||
199 | } else { | ||
200 | return -EINVAL; | ||
201 | } | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream, | ||
206 | int cmd) | ||
207 | { | ||
208 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
209 | snd_card_dummy_pcm_timer_start(substream); | ||
210 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
211 | snd_card_dummy_pcm_timer_stop(substream); | ||
212 | } else { | ||
213 | return -EINVAL; | ||
214 | } | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream) | ||
219 | { | ||
220 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
221 | snd_card_dummy_pcm_t *dpcm = runtime->private_data; | ||
222 | unsigned int bps; | ||
223 | |||
224 | bps = runtime->rate * runtime->channels; | ||
225 | bps *= snd_pcm_format_width(runtime->format); | ||
226 | bps /= 8; | ||
227 | if (bps <= 0) | ||
228 | return -EINVAL; | ||
229 | dpcm->pcm_bps = bps; | ||
230 | dpcm->pcm_jiffie = bps / HZ; | ||
231 | dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream); | ||
232 | dpcm->pcm_count = snd_pcm_lib_period_bytes(substream); | ||
233 | dpcm->pcm_irq_pos = 0; | ||
234 | dpcm->pcm_buf_pos = 0; | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream) | ||
239 | { | ||
240 | return snd_card_dummy_pcm_prepare(substream); | ||
241 | } | ||
242 | |||
243 | static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream) | ||
244 | { | ||
245 | return snd_card_dummy_pcm_prepare(substream); | ||
246 | } | ||
247 | |||
248 | static void snd_card_dummy_pcm_timer_function(unsigned long data) | ||
249 | { | ||
250 | snd_card_dummy_pcm_t *dpcm = (snd_card_dummy_pcm_t *)data; | ||
251 | |||
252 | dpcm->timer.expires = 1 + jiffies; | ||
253 | add_timer(&dpcm->timer); | ||
254 | spin_lock_irq(&dpcm->lock); | ||
255 | dpcm->pcm_irq_pos += dpcm->pcm_jiffie; | ||
256 | dpcm->pcm_buf_pos += dpcm->pcm_jiffie; | ||
257 | dpcm->pcm_buf_pos %= dpcm->pcm_size; | ||
258 | if (dpcm->pcm_irq_pos >= dpcm->pcm_count) { | ||
259 | dpcm->pcm_irq_pos %= dpcm->pcm_count; | ||
260 | snd_pcm_period_elapsed(dpcm->substream); | ||
261 | } | ||
262 | spin_unlock_irq(&dpcm->lock); | ||
263 | } | ||
264 | |||
265 | static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream) | ||
266 | { | ||
267 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
268 | snd_card_dummy_pcm_t *dpcm = runtime->private_data; | ||
269 | |||
270 | return bytes_to_frames(runtime, dpcm->pcm_buf_pos); | ||
271 | } | ||
272 | |||
273 | static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream) | ||
274 | { | ||
275 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
276 | snd_card_dummy_pcm_t *dpcm = runtime->private_data; | ||
277 | |||
278 | return bytes_to_frames(runtime, dpcm->pcm_buf_pos); | ||
279 | } | ||
280 | |||
281 | static snd_pcm_hardware_t snd_card_dummy_playback = | ||
282 | { | ||
283 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
284 | SNDRV_PCM_INFO_MMAP_VALID), | ||
285 | .formats = USE_FORMATS, | ||
286 | .rates = USE_RATE, | ||
287 | .rate_min = USE_RATE_MIN, | ||
288 | .rate_max = USE_RATE_MAX, | ||
289 | .channels_min = USE_CHANNELS_MIN, | ||
290 | .channels_max = USE_CHANNELS_MAX, | ||
291 | .buffer_bytes_max = MAX_BUFFER_SIZE, | ||
292 | .period_bytes_min = 64, | ||
293 | .period_bytes_max = MAX_BUFFER_SIZE, | ||
294 | .periods_min = USE_PERIODS_MIN, | ||
295 | .periods_max = USE_PERIODS_MAX, | ||
296 | .fifo_size = 0, | ||
297 | }; | ||
298 | |||
299 | static snd_pcm_hardware_t snd_card_dummy_capture = | ||
300 | { | ||
301 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
302 | SNDRV_PCM_INFO_MMAP_VALID), | ||
303 | .formats = USE_FORMATS, | ||
304 | .rates = USE_RATE, | ||
305 | .rate_min = USE_RATE_MIN, | ||
306 | .rate_max = USE_RATE_MAX, | ||
307 | .channels_min = USE_CHANNELS_MIN, | ||
308 | .channels_max = USE_CHANNELS_MAX, | ||
309 | .buffer_bytes_max = MAX_BUFFER_SIZE, | ||
310 | .period_bytes_min = 64, | ||
311 | .period_bytes_max = MAX_BUFFER_SIZE, | ||
312 | .periods_min = USE_PERIODS_MIN, | ||
313 | .periods_max = USE_PERIODS_MAX, | ||
314 | .fifo_size = 0, | ||
315 | }; | ||
316 | |||
317 | static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime) | ||
318 | { | ||
319 | snd_card_dummy_pcm_t *dpcm = runtime->private_data; | ||
320 | kfree(dpcm); | ||
321 | } | ||
322 | |||
323 | static int snd_card_dummy_hw_params(snd_pcm_substream_t * substream, | ||
324 | snd_pcm_hw_params_t * hw_params) | ||
325 | { | ||
326 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
327 | } | ||
328 | |||
329 | static int snd_card_dummy_hw_free(snd_pcm_substream_t * substream) | ||
330 | { | ||
331 | return snd_pcm_lib_free_pages(substream); | ||
332 | } | ||
333 | |||
334 | static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream) | ||
335 | { | ||
336 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
337 | snd_card_dummy_pcm_t *dpcm; | ||
338 | int err; | ||
339 | |||
340 | dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL); | ||
341 | if (dpcm == NULL) | ||
342 | return -ENOMEM; | ||
343 | init_timer(&dpcm->timer); | ||
344 | dpcm->timer.data = (unsigned long) dpcm; | ||
345 | dpcm->timer.function = snd_card_dummy_pcm_timer_function; | ||
346 | spin_lock_init(&dpcm->lock); | ||
347 | dpcm->substream = substream; | ||
348 | runtime->private_data = dpcm; | ||
349 | runtime->private_free = snd_card_dummy_runtime_free; | ||
350 | runtime->hw = snd_card_dummy_playback; | ||
351 | if (substream->pcm->device & 1) { | ||
352 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | ||
353 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | ||
354 | } | ||
355 | if (substream->pcm->device & 2) | ||
356 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | ||
357 | if ((err = add_playback_constraints(runtime)) < 0) { | ||
358 | kfree(dpcm); | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream) | ||
366 | { | ||
367 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
368 | snd_card_dummy_pcm_t *dpcm; | ||
369 | int err; | ||
370 | |||
371 | dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL); | ||
372 | if (dpcm == NULL) | ||
373 | return -ENOMEM; | ||
374 | init_timer(&dpcm->timer); | ||
375 | dpcm->timer.data = (unsigned long) dpcm; | ||
376 | dpcm->timer.function = snd_card_dummy_pcm_timer_function; | ||
377 | spin_lock_init(&dpcm->lock); | ||
378 | dpcm->substream = substream; | ||
379 | runtime->private_data = dpcm; | ||
380 | runtime->private_free = snd_card_dummy_runtime_free; | ||
381 | runtime->hw = snd_card_dummy_capture; | ||
382 | if (substream->pcm->device == 1) { | ||
383 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | ||
384 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | ||
385 | } | ||
386 | if (substream->pcm->device & 2) | ||
387 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | ||
388 | if ((err = add_capture_constraints(runtime)) < 0) { | ||
389 | kfree(dpcm); | ||
390 | return err; | ||
391 | } | ||
392 | |||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream) | ||
397 | { | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream) | ||
402 | { | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static snd_pcm_ops_t snd_card_dummy_playback_ops = { | ||
407 | .open = snd_card_dummy_playback_open, | ||
408 | .close = snd_card_dummy_playback_close, | ||
409 | .ioctl = snd_pcm_lib_ioctl, | ||
410 | .hw_params = snd_card_dummy_hw_params, | ||
411 | .hw_free = snd_card_dummy_hw_free, | ||
412 | .prepare = snd_card_dummy_playback_prepare, | ||
413 | .trigger = snd_card_dummy_playback_trigger, | ||
414 | .pointer = snd_card_dummy_playback_pointer, | ||
415 | }; | ||
416 | |||
417 | static snd_pcm_ops_t snd_card_dummy_capture_ops = { | ||
418 | .open = snd_card_dummy_capture_open, | ||
419 | .close = snd_card_dummy_capture_close, | ||
420 | .ioctl = snd_pcm_lib_ioctl, | ||
421 | .hw_params = snd_card_dummy_hw_params, | ||
422 | .hw_free = snd_card_dummy_hw_free, | ||
423 | .prepare = snd_card_dummy_capture_prepare, | ||
424 | .trigger = snd_card_dummy_capture_trigger, | ||
425 | .pointer = snd_card_dummy_capture_pointer, | ||
426 | }; | ||
427 | |||
428 | static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams) | ||
429 | { | ||
430 | snd_pcm_t *pcm; | ||
431 | int err; | ||
432 | |||
433 | if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0) | ||
434 | return err; | ||
435 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); | ||
436 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); | ||
437 | pcm->private_data = dummy; | ||
438 | pcm->info_flags = 0; | ||
439 | strcpy(pcm->name, "Dummy PCM"); | ||
440 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
441 | snd_dma_continuous_data(GFP_KERNEL), | ||
442 | 0, 64*1024); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | #define DUMMY_VOLUME(xname, xindex, addr) \ | ||
447 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
448 | .info = snd_dummy_volume_info, \ | ||
449 | .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \ | ||
450 | .private_value = addr } | ||
451 | |||
452 | static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) | ||
453 | { | ||
454 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
455 | uinfo->count = 2; | ||
456 | uinfo->value.integer.min = -50; | ||
457 | uinfo->value.integer.max = 100; | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
462 | { | ||
463 | snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); | ||
464 | unsigned long flags; | ||
465 | int addr = kcontrol->private_value; | ||
466 | |||
467 | spin_lock_irqsave(&dummy->mixer_lock, flags); | ||
468 | ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0]; | ||
469 | ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; | ||
470 | spin_unlock_irqrestore(&dummy->mixer_lock, flags); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
475 | { | ||
476 | snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); | ||
477 | unsigned long flags; | ||
478 | int change, addr = kcontrol->private_value; | ||
479 | int left, right; | ||
480 | |||
481 | left = ucontrol->value.integer.value[0]; | ||
482 | if (left < -50) | ||
483 | left = -50; | ||
484 | if (left > 100) | ||
485 | left = 100; | ||
486 | right = ucontrol->value.integer.value[1]; | ||
487 | if (right < -50) | ||
488 | right = -50; | ||
489 | if (right > 100) | ||
490 | right = 100; | ||
491 | spin_lock_irqsave(&dummy->mixer_lock, flags); | ||
492 | change = dummy->mixer_volume[addr][0] != left || | ||
493 | dummy->mixer_volume[addr][1] != right; | ||
494 | dummy->mixer_volume[addr][0] = left; | ||
495 | dummy->mixer_volume[addr][1] = right; | ||
496 | spin_unlock_irqrestore(&dummy->mixer_lock, flags); | ||
497 | return change; | ||
498 | } | ||
499 | |||
500 | #define DUMMY_CAPSRC(xname, xindex, addr) \ | ||
501 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
502 | .info = snd_dummy_capsrc_info, \ | ||
503 | .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \ | ||
504 | .private_value = addr } | ||
505 | |||
506 | static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) | ||
507 | { | ||
508 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
509 | uinfo->count = 2; | ||
510 | uinfo->value.integer.min = 0; | ||
511 | uinfo->value.integer.max = 1; | ||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
516 | { | ||
517 | snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); | ||
518 | unsigned long flags; | ||
519 | int addr = kcontrol->private_value; | ||
520 | |||
521 | spin_lock_irqsave(&dummy->mixer_lock, flags); | ||
522 | ucontrol->value.integer.value[0] = dummy->capture_source[addr][0]; | ||
523 | ucontrol->value.integer.value[1] = dummy->capture_source[addr][1]; | ||
524 | spin_unlock_irqrestore(&dummy->mixer_lock, flags); | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
529 | { | ||
530 | snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); | ||
531 | unsigned long flags; | ||
532 | int change, addr = kcontrol->private_value; | ||
533 | int left, right; | ||
534 | |||
535 | left = ucontrol->value.integer.value[0] & 1; | ||
536 | right = ucontrol->value.integer.value[1] & 1; | ||
537 | spin_lock_irqsave(&dummy->mixer_lock, flags); | ||
538 | change = dummy->capture_source[addr][0] != left && | ||
539 | dummy->capture_source[addr][1] != right; | ||
540 | dummy->capture_source[addr][0] = left; | ||
541 | dummy->capture_source[addr][1] = right; | ||
542 | spin_unlock_irqrestore(&dummy->mixer_lock, flags); | ||
543 | return change; | ||
544 | } | ||
545 | |||
546 | static snd_kcontrol_new_t snd_dummy_controls[] = { | ||
547 | DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), | ||
548 | DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), | ||
549 | DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), | ||
550 | DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER), | ||
551 | DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), | ||
552 | DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER), | ||
553 | DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), | ||
554 | DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER), | ||
555 | DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), | ||
556 | DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER) | ||
557 | }; | ||
558 | |||
559 | static int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy) | ||
560 | { | ||
561 | snd_card_t *card = dummy->card; | ||
562 | unsigned int idx; | ||
563 | int err; | ||
564 | |||
565 | snd_assert(dummy != NULL, return -EINVAL); | ||
566 | spin_lock_init(&dummy->mixer_lock); | ||
567 | strcpy(card->mixername, "Dummy Mixer"); | ||
568 | |||
569 | for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { | ||
570 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0) | ||
571 | return err; | ||
572 | } | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | static int __init snd_card_dummy_probe(int dev) | ||
577 | { | ||
578 | snd_card_t *card; | ||
579 | struct snd_card_dummy *dummy; | ||
580 | int idx, err; | ||
581 | |||
582 | if (!enable[dev]) | ||
583 | return -ENODEV; | ||
584 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
585 | sizeof(struct snd_card_dummy)); | ||
586 | if (card == NULL) | ||
587 | return -ENOMEM; | ||
588 | dummy = (struct snd_card_dummy *)card->private_data; | ||
589 | dummy->card = card; | ||
590 | for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) { | ||
591 | if (pcm_substreams[dev] < 1) | ||
592 | pcm_substreams[dev] = 1; | ||
593 | if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) | ||
594 | pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; | ||
595 | if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0) | ||
596 | goto __nodev; | ||
597 | } | ||
598 | if ((err = snd_card_dummy_new_mixer(dummy)) < 0) | ||
599 | goto __nodev; | ||
600 | strcpy(card->driver, "Dummy"); | ||
601 | strcpy(card->shortname, "Dummy"); | ||
602 | sprintf(card->longname, "Dummy %i", dev + 1); | ||
603 | if ((err = snd_card_register(card)) == 0) { | ||
604 | snd_dummy_cards[dev] = card; | ||
605 | return 0; | ||
606 | } | ||
607 | __nodev: | ||
608 | snd_card_free(card); | ||
609 | return err; | ||
610 | } | ||
611 | |||
612 | static int __init alsa_card_dummy_init(void) | ||
613 | { | ||
614 | int dev, cards; | ||
615 | |||
616 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
617 | if (snd_card_dummy_probe(dev) < 0) { | ||
618 | #ifdef MODULE | ||
619 | printk(KERN_ERR "Dummy soundcard #%i not found or device busy\n", dev + 1); | ||
620 | #endif | ||
621 | break; | ||
622 | } | ||
623 | cards++; | ||
624 | } | ||
625 | if (!cards) { | ||
626 | #ifdef MODULE | ||
627 | printk(KERN_ERR "Dummy soundcard not found or device busy\n"); | ||
628 | #endif | ||
629 | return -ENODEV; | ||
630 | } | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static void __exit alsa_card_dummy_exit(void) | ||
635 | { | ||
636 | int idx; | ||
637 | |||
638 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
639 | snd_card_free(snd_dummy_cards[idx]); | ||
640 | } | ||
641 | |||
642 | module_init(alsa_card_dummy_init) | ||
643 | module_exit(alsa_card_dummy_exit) | ||
diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile new file mode 100644 index 000000000000..3fe185d19ae5 --- /dev/null +++ b/sound/drivers/mpu401/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-mpu401-objs := mpu401.o | ||
7 | snd-mpu401-uart-objs := mpu401_uart.o | ||
8 | |||
9 | obj-$(CONFIG_SND_MPU401_UART) += snd-mpu401-uart.o | ||
10 | |||
11 | # Toplevel Module Dependency | ||
12 | obj-$(CONFIG_SND_MPU401) += snd-mpu401.o | ||
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c new file mode 100644 index 000000000000..cb36ecb78697 --- /dev/null +++ b/sound/drivers/mpu401/mpu401.c | |||
@@ -0,0 +1,229 @@ | |||
1 | /* | ||
2 | * Driver for generic MPU-401 boards (UART mode only) | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * Copyright (c) 2004 by Castet Matthieu <castet.matthieu@free.fr> | ||
5 | * | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/pnp.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/mpu401.h> | ||
29 | #include <sound/initval.h> | ||
30 | |||
31 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
32 | MODULE_DESCRIPTION("MPU-401 UART"); | ||
33 | MODULE_LICENSE("GPL"); | ||
34 | |||
35 | static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* exclude the first card */ | ||
36 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
37 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
38 | #ifdef CONFIG_PNP | ||
39 | static int pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
40 | #endif | ||
41 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ | ||
42 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ | ||
43 | |||
44 | module_param_array(index, int, NULL, 0444); | ||
45 | MODULE_PARM_DESC(index, "Index value for MPU-401 device."); | ||
46 | module_param_array(id, charp, NULL, 0444); | ||
47 | MODULE_PARM_DESC(id, "ID string for MPU-401 device."); | ||
48 | module_param_array(enable, bool, NULL, 0444); | ||
49 | MODULE_PARM_DESC(enable, "Enable MPU-401 device."); | ||
50 | #ifdef CONFIG_PNP | ||
51 | module_param_array(pnp, bool, NULL, 0444); | ||
52 | MODULE_PARM_DESC(pnp, "PnP detection for MPU-401 device."); | ||
53 | #endif | ||
54 | module_param_array(port, long, NULL, 0444); | ||
55 | MODULE_PARM_DESC(port, "Port # for MPU-401 device."); | ||
56 | module_param_array(irq, int, NULL, 0444); | ||
57 | MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); | ||
58 | |||
59 | static snd_card_t *snd_mpu401_legacy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
60 | static int pnp_registered = 0; | ||
61 | |||
62 | static int snd_mpu401_create(int dev, snd_card_t **rcard) | ||
63 | { | ||
64 | snd_card_t *card; | ||
65 | int err; | ||
66 | |||
67 | *rcard = NULL; | ||
68 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
69 | if (card == NULL) | ||
70 | return -ENOMEM; | ||
71 | strcpy(card->driver, "MPU-401 UART"); | ||
72 | strcpy(card->shortname, card->driver); | ||
73 | sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]); | ||
74 | if (irq[dev] >= 0) { | ||
75 | sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]); | ||
76 | } else { | ||
77 | strcat(card->longname, "polled"); | ||
78 | } | ||
79 | |||
80 | if (snd_mpu401_uart_new(card, 0, | ||
81 | MPU401_HW_MPU401, | ||
82 | port[dev], 0, | ||
83 | irq[dev], irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) { | ||
84 | printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]); | ||
85 | snd_card_free(card); | ||
86 | return -ENODEV; | ||
87 | } | ||
88 | if ((err = snd_card_register(card)) < 0) { | ||
89 | snd_card_free(card); | ||
90 | return err; | ||
91 | } | ||
92 | *rcard = card; | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int __devinit snd_mpu401_probe(int dev) | ||
97 | { | ||
98 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
99 | snd_printk(KERN_ERR "specify port\n"); | ||
100 | return -EINVAL; | ||
101 | } | ||
102 | if (irq[dev] == SNDRV_AUTO_IRQ) { | ||
103 | snd_printk(KERN_ERR "specify or disable IRQ\n"); | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | return snd_mpu401_create(dev, &snd_mpu401_legacy_cards[dev]); | ||
107 | } | ||
108 | |||
109 | #ifdef CONFIG_PNP | ||
110 | |||
111 | #define IO_EXTENT 2 | ||
112 | |||
113 | static struct pnp_device_id snd_mpu401_pnpids[] = { | ||
114 | { .id = "PNPb006" }, | ||
115 | { .id = "" } | ||
116 | }; | ||
117 | |||
118 | MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids); | ||
119 | |||
120 | static int __init snd_mpu401_pnp(int dev, struct pnp_dev *device, | ||
121 | const struct pnp_device_id *id) | ||
122 | { | ||
123 | if (!pnp_port_valid(device, 0) || | ||
124 | pnp_port_flags(device, 0) & IORESOURCE_DISABLED) { | ||
125 | snd_printk(KERN_ERR "no PnP port\n"); | ||
126 | return -ENODEV; | ||
127 | } | ||
128 | if (pnp_port_len(device, 0) < IO_EXTENT) { | ||
129 | snd_printk(KERN_ERR "PnP port length is %ld, expected %d\n", | ||
130 | pnp_port_len(device, 0), IO_EXTENT); | ||
131 | return -ENODEV; | ||
132 | } | ||
133 | port[dev] = pnp_port_start(device, 0); | ||
134 | |||
135 | if (!pnp_irq_valid(device, 0) || | ||
136 | pnp_irq_flags(device, 0) & IORESOURCE_DISABLED) { | ||
137 | snd_printk(KERN_WARNING "no PnP irq, using polling\n"); | ||
138 | irq[dev] = -1; | ||
139 | } else { | ||
140 | irq[dev] = pnp_irq(device, 0); | ||
141 | } | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev, | ||
146 | const struct pnp_device_id *id) | ||
147 | { | ||
148 | static int dev; | ||
149 | snd_card_t *card; | ||
150 | int err; | ||
151 | |||
152 | for ( ; dev < SNDRV_CARDS; ++dev) { | ||
153 | if (!enable[dev] || !pnp[dev]) | ||
154 | continue; | ||
155 | err = snd_mpu401_pnp(dev, pnp_dev, id); | ||
156 | if (err < 0) | ||
157 | return err; | ||
158 | err = snd_mpu401_create(dev, &card); | ||
159 | if (err < 0) | ||
160 | return err; | ||
161 | snd_card_set_dev(card, &pnp_dev->dev); | ||
162 | pnp_set_drvdata(pnp_dev, card); | ||
163 | ++dev; | ||
164 | return 0; | ||
165 | } | ||
166 | return -ENODEV; | ||
167 | } | ||
168 | |||
169 | static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev) | ||
170 | { | ||
171 | snd_card_t *card = (snd_card_t *) pnp_get_drvdata(dev); | ||
172 | |||
173 | snd_card_disconnect(card); | ||
174 | snd_card_free_in_thread(card); | ||
175 | } | ||
176 | |||
177 | static struct pnp_driver snd_mpu401_pnp_driver = { | ||
178 | .name = "mpu401", | ||
179 | .id_table = snd_mpu401_pnpids, | ||
180 | .probe = snd_mpu401_pnp_probe, | ||
181 | .remove = __devexit_p(snd_mpu401_pnp_remove), | ||
182 | }; | ||
183 | #else | ||
184 | static struct pnp_driver snd_mpu401_pnp_driver; | ||
185 | #endif | ||
186 | |||
187 | static int __init alsa_card_mpu401_init(void) | ||
188 | { | ||
189 | int dev, devices = 0; | ||
190 | int err; | ||
191 | |||
192 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
193 | if (!enable[dev]) | ||
194 | continue; | ||
195 | #ifdef CONFIG_PNP | ||
196 | if (pnp[dev]) | ||
197 | continue; | ||
198 | #endif | ||
199 | if (snd_mpu401_probe(dev) >= 0) | ||
200 | devices++; | ||
201 | } | ||
202 | if ((err = pnp_register_driver(&snd_mpu401_pnp_driver)) >= 0) { | ||
203 | pnp_registered = 1; | ||
204 | devices += err; | ||
205 | } | ||
206 | |||
207 | if (!devices) { | ||
208 | #ifdef MODULE | ||
209 | printk(KERN_ERR "MPU-401 device not found or device busy\n"); | ||
210 | #endif | ||
211 | if (pnp_registered) | ||
212 | pnp_unregister_driver(&snd_mpu401_pnp_driver); | ||
213 | return -ENODEV; | ||
214 | } | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static void __exit alsa_card_mpu401_exit(void) | ||
219 | { | ||
220 | int idx; | ||
221 | |||
222 | if (pnp_registered) | ||
223 | pnp_unregister_driver(&snd_mpu401_pnp_driver); | ||
224 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
225 | snd_card_free(snd_mpu401_legacy_cards[idx]); | ||
226 | } | ||
227 | |||
228 | module_init(alsa_card_mpu401_init) | ||
229 | module_exit(alsa_card_mpu401_exit) | ||
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c new file mode 100644 index 000000000000..0f83c5241b6b --- /dev/null +++ b/sound/drivers/mpu401/mpu401_uart.c | |||
@@ -0,0 +1,541 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of MPU-401 in UART mode | ||
4 | * | ||
5 | * MPU-401 supports UART mode which is not capable generate transmit | ||
6 | * interrupts thus output is done via polling. Also, if irq < 0, then | ||
7 | * input is done also via polling. Do not expect good performance. | ||
8 | * | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | * 13-03-2003: | ||
25 | * Added support for different kind of hardware I/O. Build in choices | ||
26 | * are port and mmio. For other kind of I/O, set mpu->read and | ||
27 | * mpu->write to your own I/O functions. | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include <sound/driver.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <linux/delay.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/ioport.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <sound/core.h> | ||
40 | #include <sound/mpu401.h> | ||
41 | |||
42 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
43 | MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | |||
46 | static void snd_mpu401_uart_input_read(mpu401_t * mpu); | ||
47 | static void snd_mpu401_uart_output_write(mpu401_t * mpu); | ||
48 | |||
49 | /* | ||
50 | |||
51 | */ | ||
52 | |||
53 | #define snd_mpu401_input_avail(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x80)) | ||
54 | #define snd_mpu401_output_ready(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x40)) | ||
55 | |||
56 | #define MPU401_RESET 0xff | ||
57 | #define MPU401_ENTER_UART 0x3f | ||
58 | #define MPU401_ACK 0xfe | ||
59 | |||
60 | /* Build in lowlevel io */ | ||
61 | static void mpu401_write_port(mpu401_t *mpu, unsigned char data, unsigned long addr) | ||
62 | { | ||
63 | outb(data, addr); | ||
64 | } | ||
65 | |||
66 | static unsigned char mpu401_read_port(mpu401_t *mpu, unsigned long addr) | ||
67 | { | ||
68 | return inb(addr); | ||
69 | } | ||
70 | |||
71 | static void mpu401_write_mmio(mpu401_t *mpu, unsigned char data, unsigned long addr) | ||
72 | { | ||
73 | writeb(data, (void __iomem *)addr); | ||
74 | } | ||
75 | |||
76 | static unsigned char mpu401_read_mmio(mpu401_t *mpu, unsigned long addr) | ||
77 | { | ||
78 | return readb((void __iomem *)addr); | ||
79 | } | ||
80 | /* */ | ||
81 | |||
82 | static void snd_mpu401_uart_clear_rx(mpu401_t *mpu) | ||
83 | { | ||
84 | int timeout = 100000; | ||
85 | for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) | ||
86 | mpu->read(mpu, MPU401D(mpu)); | ||
87 | #ifdef CONFIG_SND_DEBUG | ||
88 | if (timeout <= 0) | ||
89 | snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); | ||
90 | #endif | ||
91 | } | ||
92 | |||
93 | static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) | ||
94 | { | ||
95 | spin_lock(&mpu->input_lock); | ||
96 | if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { | ||
97 | snd_mpu401_uart_input_read(mpu); | ||
98 | } else { | ||
99 | snd_mpu401_uart_clear_rx(mpu); | ||
100 | } | ||
101 | spin_unlock(&mpu->input_lock); | ||
102 | /* ok. for better Tx performance try do some output when input is done */ | ||
103 | if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && | ||
104 | test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { | ||
105 | spin_lock(&mpu->output_lock); | ||
106 | snd_mpu401_uart_output_write(mpu); | ||
107 | spin_unlock(&mpu->output_lock); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler | ||
113 | * @irq: the irq number | ||
114 | * @dev_id: mpu401 instance | ||
115 | * @regs: the reigster | ||
116 | * | ||
117 | * Processes the interrupt for MPU401-UART i/o. | ||
118 | */ | ||
119 | irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
120 | { | ||
121 | mpu401_t *mpu = dev_id; | ||
122 | |||
123 | if (mpu == NULL) | ||
124 | return IRQ_NONE; | ||
125 | _snd_mpu401_uart_interrupt(mpu); | ||
126 | return IRQ_HANDLED; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * timer callback | ||
131 | * reprogram the timer and call the interrupt job | ||
132 | */ | ||
133 | static void snd_mpu401_uart_timer(unsigned long data) | ||
134 | { | ||
135 | mpu401_t *mpu = (mpu401_t *)data; | ||
136 | |||
137 | spin_lock(&mpu->timer_lock); | ||
138 | /*mpu->mode |= MPU401_MODE_TIMER;*/ | ||
139 | mpu->timer.expires = 1 + jiffies; | ||
140 | add_timer(&mpu->timer); | ||
141 | spin_unlock(&mpu->timer_lock); | ||
142 | if (mpu->rmidi) | ||
143 | _snd_mpu401_uart_interrupt(mpu); | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * initialize the timer callback if not programmed yet | ||
148 | */ | ||
149 | static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input) | ||
150 | { | ||
151 | unsigned long flags; | ||
152 | |||
153 | spin_lock_irqsave (&mpu->timer_lock, flags); | ||
154 | if (mpu->timer_invoked == 0) { | ||
155 | init_timer(&mpu->timer); | ||
156 | mpu->timer.data = (unsigned long)mpu; | ||
157 | mpu->timer.function = snd_mpu401_uart_timer; | ||
158 | mpu->timer.expires = 1 + jiffies; | ||
159 | add_timer(&mpu->timer); | ||
160 | } | ||
161 | mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER; | ||
162 | spin_unlock_irqrestore (&mpu->timer_lock, flags); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * remove the timer callback if still active | ||
167 | */ | ||
168 | static void snd_mpu401_uart_remove_timer (mpu401_t *mpu, int input) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | |||
172 | spin_lock_irqsave (&mpu->timer_lock, flags); | ||
173 | if (mpu->timer_invoked) { | ||
174 | mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER; | ||
175 | if (! mpu->timer_invoked) | ||
176 | del_timer(&mpu->timer); | ||
177 | } | ||
178 | spin_unlock_irqrestore (&mpu->timer_lock, flags); | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | |||
183 | */ | ||
184 | |||
185 | static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack) | ||
186 | { | ||
187 | unsigned long flags; | ||
188 | int timeout, ok; | ||
189 | |||
190 | spin_lock_irqsave(&mpu->input_lock, flags); | ||
191 | if (mpu->hardware != MPU401_HW_TRID4DWAVE) { | ||
192 | mpu->write(mpu, 0x00, MPU401D(mpu)); | ||
193 | /*snd_mpu401_uart_clear_rx(mpu);*/ | ||
194 | } | ||
195 | /* ok. standard MPU-401 initialization */ | ||
196 | if (mpu->hardware != MPU401_HW_SB) { | ||
197 | for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--) | ||
198 | udelay(10); | ||
199 | #ifdef CONFIG_SND_DEBUG | ||
200 | if (!timeout) | ||
201 | snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); | ||
202 | #endif | ||
203 | } | ||
204 | mpu->write(mpu, cmd, MPU401C(mpu)); | ||
205 | if (ack) { | ||
206 | ok = 0; | ||
207 | timeout = 10000; | ||
208 | while (!ok && timeout-- > 0) { | ||
209 | if (snd_mpu401_input_avail(mpu)) { | ||
210 | if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) | ||
211 | ok = 1; | ||
212 | } | ||
213 | } | ||
214 | if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) | ||
215 | ok = 1; | ||
216 | } else { | ||
217 | ok = 1; | ||
218 | } | ||
219 | spin_unlock_irqrestore(&mpu->input_lock, flags); | ||
220 | if (! ok) | ||
221 | snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); | ||
222 | // snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * input/output open/close - protected by open_mutex in rawmidi.c | ||
227 | */ | ||
228 | static int snd_mpu401_uart_input_open(snd_rawmidi_substream_t * substream) | ||
229 | { | ||
230 | mpu401_t *mpu; | ||
231 | int err; | ||
232 | |||
233 | mpu = substream->rmidi->private_data; | ||
234 | if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) | ||
235 | return err; | ||
236 | if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { | ||
237 | snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); | ||
238 | snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); | ||
239 | } | ||
240 | mpu->substream_input = substream; | ||
241 | set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static int snd_mpu401_uart_output_open(snd_rawmidi_substream_t * substream) | ||
246 | { | ||
247 | mpu401_t *mpu; | ||
248 | int err; | ||
249 | |||
250 | mpu = substream->rmidi->private_data; | ||
251 | if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) | ||
252 | return err; | ||
253 | if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { | ||
254 | snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); | ||
255 | snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); | ||
256 | } | ||
257 | mpu->substream_output = substream; | ||
258 | set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int snd_mpu401_uart_input_close(snd_rawmidi_substream_t * substream) | ||
263 | { | ||
264 | mpu401_t *mpu; | ||
265 | |||
266 | mpu = substream->rmidi->private_data; | ||
267 | clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); | ||
268 | mpu->substream_input = NULL; | ||
269 | if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) | ||
270 | snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); | ||
271 | if (mpu->close_input) | ||
272 | mpu->close_input(mpu); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static int snd_mpu401_uart_output_close(snd_rawmidi_substream_t * substream) | ||
277 | { | ||
278 | mpu401_t *mpu; | ||
279 | |||
280 | mpu = substream->rmidi->private_data; | ||
281 | clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); | ||
282 | mpu->substream_output = NULL; | ||
283 | if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) | ||
284 | snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); | ||
285 | if (mpu->close_output) | ||
286 | mpu->close_output(mpu); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * trigger input callback | ||
292 | */ | ||
293 | static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
294 | { | ||
295 | unsigned long flags; | ||
296 | mpu401_t *mpu; | ||
297 | int max = 64; | ||
298 | |||
299 | mpu = substream->rmidi->private_data; | ||
300 | if (up) { | ||
301 | if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { | ||
302 | /* first time - flush FIFO */ | ||
303 | while (max-- > 0) | ||
304 | mpu->read(mpu, MPU401D(mpu)); | ||
305 | if (mpu->irq < 0) | ||
306 | snd_mpu401_uart_add_timer(mpu, 1); | ||
307 | } | ||
308 | |||
309 | /* read data in advance */ | ||
310 | spin_lock_irqsave(&mpu->input_lock, flags); | ||
311 | snd_mpu401_uart_input_read(mpu); | ||
312 | spin_unlock_irqrestore(&mpu->input_lock, flags); | ||
313 | } else { | ||
314 | if (mpu->irq < 0) | ||
315 | snd_mpu401_uart_remove_timer(mpu, 1); | ||
316 | clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); | ||
317 | } | ||
318 | } | ||
319 | |||
320 | /* | ||
321 | * transfer input pending data | ||
322 | * call with input_lock spinlock held | ||
323 | */ | ||
324 | static void snd_mpu401_uart_input_read(mpu401_t * mpu) | ||
325 | { | ||
326 | int max = 128; | ||
327 | unsigned char byte; | ||
328 | |||
329 | while (max-- > 0) { | ||
330 | if (snd_mpu401_input_avail(mpu)) { | ||
331 | byte = mpu->read(mpu, MPU401D(mpu)); | ||
332 | if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) | ||
333 | snd_rawmidi_receive(mpu->substream_input, &byte, 1); | ||
334 | } else { | ||
335 | break; /* input not available */ | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Tx FIFO sizes: | ||
342 | * CS4237B - 16 bytes | ||
343 | * AudioDrive ES1688 - 12 bytes | ||
344 | * S3 SonicVibes - 8 bytes | ||
345 | * SoundBlaster AWE 64 - 2 bytes (ugly hardware) | ||
346 | */ | ||
347 | |||
348 | /* | ||
349 | * write output pending bytes | ||
350 | * call with output_lock spinlock held | ||
351 | */ | ||
352 | static void snd_mpu401_uart_output_write(mpu401_t * mpu) | ||
353 | { | ||
354 | unsigned char byte; | ||
355 | int max = 256, timeout; | ||
356 | |||
357 | do { | ||
358 | if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { | ||
359 | for (timeout = 100; timeout > 0; timeout--) { | ||
360 | if (snd_mpu401_output_ready(mpu)) { | ||
361 | mpu->write(mpu, byte, MPU401D(mpu)); | ||
362 | snd_rawmidi_transmit_ack(mpu->substream_output, 1); | ||
363 | break; | ||
364 | } | ||
365 | } | ||
366 | if (timeout == 0) | ||
367 | break; /* Tx FIFO full - try again later */ | ||
368 | } else { | ||
369 | snd_mpu401_uart_remove_timer (mpu, 0); | ||
370 | break; /* no other data - leave the tx loop */ | ||
371 | } | ||
372 | } while (--max > 0); | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * output trigger callback | ||
377 | */ | ||
378 | static void snd_mpu401_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
379 | { | ||
380 | unsigned long flags; | ||
381 | mpu401_t *mpu; | ||
382 | |||
383 | mpu = substream->rmidi->private_data; | ||
384 | if (up) { | ||
385 | set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); | ||
386 | |||
387 | /* try to add the timer at each output trigger, | ||
388 | * since the output timer might have been removed in | ||
389 | * snd_mpu401_uart_output_write(). | ||
390 | */ | ||
391 | snd_mpu401_uart_add_timer(mpu, 0); | ||
392 | |||
393 | /* output pending data */ | ||
394 | spin_lock_irqsave(&mpu->output_lock, flags); | ||
395 | snd_mpu401_uart_output_write(mpu); | ||
396 | spin_unlock_irqrestore(&mpu->output_lock, flags); | ||
397 | } else { | ||
398 | snd_mpu401_uart_remove_timer(mpu, 0); | ||
399 | clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | /* | ||
404 | |||
405 | */ | ||
406 | |||
407 | static snd_rawmidi_ops_t snd_mpu401_uart_output = | ||
408 | { | ||
409 | .open = snd_mpu401_uart_output_open, | ||
410 | .close = snd_mpu401_uart_output_close, | ||
411 | .trigger = snd_mpu401_uart_output_trigger, | ||
412 | }; | ||
413 | |||
414 | static snd_rawmidi_ops_t snd_mpu401_uart_input = | ||
415 | { | ||
416 | .open = snd_mpu401_uart_input_open, | ||
417 | .close = snd_mpu401_uart_input_close, | ||
418 | .trigger = snd_mpu401_uart_input_trigger, | ||
419 | }; | ||
420 | |||
421 | static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) | ||
422 | { | ||
423 | mpu401_t *mpu = rmidi->private_data; | ||
424 | if (mpu->irq_flags && mpu->irq >= 0) | ||
425 | free_irq(mpu->irq, (void *) mpu); | ||
426 | if (mpu->res) { | ||
427 | release_resource(mpu->res); | ||
428 | kfree_nocheck(mpu->res); | ||
429 | } | ||
430 | kfree(mpu); | ||
431 | } | ||
432 | |||
433 | /** | ||
434 | * snd_mpu401_uart_new - create an MPU401-UART instance | ||
435 | * @card: the card instance | ||
436 | * @device: the device index, zero-based | ||
437 | * @hardware: the hardware type, MPU401_HW_XXXX | ||
438 | * @port: the base address of MPU401 port | ||
439 | * @integrated: non-zero if the port was already reserved by the chip | ||
440 | * @irq: the irq number, -1 if no interrupt for mpu | ||
441 | * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. | ||
442 | * @rrawmidi: the pointer to store the new rawmidi instance | ||
443 | * | ||
444 | * Creates a new MPU-401 instance. | ||
445 | * | ||
446 | * Note that the rawmidi instance is returned on the rrawmidi argument, | ||
447 | * not the mpu401 instance itself. To access to the mpu401 instance, | ||
448 | * cast from rawmidi->private_data (with mpu401_t magic-cast). | ||
449 | * | ||
450 | * Returns zero if successful, or a negative error code. | ||
451 | */ | ||
452 | int snd_mpu401_uart_new(snd_card_t * card, int device, | ||
453 | unsigned short hardware, | ||
454 | unsigned long port, int integrated, | ||
455 | int irq, int irq_flags, | ||
456 | snd_rawmidi_t ** rrawmidi) | ||
457 | { | ||
458 | mpu401_t *mpu; | ||
459 | snd_rawmidi_t *rmidi; | ||
460 | int err; | ||
461 | |||
462 | if (rrawmidi) | ||
463 | *rrawmidi = NULL; | ||
464 | if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) | ||
465 | return err; | ||
466 | mpu = kcalloc(1, sizeof(*mpu), GFP_KERNEL); | ||
467 | if (mpu == NULL) { | ||
468 | snd_device_free(card, rmidi); | ||
469 | return -ENOMEM; | ||
470 | } | ||
471 | rmidi->private_data = mpu; | ||
472 | rmidi->private_free = snd_mpu401_uart_free; | ||
473 | spin_lock_init(&mpu->input_lock); | ||
474 | spin_lock_init(&mpu->output_lock); | ||
475 | spin_lock_init(&mpu->timer_lock); | ||
476 | mpu->hardware = hardware; | ||
477 | if (!integrated) { | ||
478 | int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; | ||
479 | if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) { | ||
480 | snd_printk(KERN_ERR "mpu401_uart: unable to grab port 0x%lx size %d\n", port, res_size); | ||
481 | snd_device_free(card, rmidi); | ||
482 | return -EBUSY; | ||
483 | } | ||
484 | } | ||
485 | switch (hardware) { | ||
486 | case MPU401_HW_AUREAL: | ||
487 | mpu->write = mpu401_write_mmio; | ||
488 | mpu->read = mpu401_read_mmio; | ||
489 | break; | ||
490 | default: | ||
491 | mpu->write = mpu401_write_port; | ||
492 | mpu->read = mpu401_read_port; | ||
493 | break; | ||
494 | } | ||
495 | mpu->port = port; | ||
496 | if (hardware == MPU401_HW_PC98II) | ||
497 | mpu->cport = port + 2; | ||
498 | else | ||
499 | mpu->cport = port + 1; | ||
500 | if (irq >= 0 && irq_flags) { | ||
501 | if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) { | ||
502 | snd_printk(KERN_ERR "mpu401_uart: unable to grab IRQ %d\n", irq); | ||
503 | snd_device_free(card, rmidi); | ||
504 | return -EBUSY; | ||
505 | } | ||
506 | } | ||
507 | mpu->irq = irq; | ||
508 | mpu->irq_flags = irq_flags; | ||
509 | if (card->shortname[0]) | ||
510 | snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname); | ||
511 | else | ||
512 | sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device); | ||
513 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); | ||
514 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); | ||
515 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | | ||
516 | SNDRV_RAWMIDI_INFO_INPUT | | ||
517 | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
518 | mpu->rmidi = rmidi; | ||
519 | if (rrawmidi) | ||
520 | *rrawmidi = rmidi; | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | EXPORT_SYMBOL(snd_mpu401_uart_interrupt); | ||
525 | EXPORT_SYMBOL(snd_mpu401_uart_new); | ||
526 | |||
527 | /* | ||
528 | * INIT part | ||
529 | */ | ||
530 | |||
531 | static int __init alsa_mpu401_uart_init(void) | ||
532 | { | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static void __exit alsa_mpu401_uart_exit(void) | ||
537 | { | ||
538 | } | ||
539 | |||
540 | module_init(alsa_mpu401_uart_init) | ||
541 | module_exit(alsa_mpu401_uart_exit) | ||
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c new file mode 100644 index 000000000000..1280a57c49eb --- /dev/null +++ b/sound/drivers/mtpav.c | |||
@@ -0,0 +1,795 @@ | |||
1 | /* | ||
2 | * MOTU Midi Timepiece ALSA Main routines | ||
3 | * Copyright by Michael T. Mayers (c) Jan 09, 2000 | ||
4 | * mail: michael@tweakoz.com | ||
5 | * Thanks to John Galbraith | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * This driver is for the 'Mark Of The Unicorn' (MOTU) | ||
23 | * MidiTimePiece AV multiport MIDI interface | ||
24 | * | ||
25 | * IOPORTS | ||
26 | * ------- | ||
27 | * 8 MIDI Ins and 8 MIDI outs | ||
28 | * Video Sync In (BNC), Word Sync Out (BNC), | ||
29 | * ADAT Sync Out (DB9) | ||
30 | * SMPTE in/out (1/4") | ||
31 | * 2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs. | ||
32 | * Macintosh RS422 serial port | ||
33 | * RS422 "network" port for ganging multiple MTP's | ||
34 | * PC Parallel Port ( which this driver currently uses ) | ||
35 | * | ||
36 | * MISC FEATURES | ||
37 | * ------------- | ||
38 | * Hardware MIDI routing, merging, and filtering | ||
39 | * MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources | ||
40 | * 128 'scene' memories, recallable from MIDI program change | ||
41 | * | ||
42 | * | ||
43 | * ChangeLog | ||
44 | * Jun 11 2001 Takashi Iwai <tiwai@suse.de> | ||
45 | * - Recoded & debugged | ||
46 | * - Added timer interrupt for midi outputs | ||
47 | * - hwports is between 1 and 8, which specifies the number of hardware ports. | ||
48 | * The three global ports, computer, adat and broadcast ports, are created | ||
49 | * always after h/w and remote ports. | ||
50 | * | ||
51 | */ | ||
52 | |||
53 | #include <sound/driver.h> | ||
54 | #include <linux/init.h> | ||
55 | #include <linux/interrupt.h> | ||
56 | #include <linux/slab.h> | ||
57 | #include <linux/ioport.h> | ||
58 | #include <linux/moduleparam.h> | ||
59 | #include <sound/core.h> | ||
60 | #include <sound/initval.h> | ||
61 | #include <sound/rawmidi.h> | ||
62 | #include <linux/delay.h> | ||
63 | |||
64 | #include <asm/io.h> | ||
65 | |||
66 | /* | ||
67 | * globals | ||
68 | */ | ||
69 | MODULE_AUTHOR("Michael T. Mayers"); | ||
70 | MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); | ||
71 | MODULE_LICENSE("GPL"); | ||
72 | MODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}"); | ||
73 | |||
74 | // io resources | ||
75 | #define MTPAV_IOBASE 0x378 | ||
76 | #define MTPAV_IRQ 7 | ||
77 | #define MTPAV_MAX_PORTS 8 | ||
78 | |||
79 | static int index = SNDRV_DEFAULT_IDX1; | ||
80 | static char *id = SNDRV_DEFAULT_STR1; | ||
81 | static long port = MTPAV_IOBASE; /* 0x378, 0x278 */ | ||
82 | static int irq = MTPAV_IRQ; /* 7, 5 */ | ||
83 | static int hwports = MTPAV_MAX_PORTS; /* use hardware ports 1-8 */ | ||
84 | |||
85 | module_param(index, int, 0444); | ||
86 | MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI."); | ||
87 | module_param(id, charp, 0444); | ||
88 | MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI."); | ||
89 | module_param(port, long, 0444); | ||
90 | MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI."); | ||
91 | module_param(irq, int, 0444); | ||
92 | MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI."); | ||
93 | module_param(hwports, int, 0444); | ||
94 | MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI."); | ||
95 | |||
96 | /* | ||
97 | * defines | ||
98 | */ | ||
99 | //#define USE_FAKE_MTP // don't actually read/write to MTP device (for debugging without an actual unit) (does not work yet) | ||
100 | |||
101 | // parallel port usage masks | ||
102 | #define SIGS_BYTE 0x08 | ||
103 | #define SIGS_RFD 0x80 | ||
104 | #define SIGS_IRQ 0x40 | ||
105 | #define SIGS_IN0 0x10 | ||
106 | #define SIGS_IN1 0x20 | ||
107 | |||
108 | #define SIGC_WRITE 0x04 | ||
109 | #define SIGC_READ 0x08 | ||
110 | #define SIGC_INTEN 0x10 | ||
111 | |||
112 | #define DREG 0 | ||
113 | #define SREG 1 | ||
114 | #define CREG 2 | ||
115 | |||
116 | // | ||
117 | #define MTPAV_MODE_INPUT_OPENED 0x01 | ||
118 | #define MTPAV_MODE_OUTPUT_OPENED 0x02 | ||
119 | #define MTPAV_MODE_INPUT_TRIGGERED 0x04 | ||
120 | #define MTPAV_MODE_OUTPUT_TRIGGERED 0x08 | ||
121 | |||
122 | #define NUMPORTS (0x12+1) | ||
123 | |||
124 | |||
125 | /* | ||
126 | */ | ||
127 | |||
128 | typedef struct mtpav_port { | ||
129 | u8 number; | ||
130 | u8 hwport; | ||
131 | u8 mode; | ||
132 | u8 running_status; | ||
133 | snd_rawmidi_substream_t *input; | ||
134 | snd_rawmidi_substream_t *output; | ||
135 | } mtpav_port_t; | ||
136 | |||
137 | typedef struct mtpav { | ||
138 | snd_card_t *card; | ||
139 | unsigned long port; | ||
140 | struct resource *res_port; | ||
141 | int irq; /* interrupt (for inputs) */ | ||
142 | spinlock_t spinlock; | ||
143 | int share_irq; /* number of accesses to input interrupts */ | ||
144 | int istimer; /* number of accesses to timer interrupts */ | ||
145 | struct timer_list timer; /* timer interrupts for outputs */ | ||
146 | snd_rawmidi_t *rmidi; | ||
147 | int num_ports; /* number of hw ports (1-8) */ | ||
148 | mtpav_port_t ports[NUMPORTS]; /* all ports including computer, adat and bc */ | ||
149 | |||
150 | u32 inmidiport; /* selected input midi port */ | ||
151 | u32 inmidistate; /* during midi command 0xf5 */ | ||
152 | |||
153 | u32 outmidihwport; /* selected output midi hw port */ | ||
154 | } mtpav_t; | ||
155 | |||
156 | |||
157 | /* | ||
158 | * global instance | ||
159 | * hey, we handle at most only one card.. | ||
160 | */ | ||
161 | static mtpav_t *mtp_card; | ||
162 | |||
163 | /* | ||
164 | * possible hardware ports (selected by 0xf5 port message) | ||
165 | * 0x00 all ports | ||
166 | * 0x01 .. 0x08 this MTP's ports 1..8 | ||
167 | * 0x09 .. 0x10 networked MTP's ports (9..16) | ||
168 | * 0x11 networked MTP's computer port | ||
169 | * 0x63 to ADAT | ||
170 | * | ||
171 | * mappig: | ||
172 | * subdevice 0 - (X-1) ports | ||
173 | * X - (2*X-1) networked ports | ||
174 | * X computer | ||
175 | * X+1 ADAT | ||
176 | * X+2 all ports | ||
177 | * | ||
178 | * where X = chip->num_ports | ||
179 | */ | ||
180 | |||
181 | #define MTPAV_PIDX_COMPUTER 0 | ||
182 | #define MTPAV_PIDX_ADAT 1 | ||
183 | #define MTPAV_PIDX_BROADCAST 2 | ||
184 | |||
185 | |||
186 | static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev) | ||
187 | { | ||
188 | if (subdev < 0) | ||
189 | return 0x01; /* invalid - use port 0 as default */ | ||
190 | else if (subdev < chip->num_ports) | ||
191 | return subdev + 1; /* single mtp port */ | ||
192 | else if (subdev < chip->num_ports * 2) | ||
193 | return subdev - chip->num_ports + 0x09; /* remote port */ | ||
194 | else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER) | ||
195 | return 0x11; /* computer port */ | ||
196 | else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT) | ||
197 | return 0x63; /* ADAT */ | ||
198 | return 0; /* all ports */ | ||
199 | } | ||
200 | |||
201 | static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport) | ||
202 | { | ||
203 | int p; | ||
204 | if (hwport <= 0x00) /* all ports */ | ||
205 | return chip->num_ports + MTPAV_PIDX_BROADCAST; | ||
206 | else if (hwport <= 0x08) { /* single port */ | ||
207 | p = hwport - 1; | ||
208 | if (p >= chip->num_ports) | ||
209 | p = 0; | ||
210 | return p; | ||
211 | } else if (hwport <= 0x10) { /* remote port */ | ||
212 | p = hwport - 0x09 + chip->num_ports; | ||
213 | if (p >= chip->num_ports * 2) | ||
214 | p = chip->num_ports; | ||
215 | return p; | ||
216 | } else if (hwport == 0x11) /* computer port */ | ||
217 | return chip->num_ports + MTPAV_PIDX_COMPUTER; | ||
218 | else /* ADAT */ | ||
219 | return chip->num_ports + MTPAV_PIDX_ADAT; | ||
220 | } | ||
221 | |||
222 | |||
223 | /* | ||
224 | */ | ||
225 | |||
226 | static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg) | ||
227 | { | ||
228 | u8 rval = 0; | ||
229 | |||
230 | if (reg == SREG) { | ||
231 | rval = inb(chip->port + SREG); | ||
232 | rval = (rval & 0xf8); | ||
233 | } else if (reg == CREG) { | ||
234 | rval = inb(chip->port + CREG); | ||
235 | rval = (rval & 0x1c); | ||
236 | } | ||
237 | |||
238 | return rval; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | */ | ||
243 | |||
244 | static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val) | ||
245 | { | ||
246 | if (reg == DREG) { | ||
247 | outb(val, chip->port + DREG); | ||
248 | } else if (reg == CREG) { | ||
249 | outb(val, chip->port + CREG); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | */ | ||
255 | |||
256 | static void snd_mtpav_wait_rfdhi(mtpav_t *chip) | ||
257 | { | ||
258 | int counts = 10000; | ||
259 | u8 sbyte; | ||
260 | |||
261 | sbyte = snd_mtpav_getreg(chip, SREG); | ||
262 | while (!(sbyte & SIGS_RFD) && counts--) { | ||
263 | sbyte = snd_mtpav_getreg(chip, SREG); | ||
264 | udelay(10); | ||
265 | } | ||
266 | } | ||
267 | |||
268 | static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte) | ||
269 | { | ||
270 | u8 tcbyt; | ||
271 | u8 clrwrite; | ||
272 | u8 setwrite; | ||
273 | |||
274 | snd_mtpav_wait_rfdhi(chip); | ||
275 | |||
276 | ///////////////// | ||
277 | |||
278 | tcbyt = snd_mtpav_getreg(chip, CREG); | ||
279 | clrwrite = tcbyt & (SIGC_WRITE ^ 0xff); | ||
280 | setwrite = tcbyt | SIGC_WRITE; | ||
281 | |||
282 | snd_mtpav_mputreg(chip, DREG, byte); | ||
283 | snd_mtpav_mputreg(chip, CREG, clrwrite); // clear write bit | ||
284 | |||
285 | snd_mtpav_mputreg(chip, CREG, setwrite); // set write bit | ||
286 | |||
287 | } | ||
288 | |||
289 | |||
290 | /* | ||
291 | */ | ||
292 | |||
293 | /* call this with spin lock held */ | ||
294 | static void snd_mtpav_output_port_write(mtpav_port_t *port, | ||
295 | snd_rawmidi_substream_t *substream) | ||
296 | { | ||
297 | u8 outbyte; | ||
298 | |||
299 | // Get the outbyte first, so we can emulate running status if | ||
300 | // necessary | ||
301 | if (snd_rawmidi_transmit(substream, &outbyte, 1) != 1) | ||
302 | return; | ||
303 | |||
304 | // send port change command if necessary | ||
305 | |||
306 | if (port->hwport != mtp_card->outmidihwport) { | ||
307 | mtp_card->outmidihwport = port->hwport; | ||
308 | |||
309 | snd_mtpav_send_byte(mtp_card, 0xf5); | ||
310 | snd_mtpav_send_byte(mtp_card, port->hwport); | ||
311 | //snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport); | ||
312 | |||
313 | if (!(outbyte & 0x80) && port->running_status) | ||
314 | snd_mtpav_send_byte(mtp_card, port->running_status); | ||
315 | } | ||
316 | |||
317 | // send data | ||
318 | |||
319 | do { | ||
320 | if (outbyte & 0x80) | ||
321 | port->running_status = outbyte; | ||
322 | |||
323 | snd_mtpav_send_byte(mtp_card, outbyte); | ||
324 | } while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1); | ||
325 | } | ||
326 | |||
327 | static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream) | ||
328 | { | ||
329 | mtpav_port_t *port = &mtp_card->ports[substream->number]; | ||
330 | unsigned long flags; | ||
331 | |||
332 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
333 | snd_mtpav_output_port_write(port, substream); | ||
334 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
335 | } | ||
336 | |||
337 | |||
338 | /* | ||
339 | * mtpav control | ||
340 | */ | ||
341 | |||
342 | static void snd_mtpav_portscan(mtpav_t *chip) // put mtp into smart routing mode | ||
343 | { | ||
344 | u8 p; | ||
345 | |||
346 | for (p = 0; p < 8; p++) { | ||
347 | snd_mtpav_send_byte(chip, 0xf5); | ||
348 | snd_mtpav_send_byte(chip, p); | ||
349 | snd_mtpav_send_byte(chip, 0xfe); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | */ | ||
355 | |||
356 | static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream) | ||
357 | { | ||
358 | unsigned long flags; | ||
359 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
360 | |||
361 | //printk("mtpav port: %d opened\n", (int) substream->number); | ||
362 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
363 | portp->mode |= MTPAV_MODE_INPUT_OPENED; | ||
364 | portp->input = substream; | ||
365 | if (mtp_card->share_irq++ == 0) | ||
366 | snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts | ||
367 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | */ | ||
373 | |||
374 | static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream) | ||
375 | { | ||
376 | unsigned long flags; | ||
377 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
378 | |||
379 | //printk("mtpav port: %d closed\n", (int) portp); | ||
380 | |||
381 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
382 | |||
383 | portp->mode &= (~MTPAV_MODE_INPUT_OPENED); | ||
384 | portp->input = NULL; | ||
385 | if (--mtp_card->share_irq == 0) | ||
386 | snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts | ||
387 | |||
388 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | */ | ||
394 | |||
395 | static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
396 | { | ||
397 | unsigned long flags; | ||
398 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
399 | |||
400 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
401 | if (up) | ||
402 | portp->mode |= MTPAV_MODE_INPUT_TRIGGERED; | ||
403 | else | ||
404 | portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED; | ||
405 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
406 | |||
407 | } | ||
408 | |||
409 | |||
410 | /* | ||
411 | * timer interrupt for outputs | ||
412 | */ | ||
413 | |||
414 | static void snd_mtpav_output_timer(unsigned long data) | ||
415 | { | ||
416 | unsigned long flags; | ||
417 | mtpav_t *chip = (mtpav_t *)data; | ||
418 | int p; | ||
419 | |||
420 | spin_lock_irqsave(&chip->spinlock, flags); | ||
421 | /* reprogram timer */ | ||
422 | chip->timer.expires = 1 + jiffies; | ||
423 | add_timer(&chip->timer); | ||
424 | /* process each port */ | ||
425 | for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) { | ||
426 | mtpav_port_t *portp = &mtp_card->ports[p]; | ||
427 | if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output) | ||
428 | snd_mtpav_output_port_write(portp, portp->output); | ||
429 | } | ||
430 | spin_unlock_irqrestore(&chip->spinlock, flags); | ||
431 | } | ||
432 | |||
433 | /* spinlock held! */ | ||
434 | static void snd_mtpav_add_output_timer(mtpav_t *chip) | ||
435 | { | ||
436 | init_timer(&chip->timer); | ||
437 | chip->timer.function = snd_mtpav_output_timer; | ||
438 | chip->timer.data = (unsigned long) mtp_card; | ||
439 | chip->timer.expires = 1 + jiffies; | ||
440 | add_timer(&chip->timer); | ||
441 | } | ||
442 | |||
443 | /* spinlock held! */ | ||
444 | static void snd_mtpav_remove_output_timer(mtpav_t *chip) | ||
445 | { | ||
446 | del_timer(&chip->timer); | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | */ | ||
451 | |||
452 | static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream) | ||
453 | { | ||
454 | unsigned long flags; | ||
455 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
456 | |||
457 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
458 | portp->mode |= MTPAV_MODE_OUTPUT_OPENED; | ||
459 | portp->output = substream; | ||
460 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
461 | return 0; | ||
462 | }; | ||
463 | |||
464 | /* | ||
465 | */ | ||
466 | |||
467 | static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream) | ||
468 | { | ||
469 | unsigned long flags; | ||
470 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
471 | |||
472 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
473 | portp->mode &= (~MTPAV_MODE_OUTPUT_OPENED); | ||
474 | portp->output = NULL; | ||
475 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
476 | return 0; | ||
477 | }; | ||
478 | |||
479 | /* | ||
480 | */ | ||
481 | |||
482 | static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
483 | { | ||
484 | unsigned long flags; | ||
485 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
486 | |||
487 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
488 | if (up) { | ||
489 | if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) { | ||
490 | if (mtp_card->istimer++ == 0) | ||
491 | snd_mtpav_add_output_timer(mtp_card); | ||
492 | portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED; | ||
493 | } | ||
494 | } else { | ||
495 | portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED; | ||
496 | if (--mtp_card->istimer == 0) | ||
497 | snd_mtpav_remove_output_timer(mtp_card); | ||
498 | } | ||
499 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
500 | |||
501 | if (up) | ||
502 | snd_mtpav_output_write(substream); | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * midi interrupt for inputs | ||
507 | */ | ||
508 | |||
509 | static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte) | ||
510 | { | ||
511 | mtpav_port_t *portp; | ||
512 | |||
513 | if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST) | ||
514 | return; | ||
515 | |||
516 | portp = &mcrd->ports[mcrd->inmidiport]; | ||
517 | if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED) { | ||
518 | snd_rawmidi_receive(portp->input, &inbyte, 1); | ||
519 | } | ||
520 | } | ||
521 | |||
522 | static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte) | ||
523 | { | ||
524 | snd_assert(mcrd, return); | ||
525 | |||
526 | if (inbyte >= 0xf8) { | ||
527 | /* real-time midi code */ | ||
528 | snd_mtpav_inmidi_process(mcrd, inbyte); | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | if (mcrd->inmidistate == 0) { // awaiting command | ||
533 | if (inbyte == 0xf5) // MTP port # | ||
534 | mcrd->inmidistate = 1; | ||
535 | else | ||
536 | snd_mtpav_inmidi_process(mcrd, inbyte); | ||
537 | } else if (mcrd->inmidistate) { | ||
538 | mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte); | ||
539 | mcrd->inmidistate = 0; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | static void snd_mtpav_read_bytes(mtpav_t * mcrd) | ||
544 | { | ||
545 | u8 clrread, setread; | ||
546 | u8 mtp_read_byte; | ||
547 | u8 sr, cbyt; | ||
548 | int i; | ||
549 | |||
550 | u8 sbyt = snd_mtpav_getreg(mcrd, SREG); | ||
551 | |||
552 | //printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); | ||
553 | |||
554 | if (!(sbyt & SIGS_BYTE)) | ||
555 | return; | ||
556 | |||
557 | cbyt = snd_mtpav_getreg(mcrd, CREG); | ||
558 | clrread = cbyt & (SIGC_READ ^ 0xff); | ||
559 | setread = cbyt | SIGC_READ; | ||
560 | |||
561 | do { | ||
562 | |||
563 | mtp_read_byte = 0; | ||
564 | for (i = 0; i < 4; i++) { | ||
565 | snd_mtpav_mputreg(mcrd, CREG, setread); | ||
566 | sr = snd_mtpav_getreg(mcrd, SREG); | ||
567 | snd_mtpav_mputreg(mcrd, CREG, clrread); | ||
568 | |||
569 | sr &= SIGS_IN0 | SIGS_IN1; | ||
570 | sr >>= 4; | ||
571 | mtp_read_byte |= sr << (i * 2); | ||
572 | } | ||
573 | |||
574 | snd_mtpav_inmidi_h(mcrd, mtp_read_byte); | ||
575 | |||
576 | sbyt = snd_mtpav_getreg(mcrd, SREG); | ||
577 | |||
578 | } while (sbyt & SIGS_BYTE); | ||
579 | } | ||
580 | |||
581 | static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs) | ||
582 | { | ||
583 | mtpav_t *mcard = dev_id; | ||
584 | |||
585 | //printk("irqh()\n"); | ||
586 | spin_lock(&mcard->spinlock); | ||
587 | snd_mtpav_read_bytes(mcard); | ||
588 | spin_unlock(&mcard->spinlock); | ||
589 | return IRQ_HANDLED; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * get ISA resources | ||
594 | */ | ||
595 | static int snd_mtpav_get_ISA(mtpav_t * mcard) | ||
596 | { | ||
597 | if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) { | ||
598 | snd_printk("MTVAP port 0x%lx is busy\n", port); | ||
599 | return -EBUSY; | ||
600 | } | ||
601 | mcard->port = port; | ||
602 | if (request_irq(irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) { | ||
603 | snd_printk("MTVAP IRQ %d busy\n", irq); | ||
604 | return -EBUSY; | ||
605 | } | ||
606 | mcard->irq = irq; | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | |||
611 | /* | ||
612 | */ | ||
613 | |||
614 | static snd_rawmidi_ops_t snd_mtpav_output = { | ||
615 | .open = snd_mtpav_output_open, | ||
616 | .close = snd_mtpav_output_close, | ||
617 | .trigger = snd_mtpav_output_trigger, | ||
618 | }; | ||
619 | |||
620 | static snd_rawmidi_ops_t snd_mtpav_input = { | ||
621 | .open = snd_mtpav_input_open, | ||
622 | .close = snd_mtpav_input_close, | ||
623 | .trigger = snd_mtpav_input_trigger, | ||
624 | }; | ||
625 | |||
626 | |||
627 | /* | ||
628 | * get RAWMIDI resources | ||
629 | */ | ||
630 | |||
631 | static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream) | ||
632 | { | ||
633 | if (substream->number >= 0 && substream->number < chip->num_ports) | ||
634 | sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1); | ||
635 | else if (substream->number >= 8 && substream->number < chip->num_ports * 2) | ||
636 | sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); | ||
637 | else if (substream->number == chip->num_ports * 2) | ||
638 | strcpy(substream->name, "MTP computer"); | ||
639 | else if (substream->number == chip->num_ports * 2 + 1) | ||
640 | strcpy(substream->name, "MTP ADAT"); | ||
641 | else | ||
642 | strcpy(substream->name, "MTP broadcast"); | ||
643 | } | ||
644 | |||
645 | static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard) | ||
646 | { | ||
647 | int rval = 0; | ||
648 | snd_rawmidi_t *rawmidi; | ||
649 | snd_rawmidi_substream_t *substream; | ||
650 | struct list_head *list; | ||
651 | |||
652 | //printk("entering snd_mtpav_get_RAWMIDI\n"); | ||
653 | |||
654 | if (hwports < 1) | ||
655 | mcard->num_ports = 1; | ||
656 | else if (hwports > 8) | ||
657 | mcard->num_ports = 8; | ||
658 | else | ||
659 | mcard->num_ports = hwports; | ||
660 | |||
661 | if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0, | ||
662 | mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, | ||
663 | mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, | ||
664 | &mcard->rmidi)) < 0) | ||
665 | return rval; | ||
666 | rawmidi = mcard->rmidi; | ||
667 | |||
668 | list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { | ||
669 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
670 | snd_mtpav_set_name(mcard, substream); | ||
671 | substream->ops = &snd_mtpav_input; | ||
672 | } | ||
673 | list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { | ||
674 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
675 | snd_mtpav_set_name(mcard, substream); | ||
676 | substream->ops = &snd_mtpav_output; | ||
677 | mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number); | ||
678 | } | ||
679 | rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | | ||
680 | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
681 | sprintf(rawmidi->name, "MTP AV MIDI"); | ||
682 | //printk("exiting snd_mtpav_get_RAWMIDI() \n"); | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | /* | ||
687 | */ | ||
688 | |||
689 | static mtpav_t *new_mtpav(void) | ||
690 | { | ||
691 | mtpav_t *ncrd = kcalloc(1, sizeof(*ncrd), GFP_KERNEL); | ||
692 | if (ncrd != NULL) { | ||
693 | spin_lock_init(&ncrd->spinlock); | ||
694 | |||
695 | init_timer(&ncrd->timer); | ||
696 | ncrd->card = NULL; | ||
697 | ncrd->irq = -1; | ||
698 | ncrd->share_irq = 0; | ||
699 | |||
700 | ncrd->inmidiport = 0xffffffff; | ||
701 | ncrd->inmidistate = 0; | ||
702 | ncrd->outmidihwport = 0xffffffff; | ||
703 | } | ||
704 | return ncrd; | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | */ | ||
709 | |||
710 | static void free_mtpav(mtpav_t * crd) | ||
711 | { | ||
712 | unsigned long flags; | ||
713 | |||
714 | spin_lock_irqsave(&crd->spinlock, flags); | ||
715 | if (crd->istimer > 0) | ||
716 | snd_mtpav_remove_output_timer(crd); | ||
717 | spin_unlock_irqrestore(&crd->spinlock, flags); | ||
718 | if (crd->irq >= 0) | ||
719 | free_irq(crd->irq, (void *)crd); | ||
720 | if (crd->res_port) { | ||
721 | release_resource(crd->res_port); | ||
722 | kfree_nocheck(crd->res_port); | ||
723 | } | ||
724 | kfree(crd); | ||
725 | } | ||
726 | |||
727 | /* | ||
728 | */ | ||
729 | |||
730 | static int __init alsa_card_mtpav_init(void) | ||
731 | { | ||
732 | int err = 0; | ||
733 | char longname_buffer[80]; | ||
734 | |||
735 | mtp_card = new_mtpav(); | ||
736 | if (mtp_card == NULL) | ||
737 | return -ENOMEM; | ||
738 | |||
739 | mtp_card->card = snd_card_new(index, id, THIS_MODULE, 0); | ||
740 | if (mtp_card->card == NULL) { | ||
741 | free_mtpav(mtp_card); | ||
742 | return -ENOMEM; | ||
743 | } | ||
744 | |||
745 | err = snd_mtpav_get_ISA(mtp_card); | ||
746 | //printk("snd_mtpav_get_ISA returned: %d\n", err); | ||
747 | if (err < 0) | ||
748 | goto __error; | ||
749 | |||
750 | strcpy(mtp_card->card->driver, "MTPAV"); | ||
751 | strcpy(mtp_card->card->shortname, "MTPAV on parallel port"); | ||
752 | memset(longname_buffer, 0, sizeof(longname_buffer)); | ||
753 | sprintf(longname_buffer, "MTPAV on parallel port at"); | ||
754 | |||
755 | err = snd_mtpav_get_RAWMIDI(mtp_card); | ||
756 | //snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err); | ||
757 | if (err < 0) | ||
758 | goto __error; | ||
759 | |||
760 | err = snd_card_register(mtp_card->card); // don't snd_card_register until AFTER all cards reources done! | ||
761 | |||
762 | //printk("snd_card_register returned %d\n", err); | ||
763 | if (err < 0) | ||
764 | goto __error; | ||
765 | |||
766 | |||
767 | snd_mtpav_portscan(mtp_card); | ||
768 | |||
769 | printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port); | ||
770 | |||
771 | return 0; | ||
772 | |||
773 | __error: | ||
774 | snd_card_free(mtp_card->card); | ||
775 | free_mtpav(mtp_card); | ||
776 | return err; | ||
777 | } | ||
778 | |||
779 | /* | ||
780 | */ | ||
781 | |||
782 | static void __exit alsa_card_mtpav_exit(void) | ||
783 | { | ||
784 | if (mtp_card == NULL) | ||
785 | return; | ||
786 | if (mtp_card->card) | ||
787 | snd_card_free(mtp_card->card); | ||
788 | free_mtpav(mtp_card); | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | */ | ||
793 | |||
794 | module_init(alsa_card_mtpav_init) | ||
795 | module_exit(alsa_card_mtpav_exit) | ||
diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile new file mode 100644 index 000000000000..12059785b5cb --- /dev/null +++ b/sound/drivers/opl3/Makefile | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-opl3-lib-objs := opl3_lib.o opl3_synth.o | ||
7 | snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o | ||
8 | ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) | ||
9 | snd-opl3-synth-objs += opl3_oss.o | ||
10 | endif | ||
11 | |||
12 | # | ||
13 | # this function returns: | ||
14 | # "m" - CONFIG_SND_SEQUENCER is m | ||
15 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
16 | # otherwise parameter #1 value | ||
17 | # | ||
18 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
19 | |||
20 | obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o | ||
21 | obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o | ||
22 | obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o | ||
diff --git a/sound/drivers/opl3/opl3_drums.c b/sound/drivers/opl3/opl3_drums.c new file mode 100644 index 000000000000..f26332680c19 --- /dev/null +++ b/sound/drivers/opl3/opl3_drums.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Uros Bizjak <uros@kss-loka.si> | ||
3 | * | ||
4 | * OPL2/OPL3/OPL4 FM routines for internal percussion channels | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "opl3_voice.h" | ||
23 | |||
24 | extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; | ||
25 | |||
26 | static char snd_opl3_drum_table[47] = | ||
27 | { | ||
28 | OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */ | ||
29 | OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */ | ||
30 | OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, OPL3_BASSDRUM_ON, /* 41 - 43 */ | ||
31 | OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_HIHAT_ON, /* 44 - 46 */ | ||
32 | OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, /* 47 - 49 */ | ||
33 | |||
34 | OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 50 - 52 */ | ||
35 | OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 53 - 55 */ | ||
36 | OPL3_HIHAT_ON, OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, /* 56 - 58 */ | ||
37 | OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 59 - 61 */ | ||
38 | OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 62 - 64 */ | ||
39 | |||
40 | OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 65 - 67 */ | ||
41 | OPL3_TOMTOM_ON, OPL3_HIHAT_ON, OPL3_HIHAT_ON, /* 68 - 70 */ | ||
42 | OPL3_HIHAT_ON, OPL3_HIHAT_ON, OPL3_TOMTOM_ON, /* 71 - 73 */ | ||
43 | OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 74 - 76 */ | ||
44 | OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 77 - 79 */ | ||
45 | OPL3_CYMBAL_ON, OPL3_CYMBAL_ON /* 80 - 81 */ | ||
46 | }; | ||
47 | |||
48 | typedef struct snd_opl3_drum_voice { | ||
49 | int voice; | ||
50 | int op; | ||
51 | unsigned char am_vib; | ||
52 | unsigned char ksl_level; | ||
53 | unsigned char attack_decay; | ||
54 | unsigned char sustain_release; | ||
55 | unsigned char feedback_connection; | ||
56 | unsigned char wave_select; | ||
57 | } snd_opl3_drum_voice_t; | ||
58 | |||
59 | typedef struct snd_opl3_drum_note { | ||
60 | int voice; | ||
61 | unsigned char fnum; | ||
62 | unsigned char octave_f; | ||
63 | unsigned char feedback_connection; | ||
64 | } snd_opl3_drum_note_t; | ||
65 | |||
66 | static snd_opl3_drum_voice_t bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00}; | ||
67 | static snd_opl3_drum_voice_t bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00}; | ||
68 | static snd_opl3_drum_note_t bass_note = {6, 0x90, 0x09}; | ||
69 | |||
70 | static snd_opl3_drum_voice_t hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00}; | ||
71 | |||
72 | static snd_opl3_drum_voice_t snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02}; | ||
73 | static snd_opl3_drum_note_t snare_note = {7, 0xf4, 0x0d}; | ||
74 | |||
75 | static snd_opl3_drum_voice_t tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00}; | ||
76 | static snd_opl3_drum_note_t tomtom_note = {8, 0xf4, 0x09}; | ||
77 | |||
78 | static snd_opl3_drum_voice_t cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00}; | ||
79 | |||
80 | /* | ||
81 | * set drum voice characteristics | ||
82 | */ | ||
83 | static void snd_opl3_drum_voice_set(opl3_t *opl3, snd_opl3_drum_voice_t *data) | ||
84 | { | ||
85 | unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; | ||
86 | unsigned char voice_offset = data->voice; | ||
87 | unsigned short opl3_reg; | ||
88 | |||
89 | /* Set OPL3 AM_VIB register */ | ||
90 | opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset); | ||
91 | opl3->command(opl3, opl3_reg, data->am_vib); | ||
92 | |||
93 | /* Set OPL3 KSL_LEVEL register */ | ||
94 | opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); | ||
95 | opl3->command(opl3, opl3_reg, data->ksl_level); | ||
96 | |||
97 | /* Set OPL3 ATTACK_DECAY register */ | ||
98 | opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset); | ||
99 | opl3->command(opl3, opl3_reg, data->attack_decay); | ||
100 | |||
101 | /* Set OPL3 SUSTAIN_RELEASE register */ | ||
102 | opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset); | ||
103 | opl3->command(opl3, opl3_reg, data->sustain_release); | ||
104 | |||
105 | /* Set OPL3 FEEDBACK_CONNECTION register */ | ||
106 | opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); | ||
107 | opl3->command(opl3, opl3_reg, data->feedback_connection); | ||
108 | |||
109 | /* Select waveform */ | ||
110 | opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset); | ||
111 | opl3->command(opl3, opl3_reg, data->wave_select); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Set drum voice pitch | ||
116 | */ | ||
117 | static void snd_opl3_drum_note_set(opl3_t *opl3, snd_opl3_drum_note_t *data) | ||
118 | { | ||
119 | unsigned char voice_offset = data->voice; | ||
120 | unsigned short opl3_reg; | ||
121 | |||
122 | /* Set OPL3 FNUM_LOW register */ | ||
123 | opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset); | ||
124 | opl3->command(opl3, opl3_reg, data->fnum); | ||
125 | |||
126 | /* Set OPL3 KEYON_BLOCK register */ | ||
127 | opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
128 | opl3->command(opl3, opl3_reg, data->octave_f); | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * Set drum voice volume and position | ||
133 | */ | ||
134 | static void snd_opl3_drum_vol_set(opl3_t *opl3, snd_opl3_drum_voice_t *data, | ||
135 | int vel, snd_midi_channel_t *chan) | ||
136 | { | ||
137 | unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; | ||
138 | unsigned char voice_offset = data->voice; | ||
139 | unsigned char reg_val; | ||
140 | unsigned short opl3_reg; | ||
141 | |||
142 | /* Set OPL3 KSL_LEVEL register */ | ||
143 | reg_val = data->ksl_level; | ||
144 | snd_opl3_calc_volume(®_val, vel, chan); | ||
145 | opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); | ||
146 | opl3->command(opl3, opl3_reg, reg_val); | ||
147 | |||
148 | /* Set OPL3 FEEDBACK_CONNECTION register */ | ||
149 | /* Set output voice connection */ | ||
150 | reg_val = data->feedback_connection | OPL3_STEREO_BITS; | ||
151 | if (chan->gm_pan < 43) | ||
152 | reg_val &= ~OPL3_VOICE_TO_RIGHT; | ||
153 | if (chan->gm_pan > 85) | ||
154 | reg_val &= ~OPL3_VOICE_TO_LEFT; | ||
155 | opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); | ||
156 | opl3->command(opl3, opl3_reg, reg_val); | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Loads drum voices at init time | ||
161 | */ | ||
162 | void snd_opl3_load_drums(opl3_t *opl3) | ||
163 | { | ||
164 | snd_opl3_drum_voice_set(opl3, &bass_op0); | ||
165 | snd_opl3_drum_voice_set(opl3, &bass_op1); | ||
166 | snd_opl3_drum_note_set(opl3, &bass_note); | ||
167 | |||
168 | snd_opl3_drum_voice_set(opl3, &hihat); | ||
169 | |||
170 | snd_opl3_drum_voice_set(opl3, &snare); | ||
171 | snd_opl3_drum_note_set(opl3, &snare_note); | ||
172 | |||
173 | snd_opl3_drum_voice_set(opl3, &tomtom); | ||
174 | snd_opl3_drum_note_set(opl3, &tomtom_note); | ||
175 | |||
176 | snd_opl3_drum_voice_set(opl3, &cymbal); | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * Switch drum voice on or off | ||
181 | */ | ||
182 | void snd_opl3_drum_switch(opl3_t *opl3, int note, int vel, int on_off, | ||
183 | snd_midi_channel_t *chan) | ||
184 | { | ||
185 | unsigned char drum_mask; | ||
186 | snd_opl3_drum_voice_t *drum_voice; | ||
187 | |||
188 | if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE)) | ||
189 | return; | ||
190 | |||
191 | if ((note < 35) || (note > 81)) | ||
192 | return; | ||
193 | drum_mask = snd_opl3_drum_table[note - 35]; | ||
194 | |||
195 | if (on_off) { | ||
196 | switch (drum_mask) { | ||
197 | case OPL3_BASSDRUM_ON: | ||
198 | drum_voice = &bass_op1; | ||
199 | break; | ||
200 | case OPL3_HIHAT_ON: | ||
201 | drum_voice = &hihat; | ||
202 | break; | ||
203 | case OPL3_SNAREDRUM_ON: | ||
204 | drum_voice = &snare; | ||
205 | break; | ||
206 | case OPL3_TOMTOM_ON: | ||
207 | drum_voice = &tomtom; | ||
208 | break; | ||
209 | case OPL3_CYMBAL_ON: | ||
210 | drum_voice = &cymbal; | ||
211 | break; | ||
212 | default: | ||
213 | drum_voice = &tomtom; | ||
214 | } | ||
215 | |||
216 | snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan); | ||
217 | opl3->drum_reg |= drum_mask; | ||
218 | } else { | ||
219 | opl3->drum_reg &= ~drum_mask; | ||
220 | } | ||
221 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, | ||
222 | opl3->drum_reg); | ||
223 | } | ||
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c new file mode 100644 index 000000000000..c313e5205cb8 --- /dev/null +++ b/sound/drivers/opl3/opl3_lib.c | |||
@@ -0,0 +1,558 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz>, | ||
3 | * Hannu Savolainen 1993-1996, | ||
4 | * Rob Hooft | ||
5 | * | ||
6 | * Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) | ||
7 | * | ||
8 | * Most if code is ported from OSS/Lite. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <sound/opl3.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <sound/minors.h> | ||
33 | |||
34 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Hannu Savolainen 1993-1996, Rob Hooft"); | ||
35 | MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; | ||
39 | |||
40 | static void snd_opl2_command(opl3_t * opl3, unsigned short cmd, unsigned char val) | ||
41 | { | ||
42 | unsigned long flags; | ||
43 | unsigned long port; | ||
44 | |||
45 | /* | ||
46 | * The original 2-OP synth requires a quite long delay | ||
47 | * after writing to a register. | ||
48 | */ | ||
49 | |||
50 | port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; | ||
51 | |||
52 | spin_lock_irqsave(&opl3->reg_lock, flags); | ||
53 | |||
54 | outb((unsigned char) cmd, port); | ||
55 | udelay(10); | ||
56 | |||
57 | outb((unsigned char) val, port + 1); | ||
58 | udelay(30); | ||
59 | |||
60 | spin_unlock_irqrestore(&opl3->reg_lock, flags); | ||
61 | } | ||
62 | |||
63 | static void snd_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val) | ||
64 | { | ||
65 | unsigned long flags; | ||
66 | unsigned long port; | ||
67 | |||
68 | /* | ||
69 | * The OPL-3 survives with just two INBs | ||
70 | * after writing to a register. | ||
71 | */ | ||
72 | |||
73 | port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; | ||
74 | |||
75 | spin_lock_irqsave(&opl3->reg_lock, flags); | ||
76 | |||
77 | outb((unsigned char) cmd, port); | ||
78 | inb(opl3->l_port); | ||
79 | inb(opl3->l_port); | ||
80 | |||
81 | outb((unsigned char) val, port + 1); | ||
82 | inb(opl3->l_port); | ||
83 | inb(opl3->l_port); | ||
84 | |||
85 | spin_unlock_irqrestore(&opl3->reg_lock, flags); | ||
86 | } | ||
87 | |||
88 | static int snd_opl3_detect(opl3_t * opl3) | ||
89 | { | ||
90 | /* | ||
91 | * This function returns 1 if the FM chip is present at the given I/O port | ||
92 | * The detection algorithm plays with the timer built in the FM chip and | ||
93 | * looks for a change in the status register. | ||
94 | * | ||
95 | * Note! The timers of the FM chip are not connected to AdLib (and compatible) | ||
96 | * boards. | ||
97 | * | ||
98 | * Note2! The chip is initialized if detected. | ||
99 | */ | ||
100 | |||
101 | unsigned char stat1, stat2, signature; | ||
102 | |||
103 | /* Reset timers 1 and 2 */ | ||
104 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); | ||
105 | /* Reset the IRQ of the FM chip */ | ||
106 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); | ||
107 | signature = stat1 = inb(opl3->l_port); /* Status register */ | ||
108 | if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */ | ||
109 | snd_printd("OPL3: stat1 = 0x%x\n", stat1); | ||
110 | return -ENODEV; | ||
111 | } | ||
112 | /* Set timer1 to 0xff */ | ||
113 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff); | ||
114 | /* Unmask and start timer 1 */ | ||
115 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START); | ||
116 | /* Now we have to delay at least 80us */ | ||
117 | udelay(200); | ||
118 | /* Read status after timers have expired */ | ||
119 | stat2 = inb(opl3->l_port); | ||
120 | /* Stop the timers */ | ||
121 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); | ||
122 | /* Reset the IRQ of the FM chip */ | ||
123 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); | ||
124 | if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */ | ||
125 | snd_printd("OPL3: stat2 = 0x%x\n", stat2); | ||
126 | return -ENODEV; | ||
127 | } | ||
128 | |||
129 | /* If the toplevel code knows exactly the type of chip, don't try | ||
130 | to detect it. */ | ||
131 | if (opl3->hardware != OPL3_HW_AUTO) | ||
132 | return 0; | ||
133 | |||
134 | /* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */ | ||
135 | if (signature == 0x06) { /* OPL2 */ | ||
136 | opl3->hardware = OPL3_HW_OPL2; | ||
137 | } else { | ||
138 | /* | ||
139 | * If we had an OPL4 chip, opl3->hardware would have been set | ||
140 | * by the OPL4 driver; so we can assume OPL3 here. | ||
141 | */ | ||
142 | snd_assert(opl3->r_port != 0, return -ENODEV); | ||
143 | opl3->hardware = OPL3_HW_OPL3; | ||
144 | } | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * AdLib timers | ||
150 | */ | ||
151 | |||
152 | /* | ||
153 | * Timer 1 - 80us | ||
154 | */ | ||
155 | |||
156 | static int snd_opl3_timer1_start(snd_timer_t * timer) | ||
157 | { | ||
158 | unsigned long flags; | ||
159 | unsigned char tmp; | ||
160 | unsigned int ticks; | ||
161 | opl3_t *opl3; | ||
162 | |||
163 | opl3 = snd_timer_chip(timer); | ||
164 | spin_lock_irqsave(&opl3->timer_lock, flags); | ||
165 | ticks = timer->sticks; | ||
166 | tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK; | ||
167 | opl3->timer_enable = tmp; | ||
168 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */ | ||
169 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ | ||
170 | spin_unlock_irqrestore(&opl3->timer_lock, flags); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int snd_opl3_timer1_stop(snd_timer_t * timer) | ||
175 | { | ||
176 | unsigned long flags; | ||
177 | unsigned char tmp; | ||
178 | opl3_t *opl3; | ||
179 | |||
180 | opl3 = snd_timer_chip(timer); | ||
181 | spin_lock_irqsave(&opl3->timer_lock, flags); | ||
182 | tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START; | ||
183 | opl3->timer_enable = tmp; | ||
184 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ | ||
185 | spin_unlock_irqrestore(&opl3->timer_lock, flags); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Timer 2 - 320us | ||
191 | */ | ||
192 | |||
193 | static int snd_opl3_timer2_start(snd_timer_t * timer) | ||
194 | { | ||
195 | unsigned long flags; | ||
196 | unsigned char tmp; | ||
197 | unsigned int ticks; | ||
198 | opl3_t *opl3; | ||
199 | |||
200 | opl3 = snd_timer_chip(timer); | ||
201 | spin_lock_irqsave(&opl3->timer_lock, flags); | ||
202 | ticks = timer->sticks; | ||
203 | tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK; | ||
204 | opl3->timer_enable = tmp; | ||
205 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */ | ||
206 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ | ||
207 | spin_unlock_irqrestore(&opl3->timer_lock, flags); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int snd_opl3_timer2_stop(snd_timer_t * timer) | ||
212 | { | ||
213 | unsigned long flags; | ||
214 | unsigned char tmp; | ||
215 | opl3_t *opl3; | ||
216 | |||
217 | opl3 = snd_timer_chip(timer); | ||
218 | spin_lock_irqsave(&opl3->timer_lock, flags); | ||
219 | tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START; | ||
220 | opl3->timer_enable = tmp; | ||
221 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ | ||
222 | spin_unlock_irqrestore(&opl3->timer_lock, flags); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | |||
228 | */ | ||
229 | |||
230 | static struct _snd_timer_hardware snd_opl3_timer1 = | ||
231 | { | ||
232 | .flags = SNDRV_TIMER_HW_STOP, | ||
233 | .resolution = 80000, | ||
234 | .ticks = 256, | ||
235 | .start = snd_opl3_timer1_start, | ||
236 | .stop = snd_opl3_timer1_stop, | ||
237 | }; | ||
238 | |||
239 | static struct _snd_timer_hardware snd_opl3_timer2 = | ||
240 | { | ||
241 | .flags = SNDRV_TIMER_HW_STOP, | ||
242 | .resolution = 320000, | ||
243 | .ticks = 256, | ||
244 | .start = snd_opl3_timer2_start, | ||
245 | .stop = snd_opl3_timer2_stop, | ||
246 | }; | ||
247 | |||
248 | static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no) | ||
249 | { | ||
250 | snd_timer_t *timer = NULL; | ||
251 | snd_timer_id_t tid; | ||
252 | int err; | ||
253 | |||
254 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
255 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
256 | tid.card = opl3->card->number; | ||
257 | tid.device = timer_no; | ||
258 | tid.subdevice = 0; | ||
259 | if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) { | ||
260 | strcpy(timer->name, "AdLib timer #1"); | ||
261 | timer->private_data = opl3; | ||
262 | timer->hw = snd_opl3_timer1; | ||
263 | } | ||
264 | opl3->timer1 = timer; | ||
265 | return err; | ||
266 | } | ||
267 | |||
268 | static int snd_opl3_timer2_init(opl3_t * opl3, int timer_no) | ||
269 | { | ||
270 | snd_timer_t *timer = NULL; | ||
271 | snd_timer_id_t tid; | ||
272 | int err; | ||
273 | |||
274 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
275 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
276 | tid.card = opl3->card->number; | ||
277 | tid.device = timer_no; | ||
278 | tid.subdevice = 0; | ||
279 | if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) { | ||
280 | strcpy(timer->name, "AdLib timer #2"); | ||
281 | timer->private_data = opl3; | ||
282 | timer->hw = snd_opl3_timer2; | ||
283 | } | ||
284 | opl3->timer2 = timer; | ||
285 | return err; | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | |||
290 | */ | ||
291 | |||
292 | void snd_opl3_interrupt(snd_hwdep_t * hw) | ||
293 | { | ||
294 | unsigned char status; | ||
295 | opl3_t *opl3; | ||
296 | snd_timer_t *timer; | ||
297 | |||
298 | if (hw == NULL) | ||
299 | return; | ||
300 | |||
301 | opl3 = hw->private_data; | ||
302 | status = inb(opl3->l_port); | ||
303 | #if 0 | ||
304 | snd_printk("AdLib IRQ status = 0x%x\n", status); | ||
305 | #endif | ||
306 | if (!(status & 0x80)) | ||
307 | return; | ||
308 | |||
309 | if (status & 0x40) { | ||
310 | timer = opl3->timer1; | ||
311 | snd_timer_interrupt(timer, timer->sticks); | ||
312 | } | ||
313 | if (status & 0x20) { | ||
314 | timer = opl3->timer2; | ||
315 | snd_timer_interrupt(timer, timer->sticks); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | |||
321 | */ | ||
322 | |||
323 | static int snd_opl3_free(opl3_t *opl3) | ||
324 | { | ||
325 | snd_assert(opl3 != NULL, return -ENXIO); | ||
326 | if (opl3->private_free) | ||
327 | opl3->private_free(opl3); | ||
328 | if (opl3->res_l_port) { | ||
329 | release_resource(opl3->res_l_port); | ||
330 | kfree_nocheck(opl3->res_l_port); | ||
331 | } | ||
332 | if (opl3->res_r_port) { | ||
333 | release_resource(opl3->res_r_port); | ||
334 | kfree_nocheck(opl3->res_r_port); | ||
335 | } | ||
336 | kfree(opl3); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static int snd_opl3_dev_free(snd_device_t *device) | ||
341 | { | ||
342 | opl3_t *opl3 = device->device_data; | ||
343 | return snd_opl3_free(opl3); | ||
344 | } | ||
345 | |||
346 | int snd_opl3_new(snd_card_t *card, | ||
347 | unsigned short hardware, | ||
348 | opl3_t **ropl3) | ||
349 | { | ||
350 | static snd_device_ops_t ops = { | ||
351 | .dev_free = snd_opl3_dev_free, | ||
352 | }; | ||
353 | opl3_t *opl3; | ||
354 | int err; | ||
355 | |||
356 | *ropl3 = NULL; | ||
357 | opl3 = kcalloc(1, sizeof(*opl3), GFP_KERNEL); | ||
358 | if (opl3 == NULL) | ||
359 | return -ENOMEM; | ||
360 | |||
361 | opl3->card = card; | ||
362 | opl3->hardware = hardware; | ||
363 | spin_lock_init(&opl3->reg_lock); | ||
364 | spin_lock_init(&opl3->timer_lock); | ||
365 | init_MUTEX(&opl3->access_mutex); | ||
366 | |||
367 | if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) { | ||
368 | snd_opl3_free(opl3); | ||
369 | return err; | ||
370 | } | ||
371 | |||
372 | *ropl3 = opl3; | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | int snd_opl3_init(opl3_t *opl3) | ||
377 | { | ||
378 | if (! opl3->command) { | ||
379 | printk(KERN_ERR "snd_opl3_init: command not defined!\n"); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | |||
383 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); | ||
384 | /* Melodic mode */ | ||
385 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); | ||
386 | |||
387 | switch (opl3->hardware & OPL3_HW_MASK) { | ||
388 | case OPL3_HW_OPL2: | ||
389 | opl3->max_voices = MAX_OPL2_VOICES; | ||
390 | break; | ||
391 | case OPL3_HW_OPL3: | ||
392 | case OPL3_HW_OPL4: | ||
393 | opl3->max_voices = MAX_OPL3_VOICES; | ||
394 | /* Enter OPL3 mode */ | ||
395 | opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); | ||
396 | } | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | int snd_opl3_create(snd_card_t * card, | ||
401 | unsigned long l_port, | ||
402 | unsigned long r_port, | ||
403 | unsigned short hardware, | ||
404 | int integrated, | ||
405 | opl3_t ** ropl3) | ||
406 | { | ||
407 | opl3_t *opl3; | ||
408 | int err; | ||
409 | |||
410 | *ropl3 = NULL; | ||
411 | if ((err = snd_opl3_new(card, hardware, &opl3)) < 0) | ||
412 | return err; | ||
413 | if (! integrated) { | ||
414 | if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) { | ||
415 | snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port); | ||
416 | snd_opl3_free(opl3); | ||
417 | return -EBUSY; | ||
418 | } | ||
419 | if (r_port != 0 && | ||
420 | (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) { | ||
421 | snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port); | ||
422 | snd_opl3_free(opl3); | ||
423 | return -EBUSY; | ||
424 | } | ||
425 | } | ||
426 | opl3->l_port = l_port; | ||
427 | opl3->r_port = r_port; | ||
428 | |||
429 | switch (opl3->hardware) { | ||
430 | /* some hardware doesn't support timers */ | ||
431 | case OPL3_HW_OPL3_SV: | ||
432 | case OPL3_HW_OPL3_CS: | ||
433 | case OPL3_HW_OPL3_FM801: | ||
434 | opl3->command = &snd_opl3_command; | ||
435 | break; | ||
436 | default: | ||
437 | opl3->command = &snd_opl2_command; | ||
438 | if ((err = snd_opl3_detect(opl3)) < 0) { | ||
439 | snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n", | ||
440 | opl3->l_port, opl3->r_port); | ||
441 | snd_opl3_free(opl3); | ||
442 | return err; | ||
443 | } | ||
444 | /* detect routine returns correct hardware type */ | ||
445 | switch (opl3->hardware & OPL3_HW_MASK) { | ||
446 | case OPL3_HW_OPL3: | ||
447 | case OPL3_HW_OPL4: | ||
448 | opl3->command = &snd_opl3_command; | ||
449 | } | ||
450 | } | ||
451 | |||
452 | snd_opl3_init(opl3); | ||
453 | |||
454 | *ropl3 = opl3; | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev) | ||
459 | { | ||
460 | int err; | ||
461 | |||
462 | if (timer1_dev >= 0) | ||
463 | if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0) | ||
464 | return err; | ||
465 | if (timer2_dev >= 0) { | ||
466 | if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) { | ||
467 | snd_device_free(opl3->card, opl3->timer1); | ||
468 | opl3->timer1 = NULL; | ||
469 | return err; | ||
470 | } | ||
471 | } | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | int snd_opl3_hwdep_new(opl3_t * opl3, | ||
476 | int device, int seq_device, | ||
477 | snd_hwdep_t ** rhwdep) | ||
478 | { | ||
479 | snd_hwdep_t *hw; | ||
480 | snd_card_t *card = opl3->card; | ||
481 | int err; | ||
482 | |||
483 | if (rhwdep) | ||
484 | *rhwdep = NULL; | ||
485 | |||
486 | /* create hardware dependent device (direct FM) */ | ||
487 | |||
488 | if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) { | ||
489 | snd_device_free(card, opl3); | ||
490 | return err; | ||
491 | } | ||
492 | hw->private_data = opl3; | ||
493 | #ifdef CONFIG_SND_OSSEMUL | ||
494 | if (device == 0) { | ||
495 | hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; | ||
496 | sprintf(hw->oss_dev, "dmfm%i", card->number); | ||
497 | } | ||
498 | #endif | ||
499 | strcpy(hw->name, hw->id); | ||
500 | switch (opl3->hardware & OPL3_HW_MASK) { | ||
501 | case OPL3_HW_OPL2: | ||
502 | strcpy(hw->name, "OPL2 FM"); | ||
503 | hw->iface = SNDRV_HWDEP_IFACE_OPL2; | ||
504 | break; | ||
505 | case OPL3_HW_OPL3: | ||
506 | strcpy(hw->name, "OPL3 FM"); | ||
507 | hw->iface = SNDRV_HWDEP_IFACE_OPL3; | ||
508 | break; | ||
509 | case OPL3_HW_OPL4: | ||
510 | strcpy(hw->name, "OPL4 FM"); | ||
511 | hw->iface = SNDRV_HWDEP_IFACE_OPL4; | ||
512 | break; | ||
513 | } | ||
514 | |||
515 | /* operators - only ioctl */ | ||
516 | hw->ops.open = snd_opl3_open; | ||
517 | hw->ops.ioctl = snd_opl3_ioctl; | ||
518 | hw->ops.release = snd_opl3_release; | ||
519 | |||
520 | opl3->seq_dev_num = seq_device; | ||
521 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
522 | if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, | ||
523 | sizeof(opl3_t*), &opl3->seq_dev) >= 0) { | ||
524 | strcpy(opl3->seq_dev->name, hw->name); | ||
525 | *(opl3_t**)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; | ||
526 | } | ||
527 | #endif | ||
528 | if (rhwdep) | ||
529 | *rhwdep = hw; | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | EXPORT_SYMBOL(snd_opl3_interrupt); | ||
534 | EXPORT_SYMBOL(snd_opl3_new); | ||
535 | EXPORT_SYMBOL(snd_opl3_init); | ||
536 | EXPORT_SYMBOL(snd_opl3_create); | ||
537 | EXPORT_SYMBOL(snd_opl3_timer_new); | ||
538 | EXPORT_SYMBOL(snd_opl3_hwdep_new); | ||
539 | |||
540 | /* opl3_synth.c */ | ||
541 | EXPORT_SYMBOL(snd_opl3_regmap); | ||
542 | EXPORT_SYMBOL(snd_opl3_reset); | ||
543 | |||
544 | /* | ||
545 | * INIT part | ||
546 | */ | ||
547 | |||
548 | static int __init alsa_opl3_init(void) | ||
549 | { | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static void __exit alsa_opl3_exit(void) | ||
554 | { | ||
555 | } | ||
556 | |||
557 | module_init(alsa_opl3_init) | ||
558 | module_exit(alsa_opl3_exit) | ||
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c new file mode 100644 index 000000000000..93d674070b71 --- /dev/null +++ b/sound/drivers/opl3/opl3_midi.c | |||
@@ -0,0 +1,873 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Uros Bizjak <uros@kss-loka.si> | ||
3 | * | ||
4 | * Midi synth routines for OPL2/OPL3/OPL4 FM | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #undef DEBUG_ALLOC | ||
23 | #undef DEBUG_MIDI | ||
24 | |||
25 | #include "opl3_voice.h" | ||
26 | #include <sound/asoundef.h> | ||
27 | |||
28 | extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; | ||
29 | |||
30 | extern int use_internal_drums; | ||
31 | |||
32 | /* | ||
33 | * The next table looks magical, but it certainly is not. Its values have | ||
34 | * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception | ||
35 | * for i=0. This log-table converts a linear volume-scaling (0..127) to a | ||
36 | * logarithmic scaling as present in the FM-synthesizer chips. so : Volume | ||
37 | * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative | ||
38 | * volume -8 it was implemented as a table because it is only 128 bytes and | ||
39 | * it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>) | ||
40 | */ | ||
41 | |||
42 | static char opl3_volume_table[128] = | ||
43 | { | ||
44 | -63, -48, -40, -35, -32, -29, -27, -26, | ||
45 | -24, -23, -21, -20, -19, -18, -18, -17, | ||
46 | -16, -15, -15, -14, -13, -13, -12, -12, | ||
47 | -11, -11, -10, -10, -10, -9, -9, -8, | ||
48 | -8, -8, -7, -7, -7, -6, -6, -6, | ||
49 | -5, -5, -5, -5, -4, -4, -4, -4, | ||
50 | -3, -3, -3, -3, -2, -2, -2, -2, | ||
51 | -2, -1, -1, -1, -1, 0, 0, 0, | ||
52 | 0, 0, 0, 1, 1, 1, 1, 1, | ||
53 | 1, 2, 2, 2, 2, 2, 2, 2, | ||
54 | 3, 3, 3, 3, 3, 3, 3, 4, | ||
55 | 4, 4, 4, 4, 4, 4, 4, 5, | ||
56 | 5, 5, 5, 5, 5, 5, 5, 5, | ||
57 | 6, 6, 6, 6, 6, 6, 6, 6, | ||
58 | 6, 7, 7, 7, 7, 7, 7, 7, | ||
59 | 7, 7, 7, 8, 8, 8, 8, 8 | ||
60 | }; | ||
61 | |||
62 | void snd_opl3_calc_volume(unsigned char *volbyte, int vel, | ||
63 | snd_midi_channel_t *chan) | ||
64 | { | ||
65 | int oldvol, newvol, n; | ||
66 | int volume; | ||
67 | |||
68 | volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127); | ||
69 | if (volume > 127) | ||
70 | volume = 127; | ||
71 | |||
72 | oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK); | ||
73 | |||
74 | newvol = opl3_volume_table[volume] + oldvol; | ||
75 | if (newvol > OPL3_TOTAL_LEVEL_MASK) | ||
76 | newvol = OPL3_TOTAL_LEVEL_MASK; | ||
77 | else if (newvol < 0) | ||
78 | newvol = 0; | ||
79 | |||
80 | n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK); | ||
81 | |||
82 | *volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Converts the note frequency to block and fnum values for the FM chip | ||
87 | */ | ||
88 | static short opl3_note_table[16] = | ||
89 | { | ||
90 | 305, 323, /* for pitch bending, -2 semitones */ | ||
91 | 343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, | ||
92 | 686, 726 /* for pitch bending, +2 semitones */ | ||
93 | }; | ||
94 | |||
95 | static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum, | ||
96 | int note, snd_midi_channel_t *chan) | ||
97 | { | ||
98 | int block = ((note / 12) & 0x07) - 1; | ||
99 | int idx = (note % 12) + 2; | ||
100 | int freq; | ||
101 | |||
102 | if (chan->midi_pitchbend) { | ||
103 | int pitchbend = chan->midi_pitchbend; | ||
104 | int segment; | ||
105 | |||
106 | if (pitchbend > 0x1FFF) | ||
107 | pitchbend = 0x1FFF; | ||
108 | |||
109 | segment = pitchbend / 0x1000; | ||
110 | freq = opl3_note_table[idx+segment]; | ||
111 | freq += ((opl3_note_table[idx+segment+1] - freq) * | ||
112 | (pitchbend % 0x1000)) / 0x1000; | ||
113 | } else { | ||
114 | freq = opl3_note_table[idx]; | ||
115 | } | ||
116 | |||
117 | *fnum = (unsigned char) freq; | ||
118 | *blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) | | ||
119 | ((block << 2) & OPL3_BLOCKNUM_MASK); | ||
120 | } | ||
121 | |||
122 | |||
123 | #ifdef DEBUG_ALLOC | ||
124 | static void debug_alloc(opl3_t *opl3, char *s, int voice) { | ||
125 | int i; | ||
126 | char *str = "x.24"; | ||
127 | |||
128 | printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice); | ||
129 | for (i = 0; i < opl3->max_voices; i++) | ||
130 | printk("%c", *(str + opl3->voices[i].state + 1)); | ||
131 | printk("\n"); | ||
132 | } | ||
133 | #endif | ||
134 | |||
135 | /* | ||
136 | * Get a FM voice (channel) to play a note on. | ||
137 | */ | ||
138 | static int opl3_get_voice(opl3_t *opl3, int instr_4op, | ||
139 | snd_midi_channel_t *chan) { | ||
140 | int chan_4op_1; /* first voice for 4op instrument */ | ||
141 | int chan_4op_2; /* second voice for 4op instrument */ | ||
142 | |||
143 | snd_opl3_voice_t *vp, *vp2; | ||
144 | unsigned int voice_time; | ||
145 | int i; | ||
146 | |||
147 | #ifdef DEBUG_ALLOC | ||
148 | char *alloc_type[3] = { "FREE ", "CHEAP ", "EXPENSIVE" }; | ||
149 | #endif | ||
150 | |||
151 | /* This is our "allocation cost" table */ | ||
152 | enum { | ||
153 | FREE = 0, CHEAP, EXPENSIVE, END | ||
154 | }; | ||
155 | |||
156 | /* Keeps track of what we are finding */ | ||
157 | struct best { | ||
158 | unsigned int time; | ||
159 | int voice; | ||
160 | } best[END]; | ||
161 | struct best *bp; | ||
162 | |||
163 | for (i = 0; i < END; i++) { | ||
164 | best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; | ||
165 | best[i].voice = -1; | ||
166 | } | ||
167 | |||
168 | /* Look through all the channels for the most suitable. */ | ||
169 | for (i = 0; i < opl3->max_voices; i++) { | ||
170 | vp = &opl3->voices[i]; | ||
171 | |||
172 | if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL) | ||
173 | /* skip unavailable channels, allocated by | ||
174 | drum voices or by bounded 4op voices) */ | ||
175 | continue; | ||
176 | |||
177 | voice_time = vp->time; | ||
178 | bp = best; | ||
179 | |||
180 | chan_4op_1 = ((i < 3) || (i > 8 && i < 12)); | ||
181 | chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15)); | ||
182 | if (instr_4op) { | ||
183 | /* allocate 4op voice */ | ||
184 | /* skip channels unavailable to 4op instrument */ | ||
185 | if (!chan_4op_1) | ||
186 | continue; | ||
187 | |||
188 | if (vp->state) | ||
189 | /* kill one voice, CHEAP */ | ||
190 | bp++; | ||
191 | /* get state of bounded 2op channel | ||
192 | to be allocated for 4op instrument */ | ||
193 | vp2 = &opl3->voices[i + 3]; | ||
194 | if (vp2->state == SNDRV_OPL3_ST_ON_2OP) { | ||
195 | /* kill two voices, EXPENSIVE */ | ||
196 | bp++; | ||
197 | voice_time = (voice_time > vp->time) ? | ||
198 | voice_time : vp->time; | ||
199 | } | ||
200 | } else { | ||
201 | /* allocate 2op voice */ | ||
202 | if ((chan_4op_1) || (chan_4op_2)) | ||
203 | /* use bounded channels for 2op, CHEAP */ | ||
204 | bp++; | ||
205 | else if (vp->state) | ||
206 | /* kill one voice on 2op channel, CHEAP */ | ||
207 | bp++; | ||
208 | /* raise kill cost to EXPENSIVE for all channels */ | ||
209 | if (vp->state) | ||
210 | bp++; | ||
211 | } | ||
212 | if (voice_time < bp->time) { | ||
213 | bp->time = voice_time; | ||
214 | bp->voice = i; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | for (i = 0; i < END; i++) { | ||
219 | if (best[i].voice >= 0) { | ||
220 | #ifdef DEBUG_ALLOC | ||
221 | printk("%s %iop allocation on voice %i\n", | ||
222 | alloc_type[i], instr_4op ? 4 : 2, | ||
223 | best[i].voice); | ||
224 | #endif | ||
225 | return best[i].voice; | ||
226 | } | ||
227 | } | ||
228 | /* not found */ | ||
229 | return -1; | ||
230 | } | ||
231 | |||
232 | /* ------------------------------ */ | ||
233 | |||
234 | /* | ||
235 | * System timer interrupt function | ||
236 | */ | ||
237 | void snd_opl3_timer_func(unsigned long data) | ||
238 | { | ||
239 | |||
240 | opl3_t *opl3 = (opl3_t *)data; | ||
241 | int again = 0; | ||
242 | int i; | ||
243 | |||
244 | spin_lock(&opl3->sys_timer_lock); | ||
245 | for (i = 0; i < opl3->max_voices; i++) { | ||
246 | snd_opl3_voice_t *vp = &opl3->voices[i]; | ||
247 | if (vp->state > 0 && vp->note_off_check) { | ||
248 | if (vp->note_off == jiffies) | ||
249 | snd_opl3_note_off(opl3, vp->note, 0, vp->chan); | ||
250 | else | ||
251 | again++; | ||
252 | } | ||
253 | } | ||
254 | if (again) { | ||
255 | opl3->tlist.expires = jiffies + 1; /* invoke again */ | ||
256 | add_timer(&opl3->tlist); | ||
257 | } else { | ||
258 | opl3->sys_timer_status = 0; | ||
259 | } | ||
260 | spin_unlock(&opl3->sys_timer_lock); | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Start system timer | ||
265 | */ | ||
266 | static void snd_opl3_start_timer(opl3_t *opl3) | ||
267 | { | ||
268 | unsigned long flags; | ||
269 | spin_lock_irqsave(&opl3->sys_timer_lock, flags); | ||
270 | if (! opl3->sys_timer_status) { | ||
271 | opl3->tlist.expires = jiffies + 1; | ||
272 | add_timer(&opl3->tlist); | ||
273 | opl3->sys_timer_status = 1; | ||
274 | } | ||
275 | spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); | ||
276 | } | ||
277 | |||
278 | /* ------------------------------ */ | ||
279 | |||
280 | |||
281 | static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { | ||
282 | 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14 | ||
283 | }; | ||
284 | |||
285 | /* | ||
286 | * Start a note. | ||
287 | */ | ||
288 | void snd_opl3_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) | ||
289 | { | ||
290 | opl3_t *opl3; | ||
291 | snd_seq_instr_t wanted; | ||
292 | snd_seq_kinstr_t *kinstr; | ||
293 | int instr_4op; | ||
294 | |||
295 | int voice; | ||
296 | snd_opl3_voice_t *vp, *vp2; | ||
297 | unsigned short connect_mask; | ||
298 | unsigned char connection; | ||
299 | unsigned char vol_op[4]; | ||
300 | |||
301 | int extra_prg = 0; | ||
302 | |||
303 | unsigned short reg_side; | ||
304 | unsigned char op_offset; | ||
305 | unsigned char voice_offset; | ||
306 | unsigned short opl3_reg; | ||
307 | unsigned char reg_val; | ||
308 | |||
309 | int key = note; | ||
310 | unsigned char fnum, blocknum; | ||
311 | int i; | ||
312 | |||
313 | fm_instrument_t *fm; | ||
314 | unsigned long flags; | ||
315 | |||
316 | opl3 = p; | ||
317 | |||
318 | #ifdef DEBUG_MIDI | ||
319 | snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", | ||
320 | chan->number, chan->midi_program, note, vel); | ||
321 | #endif | ||
322 | wanted.cluster = 0; | ||
323 | wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; | ||
324 | |||
325 | /* in SYNTH mode, application takes care of voices */ | ||
326 | /* in SEQ mode, drum voice numbers are notes on drum channel */ | ||
327 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | ||
328 | if (chan->drum_channel) { | ||
329 | /* percussion instruments are located in bank 128 */ | ||
330 | wanted.bank = 128; | ||
331 | wanted.prg = note; | ||
332 | } else { | ||
333 | wanted.bank = chan->gm_bank_select; | ||
334 | wanted.prg = chan->midi_program; | ||
335 | } | ||
336 | } else { | ||
337 | /* Prepare for OSS mode */ | ||
338 | if (chan->number >= MAX_OPL3_VOICES) | ||
339 | return; | ||
340 | |||
341 | /* OSS instruments are located in bank 127 */ | ||
342 | wanted.bank = 127; | ||
343 | wanted.prg = chan->midi_program; | ||
344 | } | ||
345 | |||
346 | spin_lock_irqsave(&opl3->voice_lock, flags); | ||
347 | |||
348 | if (use_internal_drums) { | ||
349 | snd_opl3_drum_switch(opl3, note, vel, 1, chan); | ||
350 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
351 | return; | ||
352 | } | ||
353 | |||
354 | __extra_prg: | ||
355 | kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); | ||
356 | if (kinstr == NULL) { | ||
357 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
358 | return; | ||
359 | } | ||
360 | |||
361 | fm = KINSTR_DATA(kinstr); | ||
362 | |||
363 | switch (fm->type) { | ||
364 | case FM_PATCH_OPL2: | ||
365 | instr_4op = 0; | ||
366 | break; | ||
367 | case FM_PATCH_OPL3: | ||
368 | if (opl3->hardware >= OPL3_HW_OPL3) { | ||
369 | instr_4op = 1; | ||
370 | break; | ||
371 | } | ||
372 | default: | ||
373 | snd_seq_instr_free_use(opl3->ilist, kinstr); | ||
374 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
375 | return; | ||
376 | } | ||
377 | |||
378 | #ifdef DEBUG_MIDI | ||
379 | snd_printk(" --> OPL%i instrument: %s\n", | ||
380 | instr_4op ? 3 : 2, kinstr->name); | ||
381 | #endif | ||
382 | /* in SYNTH mode, application takes care of voices */ | ||
383 | /* in SEQ mode, allocate voice on free OPL3 channel */ | ||
384 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | ||
385 | voice = opl3_get_voice(opl3, instr_4op, chan); | ||
386 | } else { | ||
387 | /* remap OSS voice */ | ||
388 | voice = snd_opl3_oss_map[chan->number]; | ||
389 | } | ||
390 | |||
391 | if (voice < MAX_OPL2_VOICES) { | ||
392 | /* Left register block for voices 0 .. 8 */ | ||
393 | reg_side = OPL3_LEFT; | ||
394 | voice_offset = voice; | ||
395 | connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07; | ||
396 | } else { | ||
397 | /* Right register block for voices 9 .. 17 */ | ||
398 | reg_side = OPL3_RIGHT; | ||
399 | voice_offset = voice - MAX_OPL2_VOICES; | ||
400 | connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38; | ||
401 | } | ||
402 | |||
403 | /* kill voice on channel */ | ||
404 | vp = &opl3->voices[voice]; | ||
405 | if (vp->state > 0) { | ||
406 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
407 | reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; | ||
408 | opl3->command(opl3, opl3_reg, reg_val); | ||
409 | } | ||
410 | if (instr_4op) { | ||
411 | vp2 = &opl3->voices[voice + 3]; | ||
412 | if (vp->state > 0) { | ||
413 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + | ||
414 | voice_offset + 3); | ||
415 | reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; | ||
416 | opl3->command(opl3, opl3_reg, reg_val); | ||
417 | } | ||
418 | } | ||
419 | |||
420 | /* set connection register */ | ||
421 | if (instr_4op) { | ||
422 | if ((opl3->connection_reg ^ connect_mask) & connect_mask) { | ||
423 | opl3->connection_reg |= connect_mask; | ||
424 | /* set connection bit */ | ||
425 | opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; | ||
426 | opl3->command(opl3, opl3_reg, opl3->connection_reg); | ||
427 | } | ||
428 | } else { | ||
429 | if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) { | ||
430 | opl3->connection_reg &= ~connect_mask; | ||
431 | /* clear connection bit */ | ||
432 | opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; | ||
433 | opl3->command(opl3, opl3_reg, opl3->connection_reg); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | #ifdef DEBUG_MIDI | ||
438 | snd_printk(" --> setting OPL3 connection: 0x%x\n", | ||
439 | opl3->connection_reg); | ||
440 | #endif | ||
441 | /* | ||
442 | * calculate volume depending on connection | ||
443 | * between FM operators (see include/opl3.h) | ||
444 | */ | ||
445 | for (i = 0; i < (instr_4op ? 4 : 2); i++) | ||
446 | vol_op[i] = fm->op[i].ksl_level; | ||
447 | |||
448 | connection = fm->feedback_connection[0] & 0x01; | ||
449 | if (instr_4op) { | ||
450 | connection <<= 1; | ||
451 | connection |= fm->feedback_connection[1] & 0x01; | ||
452 | |||
453 | snd_opl3_calc_volume(&vol_op[3], vel, chan); | ||
454 | switch (connection) { | ||
455 | case 0x03: | ||
456 | snd_opl3_calc_volume(&vol_op[2], vel, chan); | ||
457 | /* fallthru */ | ||
458 | case 0x02: | ||
459 | snd_opl3_calc_volume(&vol_op[0], vel, chan); | ||
460 | break; | ||
461 | case 0x01: | ||
462 | snd_opl3_calc_volume(&vol_op[1], vel, chan); | ||
463 | } | ||
464 | } else { | ||
465 | snd_opl3_calc_volume(&vol_op[1], vel, chan); | ||
466 | if (connection) | ||
467 | snd_opl3_calc_volume(&vol_op[0], vel, chan); | ||
468 | } | ||
469 | |||
470 | /* Program the FM voice characteristics */ | ||
471 | for (i = 0; i < (instr_4op ? 4 : 2); i++) { | ||
472 | #ifdef DEBUG_MIDI | ||
473 | snd_printk(" --> programming operator %i\n", i); | ||
474 | #endif | ||
475 | op_offset = snd_opl3_regmap[voice_offset][i]; | ||
476 | |||
477 | /* Set OPL3 AM_VIB register of requested voice/operator */ | ||
478 | reg_val = fm->op[i].am_vib; | ||
479 | opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); | ||
480 | opl3->command(opl3, opl3_reg, reg_val); | ||
481 | |||
482 | /* Set OPL3 KSL_LEVEL register of requested voice/operator */ | ||
483 | reg_val = vol_op[i]; | ||
484 | opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); | ||
485 | opl3->command(opl3, opl3_reg, reg_val); | ||
486 | |||
487 | /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ | ||
488 | reg_val = fm->op[i].attack_decay; | ||
489 | opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); | ||
490 | opl3->command(opl3, opl3_reg, reg_val); | ||
491 | |||
492 | /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ | ||
493 | reg_val = fm->op[i].sustain_release; | ||
494 | opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); | ||
495 | opl3->command(opl3, opl3_reg, reg_val); | ||
496 | |||
497 | /* Select waveform */ | ||
498 | reg_val = fm->op[i].wave_select; | ||
499 | opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); | ||
500 | opl3->command(opl3, opl3_reg, reg_val); | ||
501 | } | ||
502 | |||
503 | /* Set operator feedback and 2op inter-operator connection */ | ||
504 | reg_val = fm->feedback_connection[0]; | ||
505 | /* Set output voice connection */ | ||
506 | reg_val |= OPL3_STEREO_BITS; | ||
507 | if (chan->gm_pan < 43) | ||
508 | reg_val &= ~OPL3_VOICE_TO_RIGHT; | ||
509 | if (chan->gm_pan > 85) | ||
510 | reg_val &= ~OPL3_VOICE_TO_LEFT; | ||
511 | opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); | ||
512 | opl3->command(opl3, opl3_reg, reg_val); | ||
513 | |||
514 | if (instr_4op) { | ||
515 | /* Set 4op inter-operator connection */ | ||
516 | reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT; | ||
517 | /* Set output voice connection */ | ||
518 | reg_val |= OPL3_STEREO_BITS; | ||
519 | if (chan->gm_pan < 43) | ||
520 | reg_val &= ~OPL3_VOICE_TO_RIGHT; | ||
521 | if (chan->gm_pan > 85) | ||
522 | reg_val &= ~OPL3_VOICE_TO_LEFT; | ||
523 | opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + | ||
524 | voice_offset + 3); | ||
525 | opl3->command(opl3, opl3_reg, reg_val); | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * Special treatment of percussion notes for fm: | ||
530 | * Requested pitch is really program, and pitch for | ||
531 | * device is whatever was specified in the patch library. | ||
532 | */ | ||
533 | if (fm->fix_key) | ||
534 | note = fm->fix_key; | ||
535 | /* | ||
536 | * use transpose if defined in patch library | ||
537 | */ | ||
538 | if (fm->trnsps) | ||
539 | note += (fm->trnsps - 64); | ||
540 | |||
541 | snd_opl3_calc_pitch(&fnum, &blocknum, note, chan); | ||
542 | |||
543 | /* Set OPL3 FNUM_LOW register of requested voice */ | ||
544 | opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); | ||
545 | opl3->command(opl3, opl3_reg, fnum); | ||
546 | |||
547 | opl3->voices[voice].keyon_reg = blocknum; | ||
548 | |||
549 | /* Set output sound flag */ | ||
550 | blocknum |= OPL3_KEYON_BIT; | ||
551 | |||
552 | #ifdef DEBUG_MIDI | ||
553 | snd_printk(" --> trigger voice %i\n", voice); | ||
554 | #endif | ||
555 | /* Set OPL3 KEYON_BLOCK register of requested voice */ | ||
556 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
557 | opl3->command(opl3, opl3_reg, blocknum); | ||
558 | |||
559 | /* kill note after fixed duration (in centiseconds) */ | ||
560 | if (fm->fix_dur) { | ||
561 | opl3->voices[voice].note_off = jiffies + | ||
562 | (fm->fix_dur * HZ) / 100; | ||
563 | snd_opl3_start_timer(opl3); | ||
564 | opl3->voices[voice].note_off_check = 1; | ||
565 | } else | ||
566 | opl3->voices[voice].note_off_check = 0; | ||
567 | |||
568 | /* get extra pgm, but avoid possible loops */ | ||
569 | extra_prg = (extra_prg) ? 0 : fm->modes; | ||
570 | |||
571 | snd_seq_instr_free_use(opl3->ilist, kinstr); | ||
572 | |||
573 | /* do the bookkeeping */ | ||
574 | vp->time = opl3->use_time++; | ||
575 | vp->note = key; | ||
576 | vp->chan = chan; | ||
577 | |||
578 | if (instr_4op) { | ||
579 | vp->state = SNDRV_OPL3_ST_ON_4OP; | ||
580 | |||
581 | vp2 = &opl3->voices[voice + 3]; | ||
582 | vp2->time = opl3->use_time++; | ||
583 | vp2->note = key; | ||
584 | vp2->chan = chan; | ||
585 | vp2->state = SNDRV_OPL3_ST_NOT_AVAIL; | ||
586 | } else { | ||
587 | if (vp->state == SNDRV_OPL3_ST_ON_4OP) { | ||
588 | /* 4op killed by 2op, release bounded voice */ | ||
589 | vp2 = &opl3->voices[voice + 3]; | ||
590 | vp2->time = opl3->use_time++; | ||
591 | vp2->state = SNDRV_OPL3_ST_OFF; | ||
592 | } | ||
593 | vp->state = SNDRV_OPL3_ST_ON_2OP; | ||
594 | } | ||
595 | |||
596 | #ifdef DEBUG_ALLOC | ||
597 | debug_alloc(opl3, "note on ", voice); | ||
598 | #endif | ||
599 | |||
600 | /* allocate extra program if specified in patch library */ | ||
601 | if (extra_prg) { | ||
602 | if (extra_prg > 128) { | ||
603 | wanted.bank = 128; | ||
604 | /* percussions start at 35 */ | ||
605 | wanted.prg = extra_prg - 128 + 35 - 1; | ||
606 | } else { | ||
607 | wanted.bank = 0; | ||
608 | wanted.prg = extra_prg - 1; | ||
609 | } | ||
610 | #ifdef DEBUG_MIDI | ||
611 | snd_printk(" *** allocating extra program\n"); | ||
612 | #endif | ||
613 | goto __extra_prg; | ||
614 | } | ||
615 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
616 | } | ||
617 | |||
618 | static void snd_opl3_kill_voice(opl3_t *opl3, int voice) | ||
619 | { | ||
620 | unsigned short reg_side; | ||
621 | unsigned char voice_offset; | ||
622 | unsigned short opl3_reg; | ||
623 | |||
624 | snd_opl3_voice_t *vp, *vp2; | ||
625 | |||
626 | snd_assert(voice < MAX_OPL3_VOICES, return); | ||
627 | |||
628 | vp = &opl3->voices[voice]; | ||
629 | if (voice < MAX_OPL2_VOICES) { | ||
630 | /* Left register block for voices 0 .. 8 */ | ||
631 | reg_side = OPL3_LEFT; | ||
632 | voice_offset = voice; | ||
633 | } else { | ||
634 | /* Right register block for voices 9 .. 17 */ | ||
635 | reg_side = OPL3_RIGHT; | ||
636 | voice_offset = voice - MAX_OPL2_VOICES; | ||
637 | } | ||
638 | |||
639 | /* kill voice */ | ||
640 | #ifdef DEBUG_MIDI | ||
641 | snd_printk(" --> kill voice %i\n", voice); | ||
642 | #endif | ||
643 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
644 | /* clear Key ON bit */ | ||
645 | opl3->command(opl3, opl3_reg, vp->keyon_reg); | ||
646 | |||
647 | /* do the bookkeeping */ | ||
648 | vp->time = opl3->use_time++; | ||
649 | |||
650 | if (vp->state == SNDRV_OPL3_ST_ON_4OP) { | ||
651 | vp2 = &opl3->voices[voice + 3]; | ||
652 | |||
653 | vp2->time = opl3->use_time++; | ||
654 | vp2->state = SNDRV_OPL3_ST_OFF; | ||
655 | } | ||
656 | vp->state = SNDRV_OPL3_ST_OFF; | ||
657 | #ifdef DEBUG_ALLOC | ||
658 | debug_alloc(opl3, "note off", voice); | ||
659 | #endif | ||
660 | |||
661 | } | ||
662 | |||
663 | /* | ||
664 | * Release a note in response to a midi note off. | ||
665 | */ | ||
666 | void snd_opl3_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) | ||
667 | { | ||
668 | opl3_t *opl3; | ||
669 | |||
670 | int voice; | ||
671 | snd_opl3_voice_t *vp; | ||
672 | |||
673 | unsigned long flags; | ||
674 | |||
675 | opl3 = p; | ||
676 | |||
677 | #ifdef DEBUG_MIDI | ||
678 | snd_printk("Note off, ch %i, inst %i, note %i\n", | ||
679 | chan->number, chan->midi_program, note); | ||
680 | #endif | ||
681 | |||
682 | spin_lock_irqsave(&opl3->voice_lock, flags); | ||
683 | |||
684 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | ||
685 | if (chan->drum_channel && use_internal_drums) { | ||
686 | snd_opl3_drum_switch(opl3, note, vel, 0, chan); | ||
687 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
688 | return; | ||
689 | } | ||
690 | /* this loop will hopefully kill all extra voices, because | ||
691 | they are grouped by the same channel and note values */ | ||
692 | for (voice = 0; voice < opl3->max_voices; voice++) { | ||
693 | vp = &opl3->voices[voice]; | ||
694 | if (vp->state > 0 && vp->chan == chan && vp->note == note) { | ||
695 | snd_opl3_kill_voice(opl3, voice); | ||
696 | } | ||
697 | } | ||
698 | } else { | ||
699 | /* remap OSS voices */ | ||
700 | if (chan->number < MAX_OPL3_VOICES) { | ||
701 | voice = snd_opl3_oss_map[chan->number]; | ||
702 | snd_opl3_kill_voice(opl3, voice); | ||
703 | } | ||
704 | } | ||
705 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
706 | } | ||
707 | |||
708 | /* | ||
709 | * key pressure change | ||
710 | */ | ||
711 | void snd_opl3_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) | ||
712 | { | ||
713 | opl3_t *opl3; | ||
714 | |||
715 | opl3 = p; | ||
716 | #ifdef DEBUG_MIDI | ||
717 | snd_printk("Key pressure, ch#: %i, inst#: %i\n", | ||
718 | chan->number, chan->midi_program); | ||
719 | #endif | ||
720 | } | ||
721 | |||
722 | /* | ||
723 | * terminate note | ||
724 | */ | ||
725 | void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan) | ||
726 | { | ||
727 | opl3_t *opl3; | ||
728 | |||
729 | opl3 = p; | ||
730 | #ifdef DEBUG_MIDI | ||
731 | snd_printk("Terminate note, ch#: %i, inst#: %i\n", | ||
732 | chan->number, chan->midi_program); | ||
733 | #endif | ||
734 | } | ||
735 | |||
736 | static void snd_opl3_update_pitch(opl3_t *opl3, int voice) | ||
737 | { | ||
738 | unsigned short reg_side; | ||
739 | unsigned char voice_offset; | ||
740 | unsigned short opl3_reg; | ||
741 | |||
742 | unsigned char fnum, blocknum; | ||
743 | |||
744 | snd_opl3_voice_t *vp; | ||
745 | |||
746 | snd_assert(voice < MAX_OPL3_VOICES, return); | ||
747 | |||
748 | vp = &opl3->voices[voice]; | ||
749 | if (vp->chan == NULL) | ||
750 | return; /* not allocated? */ | ||
751 | |||
752 | if (voice < MAX_OPL2_VOICES) { | ||
753 | /* Left register block for voices 0 .. 8 */ | ||
754 | reg_side = OPL3_LEFT; | ||
755 | voice_offset = voice; | ||
756 | } else { | ||
757 | /* Right register block for voices 9 .. 17 */ | ||
758 | reg_side = OPL3_RIGHT; | ||
759 | voice_offset = voice - MAX_OPL2_VOICES; | ||
760 | } | ||
761 | |||
762 | snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan); | ||
763 | |||
764 | /* Set OPL3 FNUM_LOW register of requested voice */ | ||
765 | opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); | ||
766 | opl3->command(opl3, opl3_reg, fnum); | ||
767 | |||
768 | vp->keyon_reg = blocknum; | ||
769 | |||
770 | /* Set output sound flag */ | ||
771 | blocknum |= OPL3_KEYON_BIT; | ||
772 | |||
773 | /* Set OPL3 KEYON_BLOCK register of requested voice */ | ||
774 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
775 | opl3->command(opl3, opl3_reg, blocknum); | ||
776 | |||
777 | vp->time = opl3->use_time++; | ||
778 | } | ||
779 | |||
780 | /* | ||
781 | * Update voice pitch controller | ||
782 | */ | ||
783 | static void snd_opl3_pitch_ctrl(opl3_t *opl3, snd_midi_channel_t *chan) | ||
784 | { | ||
785 | int voice; | ||
786 | snd_opl3_voice_t *vp; | ||
787 | |||
788 | unsigned long flags; | ||
789 | |||
790 | spin_lock_irqsave(&opl3->voice_lock, flags); | ||
791 | |||
792 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | ||
793 | for (voice = 0; voice < opl3->max_voices; voice++) { | ||
794 | vp = &opl3->voices[voice]; | ||
795 | if (vp->state > 0 && vp->chan == chan) { | ||
796 | snd_opl3_update_pitch(opl3, voice); | ||
797 | } | ||
798 | } | ||
799 | } else { | ||
800 | /* remap OSS voices */ | ||
801 | if (chan->number < MAX_OPL3_VOICES) { | ||
802 | voice = snd_opl3_oss_map[chan->number]; | ||
803 | snd_opl3_update_pitch(opl3, voice); | ||
804 | } | ||
805 | } | ||
806 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | ||
807 | } | ||
808 | |||
809 | /* | ||
810 | * Deal with a controler type event. This includes all types of | ||
811 | * control events, not just the midi controllers | ||
812 | */ | ||
813 | void snd_opl3_control(void *p, int type, snd_midi_channel_t *chan) | ||
814 | { | ||
815 | opl3_t *opl3; | ||
816 | |||
817 | opl3 = p; | ||
818 | #ifdef DEBUG_MIDI | ||
819 | snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n", | ||
820 | type, chan->number, chan->midi_program); | ||
821 | #endif | ||
822 | |||
823 | switch (type) { | ||
824 | case MIDI_CTL_MSB_MODWHEEL: | ||
825 | if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63) | ||
826 | opl3->drum_reg |= OPL3_VIBRATO_DEPTH; | ||
827 | else | ||
828 | opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH; | ||
829 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, | ||
830 | opl3->drum_reg); | ||
831 | break; | ||
832 | case MIDI_CTL_E2_TREMOLO_DEPTH: | ||
833 | if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63) | ||
834 | opl3->drum_reg |= OPL3_TREMOLO_DEPTH; | ||
835 | else | ||
836 | opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH; | ||
837 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, | ||
838 | opl3->drum_reg); | ||
839 | break; | ||
840 | case MIDI_CTL_PITCHBEND: | ||
841 | snd_opl3_pitch_ctrl(opl3, chan); | ||
842 | break; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | /* | ||
847 | * NRPN events | ||
848 | */ | ||
849 | void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, | ||
850 | snd_midi_channel_set_t *chset) | ||
851 | { | ||
852 | opl3_t *opl3; | ||
853 | |||
854 | opl3 = p; | ||
855 | #ifdef DEBUG_MIDI | ||
856 | snd_printk("NRPN, ch#: %i, inst#: %i\n", | ||
857 | chan->number, chan->midi_program); | ||
858 | #endif | ||
859 | } | ||
860 | |||
861 | /* | ||
862 | * receive sysex | ||
863 | */ | ||
864 | void snd_opl3_sysex(void *p, unsigned char *buf, int len, | ||
865 | int parsed, snd_midi_channel_set_t *chset) | ||
866 | { | ||
867 | opl3_t *opl3; | ||
868 | |||
869 | opl3 = p; | ||
870 | #ifdef DEBUG_MIDI | ||
871 | snd_printk("SYSEX\n"); | ||
872 | #endif | ||
873 | } | ||
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c new file mode 100644 index 000000000000..33da334ae981 --- /dev/null +++ b/sound/drivers/opl3/opl3_oss.c | |||
@@ -0,0 +1,356 @@ | |||
1 | /* | ||
2 | * Interface for OSS sequencer emulation | ||
3 | * | ||
4 | * Copyright (C) 2000 Uros Bizjak <uros@kss-loka.si> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include "opl3_voice.h" | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); | ||
25 | static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg); | ||
26 | static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); | ||
27 | static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char __user *buf, int offs, int count); | ||
28 | static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg); | ||
29 | |||
30 | /* */ | ||
31 | |||
32 | static inline mm_segment_t snd_enter_user(void) | ||
33 | { | ||
34 | mm_segment_t fs = get_fs(); | ||
35 | set_fs(get_ds()); | ||
36 | return fs; | ||
37 | } | ||
38 | |||
39 | static inline void snd_leave_user(mm_segment_t fs) | ||
40 | { | ||
41 | set_fs(fs); | ||
42 | } | ||
43 | |||
44 | /* operators */ | ||
45 | |||
46 | extern snd_midi_op_t opl3_ops; | ||
47 | |||
48 | static snd_seq_oss_callback_t oss_callback = { | ||
49 | .owner = THIS_MODULE, | ||
50 | .open = snd_opl3_open_seq_oss, | ||
51 | .close = snd_opl3_close_seq_oss, | ||
52 | .ioctl = snd_opl3_ioctl_seq_oss, | ||
53 | .load_patch = snd_opl3_load_patch_seq_oss, | ||
54 | .reset = snd_opl3_reset_seq_oss, | ||
55 | }; | ||
56 | |||
57 | static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct, | ||
58 | void *private_data, int atomic, int hop) | ||
59 | { | ||
60 | opl3_t *opl3 = private_data; | ||
61 | |||
62 | if (ev->type != SNDRV_SEQ_EVENT_OSS) | ||
63 | snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset); | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | /* ------------------------------ */ | ||
68 | |||
69 | static void snd_opl3_oss_free_port(void *private_data) | ||
70 | { | ||
71 | opl3_t *opl3 = private_data; | ||
72 | |||
73 | snd_midi_channel_free_set(opl3->oss_chset); | ||
74 | } | ||
75 | |||
76 | static int snd_opl3_oss_create_port(opl3_t * opl3) | ||
77 | { | ||
78 | snd_seq_port_callback_t callbacks; | ||
79 | char name[32]; | ||
80 | int voices, opl_ver; | ||
81 | |||
82 | voices = (opl3->hardware < OPL3_HW_OPL3) ? | ||
83 | MAX_OPL2_VOICES : MAX_OPL3_VOICES; | ||
84 | opl3->oss_chset = snd_midi_channel_alloc_set(voices); | ||
85 | if (opl3->oss_chset == NULL) | ||
86 | return -ENOMEM; | ||
87 | opl3->oss_chset->private_data = opl3; | ||
88 | |||
89 | memset(&callbacks, 0, sizeof(callbacks)); | ||
90 | callbacks.owner = THIS_MODULE; | ||
91 | callbacks.event_input = snd_opl3_oss_event_input; | ||
92 | callbacks.private_free = snd_opl3_oss_free_port; | ||
93 | callbacks.private_data = opl3; | ||
94 | |||
95 | opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; | ||
96 | sprintf(name, "OPL%i OSS Port", opl_ver); | ||
97 | |||
98 | opl3->oss_chset->client = opl3->seq_client; | ||
99 | opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, | ||
100 | SNDRV_SEQ_PORT_CAP_WRITE, | ||
101 | SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | | ||
102 | SNDRV_SEQ_PORT_TYPE_MIDI_GM | | ||
103 | SNDRV_SEQ_PORT_TYPE_SYNTH, | ||
104 | voices, voices, | ||
105 | name); | ||
106 | if (opl3->oss_chset->port < 0) { | ||
107 | snd_midi_channel_free_set(opl3->oss_chset); | ||
108 | return opl3->oss_chset->port; | ||
109 | } | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* ------------------------------ */ | ||
114 | |||
115 | /* register OSS synth */ | ||
116 | void snd_opl3_init_seq_oss(opl3_t *opl3, char *name) | ||
117 | { | ||
118 | snd_seq_oss_reg_t *arg; | ||
119 | snd_seq_device_t *dev; | ||
120 | |||
121 | if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS, | ||
122 | sizeof(snd_seq_oss_reg_t), &dev) < 0) | ||
123 | return; | ||
124 | |||
125 | opl3->oss_seq_dev = dev; | ||
126 | strlcpy(dev->name, name, sizeof(dev->name)); | ||
127 | arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
128 | arg->type = SYNTH_TYPE_FM; | ||
129 | if (opl3->hardware < OPL3_HW_OPL3) { | ||
130 | arg->subtype = FM_TYPE_ADLIB; | ||
131 | arg->nvoices = MAX_OPL2_VOICES; | ||
132 | } else { | ||
133 | arg->subtype = FM_TYPE_OPL3; | ||
134 | arg->nvoices = MAX_OPL3_VOICES; | ||
135 | } | ||
136 | arg->oper = oss_callback; | ||
137 | arg->private_data = opl3; | ||
138 | |||
139 | snd_opl3_oss_create_port(opl3); | ||
140 | |||
141 | /* register to OSS synth table */ | ||
142 | snd_device_register(opl3->card, dev); | ||
143 | } | ||
144 | |||
145 | /* unregister */ | ||
146 | void snd_opl3_free_seq_oss(opl3_t *opl3) | ||
147 | { | ||
148 | if (opl3->oss_seq_dev) { | ||
149 | snd_device_free(opl3->card, opl3->oss_seq_dev); | ||
150 | opl3->oss_seq_dev = NULL; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /* ------------------------------ */ | ||
155 | |||
156 | /* open OSS sequencer */ | ||
157 | static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) | ||
158 | { | ||
159 | opl3_t *opl3 = closure; | ||
160 | int err; | ||
161 | |||
162 | snd_assert(arg != NULL, return -ENXIO); | ||
163 | |||
164 | if ((err = snd_opl3_synth_setup(opl3)) < 0) | ||
165 | return err; | ||
166 | |||
167 | /* fill the argument data */ | ||
168 | arg->private_data = opl3; | ||
169 | arg->addr.client = opl3->oss_chset->client; | ||
170 | arg->addr.port = opl3->oss_chset->port; | ||
171 | |||
172 | if ((err = snd_opl3_synth_use_inc(opl3)) < 0) | ||
173 | return err; | ||
174 | |||
175 | opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | /* close OSS sequencer */ | ||
180 | static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg) | ||
181 | { | ||
182 | opl3_t *opl3; | ||
183 | |||
184 | snd_assert(arg != NULL, return -ENXIO); | ||
185 | opl3 = arg->private_data; | ||
186 | |||
187 | snd_opl3_synth_cleanup(opl3); | ||
188 | |||
189 | snd_opl3_synth_use_dec(opl3); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | /* load patch */ | ||
194 | |||
195 | /* offsets for SBI params */ | ||
196 | #define AM_VIB 0 | ||
197 | #define KSL_LEVEL 2 | ||
198 | #define ATTACK_DECAY 4 | ||
199 | #define SUSTAIN_RELEASE 6 | ||
200 | #define WAVE_SELECT 8 | ||
201 | |||
202 | /* offset for SBI instrument */ | ||
203 | #define CONNECTION 10 | ||
204 | #define OFFSET_4OP 11 | ||
205 | |||
206 | /* from sound_config.h */ | ||
207 | #define SBFM_MAXINSTR 256 | ||
208 | |||
209 | static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, | ||
210 | const char __user *buf, int offs, int count) | ||
211 | { | ||
212 | opl3_t *opl3; | ||
213 | int err = -EINVAL; | ||
214 | |||
215 | snd_assert(arg != NULL, return -ENXIO); | ||
216 | opl3 = arg->private_data; | ||
217 | |||
218 | if ((format == FM_PATCH) || (format == OPL3_PATCH)) { | ||
219 | struct sbi_instrument sbi; | ||
220 | |||
221 | size_t size; | ||
222 | snd_seq_instr_header_t *put; | ||
223 | snd_seq_instr_data_t *data; | ||
224 | fm_xinstrument_t *xinstr; | ||
225 | |||
226 | snd_seq_event_t ev; | ||
227 | int i; | ||
228 | |||
229 | mm_segment_t fs; | ||
230 | |||
231 | if (count < (int)sizeof(sbi)) { | ||
232 | snd_printk("FM Error: Patch record too short\n"); | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | if (copy_from_user(&sbi, buf, sizeof(sbi))) | ||
236 | return -EFAULT; | ||
237 | |||
238 | if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { | ||
239 | snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | |||
243 | size = sizeof(*put) + sizeof(fm_xinstrument_t); | ||
244 | put = kcalloc(1, size, GFP_KERNEL); | ||
245 | if (put == NULL) | ||
246 | return -ENOMEM; | ||
247 | /* build header */ | ||
248 | data = &put->data; | ||
249 | data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; | ||
250 | strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); | ||
251 | /* build data section */ | ||
252 | xinstr = (fm_xinstrument_t *)(data + 1); | ||
253 | xinstr->stype = FM_STRU_INSTR; | ||
254 | |||
255 | for (i = 0; i < 2; i++) { | ||
256 | xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; | ||
257 | xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; | ||
258 | xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; | ||
259 | xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; | ||
260 | xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; | ||
261 | } | ||
262 | xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; | ||
263 | |||
264 | if (format == OPL3_PATCH) { | ||
265 | xinstr->type = FM_PATCH_OPL3; | ||
266 | for (i = 0; i < 2; i++) { | ||
267 | xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; | ||
268 | xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; | ||
269 | xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; | ||
270 | xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; | ||
271 | xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; | ||
272 | } | ||
273 | xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; | ||
274 | } else { | ||
275 | xinstr->type = FM_PATCH_OPL2; | ||
276 | } | ||
277 | |||
278 | put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; | ||
279 | put->id.instr.bank = 127; | ||
280 | put->id.instr.prg = sbi.channel; | ||
281 | put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; | ||
282 | |||
283 | memset (&ev, 0, sizeof(ev)); | ||
284 | ev.source.client = SNDRV_SEQ_CLIENT_OSS; | ||
285 | ev.dest = arg->addr; | ||
286 | |||
287 | ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; | ||
288 | ev.queue = SNDRV_SEQ_QUEUE_DIRECT; | ||
289 | |||
290 | fs = snd_enter_user(); | ||
291 | __again: | ||
292 | ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; | ||
293 | ev.data.ext.len = size; | ||
294 | ev.data.ext.ptr = put; | ||
295 | |||
296 | err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, | ||
297 | opl3->seq_client, 0, 0); | ||
298 | if (err == -EBUSY) { | ||
299 | snd_seq_instr_header_t remove; | ||
300 | |||
301 | memset (&remove, 0, sizeof(remove)); | ||
302 | remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; | ||
303 | remove.id.instr = put->id.instr; | ||
304 | |||
305 | /* remove instrument */ | ||
306 | ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; | ||
307 | ev.data.ext.len = sizeof(remove); | ||
308 | ev.data.ext.ptr = &remove; | ||
309 | |||
310 | snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, | ||
311 | opl3->seq_client, 0, 0); | ||
312 | goto __again; | ||
313 | } | ||
314 | snd_leave_user(fs); | ||
315 | |||
316 | kfree(put); | ||
317 | } | ||
318 | return err; | ||
319 | } | ||
320 | |||
321 | /* ioctl */ | ||
322 | static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, | ||
323 | unsigned long ioarg) | ||
324 | { | ||
325 | opl3_t *opl3; | ||
326 | |||
327 | snd_assert(arg != NULL, return -ENXIO); | ||
328 | opl3 = arg->private_data; | ||
329 | switch (cmd) { | ||
330 | case SNDCTL_FM_LOAD_INSTR: | ||
331 | snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); | ||
332 | return -EINVAL; | ||
333 | |||
334 | case SNDCTL_SYNTH_MEMAVL: | ||
335 | return 0x7fffffff; | ||
336 | |||
337 | case SNDCTL_FM_4OP_ENABLE: | ||
338 | // handled automatically by OPL instrument type | ||
339 | return 0; | ||
340 | |||
341 | default: | ||
342 | return -EINVAL; | ||
343 | } | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /* reset device */ | ||
348 | static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg) | ||
349 | { | ||
350 | opl3_t *opl3; | ||
351 | |||
352 | snd_assert(arg != NULL, return -ENXIO); | ||
353 | opl3 = arg->private_data; | ||
354 | |||
355 | return 0; | ||
356 | } | ||
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c new file mode 100644 index 000000000000..136964b844de --- /dev/null +++ b/sound/drivers/opl3/opl3_seq.c | |||
@@ -0,0 +1,314 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Uros Bizjak <uros@kss-loka.si> | ||
3 | * | ||
4 | * Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM | ||
5 | * | ||
6 | * OPL2/3 FM instrument loader: | ||
7 | * alsa-tools/seq/sbiload/ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include "opl3_voice.h" | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/initval.h> | ||
29 | |||
30 | MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>"); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth"); | ||
33 | |||
34 | int use_internal_drums = 0; | ||
35 | module_param(use_internal_drums, bool, 0444); | ||
36 | MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums."); | ||
37 | |||
38 | int snd_opl3_synth_use_inc(opl3_t * opl3) | ||
39 | { | ||
40 | if (!try_module_get(opl3->card->module)) | ||
41 | return -EFAULT; | ||
42 | return 0; | ||
43 | |||
44 | } | ||
45 | |||
46 | void snd_opl3_synth_use_dec(opl3_t * opl3) | ||
47 | { | ||
48 | module_put(opl3->card->module); | ||
49 | } | ||
50 | |||
51 | int snd_opl3_synth_setup(opl3_t * opl3) | ||
52 | { | ||
53 | int idx; | ||
54 | |||
55 | down(&opl3->access_mutex); | ||
56 | if (opl3->used) { | ||
57 | up(&opl3->access_mutex); | ||
58 | return -EBUSY; | ||
59 | } | ||
60 | opl3->used++; | ||
61 | up(&opl3->access_mutex); | ||
62 | |||
63 | snd_opl3_reset(opl3); | ||
64 | |||
65 | for (idx = 0; idx < MAX_OPL3_VOICES; idx++) { | ||
66 | opl3->voices[idx].state = SNDRV_OPL3_ST_OFF; | ||
67 | opl3->voices[idx].time = 0; | ||
68 | opl3->voices[idx].keyon_reg = 0x00; | ||
69 | } | ||
70 | opl3->use_time = 0; | ||
71 | opl3->connection_reg = 0x00; | ||
72 | if (opl3->hardware >= OPL3_HW_OPL3) { | ||
73 | /* Clear 4-op connections */ | ||
74 | opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, | ||
75 | opl3->connection_reg); | ||
76 | opl3->max_voices = MAX_OPL3_VOICES; | ||
77 | } | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | void snd_opl3_synth_cleanup(opl3_t * opl3) | ||
82 | { | ||
83 | unsigned long flags; | ||
84 | |||
85 | /* Stop system timer */ | ||
86 | spin_lock_irqsave(&opl3->sys_timer_lock, flags); | ||
87 | if (opl3->sys_timer_status) { | ||
88 | del_timer(&opl3->tlist); | ||
89 | opl3->sys_timer_status = 0; | ||
90 | } | ||
91 | spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); | ||
92 | |||
93 | snd_opl3_reset(opl3); | ||
94 | down(&opl3->access_mutex); | ||
95 | opl3->used--; | ||
96 | up(&opl3->access_mutex); | ||
97 | } | ||
98 | |||
99 | static int snd_opl3_synth_use(void *private_data, snd_seq_port_subscribe_t * info) | ||
100 | { | ||
101 | opl3_t *opl3 = private_data; | ||
102 | int err; | ||
103 | |||
104 | if ((err = snd_opl3_synth_setup(opl3)) < 0) | ||
105 | return err; | ||
106 | |||
107 | if (use_internal_drums) { | ||
108 | /* Percussion mode */ | ||
109 | opl3->voices[6].state = opl3->voices[7].state = | ||
110 | opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL; | ||
111 | snd_opl3_load_drums(opl3); | ||
112 | opl3->drum_reg = OPL3_PERCUSSION_ENABLE; | ||
113 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); | ||
114 | } else { | ||
115 | opl3->drum_reg = 0x00; | ||
116 | } | ||
117 | |||
118 | if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) { | ||
119 | if ((err = snd_opl3_synth_use_inc(opl3)) < 0) | ||
120 | return err; | ||
121 | } | ||
122 | opl3->synth_mode = SNDRV_OPL3_MODE_SEQ; | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int snd_opl3_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) | ||
127 | { | ||
128 | opl3_t *opl3 = private_data; | ||
129 | |||
130 | snd_opl3_synth_cleanup(opl3); | ||
131 | |||
132 | if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) | ||
133 | snd_opl3_synth_use_dec(opl3); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * MIDI emulation operators | ||
139 | */ | ||
140 | snd_midi_op_t opl3_ops = { | ||
141 | .note_on = snd_opl3_note_on, | ||
142 | .note_off = snd_opl3_note_off, | ||
143 | .key_press = snd_opl3_key_press, | ||
144 | .note_terminate = snd_opl3_terminate_note, | ||
145 | .control = snd_opl3_control, | ||
146 | .nrpn = snd_opl3_nrpn, | ||
147 | .sysex = snd_opl3_sysex, | ||
148 | }; | ||
149 | |||
150 | static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct, | ||
151 | void *private_data, int atomic, int hop) | ||
152 | { | ||
153 | opl3_t *opl3 = private_data; | ||
154 | |||
155 | if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && | ||
156 | ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { | ||
157 | if (direct) { | ||
158 | snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, | ||
159 | opl3->seq_client, atomic, hop); | ||
160 | } | ||
161 | } else { | ||
162 | snd_midi_process_event(&opl3_ops, ev, opl3->chset); | ||
163 | } | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | /* ------------------------------ */ | ||
168 | |||
169 | static void snd_opl3_synth_free_port(void *private_data) | ||
170 | { | ||
171 | opl3_t *opl3 = private_data; | ||
172 | |||
173 | snd_midi_channel_free_set(opl3->chset); | ||
174 | } | ||
175 | |||
176 | static int snd_opl3_synth_create_port(opl3_t * opl3) | ||
177 | { | ||
178 | snd_seq_port_callback_t callbacks; | ||
179 | char name[32]; | ||
180 | int voices, opl_ver; | ||
181 | |||
182 | voices = (opl3->hardware < OPL3_HW_OPL3) ? | ||
183 | MAX_OPL2_VOICES : MAX_OPL3_VOICES; | ||
184 | opl3->chset = snd_midi_channel_alloc_set(16); | ||
185 | if (opl3->chset == NULL) | ||
186 | return -ENOMEM; | ||
187 | opl3->chset->private_data = opl3; | ||
188 | |||
189 | memset(&callbacks, 0, sizeof(callbacks)); | ||
190 | callbacks.owner = THIS_MODULE; | ||
191 | callbacks.use = snd_opl3_synth_use; | ||
192 | callbacks.unuse = snd_opl3_synth_unuse; | ||
193 | callbacks.event_input = snd_opl3_synth_event_input; | ||
194 | callbacks.private_free = snd_opl3_synth_free_port; | ||
195 | callbacks.private_data = opl3; | ||
196 | |||
197 | opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; | ||
198 | sprintf(name, "OPL%i FM Port", opl_ver); | ||
199 | |||
200 | opl3->chset->client = opl3->seq_client; | ||
201 | opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, | ||
202 | SNDRV_SEQ_PORT_CAP_WRITE | | ||
203 | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, | ||
204 | SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | | ||
205 | SNDRV_SEQ_PORT_TYPE_MIDI_GM | | ||
206 | SNDRV_SEQ_PORT_TYPE_SYNTH, | ||
207 | 16, voices, | ||
208 | name); | ||
209 | if (opl3->chset->port < 0) { | ||
210 | snd_midi_channel_free_set(opl3->chset); | ||
211 | return opl3->chset->port; | ||
212 | } | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* ------------------------------ */ | ||
217 | |||
218 | static int snd_opl3_seq_new_device(snd_seq_device_t *dev) | ||
219 | { | ||
220 | opl3_t *opl3; | ||
221 | int client; | ||
222 | snd_seq_client_callback_t callbacks; | ||
223 | snd_seq_client_info_t cinfo; | ||
224 | int opl_ver; | ||
225 | |||
226 | opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
227 | if (opl3 == NULL) | ||
228 | return -EINVAL; | ||
229 | |||
230 | spin_lock_init(&opl3->voice_lock); | ||
231 | |||
232 | opl3->seq_client = -1; | ||
233 | |||
234 | /* allocate new client */ | ||
235 | memset(&callbacks, 0, sizeof(callbacks)); | ||
236 | callbacks.private_data = opl3; | ||
237 | callbacks.allow_output = callbacks.allow_input = 1; | ||
238 | client = opl3->seq_client = | ||
239 | snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, &callbacks); | ||
240 | if (client < 0) | ||
241 | return client; | ||
242 | |||
243 | /* change name of client */ | ||
244 | memset(&cinfo, 0, sizeof(cinfo)); | ||
245 | cinfo.client = client; | ||
246 | cinfo.type = KERNEL_CLIENT; | ||
247 | opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; | ||
248 | sprintf(cinfo.name, "OPL%i FM synth", opl_ver); | ||
249 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); | ||
250 | |||
251 | snd_opl3_synth_create_port(opl3); | ||
252 | |||
253 | /* initialize instrument list */ | ||
254 | opl3->ilist = snd_seq_instr_list_new(); | ||
255 | if (opl3->ilist == NULL) { | ||
256 | snd_seq_delete_kernel_client(client); | ||
257 | opl3->seq_client = -1; | ||
258 | return -ENOMEM; | ||
259 | } | ||
260 | opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; | ||
261 | snd_seq_fm_init(&opl3->fm_ops, NULL); | ||
262 | |||
263 | /* setup system timer */ | ||
264 | init_timer(&opl3->tlist); | ||
265 | opl3->tlist.function = snd_opl3_timer_func; | ||
266 | opl3->tlist.data = (unsigned long) opl3; | ||
267 | spin_lock_init(&opl3->sys_timer_lock); | ||
268 | opl3->sys_timer_status = 0; | ||
269 | |||
270 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
271 | snd_opl3_init_seq_oss(opl3, cinfo.name); | ||
272 | #endif | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static int snd_opl3_seq_delete_device(snd_seq_device_t *dev) | ||
277 | { | ||
278 | opl3_t *opl3; | ||
279 | |||
280 | opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
281 | if (opl3 == NULL) | ||
282 | return -EINVAL; | ||
283 | |||
284 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
285 | snd_opl3_free_seq_oss(opl3); | ||
286 | #endif | ||
287 | if (opl3->seq_client >= 0) { | ||
288 | snd_seq_delete_kernel_client(opl3->seq_client); | ||
289 | opl3->seq_client = -1; | ||
290 | } | ||
291 | if (opl3->ilist) | ||
292 | snd_seq_instr_list_free(&opl3->ilist); | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int __init alsa_opl3_seq_init(void) | ||
297 | { | ||
298 | static snd_seq_dev_ops_t ops = | ||
299 | { | ||
300 | snd_opl3_seq_new_device, | ||
301 | snd_opl3_seq_delete_device | ||
302 | }; | ||
303 | |||
304 | return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, | ||
305 | sizeof(opl3_t*)); | ||
306 | } | ||
307 | |||
308 | static void __exit alsa_opl3_seq_exit(void) | ||
309 | { | ||
310 | snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); | ||
311 | } | ||
312 | |||
313 | module_init(alsa_opl3_seq_init) | ||
314 | module_exit(alsa_opl3_seq_exit) | ||
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c new file mode 100644 index 000000000000..04f9f955e5b7 --- /dev/null +++ b/sound/drivers/opl3/opl3_synth.c | |||
@@ -0,0 +1,447 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Uros Bizjak <uros@kss-loka.si> | ||
3 | * | ||
4 | * Routines for OPL2/OPL3/OPL4 control | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/opl3.h> | ||
23 | #include <sound/asound_fm.h> | ||
24 | |||
25 | /* | ||
26 | * There is 18 possible 2 OP voices | ||
27 | * (9 in the left and 9 in the right). | ||
28 | * The first OP is the modulator and 2nd is the carrier. | ||
29 | * | ||
30 | * The first three voices in the both sides may be connected | ||
31 | * with another voice to a 4 OP voice. For example voice 0 | ||
32 | * can be connected with voice 3. The operators of voice 3 are | ||
33 | * used as operators 3 and 4 of the new 4 OP voice. | ||
34 | * In this case the 2 OP voice number 0 is the 'first half' and | ||
35 | * voice 3 is the second. | ||
36 | */ | ||
37 | |||
38 | |||
39 | /* | ||
40 | * Register offset table for OPL2/3 voices, | ||
41 | * OPL2 / one OPL3 register array side only | ||
42 | */ | ||
43 | |||
44 | char snd_opl3_regmap[MAX_OPL2_VOICES][4] = | ||
45 | { | ||
46 | /* OP1 OP2 OP3 OP4 */ | ||
47 | /* ------------------------ */ | ||
48 | { 0x00, 0x03, 0x08, 0x0b }, | ||
49 | { 0x01, 0x04, 0x09, 0x0c }, | ||
50 | { 0x02, 0x05, 0x0a, 0x0d }, | ||
51 | |||
52 | { 0x08, 0x0b, 0x00, 0x00 }, | ||
53 | { 0x09, 0x0c, 0x00, 0x00 }, | ||
54 | { 0x0a, 0x0d, 0x00, 0x00 }, | ||
55 | |||
56 | { 0x10, 0x13, 0x00, 0x00 }, /* used by percussive voices */ | ||
57 | { 0x11, 0x14, 0x00, 0x00 }, /* if the percussive mode */ | ||
58 | { 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */ | ||
59 | }; | ||
60 | |||
61 | /* | ||
62 | * prototypes | ||
63 | */ | ||
64 | static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note); | ||
65 | static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice); | ||
66 | static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params); | ||
67 | static int snd_opl3_set_mode(opl3_t * opl3, int mode); | ||
68 | static int snd_opl3_set_connection(opl3_t * opl3, int connection); | ||
69 | |||
70 | /* ------------------------------ */ | ||
71 | |||
72 | /* | ||
73 | * open the device exclusively | ||
74 | */ | ||
75 | int snd_opl3_open(snd_hwdep_t * hw, struct file *file) | ||
76 | { | ||
77 | opl3_t *opl3 = hw->private_data; | ||
78 | |||
79 | down(&opl3->access_mutex); | ||
80 | if (opl3->used) { | ||
81 | up(&opl3->access_mutex); | ||
82 | return -EAGAIN; | ||
83 | } | ||
84 | opl3->used++; | ||
85 | up(&opl3->access_mutex); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * ioctl for hwdep device: | ||
92 | */ | ||
93 | int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, | ||
94 | unsigned int cmd, unsigned long arg) | ||
95 | { | ||
96 | opl3_t *opl3 = hw->private_data; | ||
97 | void __user *argp = (void __user *)arg; | ||
98 | |||
99 | snd_assert(opl3 != NULL, return -EINVAL); | ||
100 | |||
101 | switch (cmd) { | ||
102 | /* get information */ | ||
103 | case SNDRV_DM_FM_IOCTL_INFO: | ||
104 | { | ||
105 | snd_dm_fm_info_t info; | ||
106 | |||
107 | info.fm_mode = opl3->fm_mode; | ||
108 | info.rhythm = opl3->rhythm; | ||
109 | if (copy_to_user(argp, &info, sizeof(snd_dm_fm_info_t))) | ||
110 | return -EFAULT; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | case SNDRV_DM_FM_IOCTL_RESET: | ||
115 | #ifdef CONFIG_SND_OSSEMUL | ||
116 | case SNDRV_DM_FM_OSS_IOCTL_RESET: | ||
117 | #endif | ||
118 | snd_opl3_reset(opl3); | ||
119 | return 0; | ||
120 | |||
121 | case SNDRV_DM_FM_IOCTL_PLAY_NOTE: | ||
122 | #ifdef CONFIG_SND_OSSEMUL | ||
123 | case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE: | ||
124 | #endif | ||
125 | { | ||
126 | snd_dm_fm_note_t note; | ||
127 | if (copy_from_user(¬e, argp, sizeof(snd_dm_fm_note_t))) | ||
128 | return -EFAULT; | ||
129 | return snd_opl3_play_note(opl3, ¬e); | ||
130 | } | ||
131 | |||
132 | case SNDRV_DM_FM_IOCTL_SET_VOICE: | ||
133 | #ifdef CONFIG_SND_OSSEMUL | ||
134 | case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE: | ||
135 | #endif | ||
136 | { | ||
137 | snd_dm_fm_voice_t voice; | ||
138 | if (copy_from_user(&voice, argp, sizeof(snd_dm_fm_voice_t))) | ||
139 | return -EFAULT; | ||
140 | return snd_opl3_set_voice(opl3, &voice); | ||
141 | } | ||
142 | |||
143 | case SNDRV_DM_FM_IOCTL_SET_PARAMS: | ||
144 | #ifdef CONFIG_SND_OSSEMUL | ||
145 | case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS: | ||
146 | #endif | ||
147 | { | ||
148 | snd_dm_fm_params_t params; | ||
149 | if (copy_from_user(¶ms, argp, sizeof(snd_dm_fm_params_t))) | ||
150 | return -EFAULT; | ||
151 | return snd_opl3_set_params(opl3, ¶ms); | ||
152 | } | ||
153 | |||
154 | case SNDRV_DM_FM_IOCTL_SET_MODE: | ||
155 | #ifdef CONFIG_SND_OSSEMUL | ||
156 | case SNDRV_DM_FM_OSS_IOCTL_SET_MODE: | ||
157 | #endif | ||
158 | return snd_opl3_set_mode(opl3, (int) arg); | ||
159 | |||
160 | case SNDRV_DM_FM_IOCTL_SET_CONNECTION: | ||
161 | #ifdef CONFIG_SND_OSSEMUL | ||
162 | case SNDRV_DM_FM_OSS_IOCTL_SET_OPL: | ||
163 | #endif | ||
164 | return snd_opl3_set_connection(opl3, (int) arg); | ||
165 | |||
166 | #ifdef CONFIG_SND_DEBUG | ||
167 | default: | ||
168 | snd_printk("unknown IOCTL: 0x%x\n", cmd); | ||
169 | #endif | ||
170 | } | ||
171 | return -ENOTTY; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * close the device | ||
176 | */ | ||
177 | int snd_opl3_release(snd_hwdep_t * hw, struct file *file) | ||
178 | { | ||
179 | opl3_t *opl3 = hw->private_data; | ||
180 | |||
181 | snd_opl3_reset(opl3); | ||
182 | down(&opl3->access_mutex); | ||
183 | opl3->used--; | ||
184 | up(&opl3->access_mutex); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* ------------------------------ */ | ||
190 | |||
191 | void snd_opl3_reset(opl3_t * opl3) | ||
192 | { | ||
193 | unsigned short opl3_reg; | ||
194 | |||
195 | unsigned short reg_side; | ||
196 | unsigned char voice_offset; | ||
197 | |||
198 | int max_voices, i; | ||
199 | |||
200 | max_voices = (opl3->hardware < OPL3_HW_OPL3) ? | ||
201 | MAX_OPL2_VOICES : MAX_OPL3_VOICES; | ||
202 | |||
203 | for (i = 0; i < max_voices; i++) { | ||
204 | /* Get register array side and offset of voice */ | ||
205 | if (i < MAX_OPL2_VOICES) { | ||
206 | /* Left register block for voices 0 .. 8 */ | ||
207 | reg_side = OPL3_LEFT; | ||
208 | voice_offset = i; | ||
209 | } else { | ||
210 | /* Right register block for voices 9 .. 17 */ | ||
211 | reg_side = OPL3_RIGHT; | ||
212 | voice_offset = i - MAX_OPL2_VOICES; | ||
213 | } | ||
214 | opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]); | ||
215 | opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */ | ||
216 | opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]); | ||
217 | opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */ | ||
218 | |||
219 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
220 | opl3->command(opl3, opl3_reg, 0x00); /* Note off */ | ||
221 | } | ||
222 | |||
223 | opl3->max_voices = MAX_OPL2_VOICES; | ||
224 | opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; | ||
225 | |||
226 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); | ||
227 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ | ||
228 | opl3->rhythm = 0; | ||
229 | } | ||
230 | |||
231 | |||
232 | static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note) | ||
233 | { | ||
234 | unsigned short reg_side; | ||
235 | unsigned char voice_offset; | ||
236 | |||
237 | unsigned short opl3_reg; | ||
238 | unsigned char reg_val; | ||
239 | |||
240 | /* Voices 0 - 8 in OPL2 mode */ | ||
241 | /* Voices 0 - 17 in OPL3 mode */ | ||
242 | if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? | ||
243 | MAX_OPL3_VOICES : MAX_OPL2_VOICES)) | ||
244 | return -EINVAL; | ||
245 | |||
246 | /* Get register array side and offset of voice */ | ||
247 | if (note->voice < MAX_OPL2_VOICES) { | ||
248 | /* Left register block for voices 0 .. 8 */ | ||
249 | reg_side = OPL3_LEFT; | ||
250 | voice_offset = note->voice; | ||
251 | } else { | ||
252 | /* Right register block for voices 9 .. 17 */ | ||
253 | reg_side = OPL3_RIGHT; | ||
254 | voice_offset = note->voice - MAX_OPL2_VOICES; | ||
255 | } | ||
256 | |||
257 | /* Set lower 8 bits of note frequency */ | ||
258 | reg_val = (unsigned char) note->fnum; | ||
259 | opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); | ||
260 | opl3->command(opl3, opl3_reg, reg_val); | ||
261 | |||
262 | reg_val = 0x00; | ||
263 | /* Set output sound flag */ | ||
264 | if (note->key_on) | ||
265 | reg_val |= OPL3_KEYON_BIT; | ||
266 | /* Set octave */ | ||
267 | reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK; | ||
268 | /* Set higher 2 bits of note frequency */ | ||
269 | reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK; | ||
270 | |||
271 | /* Set OPL3 KEYON_BLOCK register of requested voice */ | ||
272 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | ||
273 | opl3->command(opl3, opl3_reg, reg_val); | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | |||
279 | static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice) | ||
280 | { | ||
281 | unsigned short reg_side; | ||
282 | unsigned char op_offset; | ||
283 | unsigned char voice_offset; | ||
284 | |||
285 | unsigned short opl3_reg; | ||
286 | unsigned char reg_val; | ||
287 | |||
288 | /* Only operators 1 and 2 */ | ||
289 | if (voice->op > 1) | ||
290 | return -EINVAL; | ||
291 | /* Voices 0 - 8 in OPL2 mode */ | ||
292 | /* Voices 0 - 17 in OPL3 mode */ | ||
293 | if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? | ||
294 | MAX_OPL3_VOICES : MAX_OPL2_VOICES)) | ||
295 | return -EINVAL; | ||
296 | |||
297 | /* Get register array side and offset of voice */ | ||
298 | if (voice->voice < MAX_OPL2_VOICES) { | ||
299 | /* Left register block for voices 0 .. 8 */ | ||
300 | reg_side = OPL3_LEFT; | ||
301 | voice_offset = voice->voice; | ||
302 | } else { | ||
303 | /* Right register block for voices 9 .. 17 */ | ||
304 | reg_side = OPL3_RIGHT; | ||
305 | voice_offset = voice->voice - MAX_OPL2_VOICES; | ||
306 | } | ||
307 | /* Get register offset of operator */ | ||
308 | op_offset = snd_opl3_regmap[voice_offset][voice->op]; | ||
309 | |||
310 | reg_val = 0x00; | ||
311 | /* Set amplitude modulation (tremolo) effect */ | ||
312 | if (voice->am) | ||
313 | reg_val |= OPL3_TREMOLO_ON; | ||
314 | /* Set vibrato effect */ | ||
315 | if (voice->vibrato) | ||
316 | reg_val |= OPL3_VIBRATO_ON; | ||
317 | /* Set sustaining sound phase */ | ||
318 | if (voice->do_sustain) | ||
319 | reg_val |= OPL3_SUSTAIN_ON; | ||
320 | /* Set keyboard scaling bit */ | ||
321 | if (voice->kbd_scale) | ||
322 | reg_val |= OPL3_KSR; | ||
323 | /* Set harmonic or frequency multiplier */ | ||
324 | reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK; | ||
325 | |||
326 | /* Set OPL3 AM_VIB register of requested voice/operator */ | ||
327 | opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); | ||
328 | opl3->command(opl3, opl3_reg, reg_val); | ||
329 | |||
330 | /* Set decreasing volume of higher notes */ | ||
331 | reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK; | ||
332 | /* Set output volume */ | ||
333 | reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK; | ||
334 | |||
335 | /* Set OPL3 KSL_LEVEL register of requested voice/operator */ | ||
336 | opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); | ||
337 | opl3->command(opl3, opl3_reg, reg_val); | ||
338 | |||
339 | /* Set attack phase level */ | ||
340 | reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK; | ||
341 | /* Set decay phase level */ | ||
342 | reg_val |= voice->decay & OPL3_DECAY_MASK; | ||
343 | |||
344 | /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ | ||
345 | opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); | ||
346 | opl3->command(opl3, opl3_reg, reg_val); | ||
347 | |||
348 | /* Set sustain phase level */ | ||
349 | reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK; | ||
350 | /* Set release phase level */ | ||
351 | reg_val |= voice->release & OPL3_RELEASE_MASK; | ||
352 | |||
353 | /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ | ||
354 | opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); | ||
355 | opl3->command(opl3, opl3_reg, reg_val); | ||
356 | |||
357 | /* Set inter-operator feedback */ | ||
358 | reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK; | ||
359 | /* Set inter-operator connection */ | ||
360 | if (voice->connection) | ||
361 | reg_val |= OPL3_CONNECTION_BIT; | ||
362 | /* OPL-3 only */ | ||
363 | if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) { | ||
364 | if (voice->left) | ||
365 | reg_val |= OPL3_VOICE_TO_LEFT; | ||
366 | if (voice->right) | ||
367 | reg_val |= OPL3_VOICE_TO_RIGHT; | ||
368 | } | ||
369 | /* Feedback/connection bits are applicable to voice */ | ||
370 | opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); | ||
371 | opl3->command(opl3, opl3_reg, reg_val); | ||
372 | |||
373 | /* Select waveform */ | ||
374 | reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK; | ||
375 | opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); | ||
376 | opl3->command(opl3, opl3_reg, reg_val); | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params) | ||
382 | { | ||
383 | unsigned char reg_val; | ||
384 | |||
385 | reg_val = 0x00; | ||
386 | /* Set keyboard split method */ | ||
387 | if (params->kbd_split) | ||
388 | reg_val |= OPL3_KEYBOARD_SPLIT; | ||
389 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val); | ||
390 | |||
391 | reg_val = 0x00; | ||
392 | /* Set amplitude modulation (tremolo) depth */ | ||
393 | if (params->am_depth) | ||
394 | reg_val |= OPL3_TREMOLO_DEPTH; | ||
395 | /* Set vibrato depth */ | ||
396 | if (params->vib_depth) | ||
397 | reg_val |= OPL3_VIBRATO_DEPTH; | ||
398 | /* Set percussion mode */ | ||
399 | if (params->rhythm) { | ||
400 | reg_val |= OPL3_PERCUSSION_ENABLE; | ||
401 | opl3->rhythm = 1; | ||
402 | } else { | ||
403 | opl3->rhythm = 0; | ||
404 | } | ||
405 | /* Play percussion instruments */ | ||
406 | if (params->bass) | ||
407 | reg_val |= OPL3_BASSDRUM_ON; | ||
408 | if (params->snare) | ||
409 | reg_val |= OPL3_SNAREDRUM_ON; | ||
410 | if (params->tomtom) | ||
411 | reg_val |= OPL3_TOMTOM_ON; | ||
412 | if (params->cymbal) | ||
413 | reg_val |= OPL3_CYMBAL_ON; | ||
414 | if (params->hihat) | ||
415 | reg_val |= OPL3_HIHAT_ON; | ||
416 | |||
417 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val); | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int snd_opl3_set_mode(opl3_t * opl3, int mode) | ||
422 | { | ||
423 | if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3)) | ||
424 | return -EINVAL; | ||
425 | |||
426 | opl3->fm_mode = mode; | ||
427 | if (opl3->hardware >= OPL3_HW_OPL3) | ||
428 | opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00); /* Clear 4-op connections */ | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static int snd_opl3_set_connection(opl3_t * opl3, int connection) | ||
434 | { | ||
435 | unsigned char reg_val; | ||
436 | |||
437 | /* OPL-3 only */ | ||
438 | if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3) | ||
439 | return -EINVAL; | ||
440 | |||
441 | reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 | | ||
442 | OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2); | ||
443 | /* Set 4-op connections */ | ||
444 | opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val); | ||
445 | |||
446 | return 0; | ||
447 | } | ||
diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h new file mode 100644 index 000000000000..63346a5c3498 --- /dev/null +++ b/sound/drivers/opl3/opl3_voice.h | |||
@@ -0,0 +1,52 @@ | |||
1 | #ifndef __OPL3_VOICE_H | ||
2 | #define __OPL3_VOICE_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <sound/opl3.h> | ||
23 | |||
24 | /* Prototypes for opl3_seq.c */ | ||
25 | int snd_opl3_synth_use_inc(opl3_t * opl3); | ||
26 | void snd_opl3_synth_use_dec(opl3_t * opl3); | ||
27 | int snd_opl3_synth_setup(opl3_t * opl3); | ||
28 | void snd_opl3_synth_cleanup(opl3_t * opl3); | ||
29 | |||
30 | /* Prototypes for opl3_midi.c */ | ||
31 | void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); | ||
32 | void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); | ||
33 | void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); | ||
34 | void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan); | ||
35 | void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan); | ||
36 | void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); | ||
37 | void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); | ||
38 | |||
39 | void snd_opl3_calc_volume(unsigned char *reg, int vel, snd_midi_channel_t *chan); | ||
40 | void snd_opl3_timer_func(unsigned long data); | ||
41 | |||
42 | /* Prototypes for opl3_drums.c */ | ||
43 | void snd_opl3_load_drums(opl3_t *opl3); | ||
44 | void snd_opl3_drum_switch(opl3_t *opl3, int note, int on_off, int vel, snd_midi_channel_t *chan); | ||
45 | |||
46 | /* Prototypes for opl3_oss.c */ | ||
47 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
48 | void snd_opl3_init_seq_oss(opl3_t *opl3, char *name); | ||
49 | void snd_opl3_free_seq_oss(opl3_t *opl3); | ||
50 | #endif | ||
51 | |||
52 | #endif | ||
diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile new file mode 100644 index 000000000000..141aacbaf315 --- /dev/null +++ b/sound/drivers/opl4/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o | ||
7 | snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o | ||
8 | |||
9 | # | ||
10 | # this function returns: | ||
11 | # "m" - CONFIG_SND_SEQUENCER is m | ||
12 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
13 | # otherwise parameter #1 value | ||
14 | # | ||
15 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
16 | |||
17 | obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o | ||
18 | obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o | ||
diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c new file mode 100644 index 000000000000..8261464dade8 --- /dev/null +++ b/sound/drivers/opl4/opl4_lib.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* | ||
2 | * Functions for accessing OPL4 devices | ||
3 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
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, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include "opl4_local.h" | ||
21 | #include <sound/initval.h> | ||
22 | #include <linux/ioport.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <asm/io.h> | ||
25 | |||
26 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | ||
27 | MODULE_DESCRIPTION("OPL4 driver"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | static void inline snd_opl4_wait(opl4_t *opl4) | ||
31 | { | ||
32 | int timeout = 10; | ||
33 | while ((inb(opl4->fm_port) & OPL4_STATUS_BUSY) && --timeout > 0) | ||
34 | ; | ||
35 | } | ||
36 | |||
37 | void snd_opl4_write(opl4_t *opl4, u8 reg, u8 value) | ||
38 | { | ||
39 | snd_opl4_wait(opl4); | ||
40 | outb(reg, opl4->pcm_port); | ||
41 | |||
42 | snd_opl4_wait(opl4); | ||
43 | outb(value, opl4->pcm_port + 1); | ||
44 | } | ||
45 | |||
46 | u8 snd_opl4_read(opl4_t *opl4, u8 reg) | ||
47 | { | ||
48 | snd_opl4_wait(opl4); | ||
49 | outb(reg, opl4->pcm_port); | ||
50 | |||
51 | snd_opl4_wait(opl4); | ||
52 | return inb(opl4->pcm_port + 1); | ||
53 | } | ||
54 | |||
55 | void snd_opl4_read_memory(opl4_t *opl4, char *buf, int offset, int size) | ||
56 | { | ||
57 | unsigned long flags; | ||
58 | u8 memcfg; | ||
59 | |||
60 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
61 | |||
62 | memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); | ||
63 | snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT); | ||
64 | |||
65 | snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16); | ||
66 | snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8); | ||
67 | snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset); | ||
68 | |||
69 | snd_opl4_wait(opl4); | ||
70 | outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port); | ||
71 | snd_opl4_wait(opl4); | ||
72 | insb(opl4->pcm_port + 1, buf, size); | ||
73 | |||
74 | snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg); | ||
75 | |||
76 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
77 | } | ||
78 | |||
79 | void snd_opl4_write_memory(opl4_t *opl4, const char *buf, int offset, int size) | ||
80 | { | ||
81 | unsigned long flags; | ||
82 | u8 memcfg; | ||
83 | |||
84 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
85 | |||
86 | memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); | ||
87 | snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT); | ||
88 | |||
89 | snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16); | ||
90 | snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8); | ||
91 | snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset); | ||
92 | |||
93 | snd_opl4_wait(opl4); | ||
94 | outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port); | ||
95 | snd_opl4_wait(opl4); | ||
96 | outsb(opl4->pcm_port + 1, buf, size); | ||
97 | |||
98 | snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg); | ||
99 | |||
100 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
101 | } | ||
102 | |||
103 | static void snd_opl4_enable_opl4(opl4_t *opl4) | ||
104 | { | ||
105 | outb(OPL3_REG_MODE, opl4->fm_port + 2); | ||
106 | inb(opl4->fm_port); | ||
107 | inb(opl4->fm_port); | ||
108 | outb(OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE, opl4->fm_port + 3); | ||
109 | inb(opl4->fm_port); | ||
110 | inb(opl4->fm_port); | ||
111 | } | ||
112 | |||
113 | static int snd_opl4_detect(opl4_t *opl4) | ||
114 | { | ||
115 | u8 id1, id2; | ||
116 | |||
117 | snd_opl4_enable_opl4(opl4); | ||
118 | |||
119 | id1 = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); | ||
120 | snd_printdd("OPL4[02]=%02x\n", id1); | ||
121 | switch (id1 & OPL4_DEVICE_ID_MASK) { | ||
122 | case 0x20: | ||
123 | opl4->hardware = OPL3_HW_OPL4; | ||
124 | break; | ||
125 | case 0x40: | ||
126 | opl4->hardware = OPL3_HW_OPL4_ML; | ||
127 | break; | ||
128 | default: | ||
129 | return -ENODEV; | ||
130 | } | ||
131 | |||
132 | snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x00); | ||
133 | snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0xff); | ||
134 | id1 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_FM); | ||
135 | id2 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_PCM); | ||
136 | snd_printdd("OPL4 id1=%02x id2=%02x\n", id1, id2); | ||
137 | if (id1 != 0x00 || id2 != 0xff) | ||
138 | return -ENODEV; | ||
139 | |||
140 | snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x3f); | ||
141 | snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0x3f); | ||
142 | snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, 0x00); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
147 | static void snd_opl4_seq_dev_free(snd_seq_device_t *seq_dev) | ||
148 | { | ||
149 | opl4_t *opl4 = seq_dev->private_data; | ||
150 | opl4->seq_dev = NULL; | ||
151 | } | ||
152 | |||
153 | static int snd_opl4_create_seq_dev(opl4_t *opl4, int seq_device) | ||
154 | { | ||
155 | opl4->seq_dev_num = seq_device; | ||
156 | if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4, | ||
157 | sizeof(opl4_t *), &opl4->seq_dev) >= 0) { | ||
158 | strcpy(opl4->seq_dev->name, "OPL4 Wavetable"); | ||
159 | *(opl4_t **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4; | ||
160 | opl4->seq_dev->private_data = opl4; | ||
161 | opl4->seq_dev->private_free = snd_opl4_seq_dev_free; | ||
162 | } | ||
163 | return 0; | ||
164 | } | ||
165 | #endif | ||
166 | |||
167 | static void snd_opl4_free(opl4_t *opl4) | ||
168 | { | ||
169 | #ifdef CONFIG_PROC_FS | ||
170 | snd_opl4_free_proc(opl4); | ||
171 | #endif | ||
172 | if (opl4->res_fm_port) { | ||
173 | release_resource(opl4->res_fm_port); | ||
174 | kfree_nocheck(opl4->res_fm_port); | ||
175 | } | ||
176 | if (opl4->res_pcm_port) { | ||
177 | release_resource(opl4->res_pcm_port); | ||
178 | kfree_nocheck(opl4->res_pcm_port); | ||
179 | } | ||
180 | kfree(opl4); | ||
181 | } | ||
182 | |||
183 | static int snd_opl4_dev_free(snd_device_t *device) | ||
184 | { | ||
185 | opl4_t *opl4 = device->device_data; | ||
186 | snd_opl4_free(opl4); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | int snd_opl4_create(snd_card_t *card, | ||
191 | unsigned long fm_port, unsigned long pcm_port, | ||
192 | int seq_device, | ||
193 | opl3_t **ropl3, opl4_t **ropl4) | ||
194 | { | ||
195 | opl4_t *opl4; | ||
196 | opl3_t *opl3; | ||
197 | int err; | ||
198 | static snd_device_ops_t ops = { | ||
199 | .dev_free = snd_opl4_dev_free | ||
200 | }; | ||
201 | |||
202 | if (ropl3) | ||
203 | *ropl3 = NULL; | ||
204 | if (ropl4) | ||
205 | *ropl4 = NULL; | ||
206 | |||
207 | opl4 = kcalloc(1, sizeof(*opl4), GFP_KERNEL); | ||
208 | if (!opl4) | ||
209 | return -ENOMEM; | ||
210 | |||
211 | opl4->res_fm_port = request_region(fm_port, 8, "OPL4 FM"); | ||
212 | opl4->res_pcm_port = request_region(pcm_port, 8, "OPL4 PCM/MIX"); | ||
213 | if (!opl4->res_fm_port || !opl4->res_pcm_port) { | ||
214 | snd_printk(KERN_ERR "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port); | ||
215 | snd_opl4_free(opl4); | ||
216 | return -EBUSY; | ||
217 | } | ||
218 | |||
219 | opl4->card = card; | ||
220 | opl4->fm_port = fm_port; | ||
221 | opl4->pcm_port = pcm_port; | ||
222 | spin_lock_init(&opl4->reg_lock); | ||
223 | init_MUTEX(&opl4->access_mutex); | ||
224 | |||
225 | err = snd_opl4_detect(opl4); | ||
226 | if (err < 0) { | ||
227 | snd_opl4_free(opl4); | ||
228 | snd_printd("OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port); | ||
229 | return err; | ||
230 | } | ||
231 | |||
232 | err = snd_device_new(card, SNDRV_DEV_CODEC, opl4, &ops); | ||
233 | if (err < 0) { | ||
234 | snd_opl4_free(opl4); | ||
235 | return err; | ||
236 | } | ||
237 | |||
238 | err = snd_opl3_create(card, fm_port, fm_port + 2, opl4->hardware, 1, &opl3); | ||
239 | if (err < 0) { | ||
240 | snd_device_free(card, opl4); | ||
241 | return err; | ||
242 | } | ||
243 | |||
244 | /* opl3 initialization disabled opl4, so reenable */ | ||
245 | snd_opl4_enable_opl4(opl4); | ||
246 | |||
247 | snd_opl4_create_mixer(opl4); | ||
248 | #ifdef CONFIG_PROC_FS | ||
249 | snd_opl4_create_proc(opl4); | ||
250 | #endif | ||
251 | |||
252 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
253 | opl4->seq_client = -1; | ||
254 | if (opl4->hardware < OPL3_HW_OPL4_ML) | ||
255 | snd_opl4_create_seq_dev(opl4, seq_device); | ||
256 | #endif | ||
257 | |||
258 | if (ropl3) | ||
259 | *ropl3 = opl3; | ||
260 | if (ropl4) | ||
261 | *ropl4 = opl4; | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | EXPORT_SYMBOL(snd_opl4_write); | ||
266 | EXPORT_SYMBOL(snd_opl4_read); | ||
267 | EXPORT_SYMBOL(snd_opl4_write_memory); | ||
268 | EXPORT_SYMBOL(snd_opl4_read_memory); | ||
269 | EXPORT_SYMBOL(snd_opl4_create); | ||
270 | |||
271 | static int __init alsa_opl4_init(void) | ||
272 | { | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static void __exit alsa_opl4_exit(void) | ||
277 | { | ||
278 | } | ||
279 | |||
280 | module_init(alsa_opl4_init) | ||
281 | module_exit(alsa_opl4_exit) | ||
diff --git a/sound/drivers/opl4/opl4_local.h b/sound/drivers/opl4/opl4_local.h new file mode 100644 index 000000000000..c455680843f6 --- /dev/null +++ b/sound/drivers/opl4/opl4_local.h | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * Local definitions for the OPL4 driver | ||
3 | * | ||
4 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions, and the following disclaimer, | ||
12 | * without modification. | ||
13 | * 2. The name of the author may not be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * Alternatively, this software may be distributed and/or modified under the | ||
17 | * terms of the GNU General Public License as published by the Free Software | ||
18 | * Foundation; either version 2 of the License, or (at your option) any later | ||
19 | * version. | ||
20 | * | ||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | ||
25 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
31 | * SUCH DAMAGE. | ||
32 | */ | ||
33 | |||
34 | #ifndef __OPL4_LOCAL_H | ||
35 | #define __OPL4_LOCAL_H | ||
36 | |||
37 | #include <sound/opl4.h> | ||
38 | |||
39 | /* | ||
40 | * Register numbers | ||
41 | */ | ||
42 | |||
43 | #define OPL4_REG_TEST0 0x00 | ||
44 | #define OPL4_REG_TEST1 0x01 | ||
45 | |||
46 | #define OPL4_REG_MEMORY_CONFIGURATION 0x02 | ||
47 | #define OPL4_MODE_BIT 0x01 | ||
48 | #define OPL4_MTYPE_BIT 0x02 | ||
49 | #define OPL4_TONE_HEADER_MASK 0x1c | ||
50 | #define OPL4_DEVICE_ID_MASK 0xe0 | ||
51 | |||
52 | #define OPL4_REG_MEMORY_ADDRESS_HIGH 0x03 | ||
53 | #define OPL4_REG_MEMORY_ADDRESS_MID 0x04 | ||
54 | #define OPL4_REG_MEMORY_ADDRESS_LOW 0x05 | ||
55 | #define OPL4_REG_MEMORY_DATA 0x06 | ||
56 | |||
57 | /* | ||
58 | * Offsets to the register banks for voices. To get the | ||
59 | * register number just add the voice number to the bank offset. | ||
60 | * | ||
61 | * Wave Table Number low bits (0x08 to 0x1F) | ||
62 | */ | ||
63 | #define OPL4_REG_TONE_NUMBER 0x08 | ||
64 | |||
65 | /* Wave Table Number high bit, F-Number low bits (0x20 to 0x37) */ | ||
66 | #define OPL4_REG_F_NUMBER 0x20 | ||
67 | #define OPL4_TONE_NUMBER_BIT8 0x01 | ||
68 | #define OPL4_F_NUMBER_LOW_MASK 0xfe | ||
69 | |||
70 | /* F-Number high bits, Octave, Pseudo-Reverb (0x38 to 0x4F) */ | ||
71 | #define OPL4_REG_OCTAVE 0x38 | ||
72 | #define OPL4_F_NUMBER_HIGH_MASK 0x07 | ||
73 | #define OPL4_BLOCK_MASK 0xf0 | ||
74 | #define OPL4_PSEUDO_REVERB_BIT 0x08 | ||
75 | |||
76 | /* Total Level, Level Direct (0x50 to 0x67) */ | ||
77 | #define OPL4_REG_LEVEL 0x50 | ||
78 | #define OPL4_TOTAL_LEVEL_MASK 0xfe | ||
79 | #define OPL4_LEVEL_DIRECT_BIT 0x01 | ||
80 | |||
81 | /* Key On, Damp, LFO RST, CH, Panpot (0x68 to 0x7F) */ | ||
82 | #define OPL4_REG_MISC 0x68 | ||
83 | #define OPL4_KEY_ON_BIT 0x80 | ||
84 | #define OPL4_DAMP_BIT 0x40 | ||
85 | #define OPL4_LFO_RESET_BIT 0x20 | ||
86 | #define OPL4_OUTPUT_CHANNEL_BIT 0x10 | ||
87 | #define OPL4_PAN_POT_MASK 0x0f | ||
88 | |||
89 | /* LFO, VIB (0x80 to 0x97) */ | ||
90 | #define OPL4_REG_LFO_VIBRATO 0x80 | ||
91 | #define OPL4_LFO_FREQUENCY_MASK 0x38 | ||
92 | #define OPL4_VIBRATO_DEPTH_MASK 0x07 | ||
93 | #define OPL4_CHORUS_SEND_MASK 0xc0 /* ML only */ | ||
94 | |||
95 | /* Attack / Decay 1 rate (0x98 to 0xAF) */ | ||
96 | #define OPL4_REG_ATTACK_DECAY1 0x98 | ||
97 | #define OPL4_ATTACK_RATE_MASK 0xf0 | ||
98 | #define OPL4_DECAY1_RATE_MASK 0x0f | ||
99 | |||
100 | /* Decay level / 2 rate (0xB0 to 0xC7) */ | ||
101 | #define OPL4_REG_LEVEL_DECAY2 0xb0 | ||
102 | #define OPL4_DECAY_LEVEL_MASK 0xf0 | ||
103 | #define OPL4_DECAY2_RATE_MASK 0x0f | ||
104 | |||
105 | /* Release rate / Rate correction (0xC8 to 0xDF) */ | ||
106 | #define OPL4_REG_RELEASE_CORRECTION 0xc8 | ||
107 | #define OPL4_RELEASE_RATE_MASK 0x0f | ||
108 | #define OPL4_RATE_INTERPOLATION_MASK 0xf0 | ||
109 | |||
110 | /* AM (0xE0 to 0xF7) */ | ||
111 | #define OPL4_REG_TREMOLO 0xe0 | ||
112 | #define OPL4_TREMOLO_DEPTH_MASK 0x07 | ||
113 | #define OPL4_REVERB_SEND_MASK 0xe0 /* ML only */ | ||
114 | |||
115 | /* Mixer */ | ||
116 | #define OPL4_REG_MIX_CONTROL_FM 0xf8 | ||
117 | #define OPL4_REG_MIX_CONTROL_PCM 0xf9 | ||
118 | #define OPL4_MIX_LEFT_MASK 0x07 | ||
119 | #define OPL4_MIX_RIGHT_MASK 0x38 | ||
120 | |||
121 | #define OPL4_REG_ATC 0xfa | ||
122 | #define OPL4_ATC_BIT 0x01 /* ???, ML only */ | ||
123 | |||
124 | /* bits in the OPL3 Status register */ | ||
125 | #define OPL4_STATUS_BUSY 0x01 | ||
126 | #define OPL4_STATUS_LOAD 0x02 | ||
127 | |||
128 | |||
129 | #define OPL4_MAX_VOICES 24 | ||
130 | |||
131 | #define SNDRV_SEQ_DEV_ID_OPL4 "opl4-synth" | ||
132 | |||
133 | |||
134 | typedef struct opl4_sound { | ||
135 | u16 tone; | ||
136 | s16 pitch_offset; | ||
137 | u8 key_scaling; | ||
138 | s8 panpot; | ||
139 | u8 vibrato; | ||
140 | u8 tone_attenuate; | ||
141 | u8 volume_factor; | ||
142 | u8 reg_lfo_vibrato; | ||
143 | u8 reg_attack_decay1; | ||
144 | u8 reg_level_decay2; | ||
145 | u8 reg_release_correction; | ||
146 | u8 reg_tremolo; | ||
147 | } opl4_sound_t; | ||
148 | |||
149 | typedef struct opl4_region { | ||
150 | u8 key_min, key_max; | ||
151 | opl4_sound_t sound; | ||
152 | } opl4_region_t; | ||
153 | |||
154 | typedef struct opl4_region_ptr { | ||
155 | int count; | ||
156 | const opl4_region_t *regions; | ||
157 | } opl4_region_ptr_t; | ||
158 | |||
159 | typedef struct opl4_voice { | ||
160 | struct list_head list; | ||
161 | int number; | ||
162 | snd_midi_channel_t *chan; | ||
163 | int note; | ||
164 | int velocity; | ||
165 | const opl4_sound_t *sound; | ||
166 | u8 level_direct; | ||
167 | u8 reg_f_number; | ||
168 | u8 reg_misc; | ||
169 | u8 reg_lfo_vibrato; | ||
170 | } opl4_voice_t; | ||
171 | |||
172 | struct opl4 { | ||
173 | unsigned long fm_port; | ||
174 | unsigned long pcm_port; | ||
175 | struct resource *res_fm_port; | ||
176 | struct resource *res_pcm_port; | ||
177 | unsigned short hardware; | ||
178 | spinlock_t reg_lock; | ||
179 | snd_card_t *card; | ||
180 | |||
181 | #ifdef CONFIG_PROC_FS | ||
182 | snd_info_entry_t *proc_entry; | ||
183 | int memory_access; | ||
184 | #endif | ||
185 | struct semaphore access_mutex; | ||
186 | |||
187 | #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) | ||
188 | int used; | ||
189 | |||
190 | int seq_dev_num; | ||
191 | int seq_client; | ||
192 | snd_seq_device_t *seq_dev; | ||
193 | |||
194 | snd_midi_channel_set_t *chset; | ||
195 | opl4_voice_t voices[OPL4_MAX_VOICES]; | ||
196 | struct list_head off_voices; | ||
197 | struct list_head on_voices; | ||
198 | #endif | ||
199 | }; | ||
200 | |||
201 | /* opl4_lib.c */ | ||
202 | void snd_opl4_write(opl4_t *opl4, u8 reg, u8 value); | ||
203 | u8 snd_opl4_read(opl4_t *opl4, u8 reg); | ||
204 | void snd_opl4_read_memory(opl4_t *opl4, char *buf, int offset, int size); | ||
205 | void snd_opl4_write_memory(opl4_t *opl4, const char *buf, int offset, int size); | ||
206 | |||
207 | /* opl4_mixer.c */ | ||
208 | int snd_opl4_create_mixer(opl4_t *opl4); | ||
209 | |||
210 | #ifdef CONFIG_PROC_FS | ||
211 | /* opl4_proc.c */ | ||
212 | int snd_opl4_create_proc(opl4_t *opl4); | ||
213 | void snd_opl4_free_proc(opl4_t *opl4); | ||
214 | #endif | ||
215 | |||
216 | /* opl4_seq.c */ | ||
217 | extern int volume_boost; | ||
218 | |||
219 | /* opl4_synth.c */ | ||
220 | void snd_opl4_synth_reset(opl4_t *opl4); | ||
221 | void snd_opl4_synth_shutdown(opl4_t *opl4); | ||
222 | void snd_opl4_note_on(void *p, int note, int vel, snd_midi_channel_t *chan); | ||
223 | void snd_opl4_note_off(void *p, int note, int vel, snd_midi_channel_t *chan); | ||
224 | void snd_opl4_terminate_note(void *p, int note, snd_midi_channel_t *chan); | ||
225 | void snd_opl4_control(void *p, int type, snd_midi_channel_t *chan); | ||
226 | void snd_opl4_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); | ||
227 | |||
228 | /* yrw801.c */ | ||
229 | int snd_yrw801_detect(opl4_t *opl4); | ||
230 | extern const opl4_region_ptr_t snd_yrw801_regions[]; | ||
231 | |||
232 | #endif /* __OPL4_LOCAL_H */ | ||
diff --git a/sound/drivers/opl4/opl4_mixer.c b/sound/drivers/opl4/opl4_mixer.c new file mode 100644 index 000000000000..ec7a228fbe7e --- /dev/null +++ b/sound/drivers/opl4/opl4_mixer.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * OPL4 mixer functions | ||
3 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
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, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include "opl4_local.h" | ||
21 | #include <sound/control.h> | ||
22 | |||
23 | static int snd_opl4_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
24 | { | ||
25 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
26 | uinfo->count = 2; | ||
27 | uinfo->value.integer.min = 0; | ||
28 | uinfo->value.integer.max = 7; | ||
29 | return 0; | ||
30 | } | ||
31 | |||
32 | static int snd_opl4_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
33 | { | ||
34 | opl4_t *opl4 = snd_kcontrol_chip(kcontrol); | ||
35 | unsigned long flags; | ||
36 | u8 reg = kcontrol->private_value; | ||
37 | u8 value; | ||
38 | |||
39 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
40 | value = snd_opl4_read(opl4, reg); | ||
41 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
42 | ucontrol->value.integer.value[0] = 7 - (value & 7); | ||
43 | ucontrol->value.integer.value[1] = 7 - ((value >> 3) & 7); | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static int snd_opl4_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
48 | { | ||
49 | opl4_t *opl4 = snd_kcontrol_chip(kcontrol); | ||
50 | unsigned long flags; | ||
51 | u8 reg = kcontrol->private_value; | ||
52 | u8 value, old_value; | ||
53 | |||
54 | value = (7 - (ucontrol->value.integer.value[0] & 7)) | | ||
55 | ((7 - (ucontrol->value.integer.value[1] & 7)) << 3); | ||
56 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
57 | old_value = snd_opl4_read(opl4, reg); | ||
58 | snd_opl4_write(opl4, reg, value); | ||
59 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
60 | return value != old_value; | ||
61 | } | ||
62 | |||
63 | static snd_kcontrol_new_t snd_opl4_controls[] = { | ||
64 | { | ||
65 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
66 | .name = "FM Playback Volume", | ||
67 | .info = snd_opl4_ctl_info, | ||
68 | .get = snd_opl4_ctl_get, | ||
69 | .put = snd_opl4_ctl_put, | ||
70 | .private_value = OPL4_REG_MIX_CONTROL_FM | ||
71 | }, | ||
72 | { | ||
73 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
74 | .name = "Wavetable Playback Volume", | ||
75 | .info = snd_opl4_ctl_info, | ||
76 | .get = snd_opl4_ctl_get, | ||
77 | .put = snd_opl4_ctl_put, | ||
78 | .private_value = OPL4_REG_MIX_CONTROL_PCM | ||
79 | } | ||
80 | }; | ||
81 | |||
82 | int snd_opl4_create_mixer(opl4_t *opl4) | ||
83 | { | ||
84 | snd_card_t *card = opl4->card; | ||
85 | int i, err; | ||
86 | |||
87 | strcat(card->mixername, ",OPL4"); | ||
88 | |||
89 | for (i = 0; i < 2; ++i) { | ||
90 | err = snd_ctl_add(card, snd_ctl_new1(&snd_opl4_controls[i], opl4)); | ||
91 | if (err < 0) | ||
92 | return err; | ||
93 | } | ||
94 | return 0; | ||
95 | } | ||
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c new file mode 100644 index 000000000000..6a1486258acf --- /dev/null +++ b/sound/drivers/opl4/opl4_proc.c | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * Functions for the OPL4 proc file | ||
3 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
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, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include "opl4_local.h" | ||
21 | #include <linux/vmalloc.h> | ||
22 | #include <sound/info.h> | ||
23 | |||
24 | #ifdef CONFIG_PROC_FS | ||
25 | |||
26 | static int snd_opl4_mem_proc_open(snd_info_entry_t *entry, | ||
27 | unsigned short mode, void **file_private_data) | ||
28 | { | ||
29 | opl4_t *opl4 = entry->private_data; | ||
30 | |||
31 | down(&opl4->access_mutex); | ||
32 | if (opl4->memory_access) { | ||
33 | up(&opl4->access_mutex); | ||
34 | return -EBUSY; | ||
35 | } | ||
36 | opl4->memory_access++; | ||
37 | up(&opl4->access_mutex); | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | static int snd_opl4_mem_proc_release(snd_info_entry_t *entry, | ||
42 | unsigned short mode, void *file_private_data) | ||
43 | { | ||
44 | opl4_t *opl4 = entry->private_data; | ||
45 | |||
46 | down(&opl4->access_mutex); | ||
47 | opl4->memory_access--; | ||
48 | up(&opl4->access_mutex); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static long snd_opl4_mem_proc_read(snd_info_entry_t *entry, void *file_private_data, | ||
53 | struct file *file, char __user *_buf, | ||
54 | unsigned long count, unsigned long pos) | ||
55 | { | ||
56 | opl4_t *opl4 = entry->private_data; | ||
57 | long size; | ||
58 | char* buf; | ||
59 | |||
60 | size = count; | ||
61 | if (pos + size > entry->size) | ||
62 | size = entry->size - pos; | ||
63 | if (size > 0) { | ||
64 | buf = vmalloc(size); | ||
65 | if (!buf) | ||
66 | return -ENOMEM; | ||
67 | snd_opl4_read_memory(opl4, buf, pos, size); | ||
68 | if (copy_to_user(_buf, buf, size)) { | ||
69 | vfree(buf); | ||
70 | return -EFAULT; | ||
71 | } | ||
72 | vfree(buf); | ||
73 | return size; | ||
74 | } | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static long snd_opl4_mem_proc_write(snd_info_entry_t *entry, void *file_private_data, | ||
79 | struct file *file, const char __user *_buf, | ||
80 | unsigned long count, unsigned long pos) | ||
81 | { | ||
82 | opl4_t *opl4 = entry->private_data; | ||
83 | long size; | ||
84 | char *buf; | ||
85 | |||
86 | size = count; | ||
87 | if (pos + size > entry->size) | ||
88 | size = entry->size - pos; | ||
89 | if (size > 0) { | ||
90 | buf = vmalloc(size); | ||
91 | if (!buf) | ||
92 | return -ENOMEM; | ||
93 | if (copy_from_user(buf, _buf, size)) { | ||
94 | vfree(buf); | ||
95 | return -EFAULT; | ||
96 | } | ||
97 | snd_opl4_write_memory(opl4, buf, pos, size); | ||
98 | vfree(buf); | ||
99 | return size; | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static long long snd_opl4_mem_proc_llseek(snd_info_entry_t *entry, void *file_private_data, | ||
105 | struct file *file, long long offset, int orig) | ||
106 | { | ||
107 | switch (orig) { | ||
108 | case 0: /* SEEK_SET */ | ||
109 | file->f_pos = offset; | ||
110 | break; | ||
111 | case 1: /* SEEK_CUR */ | ||
112 | file->f_pos += offset; | ||
113 | break; | ||
114 | case 2: /* SEEK_END, offset is negative */ | ||
115 | file->f_pos = entry->size + offset; | ||
116 | break; | ||
117 | default: | ||
118 | return -EINVAL; | ||
119 | } | ||
120 | if (file->f_pos > entry->size) | ||
121 | file->f_pos = entry->size; | ||
122 | return file->f_pos; | ||
123 | } | ||
124 | |||
125 | static struct snd_info_entry_ops snd_opl4_mem_proc_ops = { | ||
126 | .open = snd_opl4_mem_proc_open, | ||
127 | .release = snd_opl4_mem_proc_release, | ||
128 | .read = snd_opl4_mem_proc_read, | ||
129 | .write = snd_opl4_mem_proc_write, | ||
130 | .llseek = snd_opl4_mem_proc_llseek, | ||
131 | }; | ||
132 | |||
133 | int snd_opl4_create_proc(opl4_t *opl4) | ||
134 | { | ||
135 | snd_info_entry_t *entry; | ||
136 | |||
137 | entry = snd_info_create_card_entry(opl4->card, "opl4-mem", opl4->card->proc_root); | ||
138 | if (entry) { | ||
139 | if (opl4->hardware < OPL3_HW_OPL4_ML) { | ||
140 | /* OPL4 can access 4 MB external ROM/SRAM */ | ||
141 | entry->mode |= S_IWUSR; | ||
142 | entry->size = 4 * 1024 * 1024; | ||
143 | } else { | ||
144 | /* OPL4-ML has 1 MB internal ROM */ | ||
145 | entry->size = 1 * 1024 * 1024; | ||
146 | } | ||
147 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
148 | entry->c.ops = &snd_opl4_mem_proc_ops; | ||
149 | entry->module = THIS_MODULE; | ||
150 | entry->private_data = opl4; | ||
151 | if (snd_info_register(entry) < 0) { | ||
152 | snd_info_free_entry(entry); | ||
153 | entry = NULL; | ||
154 | } | ||
155 | } | ||
156 | opl4->proc_entry = entry; | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | void snd_opl4_free_proc(opl4_t *opl4) | ||
161 | { | ||
162 | if (opl4->proc_entry) | ||
163 | snd_info_unregister(opl4->proc_entry); | ||
164 | } | ||
165 | |||
166 | #endif /* CONFIG_PROC_FS */ | ||
diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c new file mode 100644 index 000000000000..958dfe88479a --- /dev/null +++ b/sound/drivers/opl4/opl4_seq.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * OPL4 sequencer functions | ||
3 | * | ||
4 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions, and the following disclaimer, | ||
12 | * without modification. | ||
13 | * 2. The name of the author may not be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * Alternatively, this software may be distributed and/or modified under the | ||
17 | * terms of the GNU General Public License as published by the Free Software | ||
18 | * Foundation; either version 2 of the License, or (at your option) any later | ||
19 | * version. | ||
20 | * | ||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | ||
25 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
31 | * SUCH DAMAGE. | ||
32 | */ | ||
33 | |||
34 | #include "opl4_local.h" | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <sound/initval.h> | ||
38 | |||
39 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | ||
40 | MODULE_DESCRIPTION("OPL4 wavetable synth driver"); | ||
41 | MODULE_LICENSE("Dual BSD/GPL"); | ||
42 | |||
43 | int volume_boost = 8; | ||
44 | |||
45 | module_param(volume_boost, int, 0644); | ||
46 | MODULE_PARM_DESC(volume_boost, "Additional volume for OPL4 wavetable sounds."); | ||
47 | |||
48 | static int snd_opl4_seq_use_inc(opl4_t *opl4) | ||
49 | { | ||
50 | if (!try_module_get(opl4->card->module)) | ||
51 | return -EFAULT; | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static void snd_opl4_seq_use_dec(opl4_t *opl4) | ||
56 | { | ||
57 | module_put(opl4->card->module); | ||
58 | } | ||
59 | |||
60 | static int snd_opl4_seq_use(void *private_data, snd_seq_port_subscribe_t *info) | ||
61 | { | ||
62 | opl4_t *opl4 = private_data; | ||
63 | int err; | ||
64 | |||
65 | down(&opl4->access_mutex); | ||
66 | |||
67 | if (opl4->used) { | ||
68 | up(&opl4->access_mutex); | ||
69 | return -EBUSY; | ||
70 | } | ||
71 | opl4->used++; | ||
72 | |||
73 | if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) { | ||
74 | err = snd_opl4_seq_use_inc(opl4); | ||
75 | if (err < 0) { | ||
76 | up(&opl4->access_mutex); | ||
77 | return err; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | up(&opl4->access_mutex); | ||
82 | |||
83 | snd_opl4_synth_reset(opl4); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int snd_opl4_seq_unuse(void *private_data, snd_seq_port_subscribe_t *info) | ||
88 | { | ||
89 | opl4_t *opl4 = private_data; | ||
90 | |||
91 | snd_opl4_synth_shutdown(opl4); | ||
92 | |||
93 | down(&opl4->access_mutex); | ||
94 | opl4->used--; | ||
95 | up(&opl4->access_mutex); | ||
96 | |||
97 | if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) | ||
98 | snd_opl4_seq_use_dec(opl4); | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static snd_midi_op_t opl4_ops = { | ||
103 | .note_on = snd_opl4_note_on, | ||
104 | .note_off = snd_opl4_note_off, | ||
105 | .note_terminate = snd_opl4_terminate_note, | ||
106 | .control = snd_opl4_control, | ||
107 | .sysex = snd_opl4_sysex, | ||
108 | }; | ||
109 | |||
110 | static int snd_opl4_seq_event_input(snd_seq_event_t *ev, int direct, | ||
111 | void *private_data, int atomic, int hop) | ||
112 | { | ||
113 | opl4_t *opl4 = private_data; | ||
114 | |||
115 | snd_midi_process_event(&opl4_ops, ev, opl4->chset); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static void snd_opl4_seq_free_port(void *private_data) | ||
120 | { | ||
121 | opl4_t *opl4 = private_data; | ||
122 | |||
123 | snd_midi_channel_free_set(opl4->chset); | ||
124 | } | ||
125 | |||
126 | static int snd_opl4_seq_new_device(snd_seq_device_t *dev) | ||
127 | { | ||
128 | opl4_t *opl4; | ||
129 | int client; | ||
130 | snd_seq_client_callback_t callbacks; | ||
131 | snd_seq_client_info_t cinfo; | ||
132 | snd_seq_port_callback_t pcallbacks; | ||
133 | |||
134 | opl4 = *(opl4_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
135 | if (!opl4) | ||
136 | return -EINVAL; | ||
137 | |||
138 | if (snd_yrw801_detect(opl4) < 0) | ||
139 | return -ENODEV; | ||
140 | |||
141 | opl4->chset = snd_midi_channel_alloc_set(16); | ||
142 | if (!opl4->chset) | ||
143 | return -ENOMEM; | ||
144 | opl4->chset->private_data = opl4; | ||
145 | |||
146 | /* allocate new client */ | ||
147 | memset(&callbacks, 0, sizeof(callbacks)); | ||
148 | callbacks.private_data = opl4; | ||
149 | callbacks.allow_output = callbacks.allow_input = 1; | ||
150 | client = snd_seq_create_kernel_client(opl4->card, opl4->seq_dev_num, &callbacks); | ||
151 | if (client < 0) { | ||
152 | snd_midi_channel_free_set(opl4->chset); | ||
153 | return client; | ||
154 | } | ||
155 | opl4->seq_client = client; | ||
156 | opl4->chset->client = client; | ||
157 | |||
158 | /* change name of client */ | ||
159 | memset(&cinfo, 0, sizeof(cinfo)); | ||
160 | cinfo.client = client; | ||
161 | cinfo.type = KERNEL_CLIENT; | ||
162 | strcpy(cinfo.name, "OPL4 Wavetable"); | ||
163 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); | ||
164 | |||
165 | /* create new port */ | ||
166 | memset(&pcallbacks, 0, sizeof(pcallbacks)); | ||
167 | pcallbacks.owner = THIS_MODULE; | ||
168 | pcallbacks.use = snd_opl4_seq_use; | ||
169 | pcallbacks.unuse = snd_opl4_seq_unuse; | ||
170 | pcallbacks.event_input = snd_opl4_seq_event_input; | ||
171 | pcallbacks.private_free = snd_opl4_seq_free_port; | ||
172 | pcallbacks.private_data = opl4; | ||
173 | |||
174 | opl4->chset->port = snd_seq_event_port_attach(client, &pcallbacks, | ||
175 | SNDRV_SEQ_PORT_CAP_WRITE | | ||
176 | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, | ||
177 | SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | | ||
178 | SNDRV_SEQ_PORT_TYPE_MIDI_GM, | ||
179 | 16, 24, | ||
180 | "OPL4 Wavetable Port"); | ||
181 | if (opl4->chset->port < 0) { | ||
182 | int err = opl4->chset->port; | ||
183 | snd_midi_channel_free_set(opl4->chset); | ||
184 | snd_seq_delete_kernel_client(client); | ||
185 | opl4->seq_client = -1; | ||
186 | return err; | ||
187 | } | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int snd_opl4_seq_delete_device(snd_seq_device_t *dev) | ||
192 | { | ||
193 | opl4_t *opl4; | ||
194 | |||
195 | opl4 = *(opl4_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
196 | if (!opl4) | ||
197 | return -EINVAL; | ||
198 | |||
199 | if (opl4->seq_client >= 0) { | ||
200 | snd_seq_delete_kernel_client(opl4->seq_client); | ||
201 | opl4->seq_client = -1; | ||
202 | } | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int __init alsa_opl4_synth_init(void) | ||
207 | { | ||
208 | static snd_seq_dev_ops_t ops = { | ||
209 | snd_opl4_seq_new_device, | ||
210 | snd_opl4_seq_delete_device | ||
211 | }; | ||
212 | |||
213 | return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops, | ||
214 | sizeof(opl4_t*)); | ||
215 | } | ||
216 | |||
217 | static void __exit alsa_opl4_synth_exit(void) | ||
218 | { | ||
219 | snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4); | ||
220 | } | ||
221 | |||
222 | module_init(alsa_opl4_synth_init) | ||
223 | module_exit(alsa_opl4_synth_exit) | ||
diff --git a/sound/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c new file mode 100644 index 000000000000..b146a1c995d9 --- /dev/null +++ b/sound/drivers/opl4/opl4_synth.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * OPL4 MIDI synthesizer functions | ||
3 | * | ||
4 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions, and the following disclaimer, | ||
12 | * without modification. | ||
13 | * 2. The name of the author may not be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * Alternatively, this software may be distributed and/or modified under the | ||
17 | * terms of the GNU General Public License as published by the Free Software | ||
18 | * Foundation; either version 2 of the License, or (at your option) any later | ||
19 | * version. | ||
20 | * | ||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | ||
25 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
31 | * SUCH DAMAGE. | ||
32 | */ | ||
33 | |||
34 | #include "opl4_local.h" | ||
35 | #include <linux/delay.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <sound/asoundef.h> | ||
38 | |||
39 | /* GM2 controllers */ | ||
40 | #ifndef MIDI_CTL_RELEASE_TIME | ||
41 | #define MIDI_CTL_RELEASE_TIME 0x48 | ||
42 | #define MIDI_CTL_ATTACK_TIME 0x49 | ||
43 | #define MIDI_CTL_DECAY_TIME 0x4b | ||
44 | #define MIDI_CTL_VIBRATO_RATE 0x4c | ||
45 | #define MIDI_CTL_VIBRATO_DEPTH 0x4d | ||
46 | #define MIDI_CTL_VIBRATO_DELAY 0x4e | ||
47 | #endif | ||
48 | |||
49 | /* | ||
50 | * This table maps 100/128 cents to F_NUMBER. | ||
51 | */ | ||
52 | static const s16 snd_opl4_pitch_map[0x600] = { | ||
53 | 0x000,0x000,0x001,0x001,0x002,0x002,0x003,0x003, | ||
54 | 0x004,0x004,0x005,0x005,0x006,0x006,0x006,0x007, | ||
55 | 0x007,0x008,0x008,0x009,0x009,0x00a,0x00a,0x00b, | ||
56 | 0x00b,0x00c,0x00c,0x00d,0x00d,0x00d,0x00e,0x00e, | ||
57 | 0x00f,0x00f,0x010,0x010,0x011,0x011,0x012,0x012, | ||
58 | 0x013,0x013,0x014,0x014,0x015,0x015,0x015,0x016, | ||
59 | 0x016,0x017,0x017,0x018,0x018,0x019,0x019,0x01a, | ||
60 | 0x01a,0x01b,0x01b,0x01c,0x01c,0x01d,0x01d,0x01e, | ||
61 | 0x01e,0x01e,0x01f,0x01f,0x020,0x020,0x021,0x021, | ||
62 | 0x022,0x022,0x023,0x023,0x024,0x024,0x025,0x025, | ||
63 | 0x026,0x026,0x027,0x027,0x028,0x028,0x029,0x029, | ||
64 | 0x029,0x02a,0x02a,0x02b,0x02b,0x02c,0x02c,0x02d, | ||
65 | 0x02d,0x02e,0x02e,0x02f,0x02f,0x030,0x030,0x031, | ||
66 | 0x031,0x032,0x032,0x033,0x033,0x034,0x034,0x035, | ||
67 | 0x035,0x036,0x036,0x037,0x037,0x038,0x038,0x038, | ||
68 | 0x039,0x039,0x03a,0x03a,0x03b,0x03b,0x03c,0x03c, | ||
69 | 0x03d,0x03d,0x03e,0x03e,0x03f,0x03f,0x040,0x040, | ||
70 | 0x041,0x041,0x042,0x042,0x043,0x043,0x044,0x044, | ||
71 | 0x045,0x045,0x046,0x046,0x047,0x047,0x048,0x048, | ||
72 | 0x049,0x049,0x04a,0x04a,0x04b,0x04b,0x04c,0x04c, | ||
73 | 0x04d,0x04d,0x04e,0x04e,0x04f,0x04f,0x050,0x050, | ||
74 | 0x051,0x051,0x052,0x052,0x053,0x053,0x054,0x054, | ||
75 | 0x055,0x055,0x056,0x056,0x057,0x057,0x058,0x058, | ||
76 | 0x059,0x059,0x05a,0x05a,0x05b,0x05b,0x05c,0x05c, | ||
77 | 0x05d,0x05d,0x05e,0x05e,0x05f,0x05f,0x060,0x060, | ||
78 | 0x061,0x061,0x062,0x062,0x063,0x063,0x064,0x064, | ||
79 | 0x065,0x065,0x066,0x066,0x067,0x067,0x068,0x068, | ||
80 | 0x069,0x069,0x06a,0x06a,0x06b,0x06b,0x06c,0x06c, | ||
81 | 0x06d,0x06d,0x06e,0x06e,0x06f,0x06f,0x070,0x071, | ||
82 | 0x071,0x072,0x072,0x073,0x073,0x074,0x074,0x075, | ||
83 | 0x075,0x076,0x076,0x077,0x077,0x078,0x078,0x079, | ||
84 | 0x079,0x07a,0x07a,0x07b,0x07b,0x07c,0x07c,0x07d, | ||
85 | 0x07d,0x07e,0x07e,0x07f,0x07f,0x080,0x081,0x081, | ||
86 | 0x082,0x082,0x083,0x083,0x084,0x084,0x085,0x085, | ||
87 | 0x086,0x086,0x087,0x087,0x088,0x088,0x089,0x089, | ||
88 | 0x08a,0x08a,0x08b,0x08b,0x08c,0x08d,0x08d,0x08e, | ||
89 | 0x08e,0x08f,0x08f,0x090,0x090,0x091,0x091,0x092, | ||
90 | 0x092,0x093,0x093,0x094,0x094,0x095,0x096,0x096, | ||
91 | 0x097,0x097,0x098,0x098,0x099,0x099,0x09a,0x09a, | ||
92 | 0x09b,0x09b,0x09c,0x09c,0x09d,0x09d,0x09e,0x09f, | ||
93 | 0x09f,0x0a0,0x0a0,0x0a1,0x0a1,0x0a2,0x0a2,0x0a3, | ||
94 | 0x0a3,0x0a4,0x0a4,0x0a5,0x0a6,0x0a6,0x0a7,0x0a7, | ||
95 | 0x0a8,0x0a8,0x0a9,0x0a9,0x0aa,0x0aa,0x0ab,0x0ab, | ||
96 | 0x0ac,0x0ad,0x0ad,0x0ae,0x0ae,0x0af,0x0af,0x0b0, | ||
97 | 0x0b0,0x0b1,0x0b1,0x0b2,0x0b2,0x0b3,0x0b4,0x0b4, | ||
98 | 0x0b5,0x0b5,0x0b6,0x0b6,0x0b7,0x0b7,0x0b8,0x0b8, | ||
99 | 0x0b9,0x0ba,0x0ba,0x0bb,0x0bb,0x0bc,0x0bc,0x0bd, | ||
100 | 0x0bd,0x0be,0x0be,0x0bf,0x0c0,0x0c0,0x0c1,0x0c1, | ||
101 | 0x0c2,0x0c2,0x0c3,0x0c3,0x0c4,0x0c4,0x0c5,0x0c6, | ||
102 | 0x0c6,0x0c7,0x0c7,0x0c8,0x0c8,0x0c9,0x0c9,0x0ca, | ||
103 | 0x0cb,0x0cb,0x0cc,0x0cc,0x0cd,0x0cd,0x0ce,0x0ce, | ||
104 | 0x0cf,0x0d0,0x0d0,0x0d1,0x0d1,0x0d2,0x0d2,0x0d3, | ||
105 | 0x0d3,0x0d4,0x0d5,0x0d5,0x0d6,0x0d6,0x0d7,0x0d7, | ||
106 | 0x0d8,0x0d8,0x0d9,0x0da,0x0da,0x0db,0x0db,0x0dc, | ||
107 | 0x0dc,0x0dd,0x0de,0x0de,0x0df,0x0df,0x0e0,0x0e0, | ||
108 | 0x0e1,0x0e1,0x0e2,0x0e3,0x0e3,0x0e4,0x0e4,0x0e5, | ||
109 | 0x0e5,0x0e6,0x0e7,0x0e7,0x0e8,0x0e8,0x0e9,0x0e9, | ||
110 | 0x0ea,0x0eb,0x0eb,0x0ec,0x0ec,0x0ed,0x0ed,0x0ee, | ||
111 | 0x0ef,0x0ef,0x0f0,0x0f0,0x0f1,0x0f1,0x0f2,0x0f3, | ||
112 | 0x0f3,0x0f4,0x0f4,0x0f5,0x0f5,0x0f6,0x0f7,0x0f7, | ||
113 | 0x0f8,0x0f8,0x0f9,0x0f9,0x0fa,0x0fb,0x0fb,0x0fc, | ||
114 | 0x0fc,0x0fd,0x0fd,0x0fe,0x0ff,0x0ff,0x100,0x100, | ||
115 | 0x101,0x101,0x102,0x103,0x103,0x104,0x104,0x105, | ||
116 | 0x106,0x106,0x107,0x107,0x108,0x108,0x109,0x10a, | ||
117 | 0x10a,0x10b,0x10b,0x10c,0x10c,0x10d,0x10e,0x10e, | ||
118 | 0x10f,0x10f,0x110,0x111,0x111,0x112,0x112,0x113, | ||
119 | 0x114,0x114,0x115,0x115,0x116,0x116,0x117,0x118, | ||
120 | 0x118,0x119,0x119,0x11a,0x11b,0x11b,0x11c,0x11c, | ||
121 | 0x11d,0x11e,0x11e,0x11f,0x11f,0x120,0x120,0x121, | ||
122 | 0x122,0x122,0x123,0x123,0x124,0x125,0x125,0x126, | ||
123 | 0x126,0x127,0x128,0x128,0x129,0x129,0x12a,0x12b, | ||
124 | 0x12b,0x12c,0x12c,0x12d,0x12e,0x12e,0x12f,0x12f, | ||
125 | 0x130,0x131,0x131,0x132,0x132,0x133,0x134,0x134, | ||
126 | 0x135,0x135,0x136,0x137,0x137,0x138,0x138,0x139, | ||
127 | 0x13a,0x13a,0x13b,0x13b,0x13c,0x13d,0x13d,0x13e, | ||
128 | 0x13e,0x13f,0x140,0x140,0x141,0x141,0x142,0x143, | ||
129 | 0x143,0x144,0x144,0x145,0x146,0x146,0x147,0x148, | ||
130 | 0x148,0x149,0x149,0x14a,0x14b,0x14b,0x14c,0x14c, | ||
131 | 0x14d,0x14e,0x14e,0x14f,0x14f,0x150,0x151,0x151, | ||
132 | 0x152,0x153,0x153,0x154,0x154,0x155,0x156,0x156, | ||
133 | 0x157,0x157,0x158,0x159,0x159,0x15a,0x15b,0x15b, | ||
134 | 0x15c,0x15c,0x15d,0x15e,0x15e,0x15f,0x160,0x160, | ||
135 | 0x161,0x161,0x162,0x163,0x163,0x164,0x165,0x165, | ||
136 | 0x166,0x166,0x167,0x168,0x168,0x169,0x16a,0x16a, | ||
137 | 0x16b,0x16b,0x16c,0x16d,0x16d,0x16e,0x16f,0x16f, | ||
138 | 0x170,0x170,0x171,0x172,0x172,0x173,0x174,0x174, | ||
139 | 0x175,0x175,0x176,0x177,0x177,0x178,0x179,0x179, | ||
140 | 0x17a,0x17a,0x17b,0x17c,0x17c,0x17d,0x17e,0x17e, | ||
141 | 0x17f,0x180,0x180,0x181,0x181,0x182,0x183,0x183, | ||
142 | 0x184,0x185,0x185,0x186,0x187,0x187,0x188,0x188, | ||
143 | 0x189,0x18a,0x18a,0x18b,0x18c,0x18c,0x18d,0x18e, | ||
144 | 0x18e,0x18f,0x190,0x190,0x191,0x191,0x192,0x193, | ||
145 | 0x193,0x194,0x195,0x195,0x196,0x197,0x197,0x198, | ||
146 | 0x199,0x199,0x19a,0x19a,0x19b,0x19c,0x19c,0x19d, | ||
147 | 0x19e,0x19e,0x19f,0x1a0,0x1a0,0x1a1,0x1a2,0x1a2, | ||
148 | 0x1a3,0x1a4,0x1a4,0x1a5,0x1a6,0x1a6,0x1a7,0x1a8, | ||
149 | 0x1a8,0x1a9,0x1a9,0x1aa,0x1ab,0x1ab,0x1ac,0x1ad, | ||
150 | 0x1ad,0x1ae,0x1af,0x1af,0x1b0,0x1b1,0x1b1,0x1b2, | ||
151 | 0x1b3,0x1b3,0x1b4,0x1b5,0x1b5,0x1b6,0x1b7,0x1b7, | ||
152 | 0x1b8,0x1b9,0x1b9,0x1ba,0x1bb,0x1bb,0x1bc,0x1bd, | ||
153 | 0x1bd,0x1be,0x1bf,0x1bf,0x1c0,0x1c1,0x1c1,0x1c2, | ||
154 | 0x1c3,0x1c3,0x1c4,0x1c5,0x1c5,0x1c6,0x1c7,0x1c7, | ||
155 | 0x1c8,0x1c9,0x1c9,0x1ca,0x1cb,0x1cb,0x1cc,0x1cd, | ||
156 | 0x1cd,0x1ce,0x1cf,0x1cf,0x1d0,0x1d1,0x1d1,0x1d2, | ||
157 | 0x1d3,0x1d3,0x1d4,0x1d5,0x1d5,0x1d6,0x1d7,0x1d7, | ||
158 | 0x1d8,0x1d9,0x1d9,0x1da,0x1db,0x1db,0x1dc,0x1dd, | ||
159 | 0x1dd,0x1de,0x1df,0x1df,0x1e0,0x1e1,0x1e1,0x1e2, | ||
160 | 0x1e3,0x1e4,0x1e4,0x1e5,0x1e6,0x1e6,0x1e7,0x1e8, | ||
161 | 0x1e8,0x1e9,0x1ea,0x1ea,0x1eb,0x1ec,0x1ec,0x1ed, | ||
162 | 0x1ee,0x1ee,0x1ef,0x1f0,0x1f0,0x1f1,0x1f2,0x1f3, | ||
163 | 0x1f3,0x1f4,0x1f5,0x1f5,0x1f6,0x1f7,0x1f7,0x1f8, | ||
164 | 0x1f9,0x1f9,0x1fa,0x1fb,0x1fb,0x1fc,0x1fd,0x1fe, | ||
165 | 0x1fe,0x1ff,0x200,0x200,0x201,0x202,0x202,0x203, | ||
166 | 0x204,0x205,0x205,0x206,0x207,0x207,0x208,0x209, | ||
167 | 0x209,0x20a,0x20b,0x20b,0x20c,0x20d,0x20e,0x20e, | ||
168 | 0x20f,0x210,0x210,0x211,0x212,0x212,0x213,0x214, | ||
169 | 0x215,0x215,0x216,0x217,0x217,0x218,0x219,0x21a, | ||
170 | 0x21a,0x21b,0x21c,0x21c,0x21d,0x21e,0x21e,0x21f, | ||
171 | 0x220,0x221,0x221,0x222,0x223,0x223,0x224,0x225, | ||
172 | 0x226,0x226,0x227,0x228,0x228,0x229,0x22a,0x22b, | ||
173 | 0x22b,0x22c,0x22d,0x22d,0x22e,0x22f,0x230,0x230, | ||
174 | 0x231,0x232,0x232,0x233,0x234,0x235,0x235,0x236, | ||
175 | 0x237,0x237,0x238,0x239,0x23a,0x23a,0x23b,0x23c, | ||
176 | 0x23c,0x23d,0x23e,0x23f,0x23f,0x240,0x241,0x241, | ||
177 | 0x242,0x243,0x244,0x244,0x245,0x246,0x247,0x247, | ||
178 | 0x248,0x249,0x249,0x24a,0x24b,0x24c,0x24c,0x24d, | ||
179 | 0x24e,0x24f,0x24f,0x250,0x251,0x251,0x252,0x253, | ||
180 | 0x254,0x254,0x255,0x256,0x257,0x257,0x258,0x259, | ||
181 | 0x259,0x25a,0x25b,0x25c,0x25c,0x25d,0x25e,0x25f, | ||
182 | 0x25f,0x260,0x261,0x262,0x262,0x263,0x264,0x265, | ||
183 | 0x265,0x266,0x267,0x267,0x268,0x269,0x26a,0x26a, | ||
184 | 0x26b,0x26c,0x26d,0x26d,0x26e,0x26f,0x270,0x270, | ||
185 | 0x271,0x272,0x273,0x273,0x274,0x275,0x276,0x276, | ||
186 | 0x277,0x278,0x279,0x279,0x27a,0x27b,0x27c,0x27c, | ||
187 | 0x27d,0x27e,0x27f,0x27f,0x280,0x281,0x282,0x282, | ||
188 | 0x283,0x284,0x285,0x285,0x286,0x287,0x288,0x288, | ||
189 | 0x289,0x28a,0x28b,0x28b,0x28c,0x28d,0x28e,0x28e, | ||
190 | 0x28f,0x290,0x291,0x291,0x292,0x293,0x294,0x294, | ||
191 | 0x295,0x296,0x297,0x298,0x298,0x299,0x29a,0x29b, | ||
192 | 0x29b,0x29c,0x29d,0x29e,0x29e,0x29f,0x2a0,0x2a1, | ||
193 | 0x2a1,0x2a2,0x2a3,0x2a4,0x2a5,0x2a5,0x2a6,0x2a7, | ||
194 | 0x2a8,0x2a8,0x2a9,0x2aa,0x2ab,0x2ab,0x2ac,0x2ad, | ||
195 | 0x2ae,0x2af,0x2af,0x2b0,0x2b1,0x2b2,0x2b2,0x2b3, | ||
196 | 0x2b4,0x2b5,0x2b5,0x2b6,0x2b7,0x2b8,0x2b9,0x2b9, | ||
197 | 0x2ba,0x2bb,0x2bc,0x2bc,0x2bd,0x2be,0x2bf,0x2c0, | ||
198 | 0x2c0,0x2c1,0x2c2,0x2c3,0x2c4,0x2c4,0x2c5,0x2c6, | ||
199 | 0x2c7,0x2c7,0x2c8,0x2c9,0x2ca,0x2cb,0x2cb,0x2cc, | ||
200 | 0x2cd,0x2ce,0x2ce,0x2cf,0x2d0,0x2d1,0x2d2,0x2d2, | ||
201 | 0x2d3,0x2d4,0x2d5,0x2d6,0x2d6,0x2d7,0x2d8,0x2d9, | ||
202 | 0x2da,0x2da,0x2db,0x2dc,0x2dd,0x2dd,0x2de,0x2df, | ||
203 | 0x2e0,0x2e1,0x2e1,0x2e2,0x2e3,0x2e4,0x2e5,0x2e5, | ||
204 | 0x2e6,0x2e7,0x2e8,0x2e9,0x2e9,0x2ea,0x2eb,0x2ec, | ||
205 | 0x2ed,0x2ed,0x2ee,0x2ef,0x2f0,0x2f1,0x2f1,0x2f2, | ||
206 | 0x2f3,0x2f4,0x2f5,0x2f5,0x2f6,0x2f7,0x2f8,0x2f9, | ||
207 | 0x2f9,0x2fa,0x2fb,0x2fc,0x2fd,0x2fd,0x2fe,0x2ff, | ||
208 | 0x300,0x301,0x302,0x302,0x303,0x304,0x305,0x306, | ||
209 | 0x306,0x307,0x308,0x309,0x30a,0x30a,0x30b,0x30c, | ||
210 | 0x30d,0x30e,0x30f,0x30f,0x310,0x311,0x312,0x313, | ||
211 | 0x313,0x314,0x315,0x316,0x317,0x318,0x318,0x319, | ||
212 | 0x31a,0x31b,0x31c,0x31c,0x31d,0x31e,0x31f,0x320, | ||
213 | 0x321,0x321,0x322,0x323,0x324,0x325,0x326,0x326, | ||
214 | 0x327,0x328,0x329,0x32a,0x32a,0x32b,0x32c,0x32d, | ||
215 | 0x32e,0x32f,0x32f,0x330,0x331,0x332,0x333,0x334, | ||
216 | 0x334,0x335,0x336,0x337,0x338,0x339,0x339,0x33a, | ||
217 | 0x33b,0x33c,0x33d,0x33e,0x33e,0x33f,0x340,0x341, | ||
218 | 0x342,0x343,0x343,0x344,0x345,0x346,0x347,0x348, | ||
219 | 0x349,0x349,0x34a,0x34b,0x34c,0x34d,0x34e,0x34e, | ||
220 | 0x34f,0x350,0x351,0x352,0x353,0x353,0x354,0x355, | ||
221 | 0x356,0x357,0x358,0x359,0x359,0x35a,0x35b,0x35c, | ||
222 | 0x35d,0x35e,0x35f,0x35f,0x360,0x361,0x362,0x363, | ||
223 | 0x364,0x364,0x365,0x366,0x367,0x368,0x369,0x36a, | ||
224 | 0x36a,0x36b,0x36c,0x36d,0x36e,0x36f,0x370,0x370, | ||
225 | 0x371,0x372,0x373,0x374,0x375,0x376,0x377,0x377, | ||
226 | 0x378,0x379,0x37a,0x37b,0x37c,0x37d,0x37d,0x37e, | ||
227 | 0x37f,0x380,0x381,0x382,0x383,0x383,0x384,0x385, | ||
228 | 0x386,0x387,0x388,0x389,0x38a,0x38a,0x38b,0x38c, | ||
229 | 0x38d,0x38e,0x38f,0x390,0x391,0x391,0x392,0x393, | ||
230 | 0x394,0x395,0x396,0x397,0x398,0x398,0x399,0x39a, | ||
231 | 0x39b,0x39c,0x39d,0x39e,0x39f,0x39f,0x3a0,0x3a1, | ||
232 | 0x3a2,0x3a3,0x3a4,0x3a5,0x3a6,0x3a7,0x3a7,0x3a8, | ||
233 | 0x3a9,0x3aa,0x3ab,0x3ac,0x3ad,0x3ae,0x3ae,0x3af, | ||
234 | 0x3b0,0x3b1,0x3b2,0x3b3,0x3b4,0x3b5,0x3b6,0x3b6, | ||
235 | 0x3b7,0x3b8,0x3b9,0x3ba,0x3bb,0x3bc,0x3bd,0x3be, | ||
236 | 0x3bf,0x3bf,0x3c0,0x3c1,0x3c2,0x3c3,0x3c4,0x3c5, | ||
237 | 0x3c6,0x3c7,0x3c7,0x3c8,0x3c9,0x3ca,0x3cb,0x3cc, | ||
238 | 0x3cd,0x3ce,0x3cf,0x3d0,0x3d1,0x3d1,0x3d2,0x3d3, | ||
239 | 0x3d4,0x3d5,0x3d6,0x3d7,0x3d8,0x3d9,0x3da,0x3da, | ||
240 | 0x3db,0x3dc,0x3dd,0x3de,0x3df,0x3e0,0x3e1,0x3e2, | ||
241 | 0x3e3,0x3e4,0x3e4,0x3e5,0x3e6,0x3e7,0x3e8,0x3e9, | ||
242 | 0x3ea,0x3eb,0x3ec,0x3ed,0x3ee,0x3ef,0x3ef,0x3f0, | ||
243 | 0x3f1,0x3f2,0x3f3,0x3f4,0x3f5,0x3f6,0x3f7,0x3f8, | ||
244 | 0x3f9,0x3fa,0x3fa,0x3fb,0x3fc,0x3fd,0x3fe,0x3ff | ||
245 | }; | ||
246 | |||
247 | /* | ||
248 | * Attenuation according to GM recommendations, in -0.375 dB units. | ||
249 | * table[v] = 40 * log(v / 127) / -0.375 | ||
250 | */ | ||
251 | static unsigned char snd_opl4_volume_table[128] = { | ||
252 | 255,224,192,173,160,150,141,134, | ||
253 | 128,122,117,113,109,105,102, 99, | ||
254 | 96, 93, 90, 88, 85, 83, 81, 79, | ||
255 | 77, 75, 73, 71, 70, 68, 67, 65, | ||
256 | 64, 62, 61, 59, 58, 57, 56, 54, | ||
257 | 53, 52, 51, 50, 49, 48, 47, 46, | ||
258 | 45, 44, 43, 42, 41, 40, 39, 39, | ||
259 | 38, 37, 36, 35, 34, 34, 33, 32, | ||
260 | 31, 31, 30, 29, 29, 28, 27, 27, | ||
261 | 26, 25, 25, 24, 24, 23, 22, 22, | ||
262 | 21, 21, 20, 19, 19, 18, 18, 17, | ||
263 | 17, 16, 16, 15, 15, 14, 14, 13, | ||
264 | 13, 12, 12, 11, 11, 10, 10, 9, | ||
265 | 9, 9, 8, 8, 7, 7, 6, 6, | ||
266 | 6, 5, 5, 4, 4, 4, 3, 3, | ||
267 | 2, 2, 2, 1, 1, 0, 0, 0 | ||
268 | }; | ||
269 | |||
270 | /* | ||
271 | * Initializes all voices. | ||
272 | */ | ||
273 | void snd_opl4_synth_reset(opl4_t *opl4) | ||
274 | { | ||
275 | unsigned long flags; | ||
276 | int i; | ||
277 | |||
278 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
279 | for (i = 0; i < OPL4_MAX_VOICES; i++) | ||
280 | snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT); | ||
281 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
282 | |||
283 | INIT_LIST_HEAD(&opl4->off_voices); | ||
284 | INIT_LIST_HEAD(&opl4->on_voices); | ||
285 | memset(opl4->voices, 0, sizeof(opl4->voices)); | ||
286 | for (i = 0; i < OPL4_MAX_VOICES; i++) { | ||
287 | opl4->voices[i].number = i; | ||
288 | list_add_tail(&opl4->voices[i].list, &opl4->off_voices); | ||
289 | } | ||
290 | |||
291 | snd_midi_channel_set_clear(opl4->chset); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Shuts down all voices. | ||
296 | */ | ||
297 | void snd_opl4_synth_shutdown(opl4_t *opl4) | ||
298 | { | ||
299 | unsigned long flags; | ||
300 | int i; | ||
301 | |||
302 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
303 | for (i = 0; i < OPL4_MAX_VOICES; i++) | ||
304 | snd_opl4_write(opl4, OPL4_REG_MISC + i, | ||
305 | opl4->voices[i].reg_misc & ~OPL4_KEY_ON_BIT); | ||
306 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Executes the callback for all voices playing the specified note. | ||
311 | */ | ||
312 | static void snd_opl4_do_for_note(opl4_t *opl4, int note, snd_midi_channel_t *chan, | ||
313 | void (*func)(opl4_t *opl4, opl4_voice_t *voice)) | ||
314 | { | ||
315 | int i; | ||
316 | unsigned long flags; | ||
317 | opl4_voice_t *voice; | ||
318 | |||
319 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
320 | for (i = 0; i < OPL4_MAX_VOICES; i++) { | ||
321 | voice = &opl4->voices[i]; | ||
322 | if (voice->chan == chan && voice->note == note) { | ||
323 | func(opl4, voice); | ||
324 | } | ||
325 | } | ||
326 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Executes the callback for all voices of to the specified channel. | ||
331 | */ | ||
332 | static void snd_opl4_do_for_channel(opl4_t *opl4, snd_midi_channel_t *chan, | ||
333 | void (*func)(opl4_t *opl4, opl4_voice_t *voice)) | ||
334 | { | ||
335 | int i; | ||
336 | unsigned long flags; | ||
337 | opl4_voice_t *voice; | ||
338 | |||
339 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
340 | for (i = 0; i < OPL4_MAX_VOICES; i++) { | ||
341 | voice = &opl4->voices[i]; | ||
342 | if (voice->chan == chan) { | ||
343 | func(opl4, voice); | ||
344 | } | ||
345 | } | ||
346 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * Executes the callback for all active voices. | ||
351 | */ | ||
352 | static void snd_opl4_do_for_all(opl4_t *opl4, | ||
353 | void (*func)(opl4_t *opl4, opl4_voice_t *voice)) | ||
354 | { | ||
355 | int i; | ||
356 | unsigned long flags; | ||
357 | opl4_voice_t *voice; | ||
358 | |||
359 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
360 | for (i = 0; i < OPL4_MAX_VOICES; i++) { | ||
361 | voice = &opl4->voices[i]; | ||
362 | if (voice->chan) | ||
363 | func(opl4, voice); | ||
364 | } | ||
365 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
366 | } | ||
367 | |||
368 | static void snd_opl4_update_volume(opl4_t *opl4, opl4_voice_t *voice) | ||
369 | { | ||
370 | int att; | ||
371 | |||
372 | att = voice->sound->tone_attenuate; | ||
373 | att += snd_opl4_volume_table[opl4->chset->gs_master_volume & 0x7f]; | ||
374 | att += snd_opl4_volume_table[voice->chan->gm_volume & 0x7f]; | ||
375 | att += snd_opl4_volume_table[voice->chan->gm_expression & 0x7f]; | ||
376 | att += snd_opl4_volume_table[voice->velocity]; | ||
377 | att = 0x7f - (0x7f - att) * (voice->sound->volume_factor) / 0xfe - volume_boost; | ||
378 | if (att < 0) | ||
379 | att = 0; | ||
380 | else if (att > 0x7e) | ||
381 | att = 0x7e; | ||
382 | snd_opl4_write(opl4, OPL4_REG_LEVEL + voice->number, | ||
383 | (att << 1) | voice->level_direct); | ||
384 | voice->level_direct = 0; | ||
385 | } | ||
386 | |||
387 | static void snd_opl4_update_pan(opl4_t *opl4, opl4_voice_t *voice) | ||
388 | { | ||
389 | int pan = voice->sound->panpot; | ||
390 | |||
391 | if (!voice->chan->drum_channel) | ||
392 | pan += (voice->chan->control[MIDI_CTL_MSB_PAN] - 0x40) >> 3; | ||
393 | if (pan < -7) | ||
394 | pan = -7; | ||
395 | else if (pan > 7) | ||
396 | pan = 7; | ||
397 | voice->reg_misc = (voice->reg_misc & ~OPL4_PAN_POT_MASK) | ||
398 | | (pan & OPL4_PAN_POT_MASK); | ||
399 | snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc); | ||
400 | } | ||
401 | |||
402 | static void snd_opl4_update_vibrato_depth(opl4_t *opl4, opl4_voice_t *voice) | ||
403 | { | ||
404 | int depth; | ||
405 | |||
406 | if (voice->chan->drum_channel) | ||
407 | return; | ||
408 | depth = (7 - voice->sound->vibrato) | ||
409 | * (voice->chan->control[MIDI_CTL_VIBRATO_DEPTH] & 0x7f); | ||
410 | depth = (depth >> 7) + voice->sound->vibrato; | ||
411 | voice->reg_lfo_vibrato &= ~OPL4_VIBRATO_DEPTH_MASK; | ||
412 | voice->reg_lfo_vibrato |= depth & OPL4_VIBRATO_DEPTH_MASK; | ||
413 | snd_opl4_write(opl4, OPL4_REG_LFO_VIBRATO + voice->number, | ||
414 | voice->reg_lfo_vibrato); | ||
415 | } | ||
416 | |||
417 | static void snd_opl4_update_pitch(opl4_t *opl4, opl4_voice_t *voice) | ||
418 | { | ||
419 | snd_midi_channel_t *chan = voice->chan; | ||
420 | int note, pitch, octave; | ||
421 | |||
422 | note = chan->drum_channel ? 60 : voice->note; | ||
423 | /* | ||
424 | * pitch is in 100/128 cents, so 0x80 is one semitone and | ||
425 | * 0x600 is one octave. | ||
426 | */ | ||
427 | pitch = ((note - 60) << 7) * voice->sound->key_scaling / 100 + (60 << 7); | ||
428 | pitch += voice->sound->pitch_offset; | ||
429 | if (!chan->drum_channel) | ||
430 | pitch += chan->gm_rpn_coarse_tuning; | ||
431 | pitch += chan->gm_rpn_fine_tuning >> 7; | ||
432 | pitch += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 0x2000; | ||
433 | if (pitch < 0) | ||
434 | pitch = 0; | ||
435 | else if (pitch >= 0x6000) | ||
436 | pitch = 0x5fff; | ||
437 | octave = pitch / 0x600 - 8; | ||
438 | pitch = snd_opl4_pitch_map[pitch % 0x600]; | ||
439 | |||
440 | snd_opl4_write(opl4, OPL4_REG_OCTAVE + voice->number, | ||
441 | (octave << 4) | ((pitch >> 7) & OPL4_F_NUMBER_HIGH_MASK)); | ||
442 | voice->reg_f_number = (voice->reg_f_number & OPL4_TONE_NUMBER_BIT8) | ||
443 | | ((pitch << 1) & OPL4_F_NUMBER_LOW_MASK); | ||
444 | snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice->number, voice->reg_f_number); | ||
445 | } | ||
446 | |||
447 | static void snd_opl4_update_tone_parameters(opl4_t *opl4, opl4_voice_t *voice) | ||
448 | { | ||
449 | snd_opl4_write(opl4, OPL4_REG_ATTACK_DECAY1 + voice->number, | ||
450 | voice->sound->reg_attack_decay1); | ||
451 | snd_opl4_write(opl4, OPL4_REG_LEVEL_DECAY2 + voice->number, | ||
452 | voice->sound->reg_level_decay2); | ||
453 | snd_opl4_write(opl4, OPL4_REG_RELEASE_CORRECTION + voice->number, | ||
454 | voice->sound->reg_release_correction); | ||
455 | snd_opl4_write(opl4, OPL4_REG_TREMOLO + voice->number, | ||
456 | voice->sound->reg_tremolo); | ||
457 | } | ||
458 | |||
459 | /* allocate one voice */ | ||
460 | static opl4_voice_t *snd_opl4_get_voice(opl4_t *opl4) | ||
461 | { | ||
462 | /* first, try to get the oldest key-off voice */ | ||
463 | if (!list_empty(&opl4->off_voices)) | ||
464 | return list_entry(opl4->off_voices.next, opl4_voice_t, list); | ||
465 | /* then get the oldest key-on voice */ | ||
466 | snd_assert(!list_empty(&opl4->on_voices), ); | ||
467 | return list_entry(opl4->on_voices.next, opl4_voice_t, list); | ||
468 | } | ||
469 | |||
470 | static void snd_opl4_wait_for_wave_headers(opl4_t *opl4) | ||
471 | { | ||
472 | int timeout = 200; | ||
473 | |||
474 | while ((inb(opl4->fm_port) & OPL4_STATUS_LOAD) && --timeout > 0) | ||
475 | udelay(10); | ||
476 | } | ||
477 | |||
478 | void snd_opl4_note_on(void *private_data, int note, int vel, snd_midi_channel_t *chan) | ||
479 | { | ||
480 | opl4_t *opl4 = private_data; | ||
481 | const opl4_region_ptr_t *regions; | ||
482 | opl4_voice_t *voice[2]; | ||
483 | const opl4_sound_t *sound[2]; | ||
484 | int voices = 0, i; | ||
485 | unsigned long flags; | ||
486 | |||
487 | /* determine the number of voices and voice parameters */ | ||
488 | i = chan->drum_channel ? 0x80 : (chan->midi_program & 0x7f); | ||
489 | regions = &snd_yrw801_regions[i]; | ||
490 | for (i = 0; i < regions->count; i++) { | ||
491 | if (note >= regions->regions[i].key_min && | ||
492 | note <= regions->regions[i].key_max) { | ||
493 | sound[voices] = ®ions->regions[i].sound; | ||
494 | if (++voices >= 2) | ||
495 | break; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | /* allocate and initialize the needed voices */ | ||
500 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
501 | for (i = 0; i < voices; i++) { | ||
502 | voice[i] = snd_opl4_get_voice(opl4); | ||
503 | list_del(&voice[i]->list); | ||
504 | list_add_tail(&voice[i]->list, &opl4->on_voices); | ||
505 | voice[i]->chan = chan; | ||
506 | voice[i]->note = note; | ||
507 | voice[i]->velocity = vel & 0x7f; | ||
508 | voice[i]->sound = sound[i]; | ||
509 | } | ||
510 | |||
511 | /* set tone number (triggers header loading) */ | ||
512 | for (i = 0; i < voices; i++) { | ||
513 | voice[i]->reg_f_number = | ||
514 | (sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8; | ||
515 | snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number, | ||
516 | voice[i]->reg_f_number); | ||
517 | snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number, | ||
518 | sound[i]->tone & 0xff); | ||
519 | } | ||
520 | |||
521 | /* set parameters which can be set while loading */ | ||
522 | for (i = 0; i < voices; i++) { | ||
523 | voice[i]->reg_misc = OPL4_LFO_RESET_BIT; | ||
524 | snd_opl4_update_pan(opl4, voice[i]); | ||
525 | snd_opl4_update_pitch(opl4, voice[i]); | ||
526 | voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT; | ||
527 | snd_opl4_update_volume(opl4, voice[i]); | ||
528 | } | ||
529 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
530 | |||
531 | /* wait for completion of loading */ | ||
532 | snd_opl4_wait_for_wave_headers(opl4); | ||
533 | |||
534 | /* set remaining parameters */ | ||
535 | spin_lock_irqsave(&opl4->reg_lock, flags); | ||
536 | for (i = 0; i < voices; i++) { | ||
537 | snd_opl4_update_tone_parameters(opl4, voice[i]); | ||
538 | voice[i]->reg_lfo_vibrato = voice[i]->sound->reg_lfo_vibrato; | ||
539 | snd_opl4_update_vibrato_depth(opl4, voice[i]); | ||
540 | } | ||
541 | |||
542 | /* finally, switch on all voices */ | ||
543 | for (i = 0; i < voices; i++) { | ||
544 | voice[i]->reg_misc = | ||
545 | (voice[i]->reg_misc & 0x1f) | OPL4_KEY_ON_BIT; | ||
546 | snd_opl4_write(opl4, OPL4_REG_MISC + voice[i]->number, | ||
547 | voice[i]->reg_misc); | ||
548 | } | ||
549 | spin_unlock_irqrestore(&opl4->reg_lock, flags); | ||
550 | } | ||
551 | |||
552 | static void snd_opl4_voice_off(opl4_t *opl4, opl4_voice_t *voice) | ||
553 | { | ||
554 | list_del(&voice->list); | ||
555 | list_add_tail(&voice->list, &opl4->off_voices); | ||
556 | |||
557 | voice->reg_misc &= ~OPL4_KEY_ON_BIT; | ||
558 | snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc); | ||
559 | } | ||
560 | |||
561 | void snd_opl4_note_off(void *private_data, int note, int vel, snd_midi_channel_t *chan) | ||
562 | { | ||
563 | opl4_t *opl4 = private_data; | ||
564 | |||
565 | snd_opl4_do_for_note(opl4, note, chan, snd_opl4_voice_off); | ||
566 | } | ||
567 | |||
568 | static void snd_opl4_terminate_voice(opl4_t *opl4, opl4_voice_t *voice) | ||
569 | { | ||
570 | list_del(&voice->list); | ||
571 | list_add_tail(&voice->list, &opl4->off_voices); | ||
572 | |||
573 | voice->reg_misc = (voice->reg_misc & ~OPL4_KEY_ON_BIT) | OPL4_DAMP_BIT; | ||
574 | snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc); | ||
575 | } | ||
576 | |||
577 | void snd_opl4_terminate_note(void *private_data, int note, snd_midi_channel_t *chan) | ||
578 | { | ||
579 | opl4_t *opl4 = private_data; | ||
580 | |||
581 | snd_opl4_do_for_note(opl4, note, chan, snd_opl4_terminate_voice); | ||
582 | } | ||
583 | |||
584 | void snd_opl4_control(void *private_data, int type, snd_midi_channel_t *chan) | ||
585 | { | ||
586 | opl4_t *opl4 = private_data; | ||
587 | |||
588 | switch (type) { | ||
589 | case MIDI_CTL_MSB_MODWHEEL: | ||
590 | chan->control[MIDI_CTL_VIBRATO_DEPTH] = chan->control[MIDI_CTL_MSB_MODWHEEL]; | ||
591 | snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_vibrato_depth); | ||
592 | break; | ||
593 | case MIDI_CTL_MSB_MAIN_VOLUME: | ||
594 | snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_volume); | ||
595 | break; | ||
596 | case MIDI_CTL_MSB_PAN: | ||
597 | snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_pan); | ||
598 | break; | ||
599 | case MIDI_CTL_MSB_EXPRESSION: | ||
600 | snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_volume); | ||
601 | break; | ||
602 | case MIDI_CTL_VIBRATO_RATE: | ||
603 | /* not yet supported */ | ||
604 | break; | ||
605 | case MIDI_CTL_VIBRATO_DEPTH: | ||
606 | snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_vibrato_depth); | ||
607 | break; | ||
608 | case MIDI_CTL_VIBRATO_DELAY: | ||
609 | /* not yet supported */ | ||
610 | break; | ||
611 | case MIDI_CTL_E1_REVERB_DEPTH: | ||
612 | /* | ||
613 | * Each OPL4 voice has a bit called "Pseudo-Reverb", but | ||
614 | * IMHO _not_ using it enhances the listening experience. | ||
615 | */ | ||
616 | break; | ||
617 | case MIDI_CTL_PITCHBEND: | ||
618 | snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_pitch); | ||
619 | break; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | void snd_opl4_sysex(void *private_data, unsigned char *buf, int len, | ||
624 | int parsed, snd_midi_channel_set_t *chset) | ||
625 | { | ||
626 | opl4_t *opl4 = private_data; | ||
627 | |||
628 | if (parsed == SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME) | ||
629 | snd_opl4_do_for_all(opl4, snd_opl4_update_volume); | ||
630 | } | ||
diff --git a/sound/drivers/opl4/yrw801.c b/sound/drivers/opl4/yrw801.c new file mode 100644 index 000000000000..a51174dd3e56 --- /dev/null +++ b/sound/drivers/opl4/yrw801.c | |||
@@ -0,0 +1,961 @@ | |||
1 | /* | ||
2 | * Information about the Yamaha YRW801 wavetable ROM chip | ||
3 | * | ||
4 | * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions, and the following disclaimer, | ||
12 | * without modification. | ||
13 | * 2. The name of the author may not be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * Alternatively, this software may be distributed and/or modified under the | ||
17 | * terms of the GNU General Public License as published by the Free Software | ||
18 | * Foundation; either version 2 of the License, or (at your option) any later | ||
19 | * version. | ||
20 | * | ||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | ||
25 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
31 | * SUCH DAMAGE. | ||
32 | */ | ||
33 | |||
34 | #include "opl4_local.h" | ||
35 | |||
36 | int snd_yrw801_detect(opl4_t *opl4) | ||
37 | { | ||
38 | char buf[15]; | ||
39 | |||
40 | snd_opl4_read_memory(opl4, buf, 0x001200, 15); | ||
41 | if (memcmp(buf, "CopyrightYAMAHA", 15)) | ||
42 | return -ENODEV; | ||
43 | snd_opl4_read_memory(opl4, buf, 0x1ffffe, 2); | ||
44 | if (buf[0] != 0x01) | ||
45 | return -ENODEV; | ||
46 | snd_printdd("YRW801 ROM version %02x.%02x\n", buf[0], buf[1]); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * The instrument definitions are stored statically because, in practice, the | ||
52 | * OPL4 is always coupled with a YRW801. Dynamic instrument loading would be | ||
53 | * required if downloading sample data to external SRAM was actually supported | ||
54 | * by this driver. | ||
55 | */ | ||
56 | |||
57 | static const opl4_region_t regions_00[] = { /* Acoustic Grand Piano */ | ||
58 | {0x14, 0x27, {0x12c,7474,100, 0,0,0x00,0xc8,0x20,0xf2,0x13,0x08,0x0}}, | ||
59 | {0x28, 0x2d, {0x12d,6816,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
60 | {0x2e, 0x33, {0x12e,5899,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
61 | {0x34, 0x39, {0x12f,5290,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
62 | {0x3a, 0x3f, {0x130,4260,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
63 | {0x40, 0x45, {0x131,3625,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
64 | {0x46, 0x4b, {0x132,3116,100, 0,0,0x04,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
65 | {0x4c, 0x52, {0x133,2081,100, 0,0,0x03,0xc8,0x20,0xf2,0x14,0x18,0x0}}, | ||
66 | {0x53, 0x58, {0x134,1444,100, 0,0,0x07,0xc8,0x20,0xf3,0x14,0x18,0x0}}, | ||
67 | {0x59, 0x6d, {0x135,1915,100, 0,0,0x00,0xc8,0x20,0xf4,0x15,0x08,0x0}} | ||
68 | }; | ||
69 | static const opl4_region_t regions_01[] = { /* Bright Acoustic Piano */ | ||
70 | {0x14, 0x2d, {0x12c,7474,100, 0,0,0x00,0xc8,0x20,0xf2,0x13,0x08,0x0}}, | ||
71 | {0x2e, 0x33, {0x12d,6816,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
72 | {0x34, 0x39, {0x12e,5899,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
73 | {0x3a, 0x3f, {0x12f,5290,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
74 | {0x40, 0x45, {0x130,4260,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
75 | {0x46, 0x4b, {0x131,3625,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
76 | {0x4c, 0x52, {0x132,3116,100, 0,0,0x04,0xc8,0x20,0xf2,0x14,0x08,0x0}}, | ||
77 | {0x53, 0x58, {0x133,2081,100, 0,0,0x07,0xc8,0x20,0xf2,0x14,0x18,0x0}}, | ||
78 | {0x59, 0x5e, {0x134,1444,100, 0,0,0x0a,0xc8,0x20,0xf3,0x14,0x18,0x0}}, | ||
79 | {0x5f, 0x6d, {0x135,1915,100, 0,0,0x00,0xc8,0x20,0xf4,0x15,0x08,0x0}} | ||
80 | }; | ||
81 | static const opl4_region_t regions_02[] = { /* Electric Grand Piano */ | ||
82 | {0x14, 0x2d, {0x12c,7476,100, 1,0,0x00,0xae,0x20,0xf2,0x13,0x07,0x0}}, | ||
83 | {0x2e, 0x33, {0x12d,6818,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}}, | ||
84 | {0x34, 0x39, {0x12e,5901,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}}, | ||
85 | {0x3a, 0x3f, {0x12f,5292,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}}, | ||
86 | {0x40, 0x45, {0x130,4262,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}}, | ||
87 | {0x46, 0x4b, {0x131,3627,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}}, | ||
88 | {0x4c, 0x52, {0x132,3118,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}}, | ||
89 | {0x53, 0x58, {0x133,2083,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x17,0x0}}, | ||
90 | {0x59, 0x5e, {0x134,1446,100, 1,0,0x00,0xae,0x20,0xf3,0x14,0x17,0x0}}, | ||
91 | {0x5f, 0x6d, {0x135,1917,100, 1,0,0x00,0xae,0x20,0xf4,0x15,0x07,0x0}}, | ||
92 | {0x00, 0x7f, {0x06c,6375,100,-1,0,0x00,0xc2,0x28,0xf4,0x23,0x18,0x0}} | ||
93 | }; | ||
94 | static const opl4_region_t regions_03[] = { /* Honky-Tonk Piano */ | ||
95 | {0x14, 0x27, {0x12c,7474,100, 0,0,0x00,0xb4,0x20,0xf2,0x13,0x08,0x0}}, | ||
96 | {0x28, 0x2d, {0x12d,6816,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
97 | {0x2e, 0x33, {0x12e,5899,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
98 | {0x34, 0x39, {0x12f,5290,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
99 | {0x3a, 0x3f, {0x130,4260,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
100 | {0x40, 0x45, {0x131,3625,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
101 | {0x46, 0x4b, {0x132,3116,100, 0,0,0x04,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
102 | {0x4c, 0x52, {0x133,2081,100, 0,0,0x03,0xb4,0x20,0xf2,0x14,0x18,0x0}}, | ||
103 | {0x53, 0x58, {0x134,1444,100, 0,0,0x07,0xb4,0x20,0xf3,0x14,0x18,0x0}}, | ||
104 | {0x59, 0x6d, {0x135,1915,100, 0,0,0x00,0xb4,0x20,0xf4,0x15,0x08,0x0}}, | ||
105 | {0x14, 0x27, {0x12c,7486,100, 0,0,0x00,0xb4,0x20,0xf2,0x13,0x08,0x0}}, | ||
106 | {0x28, 0x2d, {0x12d,6803,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
107 | {0x2e, 0x33, {0x12e,5912,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
108 | {0x34, 0x39, {0x12f,5275,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
109 | {0x3a, 0x3f, {0x130,4274,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
110 | {0x40, 0x45, {0x131,3611,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
111 | {0x46, 0x4b, {0x132,3129,100, 0,0,0x04,0xb4,0x20,0xf2,0x14,0x08,0x0}}, | ||
112 | {0x4c, 0x52, {0x133,2074,100, 0,0,0x07,0xb4,0x20,0xf2,0x14,0x18,0x0}}, | ||
113 | {0x53, 0x58, {0x134,1457,100, 0,0,0x01,0xb4,0x20,0xf3,0x14,0x18,0x0}}, | ||
114 | {0x59, 0x6d, {0x135,1903,100, 0,0,0x00,0xb4,0x20,0xf4,0x15,0x08,0x0}} | ||
115 | }; | ||
116 | static const opl4_region_t regions_04[] = { /* Electric Piano 1 */ | ||
117 | {0x15, 0x6c, {0x00b,6570,100, 0,0,0x00,0x28,0x38,0xf0,0x00,0x0c,0x0}}, | ||
118 | {0x00, 0x7f, {0x06c,6375,100, 0,2,0x00,0xb0,0x22,0xf4,0x23,0x19,0x0}} | ||
119 | }; | ||
120 | static const opl4_region_t regions_05[] = { /* Electric Piano 2 */ | ||
121 | {0x14, 0x27, {0x12c,7476,100, 0,3,0x00,0xa2,0x1b,0xf2,0x13,0x08,0x0}}, | ||
122 | {0x28, 0x2d, {0x12d,6818,100, 0,3,0x00,0xa2,0x1b,0xf2,0x14,0x08,0x0}}, | ||
123 | {0x2e, 0x33, {0x12e,5901,100, 0,3,0x00,0xa2,0x1b,0xf2,0x14,0x08,0x0}}, | ||
124 | {0x34, 0x39, {0x12f,5292,100, 0,3,0x00,0xa2,0x1b,0xf2,0x14,0x08,0x0}}, | ||
125 | {0x3a, 0x3f, {0x130,4262,100, 0,3,0x0a,0xa2,0x1b,0xf2,0x14,0x08,0x0}}, | ||
126 | {0x40, 0x45, {0x131,3627,100, 0,3,0x0a,0xa2,0x1b,0xf2,0x14,0x08,0x0}}, | ||
127 | {0x46, 0x4b, {0x132,3118,100, 0,3,0x04,0xa2,0x1b,0xf2,0x14,0x08,0x0}}, | ||
128 | {0x4c, 0x52, {0x133,2083,100, 0,3,0x03,0xa2,0x1b,0xf2,0x14,0x18,0x0}}, | ||
129 | {0x53, 0x58, {0x134,1446,100, 0,3,0x07,0xa2,0x1b,0xf3,0x14,0x18,0x0}}, | ||
130 | {0x59, 0x6d, {0x135,1917,100, 0,3,0x00,0xa2,0x1b,0xf4,0x15,0x08,0x0}}, | ||
131 | {0x14, 0x2d, {0x12c,7472,100, 0,0,0x00,0xa2,0x18,0xf2,0x13,0x08,0x0}}, | ||
132 | {0x2e, 0x33, {0x12d,6814,100, 0,0,0x00,0xa2,0x18,0xf2,0x14,0x08,0x0}}, | ||
133 | {0x34, 0x39, {0x12e,5897,100, 0,0,0x00,0xa2,0x18,0xf2,0x14,0x08,0x0}}, | ||
134 | {0x3a, 0x3f, {0x12f,5288,100, 0,0,0x00,0xa2,0x18,0xf2,0x14,0x08,0x0}}, | ||
135 | {0x40, 0x45, {0x130,4258,100, 0,0,0x0a,0xa2,0x18,0xf2,0x14,0x08,0x0}}, | ||
136 | {0x46, 0x4b, {0x131,3623,100, 0,0,0x0a,0xa2,0x18,0xf2,0x14,0x08,0x0}}, | ||
137 | {0x4c, 0x52, {0x132,3114,100, 0,0,0x04,0xa2,0x18,0xf2,0x14,0x08,0x0}}, | ||
138 | {0x53, 0x58, {0x133,2079,100, 0,0,0x07,0xa2,0x18,0xf2,0x14,0x18,0x0}}, | ||
139 | {0x59, 0x5e, {0x134,1442,100, 0,0,0x0a,0xa2,0x18,0xf3,0x14,0x18,0x0}}, | ||
140 | {0x5f, 0x6d, {0x135,1913,100, 0,0,0x00,0xa2,0x18,0xf4,0x15,0x08,0x0}} | ||
141 | }; | ||
142 | static const opl4_region_t regions_06[] = { /* Harpsichord */ | ||
143 | {0x15, 0x39, {0x080,5158,100, 0,0,0x00,0xb2,0x20,0xf5,0x24,0x19,0x0}}, | ||
144 | {0x3a, 0x3f, {0x081,4408,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x09,0x0}}, | ||
145 | {0x40, 0x45, {0x082,3622,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x09,0x0}}, | ||
146 | {0x46, 0x4d, {0x083,2843,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x19,0x0}}, | ||
147 | {0x4e, 0x6c, {0x084,1307,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x29,0x0}} | ||
148 | }; | ||
149 | static const opl4_region_t regions_07[] = { /* Clavinet */ | ||
150 | {0x15, 0x51, {0x027,5009,100, 0,0,0x00,0xd2,0x28,0xf5,0x13,0x2b,0x0}}, | ||
151 | {0x52, 0x6c, {0x028,3495,100, 0,0,0x00,0xd2,0x28,0xf5,0x13,0x3b,0x0}} | ||
152 | }; | ||
153 | static const opl4_region_t regions_08[] = { /* Celesta */ | ||
154 | {0x15, 0x6c, {0x02b,3267,100, 0,0,0x00,0xdc,0x20,0xf4,0x15,0x07,0x3}} | ||
155 | }; | ||
156 | static const opl4_region_t regions_09[] = { /* Glockenspiel */ | ||
157 | {0x15, 0x78, {0x0f3, 285,100, 0,0,0x00,0xc2,0x28,0xf6,0x25,0x25,0x0}} | ||
158 | }; | ||
159 | static const opl4_region_t regions_0a[] = { /* Music Box */ | ||
160 | {0x15, 0x6c, {0x0f3,3362,100, 0,0,0x00,0xb6,0x20,0xa6,0x25,0x25,0x0}}, | ||
161 | {0x15, 0x6c, {0x101,4773,100, 0,0,0x00,0xaa,0x20,0xd4,0x14,0x16,0x0}} | ||
162 | }; | ||
163 | static const opl4_region_t regions_0b[] = { /* Vibraphone */ | ||
164 | {0x15, 0x6c, {0x101,4778,100, 0,0,0x00,0xc0,0x28,0xf4,0x14,0x16,0x4}} | ||
165 | }; | ||
166 | static const opl4_region_t regions_0c[] = { /* Marimba */ | ||
167 | {0x15, 0x3f, {0x0f4,4778,100, 0,0,0x00,0xc4,0x38,0xf7,0x47,0x08,0x0}}, | ||
168 | {0x40, 0x4c, {0x0f5,3217,100, 0,0,0x00,0xc4,0x38,0xf7,0x47,0x08,0x0}}, | ||
169 | {0x4d, 0x5a, {0x0f5,3217,100, 0,0,0x00,0xc4,0x38,0xf7,0x48,0x08,0x0}}, | ||
170 | {0x5b, 0x7f, {0x0f5,3218,100, 0,0,0x00,0xc4,0x38,0xf7,0x48,0x18,0x0}} | ||
171 | }; | ||
172 | static const opl4_region_t regions_0d[] = { /* Xylophone */ | ||
173 | {0x00, 0x7f, {0x136,1729,100, 0,0,0x00,0xd2,0x38,0xf0,0x06,0x36,0x0}} | ||
174 | }; | ||
175 | static const opl4_region_t regions_0e[] = { /* Tubular Bell */ | ||
176 | {0x01, 0x7f, {0x0ff,3999,100, 0,1,0x00,0x90,0x21,0xf4,0xa3,0x25,0x1}} | ||
177 | }; | ||
178 | static const opl4_region_t regions_0f[] = { /* Dulcimer */ | ||
179 | {0x00, 0x7f, {0x03f,4236,100, 0,1,0x00,0xbc,0x29,0xf5,0x16,0x07,0x0}}, | ||
180 | {0x00, 0x7f, {0x040,4236,100, 0,2,0x0e,0x94,0x2a,0xf5,0x16,0x07,0x0}} | ||
181 | }; | ||
182 | static const opl4_region_t regions_10[] = { /* Drawbar Organ */ | ||
183 | {0x01, 0x7f, {0x08e,4394,100, 0,2,0x14,0xc2,0x3a,0xf0,0x00,0x0a,0x0}} | ||
184 | }; | ||
185 | static const opl4_region_t regions_11[] = { /* Percussive Organ */ | ||
186 | {0x15, 0x3b, {0x08c,6062,100, 0,3,0x00,0xbe,0x3b,0xf0,0x00,0x09,0x0}}, | ||
187 | {0x3c, 0x6c, {0x08d,2984,100, 0,3,0x00,0xbe,0x3b,0xf0,0x00,0x09,0x0}} | ||
188 | }; | ||
189 | static const opl4_region_t regions_12[] = { /* Rock Organ */ | ||
190 | {0x15, 0x30, {0x128,6574,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}}, | ||
191 | {0x31, 0x3c, {0x129,5040,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}}, | ||
192 | {0x3d, 0x48, {0x12a,3498,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}}, | ||
193 | {0x49, 0x54, {0x12b,1957,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}}, | ||
194 | {0x55, 0x6c, {0x127, 423,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}} | ||
195 | }; | ||
196 | static const opl4_region_t regions_13[] = { /* Church Organ */ | ||
197 | {0x15, 0x29, {0x087,7466,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}}, | ||
198 | {0x2a, 0x30, {0x088,6456,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}}, | ||
199 | {0x31, 0x38, {0x089,5428,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}}, | ||
200 | {0x39, 0x41, {0x08a,4408,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}}, | ||
201 | {0x42, 0x6c, {0x08b,3406,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}} | ||
202 | }; | ||
203 | static const opl4_region_t regions_14[] = { /* Reed Organ */ | ||
204 | {0x00, 0x53, {0x0ac,5570,100, 0,0,0x06,0xc0,0x38,0xf0,0x00,0x09,0x1}}, | ||
205 | {0x54, 0x7f, {0x0ad,2497,100, 0,0,0x00,0xc0,0x38,0xf0,0x00,0x09,0x1}} | ||
206 | }; | ||
207 | static const opl4_region_t regions_15[] = { /* Accordion */ | ||
208 | {0x15, 0x4c, {0x006,4261,100, 0,2,0x00,0xa4,0x22,0x90,0x00,0x09,0x0}}, | ||
209 | {0x4d, 0x6c, {0x007,1530,100, 0,2,0x00,0xa4,0x22,0x90,0x00,0x09,0x0}}, | ||
210 | {0x15, 0x6c, {0x070,4391,100, 0,3,0x00,0x8a,0x23,0xa0,0x00,0x09,0x0}} | ||
211 | }; | ||
212 | static const opl4_region_t regions_16[] = { /* Harmonica */ | ||
213 | {0x15, 0x6c, {0x070,4408,100, 0,0,0x00,0xae,0x30,0xa0,0x00,0x09,0x2}} | ||
214 | }; | ||
215 | static const opl4_region_t regions_17[] = { /* Tango Accordion */ | ||
216 | {0x00, 0x53, {0x0ac,5573,100, 0,0,0x00,0xae,0x38,0xf0,0x00,0x09,0x0}}, | ||
217 | {0x54, 0x7f, {0x0ad,2500,100, 0,0,0x00,0xae,0x38,0xf0,0x00,0x09,0x0}}, | ||
218 | {0x15, 0x6c, {0x041,8479,100, 0,2,0x00,0x6a,0x3a,0x75,0x20,0x0a,0x0}} | ||
219 | }; | ||
220 | static const opl4_region_t regions_18[] = { /* Nylon Guitar */ | ||
221 | {0x15, 0x2f, {0x0b3,6964,100, 0,0,0x05,0xca,0x28,0xf5,0x34,0x09,0x0}}, | ||
222 | {0x30, 0x36, {0x0b7,5567,100, 0,0,0x0c,0xca,0x28,0xf5,0x34,0x09,0x0}}, | ||
223 | {0x37, 0x3c, {0x0b5,4653,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}}, | ||
224 | {0x3d, 0x43, {0x0b4,3892,100, 0,0,0x00,0xca,0x28,0xf6,0x35,0x09,0x0}}, | ||
225 | {0x44, 0x60, {0x0b6,2723,100, 0,0,0x00,0xca,0x28,0xf6,0x35,0x19,0x0}} | ||
226 | }; | ||
227 | static const opl4_region_t regions_19[] = { /* Steel Guitar */ | ||
228 | {0x15, 0x31, {0x00c,6937,100, 0,0,0x00,0xbc,0x28,0xf0,0x04,0x19,0x0}}, | ||
229 | {0x32, 0x38, {0x00d,5410,100, 0,0,0x00,0xbc,0x28,0xf0,0x05,0x09,0x0}}, | ||
230 | {0x39, 0x47, {0x00e,4379,100, 0,0,0x00,0xbc,0x28,0xf5,0x94,0x09,0x0}}, | ||
231 | {0x48, 0x6c, {0x00f,2843,100, 0,0,0x00,0xbc,0x28,0xf6,0x95,0x09,0x0}} | ||
232 | }; | ||
233 | static const opl4_region_t regions_1a[] = { /* Jazz Guitar */ | ||
234 | {0x15, 0x31, {0x05a,6832,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}}, | ||
235 | {0x32, 0x3f, {0x05b,4897,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}}, | ||
236 | {0x40, 0x6c, {0x05c,3218,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}} | ||
237 | }; | ||
238 | static const opl4_region_t regions_1b[] = { /* Clean Guitar */ | ||
239 | {0x15, 0x2c, {0x061,7053,100, 0,1,0x00,0xb4,0x29,0xf5,0x54,0x0a,0x0}}, | ||
240 | {0x2d, 0x31, {0x060,6434,100, 0,1,0x00,0xb4,0x29,0xf5,0x54,0x0a,0x0}}, | ||
241 | {0x32, 0x38, {0x063,5764,100, 0,1,0x00,0xbe,0x29,0xf5,0x55,0x0a,0x0}}, | ||
242 | {0x39, 0x3f, {0x062,4627,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x0a,0x0}}, | ||
243 | {0x40, 0x44, {0x065,3963,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x1a,0x0}}, | ||
244 | {0x45, 0x4b, {0x064,3313,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x1a,0x0}}, | ||
245 | {0x4c, 0x54, {0x066,2462,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x2a,0x0}}, | ||
246 | {0x55, 0x6c, {0x067,1307,100, 0,1,0x00,0xb4,0x29,0xf6,0x56,0x0a,0x0}} | ||
247 | }; | ||
248 | static const opl4_region_t regions_1c[] = { /* Muted Guitar */ | ||
249 | {0x01, 0x7f, {0x068,4408,100, 0,0,0x00,0xcc,0x28,0xf6,0x15,0x09,0x0}} | ||
250 | }; | ||
251 | static const opl4_region_t regions_1d[] = { /* Overdriven Guitar */ | ||
252 | {0x00, 0x40, {0x0a5,6589,100, 0,1,0x00,0xc0,0x29,0xf2,0x11,0x09,0x0}}, | ||
253 | {0x41, 0x7f, {0x0a6,5428,100, 0,1,0x00,0xc0,0x29,0xf2,0x11,0x09,0x0}} | ||
254 | }; | ||
255 | static const opl4_region_t regions_1e[] = { /* Distortion Guitar */ | ||
256 | {0x15, 0x2a, {0x051,6928,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
257 | {0x2b, 0x2e, {0x052,6433,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
258 | {0x2f, 0x32, {0x053,5944,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
259 | {0x33, 0x36, {0x054,5391,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
260 | {0x37, 0x3a, {0x055,4897,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
261 | {0x3b, 0x3e, {0x056,4408,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
262 | {0x3f, 0x42, {0x057,3892,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
263 | {0x43, 0x46, {0x058,3361,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}, | ||
264 | {0x47, 0x6c, {0x059,2784,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}} | ||
265 | }; | ||
266 | static const opl4_region_t regions_1f[] = { /* Guitar Harmonics */ | ||
267 | {0x15, 0x44, {0x05e,5499,100, 0,0,0x00,0xce,0x28,0xf4,0x24,0x09,0x0}}, | ||
268 | {0x45, 0x49, {0x05d,4850,100, 0,0,0x00,0xe2,0x28,0xf4,0x24,0x09,0x0}}, | ||
269 | {0x4a, 0x6c, {0x05f,4259,100, 0,0,0x00,0xce,0x28,0xf4,0x24,0x09,0x0}} | ||
270 | }; | ||
271 | static const opl4_region_t regions_20[] = { /* Acoustic Bass */ | ||
272 | {0x15, 0x30, {0x004,8053,100, 0,0,0x00,0xe2,0x18,0xf5,0x15,0x09,0x0}}, | ||
273 | {0x31, 0x6c, {0x005,4754,100, 0,0,0x00,0xe2,0x18,0xf5,0x15,0x09,0x0}} | ||
274 | }; | ||
275 | static const opl4_region_t regions_21[] = { /* Fingered Bass */ | ||
276 | {0x01, 0x20, {0x04a,8762,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}}, | ||
277 | {0x21, 0x25, {0x04b,8114,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}}, | ||
278 | {0x26, 0x2a, {0x04c,7475,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}}, | ||
279 | {0x2b, 0x7f, {0x04d,6841,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}} | ||
280 | }; | ||
281 | static const opl4_region_t regions_22[] = { /* Picked Bass */ | ||
282 | {0x15, 0x23, {0x04f,7954,100, 0,0,0x00,0xcc,0x18,0xf3,0x90,0x0a,0x0}}, | ||
283 | {0x24, 0x2a, {0x050,7318,100, 0,0,0x05,0xcc,0x18,0xf3,0x90,0x1a,0x0}}, | ||
284 | {0x2b, 0x2f, {0x06b,6654,100, 0,0,0x00,0xcc,0x18,0xf3,0x90,0x2a,0x0}}, | ||
285 | {0x30, 0x47, {0x069,6031,100, 0,0,0x00,0xcc,0x18,0xf5,0xb0,0x0a,0x0}}, | ||
286 | {0x48, 0x6c, {0x06a,5393,100, 0,0,0x00,0xcc,0x18,0xf5,0xb0,0x0a,0x0}} | ||
287 | }; | ||
288 | static const opl4_region_t regions_23[] = { /* Fretless Bass */ | ||
289 | {0x01, 0x7f, {0x04e,5297,100, 0,0,0x00,0xd2,0x10,0xf3,0x63,0x19,0x0}} | ||
290 | }; | ||
291 | static const opl4_region_t regions_24[] = { /* Slap Bass 1 */ | ||
292 | {0x15, 0x6c, {0x0a3,7606,100, 0,1,0x00,0xde,0x19,0xf5,0x32,0x1a,0x0}} | ||
293 | }; | ||
294 | static const opl4_region_t regions_25[] = { /* Slap Bass 2 */ | ||
295 | {0x01, 0x7f, {0x0a2,6694,100, 0,0,0x00,0xda,0x20,0xb0,0x02,0x09,0x0}} | ||
296 | }; | ||
297 | static const opl4_region_t regions_26[] = { /* Synth Bass 1 */ | ||
298 | {0x15, 0x6c, {0x0be,7466,100, 0,1,0x00,0xb8,0x39,0xf4,0x14,0x09,0x0}} | ||
299 | }; | ||
300 | static const opl4_region_t regions_27[] = { /* Synth Bass 2 */ | ||
301 | {0x00, 0x7f, {0x117,8103,100, 0,1,0x00,0xca,0x39,0xf3,0x50,0x08,0x0}} | ||
302 | }; | ||
303 | static const opl4_region_t regions_28[] = { /* Violin */ | ||
304 | {0x15, 0x3a, {0x105,5158,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
305 | {0x3b, 0x3f, {0x102,4754,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
306 | {0x40, 0x41, {0x106,4132,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
307 | {0x42, 0x44, {0x107,4033,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
308 | {0x45, 0x47, {0x108,3580,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
309 | {0x48, 0x4a, {0x10a,2957,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
310 | {0x4b, 0x4c, {0x10b,2724,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
311 | {0x4d, 0x4e, {0x10c,2530,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
312 | {0x4f, 0x51, {0x10d,2166,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}, | ||
313 | {0x52, 0x6c, {0x109,1825,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}} | ||
314 | }; | ||
315 | static const opl4_region_t regions_29[] = { /* Viola */ | ||
316 | {0x15, 0x32, {0x103,5780,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}}, | ||
317 | {0x33, 0x35, {0x104,5534,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}}, | ||
318 | {0x36, 0x38, {0x105,5158,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}}, | ||
319 | {0x39, 0x3d, {0x102,4754,100, 0,3,0x00,0xca,0x3b,0xa3,0x20,0x09,0x0}}, | ||
320 | {0x3e, 0x3f, {0x106,4132,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}}, | ||
321 | {0x40, 0x42, {0x107,4033,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}}, | ||
322 | {0x43, 0x45, {0x108,3580,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}}, | ||
323 | {0x46, 0x48, {0x10a,2957,100, 0,3,0x00,0xca,0x3b,0xa3,0x20,0x09,0x0}}, | ||
324 | {0x49, 0x4a, {0x10b,2724,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}}, | ||
325 | {0x4b, 0x4c, {0x10c,2530,100, 0,3,0x00,0xca,0x3b,0xa3,0x20,0x09,0x0}}, | ||
326 | {0x4d, 0x4f, {0x10d,2166,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}}, | ||
327 | {0x50, 0x6c, {0x109,1825,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}} | ||
328 | }; | ||
329 | static const opl4_region_t regions_2a[] = { /* Cello */ | ||
330 | {0x15, 0x2d, {0x112,6545,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x08,0x0}}, | ||
331 | {0x2e, 0x37, {0x113,5764,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x08,0x0}}, | ||
332 | {0x38, 0x3e, {0x115,4378,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x18,0x0}}, | ||
333 | {0x3f, 0x44, {0x116,3998,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x18,0x0}}, | ||
334 | {0x45, 0x6c, {0x114,3218,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x18,0x0}} | ||
335 | }; | ||
336 | static const opl4_region_t regions_2b[] = { /* Contrabass */ | ||
337 | {0x15, 0x29, {0x110,7713,100, 0,1,0x00,0xc2,0x19,0x90,0x00,0x09,0x0}}, | ||
338 | {0x2a, 0x6c, {0x111,6162,100, 0,1,0x00,0xc2,0x19,0x90,0x00,0x09,0x0}} | ||
339 | }; | ||
340 | static const opl4_region_t regions_2c[] = { /* Tremolo Strings */ | ||
341 | {0x15, 0x3b, {0x0b0,4810,100, 0,0,0x0a,0xde,0x38,0xf0,0x00,0x07,0x6}}, | ||
342 | {0x3c, 0x41, {0x035,4035,100, 0,0,0x05,0xde,0x38,0xf0,0x00,0x07,0x6}}, | ||
343 | {0x42, 0x47, {0x033,3129,100, 0,0,0x05,0xde,0x38,0xf0,0x00,0x07,0x6}}, | ||
344 | {0x48, 0x52, {0x034,2625,100, 0,0,0x05,0xde,0x38,0xf0,0x00,0x07,0x6}}, | ||
345 | {0x53, 0x6c, {0x0af, 936,100, 0,0,0x00,0xde,0x38,0xf0,0x00,0x07,0x6}} | ||
346 | }; | ||
347 | static const opl4_region_t regions_2d[] = { /* Pizzicato Strings */ | ||
348 | {0x15, 0x32, {0x0b8,6186,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}}, | ||
349 | {0x33, 0x3b, {0x0b9,5031,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}}, | ||
350 | {0x3c, 0x42, {0x0bb,4146,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}}, | ||
351 | {0x43, 0x48, {0x0ba,3245,100, 0,0,0x00,0xc2,0x28,0xf0,0x00,0x05,0x0}}, | ||
352 | {0x49, 0x6c, {0x0bc,2352,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}} | ||
353 | }; | ||
354 | static const opl4_region_t regions_2e[] = { /* Harp */ | ||
355 | {0x15, 0x46, {0x07e,3740,100, 0,1,0x00,0xd2,0x29,0xf5,0x25,0x07,0x0}}, | ||
356 | {0x47, 0x6c, {0x07f,2319,100, 0,1,0x00,0xd2,0x29,0xf5,0x25,0x07,0x0}} | ||
357 | }; | ||
358 | static const opl4_region_t regions_2f[] = { /* Timpani */ | ||
359 | {0x15, 0x6c, {0x100,6570,100, 0,0,0x00,0xf8,0x28,0xf0,0x05,0x16,0x0}} | ||
360 | }; | ||
361 | static const opl4_region_t regions_30[] = { /* Strings */ | ||
362 | {0x15, 0x3b, {0x13c,4806,100, 0,0,0x00,0xc8,0x20,0x80,0x00,0x07,0x0}}, | ||
363 | {0x3c, 0x41, {0x13e,4035,100, 0,0,0x00,0xc8,0x20,0x80,0x00,0x07,0x0}}, | ||
364 | {0x42, 0x47, {0x13d,3122,100, 0,0,0x00,0xc8,0x20,0x80,0x00,0x07,0x0}}, | ||
365 | {0x48, 0x52, {0x13f,2629,100, 0,0,0x00,0xbe,0x20,0x80,0x00,0x07,0x0}}, | ||
366 | {0x53, 0x6c, {0x140, 950,100, 0,0,0x00,0xbe,0x20,0x80,0x00,0x07,0x0}} | ||
367 | }; | ||
368 | static const opl4_region_t regions_31[] = { /* Slow Strings */ | ||
369 | {0x15, 0x3b, {0x0b0,4810,100, 0,1,0x0a,0xbe,0x19,0xf0,0x00,0x07,0x0}}, | ||
370 | {0x3c, 0x41, {0x035,4035,100, 0,1,0x05,0xbe,0x19,0xf0,0x00,0x07,0x0}}, | ||
371 | {0x42, 0x47, {0x033,3129,100, 0,1,0x05,0xbe,0x19,0xf0,0x00,0x07,0x0}}, | ||
372 | {0x48, 0x52, {0x034,2625,100, 0,1,0x05,0xbe,0x19,0xf0,0x00,0x07,0x0}}, | ||
373 | {0x53, 0x6c, {0x0af, 936,100, 0,1,0x00,0xbe,0x19,0xf0,0x00,0x07,0x0}} | ||
374 | }; | ||
375 | static const opl4_region_t regions_32[] = { /* Synth Strings 1 */ | ||
376 | {0x05, 0x71, {0x002,6045,100,-2,0,0x00,0xa6,0x20,0x93,0x22,0x06,0x0}}, | ||
377 | {0x15, 0x6c, {0x0ae,3261,100, 2,0,0x00,0xc6,0x20,0x70,0x01,0x06,0x0}} | ||
378 | }; | ||
379 | static const opl4_region_t regions_33[] = { /* Synth Strings 2 */ | ||
380 | {0x15, 0x6c, {0x002,4513,100, 5,1,0x00,0xb4,0x19,0x70,0x00,0x06,0x0}}, | ||
381 | {0x15, 0x6c, {0x002,4501,100,-5,1,0x00,0xb4,0x19,0x70,0x00,0x06,0x0}} | ||
382 | }; | ||
383 | static const opl4_region_t regions_34[] = { /* Choir Aahs */ | ||
384 | {0x15, 0x3a, {0x018,5010,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}}, | ||
385 | {0x3b, 0x40, {0x019,4370,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}}, | ||
386 | {0x41, 0x47, {0x01a,3478,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}}, | ||
387 | {0x48, 0x6c, {0x01b,2197,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}} | ||
388 | }; | ||
389 | static const opl4_region_t regions_35[] = { /* Voice Oohs */ | ||
390 | {0x15, 0x6c, {0x029,3596,100, 0,0,0x00,0xe6,0x20,0xf7,0x20,0x08,0x0}} | ||
391 | }; | ||
392 | static const opl4_region_t regions_36[] = { /* Synth Voice */ | ||
393 | {0x15, 0x6c, {0x02a,3482,100, 0,1,0x00,0xc2,0x19,0x85,0x21,0x07,0x0}} | ||
394 | }; | ||
395 | static const opl4_region_t regions_37[] = { /* Orchestra Hit */ | ||
396 | {0x15, 0x6c, {0x049,4394,100, 0,0,0x00,0xfe,0x30,0x80,0x05,0x05,0x0}} | ||
397 | }; | ||
398 | static const opl4_region_t regions_38[] = { /* Trumpet */ | ||
399 | {0x15, 0x3c, {0x0f6,4706,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}}, | ||
400 | {0x3d, 0x43, {0x0f8,3894,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}}, | ||
401 | {0x44, 0x48, {0x0f7,3118,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}}, | ||
402 | {0x49, 0x4e, {0x0fa,2322,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}}, | ||
403 | {0x4f, 0x55, {0x0f9,1634,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}}, | ||
404 | {0x56, 0x6c, {0x0fb, 786,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}} | ||
405 | }; | ||
406 | static const opl4_region_t regions_39[] = { /* Trombone */ | ||
407 | {0x15, 0x3a, {0x0f0,5053,100, 0,1,0x00,0xd6,0x21,0xf0,0x00,0x09,0x0}}, | ||
408 | {0x3b, 0x3f, {0x0f1,4290,100, 0,1,0x00,0xd6,0x21,0xf0,0x00,0x09,0x0}}, | ||
409 | {0x40, 0x6c, {0x0f2,3580,100, 0,1,0x00,0xd6,0x21,0xf0,0x00,0x09,0x0}} | ||
410 | }; | ||
411 | static const opl4_region_t regions_3a[] = { /* Tuba */ | ||
412 | {0x15, 0x2d, {0x085,7096,100, 0,1,0x00,0xde,0x21,0xf5,0x10,0x09,0x0}}, | ||
413 | {0x2e, 0x6c, {0x086,6014,100, 0,1,0x00,0xde,0x21,0xf5,0x10,0x09,0x0}} | ||
414 | }; | ||
415 | static const opl4_region_t regions_3b[] = { /* Muted Trumpet */ | ||
416 | {0x15, 0x45, {0x0b1,4135,100, 0,0,0x00,0xcc,0x28,0xf3,0x10,0x0a,0x1}}, | ||
417 | {0x46, 0x6c, {0x0b2,2599,100, 0,0,0x00,0xcc,0x28,0x83,0x10,0x0a,0x1}} | ||
418 | }; | ||
419 | static const opl4_region_t regions_3c[] = { /* French Horns */ | ||
420 | {0x15, 0x49, {0x07c,3624,100, 0,2,0x00,0xd0,0x1a,0xf0,0x00,0x09,0x0}}, | ||
421 | {0x4a, 0x6c, {0x07d,2664,100, 0,2,0x00,0xd0,0x1a,0xf0,0x00,0x09,0x0}} | ||
422 | }; | ||
423 | static const opl4_region_t regions_3d[] = { /* Brass Section */ | ||
424 | {0x15, 0x42, {0x0fc,4375,100, 0,0,0x00,0xd6,0x28,0xf0,0x00,0x0a,0x0}}, | ||
425 | {0x43, 0x6c, {0x0fd,2854,100, 0,0,0x00,0xd6,0x28,0xf0,0x00,0x0a,0x0}} | ||
426 | }; | ||
427 | static const opl4_region_t regions_3e[] = { /* Synth Brass 1 */ | ||
428 | {0x01, 0x27, {0x0d3,9094,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
429 | {0x28, 0x2d, {0x0da,8335,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
430 | {0x2e, 0x33, {0x0d4,7558,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
431 | {0x34, 0x39, {0x0db,6785,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
432 | {0x3a, 0x3f, {0x0d5,6042,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
433 | {0x40, 0x45, {0x0dc,5257,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
434 | {0x46, 0x4b, {0x0d6,4493,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
435 | {0x4c, 0x51, {0x0dd,3741,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
436 | {0x52, 0x57, {0x0d7,3012,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
437 | {0x58, 0x5d, {0x0de,2167,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
438 | {0x5e, 0x63, {0x0d8,1421,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
439 | {0x64, 0x7f, {0x0d9,-115,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}}, | ||
440 | {0x01, 0x27, {0x118,9103,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
441 | {0x28, 0x2d, {0x119,8340,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
442 | {0x2e, 0x33, {0x11a,7565,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
443 | {0x34, 0x39, {0x11b,6804,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
444 | {0x3a, 0x3f, {0x11c,6042,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
445 | {0x40, 0x45, {0x11d,5277,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
446 | {0x46, 0x4b, {0x11e,4520,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
447 | {0x4c, 0x51, {0x11f,3741,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
448 | {0x52, 0x57, {0x120,3012,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
449 | {0x58, 0x5d, {0x121,2166,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
450 | {0x5e, 0x64, {0x122,1421,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}, | ||
451 | {0x65, 0x7f, {0x123,-115,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}} | ||
452 | }; | ||
453 | static const opl4_region_t regions_3f[] = { /* Synth Brass 2 */ | ||
454 | {0x01, 0x27, {0x118,9113,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
455 | {0x28, 0x2d, {0x119,8350,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
456 | {0x2e, 0x33, {0x11a,7575,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
457 | {0x34, 0x39, {0x11b,6814,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
458 | {0x3a, 0x3f, {0x11c,6052,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
459 | {0x40, 0x45, {0x11d,5287,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
460 | {0x46, 0x4b, {0x11e,4530,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
461 | {0x4c, 0x51, {0x11f,3751,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
462 | {0x52, 0x57, {0x120,3022,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
463 | {0x58, 0x5d, {0x121,2176,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
464 | {0x5e, 0x64, {0x122,1431,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
465 | {0x65, 0x7f, {0x123,-105,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}}, | ||
466 | {0x00, 0x7f, {0x124,4034,100,-3,2,0x00,0xea,0x22,0x85,0x23,0x08,0x0}} | ||
467 | }; | ||
468 | static const opl4_region_t regions_40[] = { /* Soprano Sax */ | ||
469 | {0x15, 0x3f, {0x0e3,4228,100, 0,1,0x00,0xc8,0x21,0xf5,0x20,0x0a,0x0}}, | ||
470 | {0x40, 0x45, {0x0e4,3495,100, 0,1,0x00,0xc8,0x21,0xf5,0x20,0x0a,0x0}}, | ||
471 | {0x46, 0x4b, {0x0e5,2660,100, 0,1,0x00,0xd6,0x21,0xf5,0x20,0x0a,0x0}}, | ||
472 | {0x4c, 0x51, {0x0e6,2002,100, 0,1,0x00,0xd6,0x21,0xf5,0x20,0x0a,0x0}}, | ||
473 | {0x52, 0x59, {0x0e7,1186,100, 0,1,0x00,0xd6,0x21,0xf5,0x20,0x0a,0x0}}, | ||
474 | {0x59, 0x6c, {0x0e8,1730,100, 0,1,0x00,0xc8,0x21,0xf5,0x20,0x0a,0x0}} | ||
475 | }; | ||
476 | static const opl4_region_t regions_41[] = { /* Alto Sax */ | ||
477 | {0x15, 0x32, {0x092,6204,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
478 | {0x33, 0x35, {0x096,5812,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
479 | {0x36, 0x3a, {0x099,5318,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
480 | {0x3b, 0x3b, {0x08f,5076,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
481 | {0x3c, 0x3e, {0x093,4706,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
482 | {0x3f, 0x41, {0x097,4321,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
483 | {0x42, 0x44, {0x09a,3893,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
484 | {0x45, 0x47, {0x090,3497,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
485 | {0x48, 0x4a, {0x094,3119,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
486 | {0x4b, 0x4d, {0x098,2726,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
487 | {0x4e, 0x50, {0x09b,2393,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
488 | {0x51, 0x53, {0x091,2088,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}, | ||
489 | {0x54, 0x6c, {0x095,1732,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}} | ||
490 | }; | ||
491 | static const opl4_region_t regions_42[] = { /* Tenor Sax */ | ||
492 | {0x24, 0x30, {0x0e9,6301,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}, | ||
493 | {0x31, 0x34, {0x0ea,5781,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}, | ||
494 | {0x35, 0x3a, {0x0eb,5053,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}, | ||
495 | {0x3b, 0x41, {0x0ed,4165,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}, | ||
496 | {0x42, 0x47, {0x0ec,3218,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}, | ||
497 | {0x48, 0x51, {0x0ee,2462,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}, | ||
498 | {0x52, 0x6c, {0x0ef,1421,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}} | ||
499 | }; | ||
500 | static const opl4_region_t regions_43[] = { /* Baritone Sax */ | ||
501 | {0x15, 0x2d, {0x0df,6714,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}}, | ||
502 | {0x2e, 0x34, {0x0e1,5552,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}}, | ||
503 | {0x35, 0x39, {0x0e2,5178,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}}, | ||
504 | {0x3a, 0x6c, {0x0e0,4437,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}} | ||
505 | }; | ||
506 | static const opl4_region_t regions_44[] = { /* Oboe */ | ||
507 | {0x15, 0x3c, {0x042,4493,100, 0,1,0x00,0xe6,0x39,0xf4,0x10,0x0a,0x0}}, | ||
508 | {0x3d, 0x43, {0x044,3702,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}}, | ||
509 | {0x44, 0x49, {0x043,2956,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}}, | ||
510 | {0x4a, 0x4f, {0x046,2166,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}}, | ||
511 | {0x50, 0x55, {0x045,1420,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}}, | ||
512 | {0x56, 0x6c, {0x047, 630,100, 0,1,0x00,0xe6,0x39,0xf4,0x10,0x0a,0x0}} | ||
513 | }; | ||
514 | static const opl4_region_t regions_45[] = { /* English Horn */ | ||
515 | {0x15, 0x38, {0x03c,5098,100, 0,1,0x00,0xc4,0x31,0xf0,0x00,0x09,0x0}}, | ||
516 | {0x39, 0x3e, {0x03b,4291,100, 0,1,0x00,0xc4,0x31,0xf0,0x00,0x09,0x0}}, | ||
517 | {0x3f, 0x6c, {0x03d,3540,100, 0,1,0x00,0xc4,0x31,0xf0,0x00,0x09,0x0}} | ||
518 | }; | ||
519 | static const opl4_region_t regions_46[] = { /* Bassoon */ | ||
520 | {0x15, 0x22, {0x038,7833,100, 0,1,0x00,0xc6,0x31,0xf0,0x00,0x0b,0x0}}, | ||
521 | {0x23, 0x2e, {0x03a,7070,100, 0,1,0x00,0xc6,0x31,0xf0,0x00,0x0b,0x0}}, | ||
522 | {0x2f, 0x6c, {0x039,6302,100, 0,1,0x00,0xc6,0x31,0xf0,0x00,0x0b,0x0}} | ||
523 | }; | ||
524 | static const opl4_region_t regions_47[] = { /* Clarinet */ | ||
525 | {0x15, 0x3b, {0x09e,5900,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}}, | ||
526 | {0x3c, 0x41, {0x0a0,5158,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}}, | ||
527 | {0x42, 0x4a, {0x09f,4260,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}}, | ||
528 | {0x4b, 0x6c, {0x0a1,2957,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}} | ||
529 | }; | ||
530 | static const opl4_region_t regions_48[] = { /* Piccolo */ | ||
531 | {0x15, 0x40, {0x071,4803,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}}, | ||
532 | {0x41, 0x4d, {0x072,3314,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}}, | ||
533 | {0x4e, 0x53, {0x073,1731,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}}, | ||
534 | {0x54, 0x5f, {0x074,2085,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}}, | ||
535 | {0x60, 0x6c, {0x075,1421,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}} | ||
536 | }; | ||
537 | static const opl4_region_t regions_49[] = { /* Flute */ | ||
538 | {0x15, 0x40, {0x071,4803,100, 0,0,0x00,0xdc,0x38,0xf0,0x00,0x0a,0x2}}, | ||
539 | {0x41, 0x4d, {0x072,3314,100, 0,0,0x00,0xdc,0x38,0xf0,0x00,0x0a,0x2}}, | ||
540 | {0x4e, 0x6c, {0x073,1731,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}} | ||
541 | }; | ||
542 | static const opl4_region_t regions_4a[] = { /* Recorder */ | ||
543 | {0x15, 0x6f, {0x0bd,4897,100, 0,0,0x00,0xec,0x30,0x70,0x00,0x09,0x1}} | ||
544 | }; | ||
545 | static const opl4_region_t regions_4b[] = { /* Pan Flute */ | ||
546 | {0x15, 0x6c, {0x077,2359,100, 0,0,0x00,0xde,0x38,0xf0,0x00,0x09,0x3}} | ||
547 | }; | ||
548 | static const opl4_region_t regions_4c[] = { /* Bottle Blow */ | ||
549 | {0x15, 0x6c, {0x077,2359,100, 0,0,0x00,0xc8,0x38,0xf0,0x00,0x09,0x1}}, | ||
550 | {0x01, 0x7f, {0x125,7372,100, 0,0,0x1e,0x80,0x00,0xf0,0x00,0x09,0x0}} | ||
551 | }; | ||
552 | static const opl4_region_t regions_4d[] = { /* Shakuhachi */ | ||
553 | {0x00, 0x7f, {0x0ab,4548,100, 0,0,0x00,0xd6,0x30,0xf0,0x00,0x0a,0x3}}, | ||
554 | {0x15, 0x6c, {0x076,3716,100, 0,0,0x00,0xa2,0x28,0x70,0x00,0x09,0x2}} | ||
555 | }; | ||
556 | static const opl4_region_t regions_4e[] = { /* Whistle */ | ||
557 | {0x00, 0x7f, {0x0aa,1731,100, 0,4,0x00,0xd2,0x2c,0x70,0x00,0x0a,0x0}} | ||
558 | }; | ||
559 | static const opl4_region_t regions_4f[] = { /* Ocarina */ | ||
560 | {0x00, 0x7f, {0x0aa,1731,100, 0,1,0x00,0xce,0x29,0x90,0x00,0x0a,0x1}} | ||
561 | }; | ||
562 | static const opl4_region_t regions_50[] = { /* Square Lead */ | ||
563 | {0x01, 0x2a, {0x0cc,9853,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}}, | ||
564 | {0x2b, 0x36, {0x0cd,6785,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}}, | ||
565 | {0x37, 0x42, {0x0ca,5248,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}}, | ||
566 | {0x43, 0x4e, {0x0cf,3713,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}}, | ||
567 | {0x4f, 0x5a, {0x0ce,2176,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}}, | ||
568 | {0x5b, 0x7f, {0x0cb, 640,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}}, | ||
569 | {0x01, 0x2a, {0x0cc,9844,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}}, | ||
570 | {0x2b, 0x36, {0x0cd,6776,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}}, | ||
571 | {0x37, 0x42, {0x0ca,5239,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}}, | ||
572 | {0x43, 0x4e, {0x0cf,3704,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}}, | ||
573 | {0x4f, 0x5a, {0x0ce,2167,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}}, | ||
574 | {0x5b, 0x7f, {0x0cb, 631,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}} | ||
575 | }; | ||
576 | static const opl4_region_t regions_51[] = { /* Sawtooth Lead */ | ||
577 | {0x01, 0x27, {0x118,9108,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
578 | {0x28, 0x2d, {0x119,8345,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
579 | {0x2e, 0x33, {0x11a,7570,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
580 | {0x34, 0x39, {0x11b,6809,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
581 | {0x3a, 0x3f, {0x11c,6047,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
582 | {0x40, 0x45, {0x11d,5282,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
583 | {0x46, 0x4b, {0x11e,4525,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
584 | {0x4c, 0x51, {0x11f,3746,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
585 | {0x52, 0x57, {0x120,3017,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
586 | {0x58, 0x5d, {0x121,2171,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
587 | {0x5e, 0x66, {0x122,1426,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
588 | {0x67, 0x7f, {0x123,-110,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
589 | {0x01, 0x27, {0x118,9098,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
590 | {0x28, 0x2d, {0x119,8335,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
591 | {0x2e, 0x33, {0x11a,7560,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
592 | {0x34, 0x39, {0x11b,6799,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
593 | {0x3a, 0x3f, {0x11c,6037,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
594 | {0x40, 0x45, {0x11d,5272,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
595 | {0x46, 0x4b, {0x11e,4515,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
596 | {0x4c, 0x51, {0x11f,3736,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
597 | {0x52, 0x57, {0x120,3007,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
598 | {0x58, 0x5d, {0x121,2161,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
599 | {0x5e, 0x66, {0x122,1416,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}, | ||
600 | {0x67, 0x7f, {0x123,-120,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}} | ||
601 | }; | ||
602 | static const opl4_region_t regions_52[] = { /* Calliope Lead */ | ||
603 | {0x00, 0x7f, {0x0aa,1731,100, 0,0,0x00,0xc2,0x28,0x90,0x00,0x0a,0x2}}, | ||
604 | {0x15, 0x6c, {0x076,3716,100, 0,0,0x00,0xb6,0x28,0xb0,0x00,0x09,0x2}} | ||
605 | }; | ||
606 | static const opl4_region_t regions_53[] = { /* Chiffer Lead */ | ||
607 | {0x00, 0x7f, {0x13a,3665,100, 0,2,0x00,0xcc,0x2a,0xf0,0x10,0x09,0x1}}, | ||
608 | {0x01, 0x7f, {0x0fe,3660,100, 0,0,0x00,0xbe,0x28,0xf3,0x10,0x17,0x0}} | ||
609 | }; | ||
610 | static const opl4_region_t regions_54[] = { /* Charang Lead */ | ||
611 | {0x00, 0x40, {0x0a5,6594,100, 0,3,0x00,0xba,0x33,0xf2,0x11,0x09,0x0}}, | ||
612 | {0x41, 0x7f, {0x0a6,5433,100, 0,3,0x00,0xba,0x33,0xf2,0x11,0x09,0x0}}, | ||
613 | {0x01, 0x27, {0x118,9098,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
614 | {0x28, 0x2d, {0x119,8335,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
615 | {0x2e, 0x33, {0x11a,7560,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
616 | {0x34, 0x39, {0x11b,6799,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
617 | {0x3a, 0x3f, {0x11c,6037,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
618 | {0x40, 0x45, {0x11d,5272,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
619 | {0x46, 0x4b, {0x11e,4515,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
620 | {0x4c, 0x51, {0x11f,3736,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
621 | {0x52, 0x57, {0x120,3007,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
622 | {0x58, 0x5d, {0x121,2161,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
623 | {0x5e, 0x66, {0x122,1416,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}, | ||
624 | {0x67, 0x7f, {0x123,-120,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}} | ||
625 | }; | ||
626 | static const opl4_region_t regions_55[] = { /* Voice Lead */ | ||
627 | {0x00, 0x7f, {0x0aa,1739,100, 0,6,0x00,0x8c,0x2e,0x90,0x00,0x0a,0x0}}, | ||
628 | {0x15, 0x6c, {0x02a,3474,100, 0,1,0x00,0xd8,0x29,0xf0,0x05,0x0a,0x0}} | ||
629 | }; | ||
630 | static const opl4_region_t regions_56[] = { /* 5ths Lead */ | ||
631 | {0x01, 0x27, {0x118,8468,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
632 | {0x28, 0x2d, {0x119,7705,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
633 | {0x2e, 0x33, {0x11a,6930,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
634 | {0x34, 0x39, {0x11b,6169,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
635 | {0x3a, 0x3f, {0x11c,5407,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
636 | {0x40, 0x45, {0x11d,4642,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
637 | {0x46, 0x4b, {0x11e,3885,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
638 | {0x4c, 0x51, {0x11f,3106,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
639 | {0x52, 0x57, {0x120,2377,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
640 | {0x58, 0x5d, {0x121,1531,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
641 | {0x5e, 0x64, {0x122, 786,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
642 | {0x65, 0x7f, {0x123,-750,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}}, | ||
643 | {0x05, 0x71, {0x002,4503,100, 0,1,0x00,0xb8,0x31,0xb3,0x20,0x0b,0x0}} | ||
644 | }; | ||
645 | static const opl4_region_t regions_57[] = { /* Bass & Lead */ | ||
646 | {0x00, 0x7f, {0x117,8109,100, 0,1,0x00,0xbc,0x29,0xf3,0x50,0x08,0x0}}, | ||
647 | {0x01, 0x27, {0x118,9097,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
648 | {0x28, 0x2d, {0x119,8334,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
649 | {0x2e, 0x33, {0x11a,7559,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
650 | {0x34, 0x39, {0x11b,6798,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
651 | {0x3a, 0x3f, {0x11c,6036,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
652 | {0x40, 0x45, {0x11d,5271,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
653 | {0x46, 0x4b, {0x11e,4514,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
654 | {0x4c, 0x51, {0x11f,3735,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
655 | {0x52, 0x57, {0x120,3006,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
656 | {0x58, 0x5d, {0x121,2160,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
657 | {0x5e, 0x66, {0x122,1415,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}, | ||
658 | {0x67, 0x7f, {0x123,-121,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}} | ||
659 | }; | ||
660 | static const opl4_region_t regions_58[] = { /* New Age Pad */ | ||
661 | {0x15, 0x6c, {0x002,4501,100, 0,4,0x00,0xa4,0x24,0x80,0x01,0x05,0x0}}, | ||
662 | {0x15, 0x6c, {0x0f3,4253,100, 0,3,0x00,0x8c,0x23,0xa2,0x14,0x06,0x1}} | ||
663 | }; | ||
664 | static const opl4_region_t regions_59[] = { /* Warm Pad */ | ||
665 | {0x15, 0x6c, {0x04e,5306,100, 2,2,0x00,0x92,0x2a,0x34,0x23,0x05,0x2}}, | ||
666 | {0x15, 0x6c, {0x029,3575,100,-2,2,0x00,0xbe,0x22,0x31,0x23,0x06,0x0}} | ||
667 | }; | ||
668 | static const opl4_region_t regions_5a[] = { /* Polysynth Pad */ | ||
669 | {0x01, 0x27, {0x118,9111,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
670 | {0x28, 0x2d, {0x119,8348,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
671 | {0x2e, 0x33, {0x11a,7573,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
672 | {0x34, 0x39, {0x11b,6812,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
673 | {0x3a, 0x3f, {0x11c,6050,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
674 | {0x40, 0x45, {0x11d,5285,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
675 | {0x46, 0x4b, {0x11e,4528,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
676 | {0x4c, 0x51, {0x11f,3749,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
677 | {0x52, 0x57, {0x120,3020,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
678 | {0x58, 0x5d, {0x121,2174,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
679 | {0x5e, 0x66, {0x122,1429,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
680 | {0x67, 0x7f, {0x123,-107,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}}, | ||
681 | {0x00, 0x7f, {0x124,4024,100, 0,2,0x00,0xae,0x22,0xe5,0x20,0x08,0x0}} | ||
682 | }; | ||
683 | static const opl4_region_t regions_5b[] = { /* Choir Pad */ | ||
684 | {0x15, 0x3a, {0x018,5010,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}}, | ||
685 | {0x3b, 0x40, {0x019,4370,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}}, | ||
686 | {0x41, 0x47, {0x01a,3478,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}}, | ||
687 | {0x48, 0x6c, {0x01b,2197,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}}, | ||
688 | {0x15, 0x6c, {0x02a,3482,100, 0,4,0x00,0x98,0x24,0x65,0x21,0x06,0x0}} | ||
689 | }; | ||
690 | static const opl4_region_t regions_5c[] = { /* Bowed Pad */ | ||
691 | {0x15, 0x6c, {0x101,4790,100,-1,1,0x00,0xbe,0x19,0x44,0x14,0x16,0x0}}, | ||
692 | {0x00, 0x7f, {0x0aa,1720,100, 1,1,0x00,0x94,0x19,0x40,0x00,0x06,0x0}} | ||
693 | }; | ||
694 | static const opl4_region_t regions_5d[] = { /* Metallic Pad */ | ||
695 | {0x15, 0x31, {0x00c,6943,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}}, | ||
696 | {0x32, 0x38, {0x00d,5416,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}}, | ||
697 | {0x39, 0x47, {0x00e,4385,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}}, | ||
698 | {0x48, 0x6c, {0x00f,2849,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}}, | ||
699 | {0x00, 0x7f, {0x03f,4224,100, 0,1,0x00,0x9c,0x31,0x65,0x16,0x07,0x0}} | ||
700 | }; | ||
701 | static const opl4_region_t regions_5e[] = { /* Halo Pad */ | ||
702 | {0x00, 0x7f, {0x124,4038,100, 0,2,0x00,0xa6,0x1a,0x85,0x23,0x08,0x0}}, | ||
703 | {0x15, 0x6c, {0x02a,3471,100, 0,3,0x00,0xc0,0x1b,0xc0,0x05,0x06,0x0}} | ||
704 | }; | ||
705 | static const opl4_region_t regions_5f[] = { /* Sweep Pad */ | ||
706 | {0x01, 0x27, {0x0d3,9100,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
707 | {0x28, 0x2d, {0x0da,8341,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
708 | {0x2e, 0x33, {0x0d4,7564,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
709 | {0x34, 0x39, {0x0db,6791,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
710 | {0x3a, 0x3f, {0x0d5,6048,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
711 | {0x40, 0x45, {0x0dc,5263,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
712 | {0x46, 0x4b, {0x0d6,4499,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
713 | {0x4c, 0x51, {0x0dd,3747,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
714 | {0x52, 0x57, {0x0d7,3018,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
715 | {0x58, 0x5d, {0x0de,2173,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
716 | {0x5e, 0x63, {0x0d8,1427,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
717 | {0x64, 0x7f, {0x0d9,-109,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}}, | ||
718 | {0x01, 0x27, {0x0d3,9088,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
719 | {0x28, 0x2d, {0x0da,8329,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
720 | {0x2e, 0x33, {0x0d4,7552,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
721 | {0x34, 0x39, {0x0db,6779,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
722 | {0x3a, 0x3f, {0x0d5,6036,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
723 | {0x40, 0x45, {0x0dc,5251,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
724 | {0x46, 0x4b, {0x0d6,4487,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
725 | {0x4c, 0x51, {0x0dd,3735,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
726 | {0x52, 0x57, {0x0d7,3006,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
727 | {0x58, 0x5d, {0x0de,2161,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
728 | {0x5e, 0x63, {0x0d8,1415,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}, | ||
729 | {0x64, 0x7f, {0x0d9,-121,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}} | ||
730 | }; | ||
731 | static const opl4_region_t regions_60[] = { /* Ice Rain */ | ||
732 | {0x01, 0x7f, {0x04e,9345,100, 0,2,0x00,0xcc,0x22,0xa3,0x63,0x17,0x0}}, | ||
733 | {0x00, 0x7f, {0x143,5586, 20, 0,2,0x00,0x6e,0x2a,0xf0,0x05,0x05,0x0}} | ||
734 | }; | ||
735 | static const opl4_region_t regions_61[] = { /* Soundtrack */ | ||
736 | {0x15, 0x6c, {0x002,4501,100, 0,2,0x00,0xb6,0x2a,0x60,0x01,0x05,0x0}}, | ||
737 | {0x15, 0x6c, {0x0f3,1160,100, 0,5,0x00,0xa8,0x2d,0x52,0x14,0x06,0x2}} | ||
738 | }; | ||
739 | static const opl4_region_t regions_62[] = { /* Crystal */ | ||
740 | {0x15, 0x6c, {0x0f3,1826,100, 0,3,0x00,0xb8,0x33,0xf6,0x25,0x25,0x0}}, | ||
741 | {0x15, 0x2c, {0x06d,7454,100, 0,3,0x00,0xac,0x3b,0x85,0x24,0x06,0x0}}, | ||
742 | {0x2d, 0x36, {0x06e,5925,100, 0,3,0x00,0xac,0x3b,0x85,0x24,0x06,0x0}}, | ||
743 | {0x37, 0x6c, {0x06f,4403,100, 0,3,0x09,0xac,0x3b,0x85,0x24,0x06,0x0}} | ||
744 | }; | ||
745 | static const opl4_region_t regions_63[] = { /* Atmosphere */ | ||
746 | {0x05, 0x71, {0x002,4509,100, 0,2,0x00,0xc8,0x32,0x73,0x22,0x06,0x1}}, | ||
747 | {0x15, 0x2f, {0x0b3,6964,100, 0,2,0x05,0xc2,0x32,0xf5,0x34,0x07,0x2}}, | ||
748 | {0x30, 0x36, {0x0b7,5567,100, 0,2,0x0c,0xc2,0x32,0xf5,0x34,0x07,0x2}}, | ||
749 | {0x37, 0x3c, {0x0b5,4653,100, 0,2,0x00,0xc2,0x32,0xf6,0x34,0x07,0x2}}, | ||
750 | {0x3d, 0x43, {0x0b4,3892,100, 0,2,0x00,0xc2,0x32,0xf6,0x35,0x07,0x2}}, | ||
751 | {0x44, 0x60, {0x0b6,2723,100, 0,2,0x00,0xc2,0x32,0xf6,0x35,0x17,0x2}} | ||
752 | }; | ||
753 | static const opl4_region_t regions_64[] = { /* Brightness */ | ||
754 | {0x00, 0x7f, {0x137,5285,100, 0,2,0x00,0xbe,0x2a,0xa5,0x18,0x08,0x0}}, | ||
755 | {0x15, 0x6c, {0x02a,3481,100, 0,1,0x00,0xc8,0x29,0x80,0x05,0x05,0x0}} | ||
756 | }; | ||
757 | static const opl4_region_t regions_65[] = { /* Goblins */ | ||
758 | {0x15, 0x6c, {0x002,4501,100,-1,2,0x00,0xca,0x2a,0x40,0x01,0x05,0x0}}, | ||
759 | {0x15, 0x6c, {0x009,9679, 20, 1,4,0x00,0x3c,0x0c,0x22,0x11,0x06,0x0}} | ||
760 | }; | ||
761 | static const opl4_region_t regions_66[] = { /* Echoes */ | ||
762 | {0x15, 0x6c, {0x02a,3487,100, 0,3,0x00,0xae,0x2b,0xf5,0x21,0x06,0x0}}, | ||
763 | {0x00, 0x7f, {0x124,4027,100, 0,3,0x00,0xae,0x2b,0x85,0x23,0x07,0x0}} | ||
764 | }; | ||
765 | static const opl4_region_t regions_67[] = { /* Sci-Fi */ | ||
766 | {0x15, 0x31, {0x00c,6940,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}}, | ||
767 | {0x32, 0x38, {0x00d,5413,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}}, | ||
768 | {0x39, 0x47, {0x00e,4382,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}}, | ||
769 | {0x48, 0x6c, {0x00f,2846,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}}, | ||
770 | {0x15, 0x6c, {0x002,4498,100, 0,2,0x00,0xd4,0x22,0x80,0x01,0x05,0x0}} | ||
771 | }; | ||
772 | static const opl4_region_t regions_68[] = { /* Sitar */ | ||
773 | {0x00, 0x7f, {0x10f,4408,100, 0,2,0x00,0xc4,0x32,0xf4,0x15,0x16,0x1}} | ||
774 | }; | ||
775 | static const opl4_region_t regions_69[] = { /* Banjo */ | ||
776 | {0x15, 0x34, {0x013,5685,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}}, | ||
777 | {0x35, 0x38, {0x014,5009,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}}, | ||
778 | {0x39, 0x3c, {0x012,4520,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}}, | ||
779 | {0x3d, 0x44, {0x015,3622,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}}, | ||
780 | {0x45, 0x4c, {0x017,2661,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}}, | ||
781 | {0x4d, 0x6d, {0x016,1632,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}} | ||
782 | }; | ||
783 | static const opl4_region_t regions_6a[] = { /* Shamisen */ | ||
784 | {0x15, 0x6c, {0x10e,3273,100, 0,0,0x00,0xc0,0x28,0xf7,0x76,0x08,0x0}} | ||
785 | }; | ||
786 | static const opl4_region_t regions_6b[] = { /* Koto */ | ||
787 | {0x00, 0x7f, {0x0a9,4033,100, 0,0,0x00,0xc6,0x20,0xf0,0x06,0x07,0x0}} | ||
788 | }; | ||
789 | static const opl4_region_t regions_6c[] = { /* Kalimba */ | ||
790 | {0x00, 0x7f, {0x137,3749,100, 0,0,0x00,0xce,0x38,0xf5,0x18,0x08,0x0}} | ||
791 | }; | ||
792 | static const opl4_region_t regions_6d[] = { /* Bagpipe */ | ||
793 | {0x15, 0x39, {0x0a4,7683,100, 0,4,0x00,0xc0,0x1c,0xf0,0x00,0x09,0x0}}, | ||
794 | {0x15, 0x39, {0x0a7,7680,100, 0,1,0x00,0xaa,0x19,0xf0,0x00,0x09,0x0}}, | ||
795 | {0x3a, 0x6c, {0x0a8,3697,100, 0,1,0x00,0xaa,0x19,0xf0,0x00,0x09,0x0}} | ||
796 | }; | ||
797 | static const opl4_region_t regions_6e[] = { /* Fiddle */ | ||
798 | {0x15, 0x3a, {0x105,5158,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
799 | {0x3b, 0x3f, {0x102,4754,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
800 | {0x40, 0x41, {0x106,4132,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
801 | {0x42, 0x44, {0x107,4033,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
802 | {0x45, 0x47, {0x108,3580,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
803 | {0x48, 0x4a, {0x10a,2957,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
804 | {0x4b, 0x4c, {0x10b,2724,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
805 | {0x4d, 0x4e, {0x10c,2530,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
806 | {0x4f, 0x51, {0x10d,2166,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}, | ||
807 | {0x52, 0x6c, {0x109,1825,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}} | ||
808 | }; | ||
809 | static const opl4_region_t regions_6f[] = { /* Shanai */ | ||
810 | {0x15, 0x6c, {0x041,6946,100, 0,1,0x00,0xc4,0x31,0x95,0x20,0x09,0x0}} | ||
811 | }; | ||
812 | static const opl4_region_t regions_70[] = { /* Tinkle Bell */ | ||
813 | {0x15, 0x73, {0x0f3,1821,100, 0,3,0x00,0xc8,0x3b,0xd6,0x25,0x25,0x0}}, | ||
814 | {0x00, 0x7f, {0x137,5669,100, 0,3,0x00,0x66,0x3b,0xf5,0x18,0x08,0x0}} | ||
815 | }; | ||
816 | static const opl4_region_t regions_71[] = { /* Agogo */ | ||
817 | {0x15, 0x74, {0x00b,2474,100, 0,0,0x00,0xd2,0x38,0xf0,0x00,0x09,0x0}} | ||
818 | }; | ||
819 | static const opl4_region_t regions_72[] = { /* Steel Drums */ | ||
820 | {0x01, 0x7f, {0x0fe,3670,100, 0,0,0x00,0xca,0x38,0xf3,0x06,0x17,0x1}}, | ||
821 | {0x15, 0x6c, {0x100,9602,100, 0,0,0x00,0x54,0x38,0xb0,0x05,0x16,0x1}} | ||
822 | }; | ||
823 | static const opl4_region_t regions_73[] = { /* Woodblock */ | ||
824 | {0x15, 0x6c, {0x02c,2963, 50, 0,0,0x07,0xd4,0x00,0xf0,0x00,0x09,0x0}} | ||
825 | }; | ||
826 | static const opl4_region_t regions_74[] = { /* Taiko Drum */ | ||
827 | {0x13, 0x6c, {0x03e,1194, 50, 0,0,0x00,0xaa,0x38,0xf0,0x04,0x04,0x0}} | ||
828 | }; | ||
829 | static const opl4_region_t regions_75[] = { /* Melodic Tom */ | ||
830 | {0x15, 0x6c, {0x0c7,6418, 50, 0,0,0x00,0xe4,0x38,0xf0,0x05,0x01,0x0}} | ||
831 | }; | ||
832 | static const opl4_region_t regions_76[] = { /* Synth Drum */ | ||
833 | {0x15, 0x6c, {0x026,3898, 50, 0,0,0x00,0xd0,0x38,0xf0,0x04,0x04,0x0}} | ||
834 | }; | ||
835 | static const opl4_region_t regions_77[] = { /* Reverse Cymbal */ | ||
836 | {0x15, 0x6c, {0x031,4138, 50, 0,0,0x00,0xfe,0x38,0x3a,0xf0,0x09,0x0}} | ||
837 | }; | ||
838 | static const opl4_region_t regions_78[] = { /* Guitar Fret Noise */ | ||
839 | {0x15, 0x6c, {0x138,5266,100, 0,0,0x00,0xa0,0x38,0xf0,0x00,0x09,0x0}} | ||
840 | }; | ||
841 | static const opl4_region_t regions_79[] = { /* Breath Noise */ | ||
842 | {0x01, 0x7f, {0x125,4269,100, 0,0,0x1e,0xd0,0x38,0xf0,0x00,0x09,0x0}} | ||
843 | }; | ||
844 | static const opl4_region_t regions_7a[] = { /* Seashore */ | ||
845 | {0x15, 0x6c, {0x008,2965, 20,-2,0,0x00,0xfe,0x00,0x20,0x03,0x04,0x0}}, | ||
846 | {0x01, 0x7f, {0x037,4394, 20, 2,0,0x14,0xfe,0x00,0x20,0x04,0x05,0x0}} | ||
847 | }; | ||
848 | static const opl4_region_t regions_7b[] = { /* Bird Tweet */ | ||
849 | {0x15, 0x6c, {0x009,8078, 5,-4,7,0x00,0xc2,0x0f,0x22,0x12,0x07,0x0}}, | ||
850 | {0x15, 0x6c, {0x009,3583, 5, 4,5,0x00,0xae,0x15,0x72,0x12,0x07,0x0}} | ||
851 | }; | ||
852 | static const opl4_region_t regions_7c[] = { /* Telephone Ring */ | ||
853 | {0x15, 0x6c, {0x003,3602, 10, 0,0,0x00,0xce,0x00,0xf0,0x00,0x0f,0x0}} | ||
854 | }; | ||
855 | static const opl4_region_t regions_7d[] = { /* Helicopter */ | ||
856 | {0x0c, 0x7f, {0x001,2965, 10,-2,0,0x00,0xe0,0x08,0x30,0x01,0x07,0x0}}, | ||
857 | {0x01, 0x7f, {0x037,4394, 10, 2,0,0x44,0x76,0x00,0x30,0x01,0x07,0x0}} | ||
858 | }; | ||
859 | static const opl4_region_t regions_7e[] = { /* Applause */ | ||
860 | {0x15, 0x6c, {0x036,8273, 20,-6,7,0x00,0xc4,0x0f,0x70,0x01,0x05,0x0}}, | ||
861 | {0x15, 0x6c, {0x036,8115, 5, 6,7,0x00,0xc6,0x07,0x70,0x01,0x05,0x0}} | ||
862 | }; | ||
863 | static const opl4_region_t regions_7f[] = { /* Gun Shot */ | ||
864 | {0x15, 0x6c, {0x139,2858, 20, 0,0,0x00,0xbe,0x38,0xf0,0x03,0x00,0x0}} | ||
865 | }; | ||
866 | static const opl4_region_t regions_drums[] = { | ||
867 | {0x18, 0x18, {0x0cb,6397,100, 3,0,0x00,0xf4,0x38,0xc9,0x1c,0x0c,0x0}}, | ||
868 | {0x19, 0x19, {0x0c4,3714,100, 0,0,0x00,0xe0,0x00,0x97,0x19,0x09,0x0}}, | ||
869 | {0x1a, 0x1a, {0x0c4,3519,100, 0,0,0x00,0xea,0x00,0x61,0x01,0x07,0x0}}, | ||
870 | {0x1b, 0x1b, {0x0c4,3586,100, 0,0,0x00,0xea,0x00,0xf7,0x19,0x09,0x0}}, | ||
871 | {0x1c, 0x1c, {0x0c4,3586,100, 0,0,0x00,0xea,0x00,0x81,0x01,0x07,0x0}}, | ||
872 | {0x1e, 0x1e, {0x0c3,4783,100, 0,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
873 | {0x1f, 0x1f, {0x0d1,4042,100, 0,0,0x00,0xd6,0x00,0xf0,0x05,0x05,0x0}}, | ||
874 | {0x20, 0x20, {0x0d2,5943,100, 0,0,0x00,0xcc,0x00,0xf0,0x00,0x09,0x0}}, | ||
875 | {0x21, 0x21, {0x011,3842,100, 0,0,0x00,0xea,0x00,0xf0,0x16,0x06,0x0}}, | ||
876 | {0x23, 0x23, {0x011,4098,100, 0,0,0x00,0xea,0x00,0xf0,0x16,0x06,0x0}}, | ||
877 | {0x24, 0x24, {0x011,4370,100, 0,0,0x00,0xea,0x00,0xf0,0x00,0x06,0x0}}, | ||
878 | {0x25, 0x25, {0x0d2,4404,100, 0,0,0x00,0xd6,0x00,0xf0,0x00,0x06,0x0}}, | ||
879 | {0x26, 0x26, {0x0d1,4298,100, 0,0,0x00,0xd6,0x00,0xf0,0x05,0x05,0x0}}, | ||
880 | {0x27, 0x27, {0x00a,4403,100,-1,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}}, | ||
881 | {0x28, 0x28, {0x0d1,4554,100, 0,0,0x00,0xdc,0x00,0xf0,0x07,0x07,0x0}}, | ||
882 | {0x29, 0x29, {0x0c8,4242,100,-4,0,0x00,0xd6,0x00,0xf6,0x16,0x06,0x0}}, | ||
883 | {0x2a, 0x2a, {0x079,6160,100, 2,0,0x00,0xe0,0x00,0xf5,0x19,0x09,0x0}}, | ||
884 | {0x2b, 0x2b, {0x0c8,4626,100,-3,0,0x00,0xd6,0x00,0xf6,0x16,0x06,0x0}}, | ||
885 | {0x2c, 0x2c, {0x07b,6039,100, 2,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}}, | ||
886 | {0x2d, 0x2d, {0x0c8,5394,100,-2,0,0x00,0xd6,0x00,0xf6,0x16,0x06,0x0}}, | ||
887 | {0x2e, 0x2e, {0x07a,5690,100, 2,0,0x00,0xd6,0x00,0xf0,0x00,0x05,0x0}}, | ||
888 | {0x2f, 0x2f, {0x0c7,5185,100, 2,0,0x00,0xe0,0x00,0xf6,0x17,0x07,0x0}}, | ||
889 | {0x30, 0x30, {0x0c7,5650,100, 3,0,0x00,0xe0,0x00,0xf6,0x17,0x07,0x0}}, | ||
890 | {0x31, 0x31, {0x031,4395,100, 2,0,0x00,0xea,0x00,0xf0,0x05,0x05,0x0}}, | ||
891 | {0x32, 0x32, {0x0c7,6162,100, 4,0,0x00,0xe0,0x00,0xf6,0x17,0x07,0x0}}, | ||
892 | {0x33, 0x33, {0x02e,4391,100,-2,0,0x00,0xea,0x00,0xf0,0x05,0x05,0x0}}, | ||
893 | {0x34, 0x34, {0x07a,3009,100,-2,0,0x00,0xea,0x00,0xf2,0x15,0x05,0x0}}, | ||
894 | {0x35, 0x35, {0x021,4522,100,-3,0,0x00,0xd6,0x00,0xf0,0x05,0x05,0x0}}, | ||
895 | {0x36, 0x36, {0x025,5163,100, 1,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}}, | ||
896 | {0x37, 0x37, {0x031,5287,100,-1,0,0x00,0xea,0x00,0xf5,0x16,0x06,0x0}}, | ||
897 | {0x38, 0x38, {0x01d,4395,100, 2,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}}, | ||
898 | {0x39, 0x39, {0x031,4647,100,-2,0,0x00,0xea,0x00,0xf4,0x16,0x06,0x0}}, | ||
899 | {0x3a, 0x3a, {0x09d,4426,100,-4,0,0x00,0xe0,0x00,0xf4,0x17,0x07,0x0}}, | ||
900 | {0x3b, 0x3b, {0x02e,4659,100,-2,0,0x00,0xea,0x00,0xf0,0x06,0x06,0x0}}, | ||
901 | {0x3c, 0x3c, {0x01c,4769,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
902 | {0x3d, 0x3d, {0x01c,4611,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
903 | {0x3e, 0x3e, {0x01e,4402,100,-3,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
904 | {0x3f, 0x3f, {0x01f,4387,100,-3,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
905 | {0x40, 0x40, {0x01f,3983,100,-2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
906 | {0x41, 0x41, {0x09c,4526,100, 2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
907 | {0x42, 0x42, {0x09c,4016,100, 2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
908 | {0x43, 0x43, {0x00b,4739,100,-4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
909 | {0x44, 0x44, {0x00b,4179,100,-4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
910 | {0x45, 0x45, {0x02f,4787,100,-4,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}}, | ||
911 | {0x46, 0x46, {0x030,4665,100,-4,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}}, | ||
912 | {0x47, 0x47, {0x144,4519,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x0b,0x0}}, | ||
913 | {0x48, 0x48, {0x144,4111,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x0b,0x0}}, | ||
914 | {0x49, 0x49, {0x024,6408,100, 3,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}}, | ||
915 | {0x4a, 0x4a, {0x024,4144,100, 3,0,0x00,0xcc,0x00,0xf0,0x00,0x09,0x0}}, | ||
916 | {0x4b, 0x4b, {0x020,4001,100, 2,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}}, | ||
917 | {0x4c, 0x4c, {0x02c,4402,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
918 | {0x4d, 0x4d, {0x02c,3612,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
919 | {0x4e, 0x4e, {0x022,4129,100,-2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
920 | {0x4f, 0x4f, {0x023,4147,100,-2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}}, | ||
921 | {0x50, 0x50, {0x032,4412,100,-4,0,0x00,0xd6,0x00,0xf0,0x08,0x09,0x0}}, | ||
922 | {0x51, 0x51, {0x032,4385,100,-4,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}}, | ||
923 | {0x52, 0x52, {0x02f,5935,100,-1,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}} | ||
924 | }; | ||
925 | |||
926 | #define REGION(num) { ARRAY_SIZE(regions ## num), regions ## num } | ||
927 | const opl4_region_ptr_t snd_yrw801_regions[0x81] = { | ||
928 | REGION(_00), REGION(_01), REGION(_02), REGION(_03), | ||
929 | REGION(_04), REGION(_05), REGION(_06), REGION(_07), | ||
930 | REGION(_08), REGION(_09), REGION(_0a), REGION(_0b), | ||
931 | REGION(_0c), REGION(_0d), REGION(_0e), REGION(_0f), | ||
932 | REGION(_10), REGION(_11), REGION(_12), REGION(_13), | ||
933 | REGION(_14), REGION(_15), REGION(_16), REGION(_17), | ||
934 | REGION(_18), REGION(_19), REGION(_1a), REGION(_1b), | ||
935 | REGION(_1c), REGION(_1d), REGION(_1e), REGION(_1f), | ||
936 | REGION(_20), REGION(_21), REGION(_22), REGION(_23), | ||
937 | REGION(_24), REGION(_25), REGION(_26), REGION(_27), | ||
938 | REGION(_28), REGION(_29), REGION(_2a), REGION(_2b), | ||
939 | REGION(_2c), REGION(_2d), REGION(_2e), REGION(_2f), | ||
940 | REGION(_30), REGION(_31), REGION(_32), REGION(_33), | ||
941 | REGION(_34), REGION(_35), REGION(_36), REGION(_37), | ||
942 | REGION(_38), REGION(_39), REGION(_3a), REGION(_3b), | ||
943 | REGION(_3c), REGION(_3d), REGION(_3e), REGION(_3f), | ||
944 | REGION(_40), REGION(_41), REGION(_42), REGION(_43), | ||
945 | REGION(_44), REGION(_45), REGION(_46), REGION(_47), | ||
946 | REGION(_48), REGION(_49), REGION(_4a), REGION(_4b), | ||
947 | REGION(_4c), REGION(_4d), REGION(_4e), REGION(_4f), | ||
948 | REGION(_50), REGION(_51), REGION(_52), REGION(_53), | ||
949 | REGION(_54), REGION(_55), REGION(_56), REGION(_57), | ||
950 | REGION(_58), REGION(_59), REGION(_5a), REGION(_5b), | ||
951 | REGION(_5c), REGION(_5d), REGION(_5e), REGION(_5f), | ||
952 | REGION(_60), REGION(_61), REGION(_62), REGION(_63), | ||
953 | REGION(_64), REGION(_65), REGION(_66), REGION(_67), | ||
954 | REGION(_68), REGION(_69), REGION(_6a), REGION(_6b), | ||
955 | REGION(_6c), REGION(_6d), REGION(_6e), REGION(_6f), | ||
956 | REGION(_70), REGION(_71), REGION(_72), REGION(_73), | ||
957 | REGION(_74), REGION(_75), REGION(_76), REGION(_77), | ||
958 | REGION(_78), REGION(_79), REGION(_7a), REGION(_7b), | ||
959 | REGION(_7c), REGION(_7d), REGION(_7e), REGION(_7f), | ||
960 | REGION(_drums) | ||
961 | }; | ||
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c new file mode 100644 index 000000000000..964b97e70c84 --- /dev/null +++ b/sound/drivers/serial-u16550.c | |||
@@ -0,0 +1,990 @@ | |||
1 | /* | ||
2 | * serial.c | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz>, | ||
4 | * Isaku Yamahata <yamahata@private.email.ne.jp>, | ||
5 | * George Hansper <ghansper@apana.org.au>, | ||
6 | * Hannu Savolainen | ||
7 | * | ||
8 | * This code is based on the code from ALSA 0.5.9, but heavily rewritten. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com | ||
25 | * Added support for the Midiator MS-124T and for the MS-124W in | ||
26 | * Single Addressed (S/A) or Multiple Burst (M/B) mode, with | ||
27 | * power derived either parasitically from the serial port or | ||
28 | * from a separate power supply. | ||
29 | * | ||
30 | * More documentation can be found in serial-u16550.txt. | ||
31 | */ | ||
32 | |||
33 | #include <sound/driver.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/ioport.h> | ||
38 | #include <linux/moduleparam.h> | ||
39 | #include <sound/core.h> | ||
40 | #include <sound/rawmidi.h> | ||
41 | #include <sound/initval.h> | ||
42 | |||
43 | #include <linux/serial_reg.h> | ||
44 | |||
45 | #include <asm/io.h> | ||
46 | |||
47 | MODULE_DESCRIPTION("MIDI serial u16550"); | ||
48 | MODULE_LICENSE("GPL"); | ||
49 | MODULE_SUPPORTED_DEVICE("{{ALSA, MIDI serial u16550}}"); | ||
50 | |||
51 | #define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */ | ||
52 | #define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */ | ||
53 | #define SNDRV_SERIAL_MS124W_SA 2 /* Midiator MS-124W in S/A mode */ | ||
54 | #define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */ | ||
55 | #define SNDRV_SERIAL_GENERIC 4 /* Generic Interface */ | ||
56 | #define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_GENERIC | ||
57 | static char *adaptor_names[] = { | ||
58 | "Soundcanvas", | ||
59 | "MS-124T", | ||
60 | "MS-124W S/A", | ||
61 | "MS-124W M/B", | ||
62 | "Generic" | ||
63 | }; | ||
64 | |||
65 | #define SNDRV_SERIAL_NORMALBUFF 0 /* Normal blocking buffer operation */ | ||
66 | #define SNDRV_SERIAL_DROPBUFF 1 /* Non-blocking discard operation */ | ||
67 | |||
68 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
69 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
70 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
71 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */ | ||
72 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */ | ||
73 | static int speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */ | ||
74 | static int base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */ | ||
75 | static int outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */ | ||
76 | static int ins[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */ | ||
77 | static int adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS}; | ||
78 | static int droponfull[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS -1)] = SNDRV_SERIAL_NORMALBUFF }; | ||
79 | |||
80 | module_param_array(index, int, NULL, 0444); | ||
81 | MODULE_PARM_DESC(index, "Index value for Serial MIDI."); | ||
82 | module_param_array(id, charp, NULL, 0444); | ||
83 | MODULE_PARM_DESC(id, "ID string for Serial MIDI."); | ||
84 | module_param_array(enable, bool, NULL, 0444); | ||
85 | MODULE_PARM_DESC(enable, "Enable UART16550A chip."); | ||
86 | module_param_array(port, long, NULL, 0444); | ||
87 | MODULE_PARM_DESC(port, "Port # for UART16550A chip."); | ||
88 | module_param_array(irq, int, NULL, 0444); | ||
89 | MODULE_PARM_DESC(irq, "IRQ # for UART16550A chip."); | ||
90 | module_param_array(speed, int, NULL, 0444); | ||
91 | MODULE_PARM_DESC(speed, "Speed in bauds."); | ||
92 | module_param_array(base, int, NULL, 0444); | ||
93 | MODULE_PARM_DESC(base, "Base for divisor in bauds."); | ||
94 | module_param_array(outs, int, NULL, 0444); | ||
95 | MODULE_PARM_DESC(outs, "Number of MIDI outputs."); | ||
96 | module_param_array(ins, int, NULL, 0444); | ||
97 | MODULE_PARM_DESC(ins, "Number of MIDI inputs."); | ||
98 | module_param_array(droponfull, bool, NULL, 0444); | ||
99 | MODULE_PARM_DESC(droponfull, "Flag to enable drop-on-full buffer mode"); | ||
100 | |||
101 | module_param_array(adaptor, int, NULL, 0444); | ||
102 | MODULE_PARM_DESC(adaptor, "Type of adaptor."); | ||
103 | |||
104 | /*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/ /* Address outs as 0-3 instead of bitmap */ | ||
105 | |||
106 | #define SNDRV_SERIAL_MAX_OUTS 16 /* max 64, min 16 */ | ||
107 | #define SNDRV_SERIAL_MAX_INS 16 /* max 64, min 16 */ | ||
108 | |||
109 | #define TX_BUFF_SIZE (1<<15) /* Must be 2^n */ | ||
110 | #define TX_BUFF_MASK (TX_BUFF_SIZE - 1) | ||
111 | |||
112 | #define SERIAL_MODE_NOT_OPENED (0) | ||
113 | #define SERIAL_MODE_INPUT_OPEN (1 << 0) | ||
114 | #define SERIAL_MODE_OUTPUT_OPEN (1 << 1) | ||
115 | #define SERIAL_MODE_INPUT_TRIGGERED (1 << 2) | ||
116 | #define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3) | ||
117 | |||
118 | typedef struct _snd_uart16550 { | ||
119 | snd_card_t *card; | ||
120 | snd_rawmidi_t *rmidi; | ||
121 | snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS]; | ||
122 | snd_rawmidi_substream_t *midi_input[SNDRV_SERIAL_MAX_INS]; | ||
123 | |||
124 | int filemode; //open status of file | ||
125 | |||
126 | spinlock_t open_lock; | ||
127 | |||
128 | int irq; | ||
129 | |||
130 | unsigned long base; | ||
131 | struct resource *res_base; | ||
132 | |||
133 | unsigned int speed; | ||
134 | unsigned int speed_base; | ||
135 | unsigned char divisor; | ||
136 | |||
137 | unsigned char old_divisor_lsb; | ||
138 | unsigned char old_divisor_msb; | ||
139 | unsigned char old_line_ctrl_reg; | ||
140 | |||
141 | // parameter for using of write loop | ||
142 | short int fifo_limit; //used in uart16550 | ||
143 | short int fifo_count; //used in uart16550 | ||
144 | |||
145 | // type of adaptor | ||
146 | int adaptor; | ||
147 | |||
148 | // inputs | ||
149 | int prev_in; | ||
150 | unsigned char rstatus; | ||
151 | |||
152 | // outputs | ||
153 | int prev_out; | ||
154 | unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS]; | ||
155 | |||
156 | // write buffer and its writing/reading position | ||
157 | unsigned char tx_buff[TX_BUFF_SIZE]; | ||
158 | int buff_in_count; | ||
159 | int buff_in; | ||
160 | int buff_out; | ||
161 | int drop_on_full; | ||
162 | |||
163 | // wait timer | ||
164 | unsigned int timer_running:1; | ||
165 | struct timer_list buffer_timer; | ||
166 | |||
167 | } snd_uart16550_t; | ||
168 | |||
169 | static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
170 | |||
171 | inline static void snd_uart16550_add_timer(snd_uart16550_t *uart) | ||
172 | { | ||
173 | if (! uart->timer_running) { | ||
174 | /* timer 38600bps * 10bit * 16byte */ | ||
175 | uart->buffer_timer.expires = jiffies + (HZ+255)/256; | ||
176 | uart->timer_running = 1; | ||
177 | add_timer(&uart->buffer_timer); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | inline static void snd_uart16550_del_timer(snd_uart16550_t *uart) | ||
182 | { | ||
183 | if (uart->timer_running) { | ||
184 | del_timer(&uart->buffer_timer); | ||
185 | uart->timer_running = 0; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | /* This macro is only used in snd_uart16550_io_loop */ | ||
190 | inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart) | ||
191 | { | ||
192 | unsigned short buff_out = uart->buff_out; | ||
193 | if( uart->buff_in_count > 0 ) { | ||
194 | outb(uart->tx_buff[buff_out], uart->base + UART_TX); | ||
195 | uart->fifo_count++; | ||
196 | buff_out++; | ||
197 | buff_out &= TX_BUFF_MASK; | ||
198 | uart->buff_out = buff_out; | ||
199 | uart->buff_in_count--; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | /* This loop should be called with interrupts disabled | ||
204 | * We don't want to interrupt this, | ||
205 | * as we're already handling an interrupt | ||
206 | */ | ||
207 | static void snd_uart16550_io_loop(snd_uart16550_t * uart) | ||
208 | { | ||
209 | unsigned char c, status; | ||
210 | int substream; | ||
211 | |||
212 | /* recall previous stream */ | ||
213 | substream = uart->prev_in; | ||
214 | |||
215 | /* Read Loop */ | ||
216 | while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) { | ||
217 | /* while receive data ready */ | ||
218 | c = inb(uart->base + UART_RX); | ||
219 | |||
220 | /* keep track of last status byte */ | ||
221 | if (c & 0x80) { | ||
222 | uart->rstatus = c; | ||
223 | } | ||
224 | |||
225 | /* handle stream switch */ | ||
226 | if (uart->adaptor == SNDRV_SERIAL_GENERIC) { | ||
227 | if (uart->rstatus == 0xf5) { | ||
228 | if (c <= SNDRV_SERIAL_MAX_INS && c > 0) | ||
229 | substream = c - 1; | ||
230 | if (c != 0xf5) | ||
231 | uart->rstatus = 0; /* prevent future bytes from being interpreted as streams */ | ||
232 | } | ||
233 | else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) { | ||
234 | snd_rawmidi_receive(uart->midi_input[substream], &c, 1); | ||
235 | } | ||
236 | } else if ((uart->filemode & SERIAL_MODE_INPUT_OPEN) && (uart->midi_input[substream] != NULL)) { | ||
237 | snd_rawmidi_receive(uart->midi_input[substream], &c, 1); | ||
238 | } | ||
239 | |||
240 | if (status & UART_LSR_OE) | ||
241 | snd_printk("%s: Overrun on device at 0x%lx\n", | ||
242 | uart->rmidi->name, uart->base); | ||
243 | } | ||
244 | |||
245 | /* remember the last stream */ | ||
246 | uart->prev_in = substream; | ||
247 | |||
248 | /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not, | ||
249 | buffer is never filled. */ | ||
250 | /* Check write status */ | ||
251 | if (status & UART_LSR_THRE) { | ||
252 | uart->fifo_count = 0; | ||
253 | } | ||
254 | if (uart->adaptor == SNDRV_SERIAL_MS124W_SA | ||
255 | || uart->adaptor == SNDRV_SERIAL_GENERIC) { | ||
256 | /* Can't use FIFO, must send only when CTS is true */ | ||
257 | status = inb(uart->base + UART_MSR); | ||
258 | while( (uart->fifo_count == 0) && (status & UART_MSR_CTS) && | ||
259 | (uart->buff_in_count > 0) ) { | ||
260 | snd_uart16550_buffer_output(uart); | ||
261 | status = inb( uart->base + UART_MSR ); | ||
262 | } | ||
263 | } else { | ||
264 | /* Write loop */ | ||
265 | while (uart->fifo_count < uart->fifo_limit /* Can we write ? */ | ||
266 | && uart->buff_in_count > 0) /* Do we want to? */ | ||
267 | snd_uart16550_buffer_output(uart); | ||
268 | } | ||
269 | if (uart->irq < 0 && uart->buff_in_count > 0) | ||
270 | snd_uart16550_add_timer(uart); | ||
271 | } | ||
272 | |||
273 | /* NOTES ON SERVICING INTERUPTS | ||
274 | * --------------------------- | ||
275 | * After receiving a interrupt, it is important to indicate to the UART that | ||
276 | * this has been done. | ||
277 | * For a Rx interrupt, this is done by reading the received byte. | ||
278 | * For a Tx interrupt this is done by either: | ||
279 | * a) Writing a byte | ||
280 | * b) Reading the IIR | ||
281 | * It is particularly important to read the IIR if a Tx interrupt is received | ||
282 | * when there is no data in tx_buff[], as in this case there no other | ||
283 | * indication that the interrupt has been serviced, and it remains outstanding | ||
284 | * indefinitely. This has the curious side effect that and no further interrupts | ||
285 | * will be generated from this device AT ALL!!. | ||
286 | * It is also desirable to clear outstanding interrupts when the device is | ||
287 | * opened/closed. | ||
288 | * | ||
289 | * | ||
290 | * Note that some devices need OUT2 to be set before they will generate | ||
291 | * interrupts at all. (Possibly tied to an internal pull-up on CTS?) | ||
292 | */ | ||
293 | static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
294 | { | ||
295 | snd_uart16550_t *uart; | ||
296 | |||
297 | uart = (snd_uart16550_t *) dev_id; | ||
298 | spin_lock(&uart->open_lock); | ||
299 | if (uart->filemode == SERIAL_MODE_NOT_OPENED) { | ||
300 | spin_unlock(&uart->open_lock); | ||
301 | return IRQ_NONE; | ||
302 | } | ||
303 | inb(uart->base + UART_IIR); /* indicate to the UART that the interrupt has been serviced */ | ||
304 | snd_uart16550_io_loop(uart); | ||
305 | spin_unlock(&uart->open_lock); | ||
306 | return IRQ_HANDLED; | ||
307 | } | ||
308 | |||
309 | /* When the polling mode, this function calls snd_uart16550_io_loop. */ | ||
310 | static void snd_uart16550_buffer_timer(unsigned long data) | ||
311 | { | ||
312 | unsigned long flags; | ||
313 | snd_uart16550_t *uart; | ||
314 | |||
315 | uart = (snd_uart16550_t *)data; | ||
316 | spin_lock_irqsave(&uart->open_lock, flags); | ||
317 | snd_uart16550_del_timer(uart); | ||
318 | snd_uart16550_io_loop(uart); | ||
319 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * this method probes, if an uart sits on given port | ||
324 | * return 0 if found | ||
325 | * return negative error if not found | ||
326 | */ | ||
327 | static int __init snd_uart16550_detect(snd_uart16550_t *uart) | ||
328 | { | ||
329 | unsigned long io_base = uart->base; | ||
330 | int ok; | ||
331 | unsigned char c; | ||
332 | |||
333 | /* Do some vague tests for the presence of the uart */ | ||
334 | if (io_base == 0 || io_base == SNDRV_AUTO_PORT) { | ||
335 | return -ENODEV; /* Not configured */ | ||
336 | } | ||
337 | |||
338 | uart->res_base = request_region(io_base, 8, "Serial MIDI"); | ||
339 | if (uart->res_base == NULL) { | ||
340 | snd_printk(KERN_ERR "u16550: can't grab port 0x%lx\n", io_base); | ||
341 | return -EBUSY; | ||
342 | } | ||
343 | |||
344 | ok = 1; /* uart detected unless one of the following tests should fail */ | ||
345 | /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */ | ||
346 | outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */ | ||
347 | c = inb(io_base + UART_IER); | ||
348 | /* The top four bits of the IER should always == 0 */ | ||
349 | if ((c & 0xf0) != 0) | ||
350 | ok = 0; /* failed */ | ||
351 | |||
352 | outb(0xaa, io_base + UART_SCR); | ||
353 | /* Write arbitrary data into the scratch reg */ | ||
354 | c = inb(io_base + UART_SCR); | ||
355 | /* If it comes back, it's OK */ | ||
356 | if (c != 0xaa) | ||
357 | ok = 0; /* failed */ | ||
358 | |||
359 | outb(0x55, io_base + UART_SCR); | ||
360 | /* Write arbitrary data into the scratch reg */ | ||
361 | c = inb(io_base + UART_SCR); | ||
362 | /* If it comes back, it's OK */ | ||
363 | if (c != 0x55) | ||
364 | ok = 0; /* failed */ | ||
365 | |||
366 | return ok; | ||
367 | } | ||
368 | |||
369 | static void snd_uart16550_do_open(snd_uart16550_t * uart) | ||
370 | { | ||
371 | char byte; | ||
372 | |||
373 | /* Initialize basic variables */ | ||
374 | uart->buff_in_count = 0; | ||
375 | uart->buff_in = 0; | ||
376 | uart->buff_out = 0; | ||
377 | uart->fifo_limit = 1; | ||
378 | uart->fifo_count = 0; | ||
379 | uart->timer_running = 0; | ||
380 | |||
381 | outb(UART_FCR_ENABLE_FIFO /* Enable FIFO's (if available) */ | ||
382 | | UART_FCR_CLEAR_RCVR /* Clear receiver FIFO */ | ||
383 | | UART_FCR_CLEAR_XMIT /* Clear transmitter FIFO */ | ||
384 | | UART_FCR_TRIGGER_4 /* Set FIFO trigger at 4-bytes */ | ||
385 | /* NOTE: interrupt generated after T=(time)4-bytes | ||
386 | * if less than UART_FCR_TRIGGER bytes received | ||
387 | */ | ||
388 | ,uart->base + UART_FCR); /* FIFO Control Register */ | ||
389 | |||
390 | if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0) | ||
391 | uart->fifo_limit = 16; | ||
392 | if (uart->divisor != 0) { | ||
393 | uart->old_line_ctrl_reg = inb(uart->base + UART_LCR); | ||
394 | outb(UART_LCR_DLAB /* Divisor latch access bit */ | ||
395 | ,uart->base + UART_LCR); /* Line Control Register */ | ||
396 | uart->old_divisor_lsb = inb(uart->base + UART_DLL); | ||
397 | uart->old_divisor_msb = inb(uart->base + UART_DLM); | ||
398 | |||
399 | outb(uart->divisor | ||
400 | ,uart->base + UART_DLL); /* Divisor Latch Low */ | ||
401 | outb(0 | ||
402 | ,uart->base + UART_DLM); /* Divisor Latch High */ | ||
403 | /* DLAB is reset to 0 in next outb() */ | ||
404 | } | ||
405 | /* Set serial parameters (parity off, etc) */ | ||
406 | outb(UART_LCR_WLEN8 /* 8 data-bits */ | ||
407 | | 0 /* 1 stop-bit */ | ||
408 | | 0 /* parity off */ | ||
409 | | 0 /* DLAB = 0 */ | ||
410 | ,uart->base + UART_LCR); /* Line Control Register */ | ||
411 | |||
412 | switch (uart->adaptor) { | ||
413 | default: | ||
414 | outb(UART_MCR_RTS /* Set Request-To-Send line active */ | ||
415 | | UART_MCR_DTR /* Set Data-Terminal-Ready line active */ | ||
416 | | UART_MCR_OUT2 /* Set OUT2 - not always required, but when | ||
417 | * it is, it is ESSENTIAL for enabling interrupts | ||
418 | */ | ||
419 | ,uart->base + UART_MCR); /* Modem Control Register */ | ||
420 | break; | ||
421 | case SNDRV_SERIAL_MS124W_SA: | ||
422 | case SNDRV_SERIAL_MS124W_MB: | ||
423 | /* MS-124W can draw power from RTS and DTR if they | ||
424 | are in opposite states. */ | ||
425 | outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2, | ||
426 | uart->base + UART_MCR); | ||
427 | break; | ||
428 | case SNDRV_SERIAL_MS124T: | ||
429 | /* MS-124T can draw power from RTS and/or DTR (preferably | ||
430 | both) if they are both asserted. */ | ||
431 | outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2, | ||
432 | uart->base + UART_MCR); | ||
433 | break; | ||
434 | } | ||
435 | |||
436 | if (uart->irq < 0) { | ||
437 | byte = (0 & UART_IER_RDI) /* Disable Receiver data interrupt */ | ||
438 | |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interrupt */ | ||
439 | ; | ||
440 | } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) { | ||
441 | byte = UART_IER_RDI /* Enable Receiver data interrupt */ | ||
442 | | UART_IER_MSI /* Enable Modem status interrupt */ | ||
443 | ; | ||
444 | } else if (uart->adaptor == SNDRV_SERIAL_GENERIC) { | ||
445 | byte = UART_IER_RDI /* Enable Receiver data interrupt */ | ||
446 | | UART_IER_MSI /* Enable Modem status interrupt */ | ||
447 | | UART_IER_THRI /* Enable Transmitter holding register empty interrupt */ | ||
448 | ; | ||
449 | } else { | ||
450 | byte = UART_IER_RDI /* Enable Receiver data interrupt */ | ||
451 | | UART_IER_THRI /* Enable Transmitter holding register empty interrupt */ | ||
452 | ; | ||
453 | } | ||
454 | outb(byte, uart->base + UART_IER); /* Interupt enable Register */ | ||
455 | |||
456 | inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */ | ||
457 | inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */ | ||
458 | inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */ | ||
459 | } | ||
460 | |||
461 | static void snd_uart16550_do_close(snd_uart16550_t * uart) | ||
462 | { | ||
463 | if (uart->irq < 0) | ||
464 | snd_uart16550_del_timer(uart); | ||
465 | |||
466 | /* NOTE: may need to disable interrupts before de-registering out handler. | ||
467 | * For now, the consequences are harmless. | ||
468 | */ | ||
469 | |||
470 | outb((0 & UART_IER_RDI) /* Disable Receiver data interrupt */ | ||
471 | |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interrupt */ | ||
472 | ,uart->base + UART_IER); /* Interupt enable Register */ | ||
473 | |||
474 | switch (uart->adaptor) { | ||
475 | default: | ||
476 | outb((0 & UART_MCR_RTS) /* Deactivate Request-To-Send line */ | ||
477 | |(0 & UART_MCR_DTR) /* Deactivate Data-Terminal-Ready line */ | ||
478 | |(0 & UART_MCR_OUT2) /* Deactivate OUT2 */ | ||
479 | ,uart->base + UART_MCR); /* Modem Control Register */ | ||
480 | break; | ||
481 | case SNDRV_SERIAL_MS124W_SA: | ||
482 | case SNDRV_SERIAL_MS124W_MB: | ||
483 | /* MS-124W can draw power from RTS and DTR if they | ||
484 | are in opposite states; leave it powered. */ | ||
485 | outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2), | ||
486 | uart->base + UART_MCR); | ||
487 | break; | ||
488 | case SNDRV_SERIAL_MS124T: | ||
489 | /* MS-124T can draw power from RTS and/or DTR (preferably | ||
490 | both) if they are both asserted; leave it powered. */ | ||
491 | outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2), | ||
492 | uart->base + UART_MCR); | ||
493 | break; | ||
494 | } | ||
495 | |||
496 | inb(uart->base + UART_IIR); /* Clear any outstanding interrupts */ | ||
497 | |||
498 | /* Restore old divisor */ | ||
499 | if (uart->divisor != 0) { | ||
500 | outb(UART_LCR_DLAB /* Divisor latch access bit */ | ||
501 | ,uart->base + UART_LCR); /* Line Control Register */ | ||
502 | outb(uart->old_divisor_lsb | ||
503 | ,uart->base + UART_DLL); /* Divisor Latch Low */ | ||
504 | outb(uart->old_divisor_msb | ||
505 | ,uart->base + UART_DLM); /* Divisor Latch High */ | ||
506 | /* Restore old LCR (data bits, stop bits, parity, DLAB) */ | ||
507 | outb(uart->old_line_ctrl_reg | ||
508 | ,uart->base + UART_LCR); /* Line Control Register */ | ||
509 | } | ||
510 | } | ||
511 | |||
512 | static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream) | ||
513 | { | ||
514 | unsigned long flags; | ||
515 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
516 | |||
517 | spin_lock_irqsave(&uart->open_lock, flags); | ||
518 | if (uart->filemode == SERIAL_MODE_NOT_OPENED) | ||
519 | snd_uart16550_do_open(uart); | ||
520 | uart->filemode |= SERIAL_MODE_INPUT_OPEN; | ||
521 | uart->midi_input[substream->number] = substream; | ||
522 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream) | ||
527 | { | ||
528 | unsigned long flags; | ||
529 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
530 | |||
531 | spin_lock_irqsave(&uart->open_lock, flags); | ||
532 | uart->filemode &= ~SERIAL_MODE_INPUT_OPEN; | ||
533 | uart->midi_input[substream->number] = NULL; | ||
534 | if (uart->filemode == SERIAL_MODE_NOT_OPENED) | ||
535 | snd_uart16550_do_close(uart); | ||
536 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
541 | { | ||
542 | unsigned long flags; | ||
543 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
544 | |||
545 | spin_lock_irqsave(&uart->open_lock, flags); | ||
546 | if (up) { | ||
547 | uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED; | ||
548 | } else { | ||
549 | uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED; | ||
550 | } | ||
551 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
552 | } | ||
553 | |||
554 | static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream) | ||
555 | { | ||
556 | unsigned long flags; | ||
557 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
558 | |||
559 | spin_lock_irqsave(&uart->open_lock, flags); | ||
560 | if (uart->filemode == SERIAL_MODE_NOT_OPENED) | ||
561 | snd_uart16550_do_open(uart); | ||
562 | uart->filemode |= SERIAL_MODE_OUTPUT_OPEN; | ||
563 | uart->midi_output[substream->number] = substream; | ||
564 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
565 | return 0; | ||
566 | }; | ||
567 | |||
568 | static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream) | ||
569 | { | ||
570 | unsigned long flags; | ||
571 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
572 | |||
573 | spin_lock_irqsave(&uart->open_lock, flags); | ||
574 | uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN; | ||
575 | uart->midi_output[substream->number] = NULL; | ||
576 | if (uart->filemode == SERIAL_MODE_NOT_OPENED) | ||
577 | snd_uart16550_do_close(uart); | ||
578 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
579 | return 0; | ||
580 | }; | ||
581 | |||
582 | inline static int snd_uart16550_buffer_can_write( snd_uart16550_t *uart, int Num ) | ||
583 | { | ||
584 | if( uart->buff_in_count + Num < TX_BUFF_SIZE ) | ||
585 | return 1; | ||
586 | else | ||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | inline static int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) | ||
591 | { | ||
592 | unsigned short buff_in = uart->buff_in; | ||
593 | if( uart->buff_in_count < TX_BUFF_SIZE ) { | ||
594 | uart->tx_buff[buff_in] = byte; | ||
595 | buff_in++; | ||
596 | buff_in &= TX_BUFF_MASK; | ||
597 | uart->buff_in = buff_in; | ||
598 | uart->buff_in_count++; | ||
599 | if (uart->irq < 0) /* polling mode */ | ||
600 | snd_uart16550_add_timer(uart); | ||
601 | return 1; | ||
602 | } else | ||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | static int snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte) | ||
607 | { | ||
608 | if (uart->buff_in_count == 0 /* Buffer empty? */ | ||
609 | && ((uart->adaptor != SNDRV_SERIAL_MS124W_SA && | ||
610 | uart->adaptor != SNDRV_SERIAL_GENERIC) || | ||
611 | (uart->fifo_count == 0 /* FIFO empty? */ | ||
612 | && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */ | ||
613 | |||
614 | /* Tx Buffer Empty - try to write immediately */ | ||
615 | if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) { | ||
616 | /* Transmitter holding register (and Tx FIFO) empty */ | ||
617 | uart->fifo_count = 1; | ||
618 | outb(midi_byte, uart->base + UART_TX); | ||
619 | } else { | ||
620 | if (uart->fifo_count < uart->fifo_limit) { | ||
621 | uart->fifo_count++; | ||
622 | outb(midi_byte, uart->base + UART_TX); | ||
623 | } else { | ||
624 | /* Cannot write (buffer empty) - put char in buffer */ | ||
625 | snd_uart16550_write_buffer(uart, midi_byte); | ||
626 | } | ||
627 | } | ||
628 | } else { | ||
629 | if( !snd_uart16550_write_buffer(uart, midi_byte) ) { | ||
630 | snd_printk("%s: Buffer overrun on device at 0x%lx\n", | ||
631 | uart->rmidi->name, uart->base); | ||
632 | return 0; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | return 1; | ||
637 | } | ||
638 | |||
639 | static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream) | ||
640 | { | ||
641 | unsigned long flags; | ||
642 | unsigned char midi_byte, addr_byte; | ||
643 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
644 | char first; | ||
645 | static unsigned long lasttime=0; | ||
646 | |||
647 | /* Interupts are disabled during the updating of the tx_buff, | ||
648 | * since it is 'bad' to have two processes updating the same | ||
649 | * variables (ie buff_in & buff_out) | ||
650 | */ | ||
651 | |||
652 | spin_lock_irqsave(&uart->open_lock, flags); | ||
653 | |||
654 | if (uart->irq < 0) //polling | ||
655 | snd_uart16550_io_loop(uart); | ||
656 | |||
657 | if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) { | ||
658 | while (1) { | ||
659 | /* buffer full? */ | ||
660 | /* in this mode we need two bytes of space */ | ||
661 | if (uart->buff_in_count > TX_BUFF_SIZE - 2) | ||
662 | break; | ||
663 | if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) | ||
664 | break; | ||
665 | #ifdef SNDRV_SERIAL_MS124W_MB_NOCOMBO | ||
666 | /* select exactly one of the four ports */ | ||
667 | addr_byte = (1 << (substream->number + 4)) | 0x08; | ||
668 | #else | ||
669 | /* select any combination of the four ports */ | ||
670 | addr_byte = (substream->number << 4) | 0x08; | ||
671 | /* ...except none */ | ||
672 | if (addr_byte == 0x08) addr_byte = 0xf8; | ||
673 | #endif | ||
674 | snd_uart16550_output_byte(uart, substream, addr_byte); | ||
675 | /* send midi byte */ | ||
676 | snd_uart16550_output_byte(uart, substream, midi_byte); | ||
677 | } | ||
678 | } else { | ||
679 | first = 0; | ||
680 | while( 1 == snd_rawmidi_transmit_peek(substream, &midi_byte, 1) ) { | ||
681 | /* Also send F5 after 3 seconds with no data to handle device disconnect */ | ||
682 | if (first == 0 && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS || | ||
683 | uart->adaptor == SNDRV_SERIAL_GENERIC) && | ||
684 | (uart->prev_out != substream->number || jiffies-lasttime > 3*HZ)) { | ||
685 | |||
686 | if( snd_uart16550_buffer_can_write( uart, 3 ) ) { | ||
687 | /* Roland Soundcanvas part selection */ | ||
688 | /* If this substream of the data is different previous | ||
689 | substream in this uart, send the change part event */ | ||
690 | uart->prev_out = substream->number; | ||
691 | /* change part */ | ||
692 | snd_uart16550_output_byte(uart, substream, 0xf5); | ||
693 | /* data */ | ||
694 | snd_uart16550_output_byte(uart, substream, uart->prev_out + 1); | ||
695 | /* If midi_byte is a data byte, send the previous status byte */ | ||
696 | if ((midi_byte < 0x80) && (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS)) | ||
697 | snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]); | ||
698 | } else if( !uart->drop_on_full ) | ||
699 | break; | ||
700 | |||
701 | } | ||
702 | |||
703 | /* send midi byte */ | ||
704 | if( !snd_uart16550_output_byte(uart, substream, midi_byte) && !uart->drop_on_full ) | ||
705 | break; | ||
706 | |||
707 | if (midi_byte >= 0x80 && midi_byte < 0xf0) | ||
708 | uart->prev_status[uart->prev_out] = midi_byte; | ||
709 | first = 1; | ||
710 | |||
711 | snd_rawmidi_transmit_ack( substream, 1 ); | ||
712 | } | ||
713 | lasttime = jiffies; | ||
714 | } | ||
715 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
716 | } | ||
717 | |||
718 | static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
719 | { | ||
720 | unsigned long flags; | ||
721 | snd_uart16550_t *uart = substream->rmidi->private_data; | ||
722 | |||
723 | spin_lock_irqsave(&uart->open_lock, flags); | ||
724 | if (up) { | ||
725 | uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED; | ||
726 | } else { | ||
727 | uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED; | ||
728 | } | ||
729 | spin_unlock_irqrestore(&uart->open_lock, flags); | ||
730 | if (up) | ||
731 | snd_uart16550_output_write(substream); | ||
732 | } | ||
733 | |||
734 | static snd_rawmidi_ops_t snd_uart16550_output = | ||
735 | { | ||
736 | .open = snd_uart16550_output_open, | ||
737 | .close = snd_uart16550_output_close, | ||
738 | .trigger = snd_uart16550_output_trigger, | ||
739 | }; | ||
740 | |||
741 | static snd_rawmidi_ops_t snd_uart16550_input = | ||
742 | { | ||
743 | .open = snd_uart16550_input_open, | ||
744 | .close = snd_uart16550_input_close, | ||
745 | .trigger = snd_uart16550_input_trigger, | ||
746 | }; | ||
747 | |||
748 | static int snd_uart16550_free(snd_uart16550_t *uart) | ||
749 | { | ||
750 | if (uart->irq >= 0) | ||
751 | free_irq(uart->irq, (void *)uart); | ||
752 | if (uart->res_base) { | ||
753 | release_resource(uart->res_base); | ||
754 | kfree_nocheck(uart->res_base); | ||
755 | } | ||
756 | kfree(uart); | ||
757 | return 0; | ||
758 | }; | ||
759 | |||
760 | static int snd_uart16550_dev_free(snd_device_t *device) | ||
761 | { | ||
762 | snd_uart16550_t *uart = device->device_data; | ||
763 | return snd_uart16550_free(uart); | ||
764 | } | ||
765 | |||
766 | static int __init snd_uart16550_create(snd_card_t * card, | ||
767 | unsigned long iobase, | ||
768 | int irq, | ||
769 | unsigned int speed, | ||
770 | unsigned int base, | ||
771 | int adaptor, | ||
772 | int droponfull, | ||
773 | snd_uart16550_t **ruart) | ||
774 | { | ||
775 | static snd_device_ops_t ops = { | ||
776 | .dev_free = snd_uart16550_dev_free, | ||
777 | }; | ||
778 | snd_uart16550_t *uart; | ||
779 | int err; | ||
780 | |||
781 | |||
782 | if ((uart = kcalloc(1, sizeof(*uart), GFP_KERNEL)) == NULL) | ||
783 | return -ENOMEM; | ||
784 | uart->adaptor = adaptor; | ||
785 | uart->card = card; | ||
786 | spin_lock_init(&uart->open_lock); | ||
787 | uart->irq = -1; | ||
788 | uart->base = iobase; | ||
789 | uart->drop_on_full = droponfull; | ||
790 | |||
791 | if ((err = snd_uart16550_detect(uart)) <= 0) { | ||
792 | printk(KERN_ERR "no UART detected at 0x%lx\n", iobase); | ||
793 | return err; | ||
794 | } | ||
795 | |||
796 | if (irq >= 0 && irq != SNDRV_AUTO_IRQ) { | ||
797 | if (request_irq(irq, snd_uart16550_interrupt, | ||
798 | SA_INTERRUPT, "Serial MIDI", (void *) uart)) { | ||
799 | snd_printk("irq %d busy. Using Polling.\n", irq); | ||
800 | } else { | ||
801 | uart->irq = irq; | ||
802 | } | ||
803 | } | ||
804 | uart->divisor = base / speed; | ||
805 | uart->speed = base / (unsigned int)uart->divisor; | ||
806 | uart->speed_base = base; | ||
807 | uart->prev_out = -1; | ||
808 | uart->prev_in = 0; | ||
809 | uart->rstatus = 0; | ||
810 | memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS); | ||
811 | init_timer(&uart->buffer_timer); | ||
812 | uart->buffer_timer.function = snd_uart16550_buffer_timer; | ||
813 | uart->buffer_timer.data = (unsigned long)uart; | ||
814 | uart->timer_running = 0; | ||
815 | |||
816 | /* Register device */ | ||
817 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) { | ||
818 | snd_uart16550_free(uart); | ||
819 | return err; | ||
820 | } | ||
821 | |||
822 | switch (uart->adaptor) { | ||
823 | case SNDRV_SERIAL_MS124W_SA: | ||
824 | case SNDRV_SERIAL_MS124W_MB: | ||
825 | /* MS-124W can draw power from RTS and DTR if they | ||
826 | are in opposite states. */ | ||
827 | outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR); | ||
828 | break; | ||
829 | case SNDRV_SERIAL_MS124T: | ||
830 | /* MS-124T can draw power from RTS and/or DTR (preferably | ||
831 | both) if they are asserted. */ | ||
832 | outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR); | ||
833 | break; | ||
834 | default: | ||
835 | break; | ||
836 | } | ||
837 | |||
838 | if (ruart) | ||
839 | *ruart = uart; | ||
840 | |||
841 | return 0; | ||
842 | } | ||
843 | |||
844 | static void __init snd_uart16550_substreams(snd_rawmidi_str_t *stream) | ||
845 | { | ||
846 | struct list_head *list; | ||
847 | |||
848 | list_for_each(list, &stream->substreams) { | ||
849 | snd_rawmidi_substream_t *substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
850 | sprintf(substream->name, "Serial MIDI %d", substream->number + 1); | ||
851 | } | ||
852 | } | ||
853 | |||
854 | static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, int ins, snd_rawmidi_t **rmidi) | ||
855 | { | ||
856 | snd_rawmidi_t *rrawmidi; | ||
857 | int err; | ||
858 | |||
859 | if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, ins, &rrawmidi)) < 0) | ||
860 | return err; | ||
861 | snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input); | ||
862 | snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); | ||
863 | strcpy(rrawmidi->name, "Serial MIDI"); | ||
864 | snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); | ||
865 | snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); | ||
866 | rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | | ||
867 | SNDRV_RAWMIDI_INFO_INPUT | | ||
868 | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
869 | rrawmidi->private_data = uart; | ||
870 | if (rmidi) | ||
871 | *rmidi = rrawmidi; | ||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | static int __init snd_serial_probe(int dev) | ||
876 | { | ||
877 | snd_card_t *card; | ||
878 | snd_uart16550_t *uart; | ||
879 | int err; | ||
880 | |||
881 | if (!enable[dev]) | ||
882 | return -ENOENT; | ||
883 | |||
884 | switch (adaptor[dev]) { | ||
885 | case SNDRV_SERIAL_SOUNDCANVAS: | ||
886 | ins[dev] = 1; | ||
887 | break; | ||
888 | case SNDRV_SERIAL_MS124T: | ||
889 | case SNDRV_SERIAL_MS124W_SA: | ||
890 | outs[dev] = 1; | ||
891 | ins[dev] = 1; | ||
892 | break; | ||
893 | case SNDRV_SERIAL_MS124W_MB: | ||
894 | outs[dev] = 16; | ||
895 | ins[dev] = 1; | ||
896 | break; | ||
897 | case SNDRV_SERIAL_GENERIC: | ||
898 | break; | ||
899 | default: | ||
900 | snd_printk("Adaptor type is out of range 0-%d (%d)\n", | ||
901 | SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]); | ||
902 | return -ENODEV; | ||
903 | } | ||
904 | |||
905 | if (outs[dev] < 1 || outs[dev] > SNDRV_SERIAL_MAX_OUTS) { | ||
906 | snd_printk("Count of outputs is out of range 1-%d (%d)\n", | ||
907 | SNDRV_SERIAL_MAX_OUTS, outs[dev]); | ||
908 | return -ENODEV; | ||
909 | } | ||
910 | |||
911 | if (ins[dev] < 1 || ins[dev] > SNDRV_SERIAL_MAX_INS) { | ||
912 | snd_printk("Count of inputs is out of range 1-%d (%d)\n", | ||
913 | SNDRV_SERIAL_MAX_INS, ins[dev]); | ||
914 | return -ENODEV; | ||
915 | } | ||
916 | |||
917 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
918 | if (card == NULL) | ||
919 | return -ENOMEM; | ||
920 | |||
921 | strcpy(card->driver, "Serial"); | ||
922 | strcpy(card->shortname, "Serial MIDI (UART16550A)"); | ||
923 | |||
924 | if ((err = snd_uart16550_create(card, | ||
925 | port[dev], | ||
926 | irq[dev], | ||
927 | speed[dev], | ||
928 | base[dev], | ||
929 | adaptor[dev], | ||
930 | droponfull[dev], | ||
931 | &uart)) < 0) { | ||
932 | snd_card_free(card); | ||
933 | return err; | ||
934 | } | ||
935 | |||
936 | if ((err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi)) < 0) { | ||
937 | snd_card_free(card); | ||
938 | return err; | ||
939 | } | ||
940 | |||
941 | sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d ins %d adaptor %s droponfull %d", | ||
942 | card->shortname, | ||
943 | uart->base, | ||
944 | uart->irq, | ||
945 | uart->speed, | ||
946 | (int)uart->divisor, | ||
947 | outs[dev], | ||
948 | ins[dev], | ||
949 | adaptor_names[uart->adaptor], | ||
950 | uart->drop_on_full); | ||
951 | |||
952 | if ((err = snd_card_register(card)) < 0) { | ||
953 | snd_card_free(card); | ||
954 | return err; | ||
955 | } | ||
956 | snd_serial_cards[dev] = card; | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | static int __init alsa_card_serial_init(void) | ||
961 | { | ||
962 | int dev = 0; | ||
963 | int cards = 0; | ||
964 | |||
965 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
966 | if (snd_serial_probe(dev) == 0) | ||
967 | cards++; | ||
968 | } | ||
969 | |||
970 | if (cards == 0) { | ||
971 | #ifdef MODULE | ||
972 | printk(KERN_ERR "serial midi soundcard not found or device busy\n"); | ||
973 | #endif | ||
974 | return -ENODEV; | ||
975 | } | ||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | static void __exit alsa_card_serial_exit(void) | ||
980 | { | ||
981 | int dev; | ||
982 | |||
983 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
984 | if (snd_serial_cards[dev] != NULL) | ||
985 | snd_card_free(snd_serial_cards[dev]); | ||
986 | } | ||
987 | } | ||
988 | |||
989 | module_init(alsa_card_serial_init) | ||
990 | module_exit(alsa_card_serial_exit) | ||
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c new file mode 100644 index 000000000000..5937711e9505 --- /dev/null +++ b/sound/drivers/virmidi.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Dummy soundcard for virtual rawmidi devices | ||
3 | * | ||
4 | * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * VIRTUAL RAW MIDI DEVICE CARDS | ||
24 | * | ||
25 | * This dummy card contains up to 4 virtual rawmidi devices. | ||
26 | * They are not real rawmidi devices but just associated with sequencer | ||
27 | * clients, so that any input/output sources can be connected as a raw | ||
28 | * MIDI device arbitrary. | ||
29 | * Also, multiple access is allowed to a single rawmidi device. | ||
30 | * | ||
31 | * Typical usage is like following: | ||
32 | * - Load snd-virmidi module. | ||
33 | * # modprobe snd-virmidi index=2 | ||
34 | * Then, sequencer clients 72:0 to 75:0 will be created, which are | ||
35 | * mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively. | ||
36 | * | ||
37 | * - Connect input/output via aconnect. | ||
38 | * % aconnect 64:0 72:0 # keyboard input redirection 64:0 -> 72:0 | ||
39 | * % aconnect 72:0 65:0 # output device redirection 72:0 -> 65:0 | ||
40 | * | ||
41 | * - Run application using a midi device (eg. /dev/snd/midiC1D0) | ||
42 | */ | ||
43 | |||
44 | #include <sound/driver.h> | ||
45 | #include <linux/init.h> | ||
46 | #include <linux/wait.h> | ||
47 | #include <linux/sched.h> | ||
48 | #include <linux/moduleparam.h> | ||
49 | #include <sound/core.h> | ||
50 | #include <sound/seq_kernel.h> | ||
51 | #include <sound/seq_virmidi.h> | ||
52 | #include <sound/initval.h> | ||
53 | |||
54 | /* hack: OSS defines midi_devs, so undefine it (versioned symbols) */ | ||
55 | #undef midi_devs | ||
56 | |||
57 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
58 | MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices"); | ||
59 | MODULE_LICENSE("GPL"); | ||
60 | MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual rawmidi device}}"); | ||
61 | |||
62 | #define MAX_MIDI_DEVICES 8 | ||
63 | |||
64 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
65 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
66 | static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | ||
67 | static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; | ||
68 | |||
69 | module_param_array(index, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(index, "Index value for virmidi soundcard."); | ||
71 | module_param_array(id, charp, NULL, 0444); | ||
72 | MODULE_PARM_DESC(id, "ID string for virmidi soundcard."); | ||
73 | module_param_array(enable, bool, NULL, 0444); | ||
74 | MODULE_PARM_DESC(enable, "Enable this soundcard."); | ||
75 | module_param_array(midi_devs, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(midi_devs, "MIDI devices # (1-8)"); | ||
77 | |||
78 | typedef struct snd_card_virmidi { | ||
79 | snd_card_t *card; | ||
80 | snd_rawmidi_t *midi[MAX_MIDI_DEVICES]; | ||
81 | } snd_card_virmidi_t; | ||
82 | |||
83 | static snd_card_t *snd_virmidi_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
84 | |||
85 | |||
86 | static int __init snd_card_virmidi_probe(int dev) | ||
87 | { | ||
88 | snd_card_t *card; | ||
89 | struct snd_card_virmidi *vmidi; | ||
90 | int idx, err; | ||
91 | |||
92 | if (!enable[dev]) | ||
93 | return -ENODEV; | ||
94 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
95 | sizeof(struct snd_card_virmidi)); | ||
96 | if (card == NULL) | ||
97 | return -ENOMEM; | ||
98 | vmidi = (struct snd_card_virmidi *)card->private_data; | ||
99 | vmidi->card = card; | ||
100 | |||
101 | if (midi_devs[dev] > MAX_MIDI_DEVICES) { | ||
102 | snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES); | ||
103 | midi_devs[dev] = MAX_MIDI_DEVICES; | ||
104 | } | ||
105 | for (idx = 0; idx < midi_devs[dev]; idx++) { | ||
106 | snd_rawmidi_t *rmidi; | ||
107 | snd_virmidi_dev_t *rdev; | ||
108 | if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) | ||
109 | goto __nodev; | ||
110 | rdev = rmidi->private_data; | ||
111 | vmidi->midi[idx] = rmidi; | ||
112 | strcpy(rmidi->name, "Virtual Raw MIDI"); | ||
113 | rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; | ||
114 | } | ||
115 | |||
116 | strcpy(card->driver, "VirMIDI"); | ||
117 | strcpy(card->shortname, "VirMIDI"); | ||
118 | sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); | ||
119 | if ((err = snd_card_register(card)) == 0) { | ||
120 | snd_virmidi_cards[dev] = card; | ||
121 | return 0; | ||
122 | } | ||
123 | __nodev: | ||
124 | snd_card_free(card); | ||
125 | return err; | ||
126 | } | ||
127 | |||
128 | static int __init alsa_card_virmidi_init(void) | ||
129 | { | ||
130 | int dev, cards; | ||
131 | |||
132 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
133 | if (snd_card_virmidi_probe(dev) < 0) { | ||
134 | #ifdef MODULE | ||
135 | printk(KERN_ERR "Card-VirMIDI #%i not found or device busy\n", dev + 1); | ||
136 | #endif | ||
137 | break; | ||
138 | } | ||
139 | cards++; | ||
140 | } | ||
141 | if (!cards) { | ||
142 | #ifdef MODULE | ||
143 | printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n"); | ||
144 | #endif | ||
145 | return -ENODEV; | ||
146 | } | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static void __exit alsa_card_virmidi_exit(void) | ||
151 | { | ||
152 | int dev; | ||
153 | |||
154 | for (dev = 0; dev < SNDRV_CARDS; dev++) | ||
155 | snd_card_free(snd_virmidi_cards[dev]); | ||
156 | } | ||
157 | |||
158 | module_init(alsa_card_virmidi_init) | ||
159 | module_exit(alsa_card_virmidi_exit) | ||
diff --git a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile new file mode 100644 index 000000000000..269bd8544a5d --- /dev/null +++ b/sound/drivers/vx/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o | ||
7 | |||
8 | obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o | ||
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c new file mode 100644 index 000000000000..7a221349f285 --- /dev/null +++ b/sound/drivers/vx/vx_cmd.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * DSP commands | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
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 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | /* | ||
30 | * Array of DSP commands | ||
31 | */ | ||
32 | static struct vx_cmd_info vx_dsp_cmds[] = { | ||
33 | [CMD_VERSION] = { 0x010000, 2, RMH_SSIZE_FIXED, 1 }, | ||
34 | [CMD_SUPPORTED] = { 0x020000, 1, RMH_SSIZE_FIXED, 2 }, | ||
35 | [CMD_TEST_IT] = { 0x040000, 1, RMH_SSIZE_FIXED, 1 }, | ||
36 | [CMD_SEND_IRQA] = { 0x070001, 1, RMH_SSIZE_FIXED, 0 }, | ||
37 | [CMD_IBL] = { 0x080000, 1, RMH_SSIZE_FIXED, 4 }, | ||
38 | [CMD_ASYNC] = { 0x0A0000, 1, RMH_SSIZE_ARG, 0 }, | ||
39 | [CMD_RES_PIPE] = { 0x400000, 1, RMH_SSIZE_FIXED, 0 }, | ||
40 | [CMD_FREE_PIPE] = { 0x410000, 1, RMH_SSIZE_FIXED, 0 }, | ||
41 | [CMD_CONF_PIPE] = { 0x42A101, 2, RMH_SSIZE_FIXED, 0 }, | ||
42 | [CMD_ABORT_CONF_PIPE] = { 0x42A100, 2, RMH_SSIZE_FIXED, 0 }, | ||
43 | [CMD_PARAM_OUTPUT_PIPE] = { 0x43A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
44 | [CMD_STOP_PIPE] = { 0x470004, 1, RMH_SSIZE_FIXED, 0 }, | ||
45 | [CMD_PIPE_STATE] = { 0x480000, 1, RMH_SSIZE_FIXED, 1 }, | ||
46 | [CMD_PIPE_SPL_COUNT] = { 0x49A000, 2, RMH_SSIZE_FIXED, 2 }, | ||
47 | [CMD_CAN_START_PIPE] = { 0x4b0000, 1, RMH_SSIZE_FIXED, 1 }, | ||
48 | [CMD_SIZE_HBUFFER] = { 0x4C0000, 1, RMH_SSIZE_FIXED, 1 }, | ||
49 | [CMD_START_STREAM] = { 0x80A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
50 | [CMD_START_ONE_STREAM] = { 0x800000, 1, RMH_SSIZE_FIXED, 0 }, | ||
51 | [CMD_PAUSE_STREAM] = { 0x81A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
52 | [CMD_PAUSE_ONE_STREAM] = { 0x810000, 1, RMH_SSIZE_FIXED, 0 }, | ||
53 | [CMD_STREAM_OUT_LEVEL_ADJUST] = { 0x828000, 2, RMH_SSIZE_FIXED, 0 }, | ||
54 | [CMD_STOP_STREAM] = { 0x830000, 1, RMH_SSIZE_FIXED, 0 }, | ||
55 | [CMD_FORMAT_STREAM_OUT] = { 0x868000, 1, RMH_SSIZE_FIXED, 0 }, | ||
56 | [CMD_FORMAT_STREAM_IN] = { 0x878800, 1, RMH_SSIZE_FIXED, 0 }, | ||
57 | [CMD_GET_STREAM_STATE] = { 0x890001, 2, RMH_SSIZE_FIXED, 1 }, | ||
58 | [CMD_DROP_BYTES_AWAY] = { 0x8A8000, 2, RMH_SSIZE_FIXED, 0 }, | ||
59 | [CMD_GET_REMAINING_BYTES] = { 0x8D0800, 1, RMH_SSIZE_FIXED, 2 }, | ||
60 | [CMD_CONNECT_AUDIO] = { 0xC10000, 1, RMH_SSIZE_FIXED, 0 }, | ||
61 | [CMD_AUDIO_LEVEL_ADJUST] = { 0xC2A000, 3, RMH_SSIZE_FIXED, 0 }, | ||
62 | [CMD_AUDIO_VU_PIC_METER] = { 0xC3A003, 2, RMH_SSIZE_FIXED, 1 }, | ||
63 | [CMD_GET_AUDIO_LEVELS] = { 0xC4A000, 2, RMH_SSIZE_FIXED, 0 }, | ||
64 | [CMD_GET_NOTIFY_EVENT] = { 0x4D0000, 1, RMH_SSIZE_ARG, 0 }, | ||
65 | [CMD_INFO_NOTIFIED] = { 0x0B0000, 1, RMH_SSIZE_FIXED, 2 }, | ||
66 | [CMD_ACCESS_IO_FCT] = { 0x098000, 1, RMH_SSIZE_ARG, 0 }, | ||
67 | [CMD_STATUS_R_BUFFERS] = { 0x440000, 1, RMH_SSIZE_ARG, 0 }, | ||
68 | [CMD_UPDATE_R_BUFFERS] = { 0x848000, 4, RMH_SSIZE_FIXED, 0 }, | ||
69 | [CMD_LOAD_EFFECT_CONTEXT] = { 0x0c8000, 3, RMH_SSIZE_FIXED, 1 }, | ||
70 | [CMD_EFFECT_ONE_PIPE] = { 0x458000, 0, RMH_SSIZE_FIXED, 0 }, | ||
71 | [CMD_MODIFY_CLOCK] = { 0x0d0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
72 | [CMD_STREAM1_OUT_SET_N_LEVELS] ={ 0x858000, 3, RMH_SSIZE_FIXED, 0 }, | ||
73 | [CMD_PURGE_STREAM_DCMDS] = { 0x8b8000, 3, RMH_SSIZE_FIXED, 0 }, | ||
74 | [CMD_NOTIFY_PIPE_TIME] = { 0x4e0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
75 | [CMD_LOAD_EFFECT_CONTEXT_PACKET] = { 0x0c8000, 1, RMH_SSIZE_FIXED, 0 }, | ||
76 | [CMD_RELIC_R_BUFFER] = { 0x8e0800, 1, RMH_SSIZE_FIXED, 1 }, | ||
77 | [CMD_RESYNC_AUDIO_INPUTS] = { 0x0e0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
78 | [CMD_NOTIFY_STREAM_TIME] = { 0x8f0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
79 | [CMD_STREAM_SAMPLE_COUNT] = { 0x900000, 1, RMH_SSIZE_FIXED, 2 }, | ||
80 | [CMD_CONFIG_TIME_CODE] = { 0x050000, 2, RMH_SSIZE_FIXED, 0 }, | ||
81 | [CMD_GET_TIME_CODE] = { 0x060000, 1, RMH_SSIZE_FIXED, 5 }, | ||
82 | [CMD_MANAGE_SIGNAL] = { 0x0f0000, 1, RMH_SSIZE_FIXED, 0 }, | ||
83 | [CMD_PARAMETER_STREAM_OUT] = { 0x91A000, 3, RMH_SSIZE_FIXED, 0 }, | ||
84 | [CMD_READ_BOARD_FREQ] = { 0x030000, 1, RMH_SSIZE_FIXED, 2 }, | ||
85 | [CMD_GET_STREAM_LEVELS] = { 0x8c0000, 1, RMH_SSIZE_FIXED, 3 }, | ||
86 | [CMD_PURGE_PIPE_DCMDS] = { 0x4f8000, 3, RMH_SSIZE_FIXED, 0 }, | ||
87 | // [CMD_SET_STREAM_OUT_EFFECTS] = { 0x888000, 34, RMH_SSIZE_FIXED, 0 }, | ||
88 | // [CMD_GET_STREAM_OUT_EFFECTS] = { 0x928000, 2, RMH_SSIZE_FIXED, 32 }, | ||
89 | [CMD_CONNECT_MONITORING] = { 0xC00000, 1, RMH_SSIZE_FIXED, 0 }, | ||
90 | [CMD_STREAM2_OUT_SET_N_LEVELS] = { 0x938000, 3, RMH_SSIZE_FIXED, 0 }, | ||
91 | [CMD_CANCEL_R_BUFFERS] = { 0x948000, 4, RMH_SSIZE_FIXED, 0 }, | ||
92 | [CMD_NOTIFY_END_OF_BUFFER] = { 0x950000, 1, RMH_SSIZE_FIXED, 0 }, | ||
93 | [CMD_GET_STREAM_VU_METER] = { 0x95A000, 2, RMH_SSIZE_ARG, 0 }, | ||
94 | }; | ||
95 | |||
96 | /** | ||
97 | * vx_init_rmh - initialize the RMH instance | ||
98 | * @rmh: the rmh pointer to be initialized | ||
99 | * @cmd: the rmh command to be set | ||
100 | */ | ||
101 | void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd) | ||
102 | { | ||
103 | snd_assert(cmd < CMD_LAST_INDEX, return); | ||
104 | rmh->LgCmd = vx_dsp_cmds[cmd].length; | ||
105 | rmh->LgStat = vx_dsp_cmds[cmd].st_length; | ||
106 | rmh->DspStat = vx_dsp_cmds[cmd].st_type; | ||
107 | rmh->Cmd[0] = vx_dsp_cmds[cmd].opcode; | ||
108 | } | ||
109 | |||
diff --git a/sound/drivers/vx/vx_cmd.h b/sound/drivers/vx/vx_cmd.h new file mode 100644 index 000000000000..a85248ba3cc5 --- /dev/null +++ b/sound/drivers/vx/vx_cmd.h | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Definitions of DSP commands | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
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 | #ifndef __VX_CMD_H | ||
24 | #define __VX_CMD_H | ||
25 | |||
26 | enum { | ||
27 | CMD_VERSION, | ||
28 | CMD_SUPPORTED, | ||
29 | CMD_TEST_IT, | ||
30 | CMD_SEND_IRQA, | ||
31 | CMD_IBL, | ||
32 | CMD_ASYNC, | ||
33 | CMD_RES_PIPE, | ||
34 | CMD_FREE_PIPE, | ||
35 | CMD_CONF_PIPE, | ||
36 | CMD_ABORT_CONF_PIPE, | ||
37 | CMD_PARAM_OUTPUT_PIPE, | ||
38 | CMD_STOP_PIPE, | ||
39 | CMD_PIPE_STATE, | ||
40 | CMD_PIPE_SPL_COUNT, | ||
41 | CMD_CAN_START_PIPE, | ||
42 | CMD_SIZE_HBUFFER, | ||
43 | CMD_START_STREAM, | ||
44 | CMD_START_ONE_STREAM, | ||
45 | CMD_PAUSE_STREAM, | ||
46 | CMD_PAUSE_ONE_STREAM, | ||
47 | CMD_STREAM_OUT_LEVEL_ADJUST, | ||
48 | CMD_STOP_STREAM, | ||
49 | CMD_FORMAT_STREAM_OUT, | ||
50 | CMD_FORMAT_STREAM_IN, | ||
51 | CMD_GET_STREAM_STATE, | ||
52 | CMD_DROP_BYTES_AWAY, | ||
53 | CMD_GET_REMAINING_BYTES, | ||
54 | CMD_CONNECT_AUDIO, | ||
55 | CMD_AUDIO_LEVEL_ADJUST, | ||
56 | CMD_AUDIO_VU_PIC_METER, | ||
57 | CMD_GET_AUDIO_LEVELS, | ||
58 | CMD_GET_NOTIFY_EVENT, | ||
59 | CMD_INFO_NOTIFIED, | ||
60 | CMD_ACCESS_IO_FCT, | ||
61 | CMD_STATUS_R_BUFFERS, | ||
62 | CMD_UPDATE_R_BUFFERS, | ||
63 | CMD_LOAD_EFFECT_CONTEXT, | ||
64 | CMD_EFFECT_ONE_PIPE, | ||
65 | CMD_MODIFY_CLOCK, | ||
66 | CMD_STREAM1_OUT_SET_N_LEVELS, | ||
67 | CMD_PURGE_STREAM_DCMDS, | ||
68 | CMD_NOTIFY_PIPE_TIME, | ||
69 | CMD_LOAD_EFFECT_CONTEXT_PACKET, | ||
70 | CMD_RELIC_R_BUFFER, | ||
71 | CMD_RESYNC_AUDIO_INPUTS, | ||
72 | CMD_NOTIFY_STREAM_TIME, | ||
73 | CMD_STREAM_SAMPLE_COUNT, | ||
74 | CMD_CONFIG_TIME_CODE, | ||
75 | CMD_GET_TIME_CODE, | ||
76 | CMD_MANAGE_SIGNAL, | ||
77 | CMD_PARAMETER_STREAM_OUT, | ||
78 | CMD_READ_BOARD_FREQ, | ||
79 | CMD_GET_STREAM_LEVELS, | ||
80 | CMD_PURGE_PIPE_DCMDS, | ||
81 | // CMD_SET_STREAM_OUT_EFFECTS, | ||
82 | // CMD_GET_STREAM_OUT_EFFECTS, | ||
83 | CMD_CONNECT_MONITORING, | ||
84 | CMD_STREAM2_OUT_SET_N_LEVELS, | ||
85 | CMD_CANCEL_R_BUFFERS, | ||
86 | CMD_NOTIFY_END_OF_BUFFER, | ||
87 | CMD_GET_STREAM_VU_METER, | ||
88 | CMD_LAST_INDEX | ||
89 | }; | ||
90 | |||
91 | struct vx_cmd_info { | ||
92 | unsigned int opcode; /* command word */ | ||
93 | int length; /* command length (in words) */ | ||
94 | int st_type; /* status type (RMH_SSIZE_XXX) */ | ||
95 | int st_length; /* fixed length */ | ||
96 | }; | ||
97 | |||
98 | /* Family and code op of some DSP requests. */ | ||
99 | #define CODE_OP_PIPE_TIME 0x004e0000 | ||
100 | #define CODE_OP_START_STREAM 0x00800000 | ||
101 | #define CODE_OP_PAUSE_STREAM 0x00810000 | ||
102 | #define CODE_OP_OUT_STREAM_LEVEL 0x00820000 | ||
103 | #define CODE_OP_UPDATE_R_BUFFERS 0x00840000 | ||
104 | #define CODE_OP_OUT_STREAM1_LEVEL_CURVE 0x00850000 | ||
105 | #define CODE_OP_OUT_STREAM2_LEVEL_CURVE 0x00930000 | ||
106 | #define CODE_OP_OUT_STREAM_FORMAT 0x00860000 | ||
107 | #define CODE_OP_STREAM_TIME 0x008f0000 | ||
108 | #define CODE_OP_OUT_STREAM_EXTRAPARAMETER 0x00910000 | ||
109 | #define CODE_OP_OUT_AUDIO_LEVEL 0x00c20000 | ||
110 | |||
111 | #define NOTIFY_LAST_COMMAND 0x00400000 | ||
112 | |||
113 | /* Values for a user delay */ | ||
114 | #define DC_DIFFERED_DELAY (1<<BIT_DIFFERED_COMMAND) | ||
115 | #define DC_NOTIFY_DELAY (1<<BIT_NOTIFIED_COMMAND) | ||
116 | #define DC_HBUFFER_DELAY (1<<BIT_TIME_RELATIVE_TO_BUFFER) | ||
117 | #define DC_MULTIPLE_DELAY (1<<BIT_RESERVED) | ||
118 | #define DC_STREAM_TIME_DELAY (1<<BIT_STREAM_TIME) | ||
119 | #define DC_CANCELLED_DELAY (1<<BIT_CANCELLED_COMMAND) | ||
120 | |||
121 | /* Values for tiDelayed field in TIME_INFO structure, | ||
122 | * and for pbPause field in PLAY_BUFFER_INFO structure | ||
123 | */ | ||
124 | #define BIT_DIFFERED_COMMAND 0 | ||
125 | #define BIT_NOTIFIED_COMMAND 1 | ||
126 | #define BIT_TIME_RELATIVE_TO_BUFFER 2 | ||
127 | #define BIT_RESERVED 3 | ||
128 | #define BIT_STREAM_TIME 4 | ||
129 | #define BIT_CANCELLED_COMMAND 5 | ||
130 | |||
131 | /* Access to the "Size" field of the response of the CMD_GET_NOTIFY_EVENT request. */ | ||
132 | #define GET_NOTIFY_EVENT_SIZE_FIELD_MASK 0x000000ff | ||
133 | |||
134 | /* DSP commands general masks */ | ||
135 | #define OPCODE_MASK 0x00ff0000 | ||
136 | #define DSP_DIFFERED_COMMAND_MASK 0x0000C000 | ||
137 | |||
138 | /* Notifications (NOTIFY_INFO) */ | ||
139 | #define ALL_CMDS_NOTIFIED 0x0000 // reserved | ||
140 | #define START_STREAM_NOTIFIED 0x0001 | ||
141 | #define PAUSE_STREAM_NOTIFIED 0x0002 | ||
142 | #define OUT_STREAM_LEVEL_NOTIFIED 0x0003 | ||
143 | #define OUT_STREAM_PARAMETER_NOTIFIED 0x0004 // left for backward compatibility | ||
144 | #define OUT_STREAM_FORMAT_NOTIFIED 0x0004 | ||
145 | #define PIPE_TIME_NOTIFIED 0x0005 | ||
146 | #define OUT_AUDIO_LEVEL_NOTIFIED 0x0006 | ||
147 | #define OUT_STREAM_LEVEL_CURVE_NOTIFIED 0x0007 | ||
148 | #define STREAM_TIME_NOTIFIED 0x0008 | ||
149 | #define OUT_STREAM_EXTRAPARAMETER_NOTIFIED 0x0009 | ||
150 | #define UNKNOWN_COMMAND_NOTIFIED 0xffff | ||
151 | |||
152 | /* Output pipe parameters setting */ | ||
153 | #define MASK_VALID_PIPE_MPEG_PARAM 0x000040 | ||
154 | #define MASK_VALID_PIPE_BACKWARD_PARAM 0x000020 | ||
155 | #define MASK_SET_PIPE_MPEG_PARAM 0x000002 | ||
156 | #define MASK_SET_PIPE_BACKWARD_PARAM 0x000001 | ||
157 | |||
158 | #define MASK_DSP_WORD 0x00FFFFFF | ||
159 | #define MASK_ALL_STREAM 0x00FFFFFF | ||
160 | #define MASK_DSP_WORD_LEVEL 0x000001FF | ||
161 | #define MASK_FIRST_FIELD 0x0000001F | ||
162 | #define FIELD_SIZE 5 | ||
163 | |||
164 | #define COMMAND_RECORD_MASK 0x000800 | ||
165 | |||
166 | /* PipeManagement definition bits (PIPE_DECL_INFO) */ | ||
167 | #define P_UNDERRUN_SKIP_SOUND_MASK 0x01 | ||
168 | #define P_PREPARE_FOR_MPEG3_MASK 0x02 | ||
169 | #define P_DO_NOT_RESET_ANALOG_LEVELS 0x04 | ||
170 | #define P_ALLOW_UNDER_ALLOCATION_MASK 0x08 | ||
171 | #define P_DATA_MODE_MASK 0x10 | ||
172 | #define P_ASIO_BUFFER_MANAGEMENT_MASK 0x20 | ||
173 | |||
174 | #define BIT_SKIP_SOUND 0x08 // bit 3 | ||
175 | #define BIT_DATA_MODE 0x10 // bit 4 | ||
176 | |||
177 | /* Bits in the CMD_MODIFY_CLOCK request. */ | ||
178 | #define CMD_MODIFY_CLOCK_FD_BIT 0x00000001 | ||
179 | #define CMD_MODIFY_CLOCK_T_BIT 0x00000002 | ||
180 | #define CMD_MODIFY_CLOCK_S_BIT 0x00000004 | ||
181 | |||
182 | /* Access to the results of the CMD_GET_TIME_CODE RMH. */ | ||
183 | #define TIME_CODE_V_MASK 0x00800000 | ||
184 | #define TIME_CODE_N_MASK 0x00400000 | ||
185 | #define TIME_CODE_B_MASK 0x00200000 | ||
186 | #define TIME_CODE_W_MASK 0x00100000 | ||
187 | |||
188 | /* Values for the CMD_MANAGE_SIGNAL RMH. */ | ||
189 | #define MANAGE_SIGNAL_TIME_CODE 0x01 | ||
190 | #define MANAGE_SIGNAL_MIDI 0x02 | ||
191 | |||
192 | /* Values for the CMD_CONFIG_TIME_CODE RMH. */ | ||
193 | #define CONFIG_TIME_CODE_CANCEL 0x00001000 | ||
194 | |||
195 | /* Mask to get only the effective time from the | ||
196 | * high word out of the 2 returned by the DSP | ||
197 | */ | ||
198 | #define PCX_TIME_HI_MASK 0x000fffff | ||
199 | |||
200 | /* Values for setting a H-Buffer time */ | ||
201 | #define HBUFFER_TIME_HIGH 0x00200000 | ||
202 | #define HBUFFER_TIME_LOW 0x00000000 | ||
203 | |||
204 | #define NOTIFY_MASK_TIME_HIGH 0x00400000 | ||
205 | #define MULTIPLE_MASK_TIME_HIGH 0x00100000 | ||
206 | #define STREAM_MASK_TIME_HIGH 0x00800000 | ||
207 | |||
208 | |||
209 | /* | ||
210 | * | ||
211 | */ | ||
212 | void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd); | ||
213 | |||
214 | /** | ||
215 | * vx_send_pipe_cmd_params - fill first command word for pipe commands | ||
216 | * @rmh: the rmh to be modified | ||
217 | * @is_capture: 0 = playback, 1 = capture operation | ||
218 | * @param1: first pipe-parameter | ||
219 | * @param2: second pipe-parameter | ||
220 | */ | ||
221 | static inline void vx_set_pipe_cmd_params(struct vx_rmh *rmh, int is_capture, | ||
222 | int param1, int param2) | ||
223 | { | ||
224 | if (is_capture) | ||
225 | rmh->Cmd[0] |= COMMAND_RECORD_MASK; | ||
226 | rmh->Cmd[0] |= (((u32)param1 & MASK_FIRST_FIELD) << FIELD_SIZE) & MASK_DSP_WORD; | ||
227 | |||
228 | if (param2) | ||
229 | rmh->Cmd[0] |= ((u32)param2 & MASK_FIRST_FIELD) & MASK_DSP_WORD; | ||
230 | |||
231 | } | ||
232 | |||
233 | /** | ||
234 | * vx_set_stream_cmd_params - fill first command word for stream commands | ||
235 | * @rmh: the rmh to be modified | ||
236 | * @is_capture: 0 = playback, 1 = capture operation | ||
237 | * @pipe: the pipe index (zero-based) | ||
238 | */ | ||
239 | static inline void vx_set_stream_cmd_params(struct vx_rmh *rmh, int is_capture, int pipe) | ||
240 | { | ||
241 | if (is_capture) | ||
242 | rmh->Cmd[0] |= COMMAND_RECORD_MASK; | ||
243 | rmh->Cmd[0] |= (((u32)pipe & MASK_FIRST_FIELD) << FIELD_SIZE) & MASK_DSP_WORD; | ||
244 | } | ||
245 | |||
246 | #endif /* __VX_CMD_H */ | ||
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c new file mode 100644 index 000000000000..c6fa5afa3e9a --- /dev/null +++ b/sound/drivers/vx/vx_core.c | |||
@@ -0,0 +1,837 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Hardware core part | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
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 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/firmware.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/pcm.h> | ||
32 | #include <sound/asoundef.h> | ||
33 | #include <sound/info.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <sound/vx_core.h> | ||
36 | #include "vx_cmd.h" | ||
37 | |||
38 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
39 | MODULE_DESCRIPTION("Common routines for Digigram VX drivers"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | |||
43 | /* | ||
44 | * snd_vx_delay - delay for the specified time | ||
45 | * @xmsec: the time to delay in msec | ||
46 | */ | ||
47 | void snd_vx_delay(vx_core_t *chip, int xmsec) | ||
48 | { | ||
49 | if (! in_interrupt() && xmsec >= 1000 / HZ) | ||
50 | msleep(xmsec); | ||
51 | else | ||
52 | mdelay(xmsec); | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * vx_check_reg_bit - wait for the specified bit is set/reset on a register | ||
57 | * @reg: register to check | ||
58 | * @mask: bit mask | ||
59 | * @bit: resultant bit to be checked | ||
60 | * @time: time-out of loop in msec | ||
61 | * | ||
62 | * returns zero if a bit matches, or a negative error code. | ||
63 | */ | ||
64 | int snd_vx_check_reg_bit(vx_core_t *chip, int reg, int mask, int bit, int time) | ||
65 | { | ||
66 | unsigned long end_time = jiffies + (time * HZ + 999) / 1000; | ||
67 | #ifdef CONFIG_SND_DEBUG | ||
68 | static char *reg_names[VX_REG_MAX] = { | ||
69 | "ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL", | ||
70 | "DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ", | ||
71 | "ACQ", "BIT0", "BIT1", "MIC0", "MIC1", "MIC2", | ||
72 | "MIC3", "INTCSR", "CNTRL", "GPIOC", | ||
73 | "LOFREQ", "HIFREQ", "CSUER", "RUER" | ||
74 | }; | ||
75 | #endif | ||
76 | do { | ||
77 | if ((snd_vx_inb(chip, reg) & mask) == bit) | ||
78 | return 0; | ||
79 | //snd_vx_delay(chip, 10); | ||
80 | } while (time_after_eq(end_time, jiffies)); | ||
81 | snd_printd(KERN_DEBUG "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n", reg_names[reg], mask, snd_vx_inb(chip, reg)); | ||
82 | return -EIO; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * vx_send_irq_dsp - set command irq bit | ||
87 | * @num: the requested IRQ type, IRQ_XXX | ||
88 | * | ||
89 | * this triggers the specified IRQ request | ||
90 | * returns 0 if successful, or a negative error code. | ||
91 | * | ||
92 | */ | ||
93 | static int vx_send_irq_dsp(vx_core_t *chip, int num) | ||
94 | { | ||
95 | int nirq; | ||
96 | |||
97 | /* wait for Hc = 0 */ | ||
98 | if (snd_vx_check_reg_bit(chip, VX_CVR, CVR_HC, 0, 200) < 0) | ||
99 | return -EIO; | ||
100 | |||
101 | nirq = num; | ||
102 | if (vx_has_new_dsp(chip)) | ||
103 | nirq += VXP_IRQ_OFFSET; | ||
104 | vx_outb(chip, CVR, (nirq >> 1) | CVR_HC); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | |||
109 | /* | ||
110 | * vx_reset_chk - reset CHK bit on ISR | ||
111 | * | ||
112 | * returns 0 if successful, or a negative error code. | ||
113 | */ | ||
114 | static int vx_reset_chk(vx_core_t *chip) | ||
115 | { | ||
116 | /* Reset irq CHK */ | ||
117 | if (vx_send_irq_dsp(chip, IRQ_RESET_CHK) < 0) | ||
118 | return -EIO; | ||
119 | /* Wait until CHK = 0 */ | ||
120 | if (vx_check_isr(chip, ISR_CHK, 0, 200) < 0) | ||
121 | return -EIO; | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * vx_transfer_end - terminate message transfer | ||
127 | * @cmd: IRQ message to send (IRQ_MESS_XXX_END) | ||
128 | * | ||
129 | * returns 0 if successful, or a negative error code. | ||
130 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
131 | * NB: call with spinlock held! | ||
132 | */ | ||
133 | static int vx_transfer_end(vx_core_t *chip, int cmd) | ||
134 | { | ||
135 | int err; | ||
136 | |||
137 | if ((err = vx_reset_chk(chip)) < 0) | ||
138 | return err; | ||
139 | |||
140 | /* irq MESS_READ/WRITE_END */ | ||
141 | if ((err = vx_send_irq_dsp(chip, cmd)) < 0) | ||
142 | return err; | ||
143 | |||
144 | /* Wait CHK = 1 */ | ||
145 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
146 | return err; | ||
147 | |||
148 | /* If error, Read RX */ | ||
149 | if ((err = vx_inb(chip, ISR)) & ISR_ERR) { | ||
150 | if ((err = vx_wait_for_rx_full(chip)) < 0) { | ||
151 | snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n"); | ||
152 | return err; | ||
153 | } | ||
154 | err = vx_inb(chip, RXH) << 16; | ||
155 | err |= vx_inb(chip, RXM) << 8; | ||
156 | err |= vx_inb(chip, RXL); | ||
157 | snd_printd(KERN_DEBUG "transfer_end: error = 0x%x\n", err); | ||
158 | return -(VX_ERR_MASK | err); | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * vx_read_status - return the status rmh | ||
165 | * @rmh: rmh record to store the status | ||
166 | * | ||
167 | * returns 0 if successful, or a negative error code. | ||
168 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
169 | * NB: call with spinlock held! | ||
170 | */ | ||
171 | static int vx_read_status(vx_core_t *chip, struct vx_rmh *rmh) | ||
172 | { | ||
173 | int i, err, val, size; | ||
174 | |||
175 | /* no read necessary? */ | ||
176 | if (rmh->DspStat == RMH_SSIZE_FIXED && rmh->LgStat == 0) | ||
177 | return 0; | ||
178 | |||
179 | /* Wait for RX full (with timeout protection) | ||
180 | * The first word of status is in RX | ||
181 | */ | ||
182 | err = vx_wait_for_rx_full(chip); | ||
183 | if (err < 0) | ||
184 | return err; | ||
185 | |||
186 | /* Read RX */ | ||
187 | val = vx_inb(chip, RXH) << 16; | ||
188 | val |= vx_inb(chip, RXM) << 8; | ||
189 | val |= vx_inb(chip, RXL); | ||
190 | |||
191 | /* If status given by DSP, let's decode its size */ | ||
192 | switch (rmh->DspStat) { | ||
193 | case RMH_SSIZE_ARG: | ||
194 | size = val & 0xff; | ||
195 | rmh->Stat[0] = val & 0xffff00; | ||
196 | rmh->LgStat = size + 1; | ||
197 | break; | ||
198 | case RMH_SSIZE_MASK: | ||
199 | /* Let's count the arg numbers from a mask */ | ||
200 | rmh->Stat[0] = val; | ||
201 | size = 0; | ||
202 | while (val) { | ||
203 | if (val & 0x01) | ||
204 | size++; | ||
205 | val >>= 1; | ||
206 | } | ||
207 | rmh->LgStat = size + 1; | ||
208 | break; | ||
209 | default: | ||
210 | /* else retrieve the status length given by the driver */ | ||
211 | size = rmh->LgStat; | ||
212 | rmh->Stat[0] = val; /* Val is the status 1st word */ | ||
213 | size--; /* hence adjust remaining length */ | ||
214 | break; | ||
215 | } | ||
216 | |||
217 | if (size < 1) | ||
218 | return 0; | ||
219 | snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL); | ||
220 | |||
221 | for (i = 1; i <= size; i++) { | ||
222 | /* trigger an irq MESS_WRITE_NEXT */ | ||
223 | err = vx_send_irq_dsp(chip, IRQ_MESS_WRITE_NEXT); | ||
224 | if (err < 0) | ||
225 | return err; | ||
226 | /* Wait for RX full (with timeout protection) */ | ||
227 | err = vx_wait_for_rx_full(chip); | ||
228 | if (err < 0) | ||
229 | return err; | ||
230 | rmh->Stat[i] = vx_inb(chip, RXH) << 16; | ||
231 | rmh->Stat[i] |= vx_inb(chip, RXM) << 8; | ||
232 | rmh->Stat[i] |= vx_inb(chip, RXL); | ||
233 | } | ||
234 | |||
235 | return vx_transfer_end(chip, IRQ_MESS_WRITE_END); | ||
236 | } | ||
237 | |||
238 | |||
239 | #define MASK_MORE_THAN_1_WORD_COMMAND 0x00008000 | ||
240 | #define MASK_1_WORD_COMMAND 0x00ff7fff | ||
241 | |||
242 | /* | ||
243 | * vx_send_msg_nolock - send a DSP message and read back the status | ||
244 | * @rmh: the rmh record to send and receive | ||
245 | * | ||
246 | * returns 0 if successful, or a negative error code. | ||
247 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
248 | * | ||
249 | * this function doesn't call spinlock at all. | ||
250 | */ | ||
251 | int vx_send_msg_nolock(vx_core_t *chip, struct vx_rmh *rmh) | ||
252 | { | ||
253 | int i, err; | ||
254 | |||
255 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
256 | return -EBUSY; | ||
257 | |||
258 | if ((err = vx_reset_chk(chip)) < 0) { | ||
259 | snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n"); | ||
260 | return err; | ||
261 | } | ||
262 | |||
263 | #if 0 | ||
264 | printk(KERN_DEBUG "rmh: cmd = 0x%06x, length = %d, stype = %d\n", | ||
265 | rmh->Cmd[0], rmh->LgCmd, rmh->DspStat); | ||
266 | if (rmh->LgCmd > 1) { | ||
267 | printk(KERN_DEBUG " "); | ||
268 | for (i = 1; i < rmh->LgCmd; i++) | ||
269 | printk("0x%06x ", rmh->Cmd[i]); | ||
270 | printk("\n"); | ||
271 | } | ||
272 | #endif | ||
273 | /* Check bit M is set according to length of the command */ | ||
274 | if (rmh->LgCmd > 1) | ||
275 | rmh->Cmd[0] |= MASK_MORE_THAN_1_WORD_COMMAND; | ||
276 | else | ||
277 | rmh->Cmd[0] &= MASK_1_WORD_COMMAND; | ||
278 | |||
279 | /* Wait for TX empty */ | ||
280 | if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) { | ||
281 | snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n"); | ||
282 | return err; | ||
283 | } | ||
284 | |||
285 | /* Write Cmd[0] */ | ||
286 | vx_outb(chip, TXH, (rmh->Cmd[0] >> 16) & 0xff); | ||
287 | vx_outb(chip, TXM, (rmh->Cmd[0] >> 8) & 0xff); | ||
288 | vx_outb(chip, TXL, rmh->Cmd[0] & 0xff); | ||
289 | |||
290 | /* Trigger irq MESSAGE */ | ||
291 | if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) { | ||
292 | snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n"); | ||
293 | return err; | ||
294 | } | ||
295 | |||
296 | /* Wait for CHK = 1 */ | ||
297 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
298 | return err; | ||
299 | |||
300 | /* If error, get error value from RX */ | ||
301 | if (vx_inb(chip, ISR) & ISR_ERR) { | ||
302 | if ((err = vx_wait_for_rx_full(chip)) < 0) { | ||
303 | snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n"); | ||
304 | return err; | ||
305 | } | ||
306 | err = vx_inb(chip, RXH) << 16; | ||
307 | err |= vx_inb(chip, RXM) << 8; | ||
308 | err |= vx_inb(chip, RXL); | ||
309 | snd_printd(KERN_DEBUG "msg got error = 0x%x at cmd[0]\n", err); | ||
310 | err = -(VX_ERR_MASK | err); | ||
311 | return err; | ||
312 | } | ||
313 | |||
314 | /* Send the other words */ | ||
315 | if (rmh->LgCmd > 1) { | ||
316 | for (i = 1; i < rmh->LgCmd; i++) { | ||
317 | /* Wait for TX ready */ | ||
318 | if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) { | ||
319 | snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n"); | ||
320 | return err; | ||
321 | } | ||
322 | |||
323 | /* Write Cmd[i] */ | ||
324 | vx_outb(chip, TXH, (rmh->Cmd[i] >> 16) & 0xff); | ||
325 | vx_outb(chip, TXM, (rmh->Cmd[i] >> 8) & 0xff); | ||
326 | vx_outb(chip, TXL, rmh->Cmd[i] & 0xff); | ||
327 | |||
328 | /* Trigger irq MESS_READ_NEXT */ | ||
329 | if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) { | ||
330 | snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n"); | ||
331 | return err; | ||
332 | } | ||
333 | } | ||
334 | /* Wait for TX empty */ | ||
335 | if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) { | ||
336 | snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n"); | ||
337 | return err; | ||
338 | } | ||
339 | /* End of transfer */ | ||
340 | err = vx_transfer_end(chip, IRQ_MESS_READ_END); | ||
341 | if (err < 0) | ||
342 | return err; | ||
343 | } | ||
344 | |||
345 | return vx_read_status(chip, rmh); | ||
346 | } | ||
347 | |||
348 | |||
349 | /* | ||
350 | * vx_send_msg - send a DSP message with spinlock | ||
351 | * @rmh: the rmh record to send and receive | ||
352 | * | ||
353 | * returns 0 if successful, or a negative error code. | ||
354 | * see vx_send_msg_nolock(). | ||
355 | */ | ||
356 | int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh) | ||
357 | { | ||
358 | unsigned long flags; | ||
359 | int err; | ||
360 | |||
361 | spin_lock_irqsave(&chip->lock, flags); | ||
362 | err = vx_send_msg_nolock(chip, rmh); | ||
363 | spin_unlock_irqrestore(&chip->lock, flags); | ||
364 | return err; | ||
365 | } | ||
366 | |||
367 | |||
368 | /* | ||
369 | * vx_send_rih_nolock - send an RIH to xilinx | ||
370 | * @cmd: the command to send | ||
371 | * | ||
372 | * returns 0 if successful, or a negative error code. | ||
373 | * the error code can be VX-specific, retrieved via vx_get_error(). | ||
374 | * | ||
375 | * this function doesn't call spinlock at all. | ||
376 | * | ||
377 | * unlike RMH, no command is sent to DSP. | ||
378 | */ | ||
379 | int vx_send_rih_nolock(vx_core_t *chip, int cmd) | ||
380 | { | ||
381 | int err; | ||
382 | |||
383 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
384 | return -EBUSY; | ||
385 | |||
386 | #if 0 | ||
387 | printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd); | ||
388 | #endif | ||
389 | if ((err = vx_reset_chk(chip)) < 0) | ||
390 | return err; | ||
391 | /* send the IRQ */ | ||
392 | if ((err = vx_send_irq_dsp(chip, cmd)) < 0) | ||
393 | return err; | ||
394 | /* Wait CHK = 1 */ | ||
395 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
396 | return err; | ||
397 | /* If error, read RX */ | ||
398 | if (vx_inb(chip, ISR) & ISR_ERR) { | ||
399 | if ((err = vx_wait_for_rx_full(chip)) < 0) | ||
400 | return err; | ||
401 | err = vx_inb(chip, RXH) << 16; | ||
402 | err |= vx_inb(chip, RXM) << 8; | ||
403 | err |= vx_inb(chip, RXL); | ||
404 | return -(VX_ERR_MASK | err); | ||
405 | } | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | |||
410 | /* | ||
411 | * vx_send_rih - send an RIH with spinlock | ||
412 | * @cmd: the command to send | ||
413 | * | ||
414 | * see vx_send_rih_nolock(). | ||
415 | */ | ||
416 | int vx_send_rih(vx_core_t *chip, int cmd) | ||
417 | { | ||
418 | unsigned long flags; | ||
419 | int err; | ||
420 | |||
421 | spin_lock_irqsave(&chip->lock, flags); | ||
422 | err = vx_send_rih_nolock(chip, cmd); | ||
423 | spin_unlock_irqrestore(&chip->lock, flags); | ||
424 | return err; | ||
425 | } | ||
426 | |||
427 | #define END_OF_RESET_WAIT_TIME 500 /* us */ | ||
428 | |||
429 | /** | ||
430 | * snd_vx_boot_xilinx - boot up the xilinx interface | ||
431 | * @boot: the boot record to load | ||
432 | */ | ||
433 | int snd_vx_load_boot_image(vx_core_t *chip, const struct firmware *boot) | ||
434 | { | ||
435 | unsigned int i; | ||
436 | int no_fillup = vx_has_new_dsp(chip); | ||
437 | |||
438 | /* check the length of boot image */ | ||
439 | snd_assert(boot->size > 0, return -EINVAL); | ||
440 | snd_assert(boot->size % 3 == 0, return -EINVAL); | ||
441 | #if 0 | ||
442 | { | ||
443 | /* more strict check */ | ||
444 | unsigned int c = ((u32)boot->data[0] << 16) | ((u32)boot->data[1] << 8) | boot->data[2]; | ||
445 | snd_assert(boot->size == (c + 2) * 3, return -EINVAL); | ||
446 | } | ||
447 | #endif | ||
448 | |||
449 | /* reset dsp */ | ||
450 | vx_reset_dsp(chip); | ||
451 | |||
452 | udelay(END_OF_RESET_WAIT_TIME); /* another wait? */ | ||
453 | |||
454 | /* download boot strap */ | ||
455 | for (i = 0; i < 0x600; i += 3) { | ||
456 | if (i >= boot->size) { | ||
457 | if (no_fillup) | ||
458 | break; | ||
459 | if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) { | ||
460 | snd_printk(KERN_ERR "dsp boot failed at %d\n", i); | ||
461 | return -EIO; | ||
462 | } | ||
463 | vx_outb(chip, TXH, 0); | ||
464 | vx_outb(chip, TXM, 0); | ||
465 | vx_outb(chip, TXL, 0); | ||
466 | } else { | ||
467 | unsigned char *image = boot->data + i; | ||
468 | if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) { | ||
469 | snd_printk(KERN_ERR "dsp boot failed at %d\n", i); | ||
470 | return -EIO; | ||
471 | } | ||
472 | vx_outb(chip, TXH, image[0]); | ||
473 | vx_outb(chip, TXM, image[1]); | ||
474 | vx_outb(chip, TXL, image[2]); | ||
475 | } | ||
476 | } | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * vx_test_irq_src - query the source of interrupts | ||
482 | * | ||
483 | * called from irq handler only | ||
484 | */ | ||
485 | static int vx_test_irq_src(vx_core_t *chip, unsigned int *ret) | ||
486 | { | ||
487 | int err; | ||
488 | |||
489 | vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT); | ||
490 | spin_lock(&chip->lock); | ||
491 | err = vx_send_msg_nolock(chip, &chip->irq_rmh); | ||
492 | if (err < 0) | ||
493 | *ret = 0; | ||
494 | else | ||
495 | *ret = chip->irq_rmh.Stat[0]; | ||
496 | spin_unlock(&chip->lock); | ||
497 | return err; | ||
498 | } | ||
499 | |||
500 | |||
501 | /* | ||
502 | * vx_interrupt - soft irq handler | ||
503 | */ | ||
504 | static void vx_interrupt(unsigned long private_data) | ||
505 | { | ||
506 | vx_core_t *chip = (vx_core_t *) private_data; | ||
507 | unsigned int events; | ||
508 | |||
509 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
510 | return; | ||
511 | |||
512 | if (vx_test_irq_src(chip, &events) < 0) | ||
513 | return; | ||
514 | |||
515 | #if 0 | ||
516 | if (events & 0x000800) | ||
517 | printk(KERN_ERR "DSP Stream underrun ! IRQ events = 0x%x\n", events); | ||
518 | #endif | ||
519 | // printk(KERN_DEBUG "IRQ events = 0x%x\n", events); | ||
520 | |||
521 | /* We must prevent any application using this DSP | ||
522 | * and block any further request until the application | ||
523 | * either unregisters or reloads the DSP | ||
524 | */ | ||
525 | if (events & FATAL_DSP_ERROR) { | ||
526 | snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n"); | ||
527 | return; | ||
528 | } | ||
529 | |||
530 | /* The start on time code conditions are filled (ie the time code | ||
531 | * received by the board is equal to one of those given to it). | ||
532 | */ | ||
533 | if (events & TIME_CODE_EVENT_PENDING) | ||
534 | ; /* so far, nothing to do yet */ | ||
535 | |||
536 | /* The frequency has changed on the board (UER mode). */ | ||
537 | if (events & FREQUENCY_CHANGE_EVENT_PENDING) | ||
538 | vx_change_frequency(chip); | ||
539 | |||
540 | /* update the pcm streams */ | ||
541 | vx_pcm_update_intr(chip, events); | ||
542 | } | ||
543 | |||
544 | |||
545 | /** | ||
546 | * snd_vx_irq_handler - interrupt handler | ||
547 | */ | ||
548 | irqreturn_t snd_vx_irq_handler(int irq, void *dev, struct pt_regs *regs) | ||
549 | { | ||
550 | vx_core_t *chip = dev; | ||
551 | |||
552 | if (! (chip->chip_status & VX_STAT_CHIP_INIT) || | ||
553 | (chip->chip_status & VX_STAT_IS_STALE)) | ||
554 | return IRQ_NONE; | ||
555 | if (! vx_test_and_ack(chip)) | ||
556 | tasklet_hi_schedule(&chip->tq); | ||
557 | return IRQ_HANDLED; | ||
558 | } | ||
559 | |||
560 | |||
561 | /* | ||
562 | */ | ||
563 | static void vx_reset_board(vx_core_t *chip, int cold_reset) | ||
564 | { | ||
565 | snd_assert(chip->ops->reset_board, return); | ||
566 | |||
567 | /* current source, later sync'ed with target */ | ||
568 | chip->audio_source = VX_AUDIO_SRC_LINE; | ||
569 | if (cold_reset) { | ||
570 | chip->audio_source_target = chip->audio_source; | ||
571 | chip->clock_source = INTERNAL_QUARTZ; | ||
572 | chip->clock_mode = VX_CLOCK_MODE_AUTO; | ||
573 | chip->freq = 48000; | ||
574 | chip->uer_detected = VX_UER_MODE_NOT_PRESENT; | ||
575 | chip->uer_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; | ||
576 | } | ||
577 | |||
578 | chip->ops->reset_board(chip, cold_reset); | ||
579 | |||
580 | vx_reset_codec(chip, cold_reset); | ||
581 | |||
582 | vx_set_internal_clock(chip, chip->freq); | ||
583 | |||
584 | /* Reset the DSP */ | ||
585 | vx_reset_dsp(chip); | ||
586 | |||
587 | if (vx_is_pcmcia(chip)) { | ||
588 | /* Acknowledge any pending IRQ and reset the MEMIRQ flag. */ | ||
589 | vx_test_and_ack(chip); | ||
590 | vx_validate_irq(chip, 1); | ||
591 | } | ||
592 | |||
593 | /* init CBits */ | ||
594 | vx_set_iec958_status(chip, chip->uer_bits); | ||
595 | } | ||
596 | |||
597 | |||
598 | /* | ||
599 | * proc interface | ||
600 | */ | ||
601 | |||
602 | static void vx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) | ||
603 | { | ||
604 | vx_core_t *chip = entry->private_data; | ||
605 | static char *audio_src_vxp[] = { "Line", "Mic", "Digital" }; | ||
606 | static char *audio_src_vx2[] = { "Analog", "Analog", "Digital" }; | ||
607 | static char *clock_mode[] = { "Auto", "Internal", "External" }; | ||
608 | static char *clock_src[] = { "Internal", "External" }; | ||
609 | static char *uer_type[] = { "Consumer", "Professional", "Not Present" }; | ||
610 | |||
611 | snd_iprintf(buffer, "%s\n", chip->card->longname); | ||
612 | snd_iprintf(buffer, "Xilinx Firmware: %s\n", | ||
613 | chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No"); | ||
614 | snd_iprintf(buffer, "Device Initialized: %s\n", | ||
615 | chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No"); | ||
616 | snd_iprintf(buffer, "DSP audio info:"); | ||
617 | if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME) | ||
618 | snd_iprintf(buffer, " realtime"); | ||
619 | if (chip->audio_info & VX_AUDIO_INFO_OFFLINE) | ||
620 | snd_iprintf(buffer, " offline"); | ||
621 | if (chip->audio_info & VX_AUDIO_INFO_MPEG1) | ||
622 | snd_iprintf(buffer, " mpeg1"); | ||
623 | if (chip->audio_info & VX_AUDIO_INFO_MPEG2) | ||
624 | snd_iprintf(buffer, " mpeg2"); | ||
625 | if (chip->audio_info & VX_AUDIO_INFO_LINEAR_8) | ||
626 | snd_iprintf(buffer, " linear8"); | ||
627 | if (chip->audio_info & VX_AUDIO_INFO_LINEAR_16) | ||
628 | snd_iprintf(buffer, " linear16"); | ||
629 | if (chip->audio_info & VX_AUDIO_INFO_LINEAR_24) | ||
630 | snd_iprintf(buffer, " linear24"); | ||
631 | snd_iprintf(buffer, "\n"); | ||
632 | snd_iprintf(buffer, "Input Source: %s\n", vx_is_pcmcia(chip) ? | ||
633 | audio_src_vxp[chip->audio_source] : | ||
634 | audio_src_vx2[chip->audio_source]); | ||
635 | snd_iprintf(buffer, "Clock Mode: %s\n", clock_mode[chip->clock_mode]); | ||
636 | snd_iprintf(buffer, "Clock Source: %s\n", clock_src[chip->clock_source]); | ||
637 | snd_iprintf(buffer, "Frequency: %d\n", chip->freq); | ||
638 | snd_iprintf(buffer, "Detected Frequency: %d\n", chip->freq_detected); | ||
639 | snd_iprintf(buffer, "Detected UER type: %s\n", uer_type[chip->uer_detected]); | ||
640 | snd_iprintf(buffer, "Min/Max/Cur IBL: %d/%d/%d (granularity=%d)\n", | ||
641 | chip->ibl.min_size, chip->ibl.max_size, chip->ibl.size, | ||
642 | chip->ibl.granularity); | ||
643 | } | ||
644 | |||
645 | static void vx_proc_init(vx_core_t *chip) | ||
646 | { | ||
647 | snd_info_entry_t *entry; | ||
648 | |||
649 | if (! snd_card_proc_new(chip->card, "vx-status", &entry)) | ||
650 | snd_info_set_text_ops(entry, chip, 1024, vx_proc_read); | ||
651 | } | ||
652 | |||
653 | |||
654 | /** | ||
655 | * snd_vx_dsp_boot - load the DSP boot | ||
656 | */ | ||
657 | int snd_vx_dsp_boot(vx_core_t *chip, const struct firmware *boot) | ||
658 | { | ||
659 | int err; | ||
660 | int cold_reset = !(chip->chip_status & VX_STAT_DEVICE_INIT); | ||
661 | |||
662 | vx_reset_board(chip, cold_reset); | ||
663 | vx_validate_irq(chip, 0); | ||
664 | |||
665 | if ((err = snd_vx_load_boot_image(chip, boot)) < 0) | ||
666 | return err; | ||
667 | snd_vx_delay(chip, 10); | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * snd_vx_dsp_load - load the DSP image | ||
674 | */ | ||
675 | int snd_vx_dsp_load(vx_core_t *chip, const struct firmware *dsp) | ||
676 | { | ||
677 | unsigned int i; | ||
678 | int err; | ||
679 | unsigned int csum = 0; | ||
680 | unsigned char *image, *cptr; | ||
681 | |||
682 | snd_assert(dsp->size % 3 == 0, return -EINVAL); | ||
683 | |||
684 | vx_toggle_dac_mute(chip, 1); | ||
685 | |||
686 | /* Transfert data buffer from PC to DSP */ | ||
687 | for (i = 0; i < dsp->size; i += 3) { | ||
688 | image = dsp->data + i; | ||
689 | /* Wait DSP ready for a new read */ | ||
690 | if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) { | ||
691 | printk("dsp loading error at position %d\n", i); | ||
692 | return err; | ||
693 | } | ||
694 | cptr = image; | ||
695 | csum ^= *cptr; | ||
696 | csum = (csum >> 24) | (csum << 8); | ||
697 | vx_outb(chip, TXH, *cptr++); | ||
698 | csum ^= *cptr; | ||
699 | csum = (csum >> 24) | (csum << 8); | ||
700 | vx_outb(chip, TXM, *cptr++); | ||
701 | csum ^= *cptr; | ||
702 | csum = (csum >> 24) | (csum << 8); | ||
703 | vx_outb(chip, TXL, *cptr++); | ||
704 | } | ||
705 | snd_printdd(KERN_DEBUG "checksum = 0x%08x\n", csum); | ||
706 | |||
707 | snd_vx_delay(chip, 200); | ||
708 | |||
709 | if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) | ||
710 | return err; | ||
711 | |||
712 | vx_toggle_dac_mute(chip, 0); | ||
713 | |||
714 | vx_test_and_ack(chip); | ||
715 | vx_validate_irq(chip, 1); | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | #ifdef CONFIG_PM | ||
721 | /* | ||
722 | * suspend | ||
723 | */ | ||
724 | static int snd_vx_suspend(snd_card_t *card, pm_message_t state) | ||
725 | { | ||
726 | vx_core_t *chip = card->pm_private_data; | ||
727 | unsigned int i; | ||
728 | |||
729 | snd_assert(chip, return -EINVAL); | ||
730 | |||
731 | chip->chip_status |= VX_STAT_IN_SUSPEND; | ||
732 | for (i = 0; i < chip->hw->num_codecs; i++) | ||
733 | snd_pcm_suspend_all(chip->pcm[i]); | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * resume | ||
740 | */ | ||
741 | static int snd_vx_resume(snd_card_t *card) | ||
742 | { | ||
743 | vx_core_t *chip = card->pm_private_data; | ||
744 | int i, err; | ||
745 | |||
746 | snd_assert(chip, return -EINVAL); | ||
747 | |||
748 | chip->chip_status &= ~VX_STAT_CHIP_INIT; | ||
749 | |||
750 | for (i = 0; i < 4; i++) { | ||
751 | if (! chip->firmware[i]) | ||
752 | continue; | ||
753 | err = chip->ops->load_dsp(chip, i, chip->firmware[i]); | ||
754 | if (err < 0) { | ||
755 | snd_printk(KERN_ERR "vx: firmware resume error at DSP %d\n", i); | ||
756 | return -EIO; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | chip->chip_status |= VX_STAT_CHIP_INIT; | ||
761 | chip->chip_status &= ~VX_STAT_IN_SUSPEND; | ||
762 | |||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | #endif | ||
767 | |||
768 | /** | ||
769 | * snd_vx_create - constructor for vx_core_t | ||
770 | * @hw: hardware specific record | ||
771 | * | ||
772 | * this function allocates the instance and prepare for the hardware | ||
773 | * initialization. | ||
774 | * | ||
775 | * return the instance pointer if successful, NULL in error. | ||
776 | */ | ||
777 | vx_core_t *snd_vx_create(snd_card_t *card, struct snd_vx_hardware *hw, | ||
778 | struct snd_vx_ops *ops, | ||
779 | int extra_size) | ||
780 | { | ||
781 | vx_core_t *chip; | ||
782 | |||
783 | snd_assert(card && hw && ops, return NULL); | ||
784 | |||
785 | chip = kcalloc(1, sizeof(*chip) + extra_size, GFP_KERNEL); | ||
786 | if (! chip) { | ||
787 | snd_printk(KERN_ERR "vx_core: no memory\n"); | ||
788 | return NULL; | ||
789 | } | ||
790 | spin_lock_init(&chip->lock); | ||
791 | spin_lock_init(&chip->irq_lock); | ||
792 | chip->irq = -1; | ||
793 | chip->hw = hw; | ||
794 | chip->type = hw->type; | ||
795 | chip->ops = ops; | ||
796 | tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip); | ||
797 | init_MUTEX(&chip->mixer_mutex); | ||
798 | |||
799 | chip->card = card; | ||
800 | card->private_data = chip; | ||
801 | strcpy(card->driver, hw->name); | ||
802 | sprintf(card->shortname, "Digigram %s", hw->name); | ||
803 | |||
804 | snd_card_set_pm_callback(card, snd_vx_suspend, snd_vx_resume, chip); | ||
805 | |||
806 | vx_proc_init(chip); | ||
807 | |||
808 | return chip; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * module entries | ||
813 | */ | ||
814 | static int __init alsa_vx_core_init(void) | ||
815 | { | ||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | static void __exit alsa_vx_core_exit(void) | ||
820 | { | ||
821 | } | ||
822 | |||
823 | module_init(alsa_vx_core_init) | ||
824 | module_exit(alsa_vx_core_exit) | ||
825 | |||
826 | /* | ||
827 | * exports | ||
828 | */ | ||
829 | EXPORT_SYMBOL(snd_vx_check_reg_bit); | ||
830 | EXPORT_SYMBOL(snd_vx_create); | ||
831 | EXPORT_SYMBOL(snd_vx_setup_firmware); | ||
832 | EXPORT_SYMBOL(snd_vx_free_firmware); | ||
833 | EXPORT_SYMBOL(snd_vx_irq_handler); | ||
834 | EXPORT_SYMBOL(snd_vx_delay); | ||
835 | EXPORT_SYMBOL(snd_vx_dsp_boot); | ||
836 | EXPORT_SYMBOL(snd_vx_dsp_load); | ||
837 | EXPORT_SYMBOL(snd_vx_load_boot_image); | ||
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c new file mode 100644 index 000000000000..9a3dc3c3b3de --- /dev/null +++ b/sound/drivers/vx/vx_hwdep.c | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * DSP firmware management | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
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 | #include <sound/driver.h> | ||
24 | #include <linux/device.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/hwdep.h> | ||
28 | #include <sound/vx_core.h> | ||
29 | |||
30 | #ifdef SND_VX_FW_LOADER | ||
31 | |||
32 | int snd_vx_setup_firmware(vx_core_t *chip) | ||
33 | { | ||
34 | static char *fw_files[VX_TYPE_NUMS][4] = { | ||
35 | [VX_TYPE_BOARD] = { | ||
36 | NULL, "x1_1_vx2.xlx", "bd56002.boot", "l_1_vx2.d56", | ||
37 | }, | ||
38 | [VX_TYPE_V2] = { | ||
39 | NULL, "x1_2_v22.xlx", "bd563v2.boot", "l_1_v22.d56", | ||
40 | }, | ||
41 | [VX_TYPE_MIC] = { | ||
42 | NULL, "x1_2_v22.xlx", "bd563v2.boot", "l_1_v22.d56", | ||
43 | }, | ||
44 | [VX_TYPE_VXPOCKET] = { | ||
45 | "bx_1_vxp.b56", "x1_1_vxp.xlx", "bd563s3.boot", "l_1_vxp.d56" | ||
46 | }, | ||
47 | [VX_TYPE_VXP440] = { | ||
48 | "bx_1_vp4.b56", "x1_1_vp4.xlx", "bd563s3.boot", "l_1_vp4.d56" | ||
49 | }, | ||
50 | }; | ||
51 | |||
52 | int i, err; | ||
53 | |||
54 | for (i = 0; i < 4; i++) { | ||
55 | char path[32]; | ||
56 | const struct firmware *fw; | ||
57 | if (! fw_files[chip->type][i]) | ||
58 | continue; | ||
59 | sprintf(path, "vx/%s", fw_files[chip->type][i]); | ||
60 | if (request_firmware(&fw, path, chip->dev)) { | ||
61 | snd_printk(KERN_ERR "vx: can't load firmware %s\n", path); | ||
62 | return -ENOENT; | ||
63 | } | ||
64 | err = chip->ops->load_dsp(chip, i, fw); | ||
65 | if (err < 0) { | ||
66 | release_firmware(fw); | ||
67 | return err; | ||
68 | } | ||
69 | if (i == 1) | ||
70 | chip->chip_status |= VX_STAT_XILINX_LOADED; | ||
71 | #ifdef CONFIG_PM | ||
72 | chip->firmware[i] = fw; | ||
73 | #else | ||
74 | release_firmware(fw); | ||
75 | #endif | ||
76 | } | ||
77 | |||
78 | /* ok, we reached to the last one */ | ||
79 | /* create the devices if not built yet */ | ||
80 | if ((err = snd_vx_pcm_new(chip)) < 0) | ||
81 | return err; | ||
82 | |||
83 | if ((err = snd_vx_mixer_new(chip)) < 0) | ||
84 | return err; | ||
85 | |||
86 | if (chip->ops->add_controls) | ||
87 | if ((err = chip->ops->add_controls(chip)) < 0) | ||
88 | return err; | ||
89 | |||
90 | chip->chip_status |= VX_STAT_DEVICE_INIT; | ||
91 | chip->chip_status |= VX_STAT_CHIP_INIT; | ||
92 | |||
93 | return snd_card_register(chip->card); | ||
94 | } | ||
95 | |||
96 | /* exported */ | ||
97 | void snd_vx_free_firmware(vx_core_t *chip) | ||
98 | { | ||
99 | #ifdef CONFIG_PM | ||
100 | int i; | ||
101 | for (i = 0; i < 4; i++) | ||
102 | release_firmware(chip->firmware[i]); | ||
103 | #endif | ||
104 | } | ||
105 | |||
106 | #else /* old style firmware loading */ | ||
107 | |||
108 | static int vx_hwdep_open(snd_hwdep_t *hw, struct file *file) | ||
109 | { | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int vx_hwdep_release(snd_hwdep_t *hw, struct file *file) | ||
114 | { | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int vx_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info) | ||
119 | { | ||
120 | static char *type_ids[VX_TYPE_NUMS] = { | ||
121 | [VX_TYPE_BOARD] = "vxboard", | ||
122 | [VX_TYPE_V2] = "vx222", | ||
123 | [VX_TYPE_MIC] = "vx222", | ||
124 | [VX_TYPE_VXPOCKET] = "vxpocket", | ||
125 | [VX_TYPE_VXP440] = "vxp440", | ||
126 | }; | ||
127 | vx_core_t *vx = hw->private_data; | ||
128 | |||
129 | snd_assert(type_ids[vx->type], return -EINVAL); | ||
130 | strcpy(info->id, type_ids[vx->type]); | ||
131 | if (vx_is_pcmcia(vx)) | ||
132 | info->num_dsps = 4; | ||
133 | else | ||
134 | info->num_dsps = 3; | ||
135 | if (vx->chip_status & VX_STAT_CHIP_INIT) | ||
136 | info->chip_ready = 1; | ||
137 | info->version = VX_DRIVER_VERSION; | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static void free_fw(const struct firmware *fw) | ||
142 | { | ||
143 | if (fw) { | ||
144 | vfree(fw->data); | ||
145 | kfree(fw); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | static int vx_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) | ||
150 | { | ||
151 | vx_core_t *vx = hw->private_data; | ||
152 | int index, err; | ||
153 | struct firmware *fw; | ||
154 | |||
155 | snd_assert(vx->ops->load_dsp, return -ENXIO); | ||
156 | |||
157 | fw = kmalloc(sizeof(*fw), GFP_KERNEL); | ||
158 | if (! fw) { | ||
159 | snd_printk(KERN_ERR "cannot allocate firmware\n"); | ||
160 | return -ENOMEM; | ||
161 | } | ||
162 | fw->size = dsp->length; | ||
163 | fw->data = vmalloc(fw->size); | ||
164 | if (! fw->data) { | ||
165 | snd_printk(KERN_ERR "cannot allocate firmware image (length=%d)\n", | ||
166 | (int)fw->size); | ||
167 | kfree(fw); | ||
168 | return -ENOMEM; | ||
169 | } | ||
170 | if (copy_from_user(fw->data, dsp->image, dsp->length)) { | ||
171 | free_fw(fw); | ||
172 | return -EFAULT; | ||
173 | } | ||
174 | |||
175 | index = dsp->index; | ||
176 | if (! vx_is_pcmcia(vx)) | ||
177 | index++; | ||
178 | err = vx->ops->load_dsp(vx, index, fw); | ||
179 | if (err < 0) { | ||
180 | free_fw(fw); | ||
181 | return err; | ||
182 | } | ||
183 | #ifdef CONFIG_PM | ||
184 | vx->firmware[index] = fw; | ||
185 | #else | ||
186 | free_fw(fw); | ||
187 | #endif | ||
188 | |||
189 | if (index == 1) | ||
190 | vx->chip_status |= VX_STAT_XILINX_LOADED; | ||
191 | if (index < 3) | ||
192 | return 0; | ||
193 | |||
194 | /* ok, we reached to the last one */ | ||
195 | /* create the devices if not built yet */ | ||
196 | if (! (vx->chip_status & VX_STAT_DEVICE_INIT)) { | ||
197 | if ((err = snd_vx_pcm_new(vx)) < 0) | ||
198 | return err; | ||
199 | |||
200 | if ((err = snd_vx_mixer_new(vx)) < 0) | ||
201 | return err; | ||
202 | |||
203 | if (vx->ops->add_controls) | ||
204 | if ((err = vx->ops->add_controls(vx)) < 0) | ||
205 | return err; | ||
206 | |||
207 | if ((err = snd_card_register(vx->card)) < 0) | ||
208 | return err; | ||
209 | |||
210 | vx->chip_status |= VX_STAT_DEVICE_INIT; | ||
211 | } | ||
212 | vx->chip_status |= VX_STAT_CHIP_INIT; | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | |||
217 | /* exported */ | ||
218 | int snd_vx_setup_firmware(vx_core_t *chip) | ||
219 | { | ||
220 | int err; | ||
221 | snd_hwdep_t *hw; | ||
222 | |||
223 | if ((err = snd_hwdep_new(chip->card, SND_VX_HWDEP_ID, 0, &hw)) < 0) | ||
224 | return err; | ||
225 | |||
226 | hw->iface = SNDRV_HWDEP_IFACE_VX; | ||
227 | hw->private_data = chip; | ||
228 | hw->ops.open = vx_hwdep_open; | ||
229 | hw->ops.release = vx_hwdep_release; | ||
230 | hw->ops.dsp_status = vx_hwdep_dsp_status; | ||
231 | hw->ops.dsp_load = vx_hwdep_dsp_load; | ||
232 | hw->exclusive = 1; | ||
233 | sprintf(hw->name, "VX Loader (%s)", chip->card->driver); | ||
234 | chip->hwdep = hw; | ||
235 | |||
236 | return snd_card_register(chip->card); | ||
237 | } | ||
238 | |||
239 | /* exported */ | ||
240 | void snd_vx_free_firmware(vx_core_t *chip) | ||
241 | { | ||
242 | #ifdef CONFIG_PM | ||
243 | int i; | ||
244 | for (i = 0; i < 4; i++) | ||
245 | free_fw(chip->firmware[i]); | ||
246 | #endif | ||
247 | } | ||
248 | |||
249 | #endif /* SND_VX_FW_LOADER */ | ||
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c new file mode 100644 index 000000000000..f00c88886460 --- /dev/null +++ b/sound/drivers/vx/vx_mixer.c | |||
@@ -0,0 +1,1000 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Common mixer part | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
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 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/control.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | |||
30 | /* | ||
31 | * write a codec data (24bit) | ||
32 | */ | ||
33 | static void vx_write_codec_reg(vx_core_t *chip, int codec, unsigned int data) | ||
34 | { | ||
35 | unsigned long flags; | ||
36 | |||
37 | snd_assert(chip->ops->write_codec, return); | ||
38 | |||
39 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
40 | return; | ||
41 | |||
42 | spin_lock_irqsave(&chip->lock, flags); | ||
43 | chip->ops->write_codec(chip, codec, data); | ||
44 | spin_unlock_irqrestore(&chip->lock, flags); | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Data type used to access the Codec | ||
49 | */ | ||
50 | typedef union { | ||
51 | u32 l; | ||
52 | #ifdef SNDRV_BIG_ENDIAN | ||
53 | struct w { | ||
54 | u16 h; | ||
55 | u16 l; | ||
56 | } w; | ||
57 | struct b { | ||
58 | u8 hh; | ||
59 | u8 mh; | ||
60 | u8 ml; | ||
61 | u8 ll; | ||
62 | } b; | ||
63 | #else /* LITTLE_ENDIAN */ | ||
64 | struct w { | ||
65 | u16 l; | ||
66 | u16 h; | ||
67 | } w; | ||
68 | struct b { | ||
69 | u8 ll; | ||
70 | u8 ml; | ||
71 | u8 mh; | ||
72 | u8 hh; | ||
73 | } b; | ||
74 | #endif | ||
75 | } vx_codec_data_t; | ||
76 | |||
77 | #define SET_CDC_DATA_SEL(di,s) ((di).b.mh = (u8) (s)) | ||
78 | #define SET_CDC_DATA_REG(di,r) ((di).b.ml = (u8) (r)) | ||
79 | #define SET_CDC_DATA_VAL(di,d) ((di).b.ll = (u8) (d)) | ||
80 | #define SET_CDC_DATA_INIT(di) ((di).l = 0L, SET_CDC_DATA_SEL(di,XX_CODEC_SELECTOR)) | ||
81 | |||
82 | /* | ||
83 | * set up codec register and write the value | ||
84 | * @codec: the codec id, 0 or 1 | ||
85 | * @reg: register index | ||
86 | * @val: data value | ||
87 | */ | ||
88 | static void vx_set_codec_reg(vx_core_t *chip, int codec, int reg, int val) | ||
89 | { | ||
90 | vx_codec_data_t data; | ||
91 | /* DAC control register */ | ||
92 | SET_CDC_DATA_INIT(data); | ||
93 | SET_CDC_DATA_REG(data, reg); | ||
94 | SET_CDC_DATA_VAL(data, val); | ||
95 | vx_write_codec_reg(chip, codec, data.l); | ||
96 | } | ||
97 | |||
98 | |||
99 | /* | ||
100 | * vx_set_analog_output_level - set the output attenuation level | ||
101 | * @codec: the output codec, 0 or 1. (1 for VXP440 only) | ||
102 | * @left: left output level, 0 = mute | ||
103 | * @right: right output level | ||
104 | */ | ||
105 | static void vx_set_analog_output_level(vx_core_t *chip, int codec, int left, int right) | ||
106 | { | ||
107 | left = chip->hw->output_level_max - left; | ||
108 | right = chip->hw->output_level_max - right; | ||
109 | |||
110 | if (chip->ops->akm_write) { | ||
111 | chip->ops->akm_write(chip, XX_CODEC_LEVEL_LEFT_REGISTER, left); | ||
112 | chip->ops->akm_write(chip, XX_CODEC_LEVEL_RIGHT_REGISTER, right); | ||
113 | } else { | ||
114 | /* convert to attenuation level: 0 = 0dB (max), 0xe3 = -113.5 dB (min) */ | ||
115 | vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_LEFT_REGISTER, left); | ||
116 | vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_RIGHT_REGISTER, right); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | |||
121 | /* | ||
122 | * vx_toggle_dac_mute - mute/unmute DAC | ||
123 | * @mute: 0 = unmute, 1 = mute | ||
124 | */ | ||
125 | |||
126 | #define DAC_ATTEN_MIN 0x08 | ||
127 | #define DAC_ATTEN_MAX 0x38 | ||
128 | |||
129 | void vx_toggle_dac_mute(vx_core_t *chip, int mute) | ||
130 | { | ||
131 | unsigned int i; | ||
132 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
133 | if (chip->ops->akm_write) | ||
134 | chip->ops->akm_write(chip, XX_CODEC_DAC_CONTROL_REGISTER, mute); /* XXX */ | ||
135 | else | ||
136 | vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, | ||
137 | mute ? DAC_ATTEN_MAX : DAC_ATTEN_MIN); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * vx_reset_codec - reset and initialize the codecs | ||
143 | */ | ||
144 | void vx_reset_codec(vx_core_t *chip, int cold_reset) | ||
145 | { | ||
146 | unsigned int i; | ||
147 | int port = chip->type >= VX_TYPE_VXPOCKET ? 0x75 : 0x65; | ||
148 | |||
149 | chip->ops->reset_codec(chip); | ||
150 | |||
151 | /* AKM codecs should be initialized in reset_codec callback */ | ||
152 | if (! chip->ops->akm_write) { | ||
153 | /* initialize old codecs */ | ||
154 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
155 | /* DAC control register (change level when zero crossing + mute) */ | ||
156 | vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, DAC_ATTEN_MAX); | ||
157 | /* ADC control register */ | ||
158 | vx_set_codec_reg(chip, i, XX_CODEC_ADC_CONTROL_REGISTER, 0x00); | ||
159 | /* Port mode register */ | ||
160 | vx_set_codec_reg(chip, i, XX_CODEC_PORT_MODE_REGISTER, port); | ||
161 | /* Clock control register */ | ||
162 | vx_set_codec_reg(chip, i, XX_CODEC_CLOCK_CONTROL_REGISTER, 0x00); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* mute analog output */ | ||
167 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
168 | chip->output_level[i][0] = 0; | ||
169 | chip->output_level[i][1] = 0; | ||
170 | vx_set_analog_output_level(chip, i, 0, 0); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * change the audio input source | ||
176 | * @src: the target source (VX_AUDIO_SRC_XXX) | ||
177 | */ | ||
178 | static void vx_change_audio_source(vx_core_t *chip, int src) | ||
179 | { | ||
180 | unsigned long flags; | ||
181 | |||
182 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
183 | return; | ||
184 | |||
185 | spin_lock_irqsave(&chip->lock, flags); | ||
186 | chip->ops->change_audio_source(chip, src); | ||
187 | spin_unlock_irqrestore(&chip->lock, flags); | ||
188 | } | ||
189 | |||
190 | |||
191 | /* | ||
192 | * change the audio source if necessary and possible | ||
193 | * returns 1 if the source is actually changed. | ||
194 | */ | ||
195 | int vx_sync_audio_source(vx_core_t *chip) | ||
196 | { | ||
197 | if (chip->audio_source_target == chip->audio_source || | ||
198 | chip->pcm_running) | ||
199 | return 0; | ||
200 | vx_change_audio_source(chip, chip->audio_source_target); | ||
201 | chip->audio_source = chip->audio_source_target; | ||
202 | return 1; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | * audio level, mute, monitoring | ||
208 | */ | ||
209 | struct vx_audio_level { | ||
210 | unsigned int has_level: 1; | ||
211 | unsigned int has_monitor_level: 1; | ||
212 | unsigned int has_mute: 1; | ||
213 | unsigned int has_monitor_mute: 1; | ||
214 | unsigned int mute; | ||
215 | unsigned int monitor_mute; | ||
216 | short level; | ||
217 | short monitor_level; | ||
218 | }; | ||
219 | |||
220 | static int vx_adjust_audio_level(vx_core_t *chip, int audio, int capture, | ||
221 | struct vx_audio_level *info) | ||
222 | { | ||
223 | struct vx_rmh rmh; | ||
224 | |||
225 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
226 | return -EBUSY; | ||
227 | |||
228 | vx_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST); | ||
229 | if (capture) | ||
230 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
231 | /* Add Audio IO mask */ | ||
232 | rmh.Cmd[1] = 1 << audio; | ||
233 | rmh.Cmd[2] = 0; | ||
234 | if (info->has_level) { | ||
235 | rmh.Cmd[0] |= VALID_AUDIO_IO_DIGITAL_LEVEL; | ||
236 | rmh.Cmd[2] |= info->level; | ||
237 | } | ||
238 | if (info->has_monitor_level) { | ||
239 | rmh.Cmd[0] |= VALID_AUDIO_IO_MONITORING_LEVEL; | ||
240 | rmh.Cmd[2] |= ((unsigned int)info->monitor_level << 10); | ||
241 | } | ||
242 | if (info->has_mute) { | ||
243 | rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_LEVEL; | ||
244 | if (info->mute) | ||
245 | rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_LEVEL; | ||
246 | } | ||
247 | if (info->has_monitor_mute) { | ||
248 | /* validate flag for M2 at least to unmute it */ | ||
249 | rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_MONITORING_1 | VALID_AUDIO_IO_MUTE_MONITORING_2; | ||
250 | if (info->monitor_mute) | ||
251 | rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_MONITORING_1; | ||
252 | } | ||
253 | |||
254 | return vx_send_msg(chip, &rmh); | ||
255 | } | ||
256 | |||
257 | |||
258 | #if 0 // not used | ||
259 | static int vx_read_audio_level(vx_core_t *chip, int audio, int capture, | ||
260 | struct vx_audio_level *info) | ||
261 | { | ||
262 | int err; | ||
263 | struct vx_rmh rmh; | ||
264 | |||
265 | memset(info, 0, sizeof(*info)); | ||
266 | vx_init_rmh(&rmh, CMD_GET_AUDIO_LEVELS); | ||
267 | if (capture) | ||
268 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
269 | /* Add Audio IO mask */ | ||
270 | rmh.Cmd[1] = 1 << audio; | ||
271 | err = vx_send_msg(chip, &rmh); | ||
272 | if (err < 0) | ||
273 | return err; | ||
274 | info.level = rmh.Stat[0] & MASK_DSP_WORD_LEVEL; | ||
275 | info.monitor_level = (rmh.Stat[0] >> 10) & MASK_DSP_WORD_LEVEL; | ||
276 | info.mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_LEVEL) ? 1 : 0; | ||
277 | info.monitor_mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_MONITORING_1) ? 1 : 0; | ||
278 | return 0; | ||
279 | } | ||
280 | #endif // not used | ||
281 | |||
282 | /* | ||
283 | * set the monitoring level and mute state of the given audio | ||
284 | * no more static, because must be called from vx_pcm to demute monitoring | ||
285 | */ | ||
286 | int vx_set_monitor_level(vx_core_t *chip, int audio, int level, int active) | ||
287 | { | ||
288 | struct vx_audio_level info; | ||
289 | |||
290 | memset(&info, 0, sizeof(info)); | ||
291 | info.has_monitor_level = 1; | ||
292 | info.monitor_level = level; | ||
293 | info.has_monitor_mute = 1; | ||
294 | info.monitor_mute = !active; | ||
295 | chip->audio_monitor[audio] = level; | ||
296 | chip->audio_monitor_active[audio] = active; | ||
297 | return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */ | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * set the mute status of the given audio | ||
303 | */ | ||
304 | static int vx_set_audio_switch(vx_core_t *chip, int audio, int active) | ||
305 | { | ||
306 | struct vx_audio_level info; | ||
307 | |||
308 | memset(&info, 0, sizeof(info)); | ||
309 | info.has_mute = 1; | ||
310 | info.mute = !active; | ||
311 | chip->audio_active[audio] = active; | ||
312 | return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */ | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * set the mute status of the given audio | ||
317 | */ | ||
318 | static int vx_set_audio_gain(vx_core_t *chip, int audio, int capture, int level) | ||
319 | { | ||
320 | struct vx_audio_level info; | ||
321 | |||
322 | memset(&info, 0, sizeof(info)); | ||
323 | info.has_level = 1; | ||
324 | info.level = level; | ||
325 | chip->audio_gain[capture][audio] = level; | ||
326 | return vx_adjust_audio_level(chip, audio, capture, &info); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * reset all audio levels | ||
331 | */ | ||
332 | static void vx_reset_audio_levels(vx_core_t *chip) | ||
333 | { | ||
334 | unsigned int i, c; | ||
335 | struct vx_audio_level info; | ||
336 | |||
337 | memset(chip->audio_gain, 0, sizeof(chip->audio_gain)); | ||
338 | memset(chip->audio_active, 0, sizeof(chip->audio_active)); | ||
339 | memset(chip->audio_monitor, 0, sizeof(chip->audio_monitor)); | ||
340 | memset(chip->audio_monitor_active, 0, sizeof(chip->audio_monitor_active)); | ||
341 | |||
342 | for (c = 0; c < 2; c++) { | ||
343 | for (i = 0; i < chip->hw->num_ins * 2; i++) { | ||
344 | memset(&info, 0, sizeof(info)); | ||
345 | if (c == 0) { | ||
346 | info.has_monitor_level = 1; | ||
347 | info.has_mute = 1; | ||
348 | info.has_monitor_mute = 1; | ||
349 | } | ||
350 | info.has_level = 1; | ||
351 | info.level = CVAL_0DB; /* default: 0dB */ | ||
352 | vx_adjust_audio_level(chip, i, c, &info); | ||
353 | chip->audio_gain[c][i] = CVAL_0DB; | ||
354 | chip->audio_monitor[i] = CVAL_0DB; | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | |||
360 | /* | ||
361 | * VU, peak meter record | ||
362 | */ | ||
363 | |||
364 | #define VU_METER_CHANNELS 2 | ||
365 | |||
366 | struct vx_vu_meter { | ||
367 | int saturated; | ||
368 | int vu_level; | ||
369 | int peak_level; | ||
370 | }; | ||
371 | |||
372 | /* | ||
373 | * get the VU and peak meter values | ||
374 | * @audio: the audio index | ||
375 | * @capture: 0 = playback, 1 = capture operation | ||
376 | * @info: the array of vx_vu_meter records (size = 2). | ||
377 | */ | ||
378 | static int vx_get_audio_vu_meter(vx_core_t *chip, int audio, int capture, struct vx_vu_meter *info) | ||
379 | { | ||
380 | struct vx_rmh rmh; | ||
381 | int i, err; | ||
382 | |||
383 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
384 | return -EBUSY; | ||
385 | |||
386 | vx_init_rmh(&rmh, CMD_AUDIO_VU_PIC_METER); | ||
387 | rmh.LgStat += 2 * VU_METER_CHANNELS; | ||
388 | if (capture) | ||
389 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
390 | |||
391 | /* Add Audio IO mask */ | ||
392 | rmh.Cmd[1] = 0; | ||
393 | for (i = 0; i < VU_METER_CHANNELS; i++) | ||
394 | rmh.Cmd[1] |= 1 << (audio + i); | ||
395 | err = vx_send_msg(chip, &rmh); | ||
396 | if (err < 0) | ||
397 | return err; | ||
398 | /* Read response */ | ||
399 | for (i = 0; i < 2 * VU_METER_CHANNELS; i +=2) { | ||
400 | info->saturated = (rmh.Stat[0] & (1 << (audio + i))) ? 1 : 0; | ||
401 | info->vu_level = rmh.Stat[i + 1]; | ||
402 | info->peak_level = rmh.Stat[i + 2]; | ||
403 | info++; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | |||
409 | /* | ||
410 | * control API entries | ||
411 | */ | ||
412 | |||
413 | /* | ||
414 | * output level control | ||
415 | */ | ||
416 | static int vx_output_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
417 | { | ||
418 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
419 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
420 | uinfo->count = 2; | ||
421 | uinfo->value.integer.min = 0; | ||
422 | uinfo->value.integer.max = chip->hw->output_level_max; | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int vx_output_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
427 | { | ||
428 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
429 | int codec = kcontrol->id.index; | ||
430 | down(&chip->mixer_mutex); | ||
431 | ucontrol->value.integer.value[0] = chip->output_level[codec][0]; | ||
432 | ucontrol->value.integer.value[1] = chip->output_level[codec][1]; | ||
433 | up(&chip->mixer_mutex); | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static int vx_output_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
438 | { | ||
439 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
440 | int codec = kcontrol->id.index; | ||
441 | down(&chip->mixer_mutex); | ||
442 | if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] || | ||
443 | ucontrol->value.integer.value[1] != chip->output_level[codec][1]) { | ||
444 | vx_set_analog_output_level(chip, codec, | ||
445 | ucontrol->value.integer.value[0], | ||
446 | ucontrol->value.integer.value[1]); | ||
447 | chip->output_level[codec][0] = ucontrol->value.integer.value[0]; | ||
448 | chip->output_level[codec][1] = ucontrol->value.integer.value[1]; | ||
449 | up(&chip->mixer_mutex); | ||
450 | return 1; | ||
451 | } | ||
452 | up(&chip->mixer_mutex); | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static snd_kcontrol_new_t vx_control_output_level = { | ||
457 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
458 | .name = "Master Playback Volume", | ||
459 | .info = vx_output_level_info, | ||
460 | .get = vx_output_level_get, | ||
461 | .put = vx_output_level_put, | ||
462 | }; | ||
463 | |||
464 | /* | ||
465 | * audio source select | ||
466 | */ | ||
467 | static int vx_audio_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
468 | { | ||
469 | static char *texts_mic[3] = { | ||
470 | "Digital", "Line", "Mic" | ||
471 | }; | ||
472 | static char *texts_vx2[2] = { | ||
473 | "Digital", "Analog" | ||
474 | }; | ||
475 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
476 | |||
477 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
478 | uinfo->count = 1; | ||
479 | if (chip->type >= VX_TYPE_VXPOCKET) { | ||
480 | uinfo->value.enumerated.items = 3; | ||
481 | if (uinfo->value.enumerated.item > 2) | ||
482 | uinfo->value.enumerated.item = 2; | ||
483 | strcpy(uinfo->value.enumerated.name, | ||
484 | texts_mic[uinfo->value.enumerated.item]); | ||
485 | } else { | ||
486 | uinfo->value.enumerated.items = 2; | ||
487 | if (uinfo->value.enumerated.item > 1) | ||
488 | uinfo->value.enumerated.item = 1; | ||
489 | strcpy(uinfo->value.enumerated.name, | ||
490 | texts_vx2[uinfo->value.enumerated.item]); | ||
491 | } | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int vx_audio_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
496 | { | ||
497 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
498 | ucontrol->value.enumerated.item[0] = chip->audio_source_target; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int vx_audio_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
503 | { | ||
504 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
505 | down(&chip->mixer_mutex); | ||
506 | if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) { | ||
507 | chip->audio_source_target = ucontrol->value.enumerated.item[0]; | ||
508 | vx_sync_audio_source(chip); | ||
509 | up(&chip->mixer_mutex); | ||
510 | return 1; | ||
511 | } | ||
512 | up(&chip->mixer_mutex); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static snd_kcontrol_new_t vx_control_audio_src = { | ||
517 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
518 | .name = "Capture Source", | ||
519 | .info = vx_audio_src_info, | ||
520 | .get = vx_audio_src_get, | ||
521 | .put = vx_audio_src_put, | ||
522 | }; | ||
523 | |||
524 | /* | ||
525 | * clock mode selection | ||
526 | */ | ||
527 | static int vx_clock_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
528 | { | ||
529 | static char *texts[3] = { | ||
530 | "Auto", "Internal", "External" | ||
531 | }; | ||
532 | |||
533 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
534 | uinfo->count = 1; | ||
535 | uinfo->value.enumerated.items = 3; | ||
536 | if (uinfo->value.enumerated.item > 2) | ||
537 | uinfo->value.enumerated.item = 2; | ||
538 | strcpy(uinfo->value.enumerated.name, | ||
539 | texts[uinfo->value.enumerated.item]); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static int vx_clock_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
544 | { | ||
545 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
546 | ucontrol->value.enumerated.item[0] = chip->clock_mode; | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int vx_clock_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
551 | { | ||
552 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
553 | down(&chip->mixer_mutex); | ||
554 | if (chip->clock_mode != ucontrol->value.enumerated.item[0]) { | ||
555 | chip->clock_mode = ucontrol->value.enumerated.item[0]; | ||
556 | vx_set_clock(chip, chip->freq); | ||
557 | up(&chip->mixer_mutex); | ||
558 | return 1; | ||
559 | } | ||
560 | up(&chip->mixer_mutex); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static snd_kcontrol_new_t vx_control_clock_mode = { | ||
565 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
566 | .name = "Clock Mode", | ||
567 | .info = vx_clock_mode_info, | ||
568 | .get = vx_clock_mode_get, | ||
569 | .put = vx_clock_mode_put, | ||
570 | }; | ||
571 | |||
572 | /* | ||
573 | * Audio Gain | ||
574 | */ | ||
575 | static int vx_audio_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
576 | { | ||
577 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
578 | uinfo->count = 2; | ||
579 | uinfo->value.integer.min = 0; | ||
580 | uinfo->value.integer.max = CVAL_MAX; | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static int vx_audio_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
585 | { | ||
586 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
587 | int audio = kcontrol->private_value & 0xff; | ||
588 | int capture = (kcontrol->private_value >> 8) & 1; | ||
589 | |||
590 | down(&chip->mixer_mutex); | ||
591 | ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio]; | ||
592 | ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1]; | ||
593 | up(&chip->mixer_mutex); | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static int vx_audio_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
598 | { | ||
599 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
600 | int audio = kcontrol->private_value & 0xff; | ||
601 | int capture = (kcontrol->private_value >> 8) & 1; | ||
602 | |||
603 | down(&chip->mixer_mutex); | ||
604 | if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] || | ||
605 | ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) { | ||
606 | vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]); | ||
607 | vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]); | ||
608 | up(&chip->mixer_mutex); | ||
609 | return 1; | ||
610 | } | ||
611 | up(&chip->mixer_mutex); | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static int vx_audio_monitor_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
616 | { | ||
617 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
618 | int audio = kcontrol->private_value & 0xff; | ||
619 | |||
620 | down(&chip->mixer_mutex); | ||
621 | ucontrol->value.integer.value[0] = chip->audio_monitor[audio]; | ||
622 | ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1]; | ||
623 | up(&chip->mixer_mutex); | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static int vx_audio_monitor_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
628 | { | ||
629 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
630 | int audio = kcontrol->private_value & 0xff; | ||
631 | |||
632 | down(&chip->mixer_mutex); | ||
633 | if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] || | ||
634 | ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) { | ||
635 | vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0], | ||
636 | chip->audio_monitor_active[audio]); | ||
637 | vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1], | ||
638 | chip->audio_monitor_active[audio+1]); | ||
639 | up(&chip->mixer_mutex); | ||
640 | return 1; | ||
641 | } | ||
642 | up(&chip->mixer_mutex); | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | static int vx_audio_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
647 | { | ||
648 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
649 | uinfo->count = 2; | ||
650 | uinfo->value.integer.min = 0; | ||
651 | uinfo->value.integer.max = 1; | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int vx_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
656 | { | ||
657 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
658 | int audio = kcontrol->private_value & 0xff; | ||
659 | |||
660 | down(&chip->mixer_mutex); | ||
661 | ucontrol->value.integer.value[0] = chip->audio_active[audio]; | ||
662 | ucontrol->value.integer.value[1] = chip->audio_active[audio+1]; | ||
663 | up(&chip->mixer_mutex); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int vx_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
668 | { | ||
669 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
670 | int audio = kcontrol->private_value & 0xff; | ||
671 | |||
672 | down(&chip->mixer_mutex); | ||
673 | if (ucontrol->value.integer.value[0] != chip->audio_active[audio] || | ||
674 | ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) { | ||
675 | vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]); | ||
676 | vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]); | ||
677 | up(&chip->mixer_mutex); | ||
678 | return 1; | ||
679 | } | ||
680 | up(&chip->mixer_mutex); | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static int vx_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
685 | { | ||
686 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
687 | int audio = kcontrol->private_value & 0xff; | ||
688 | |||
689 | down(&chip->mixer_mutex); | ||
690 | ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio]; | ||
691 | ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1]; | ||
692 | up(&chip->mixer_mutex); | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static int vx_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
697 | { | ||
698 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
699 | int audio = kcontrol->private_value & 0xff; | ||
700 | |||
701 | down(&chip->mixer_mutex); | ||
702 | if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] || | ||
703 | ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) { | ||
704 | vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], | ||
705 | ucontrol->value.integer.value[0]); | ||
706 | vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], | ||
707 | ucontrol->value.integer.value[1]); | ||
708 | up(&chip->mixer_mutex); | ||
709 | return 1; | ||
710 | } | ||
711 | up(&chip->mixer_mutex); | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static snd_kcontrol_new_t vx_control_audio_gain = { | ||
716 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
717 | /* name will be filled later */ | ||
718 | .info = vx_audio_gain_info, | ||
719 | .get = vx_audio_gain_get, | ||
720 | .put = vx_audio_gain_put | ||
721 | }; | ||
722 | static snd_kcontrol_new_t vx_control_output_switch = { | ||
723 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
724 | .name = "PCM Playback Switch", | ||
725 | .info = vx_audio_sw_info, | ||
726 | .get = vx_audio_sw_get, | ||
727 | .put = vx_audio_sw_put | ||
728 | }; | ||
729 | static snd_kcontrol_new_t vx_control_monitor_gain = { | ||
730 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
731 | .name = "Monitoring Volume", | ||
732 | .info = vx_audio_gain_info, /* shared */ | ||
733 | .get = vx_audio_monitor_get, | ||
734 | .put = vx_audio_monitor_put | ||
735 | }; | ||
736 | static snd_kcontrol_new_t vx_control_monitor_switch = { | ||
737 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
738 | .name = "Monitoring Switch", | ||
739 | .info = vx_audio_sw_info, /* shared */ | ||
740 | .get = vx_monitor_sw_get, | ||
741 | .put = vx_monitor_sw_put | ||
742 | }; | ||
743 | |||
744 | |||
745 | /* | ||
746 | * IEC958 status bits | ||
747 | */ | ||
748 | static int vx_iec958_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
749 | { | ||
750 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
751 | uinfo->count = 1; | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int vx_iec958_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
756 | { | ||
757 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
758 | |||
759 | down(&chip->mixer_mutex); | ||
760 | ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff; | ||
761 | ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff; | ||
762 | ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff; | ||
763 | ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff; | ||
764 | up(&chip->mixer_mutex); | ||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int vx_iec958_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
769 | { | ||
770 | ucontrol->value.iec958.status[0] = 0xff; | ||
771 | ucontrol->value.iec958.status[1] = 0xff; | ||
772 | ucontrol->value.iec958.status[2] = 0xff; | ||
773 | ucontrol->value.iec958.status[3] = 0xff; | ||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static int vx_iec958_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
778 | { | ||
779 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
780 | unsigned int val; | ||
781 | |||
782 | val = (ucontrol->value.iec958.status[0] << 0) | | ||
783 | (ucontrol->value.iec958.status[1] << 8) | | ||
784 | (ucontrol->value.iec958.status[2] << 16) | | ||
785 | (ucontrol->value.iec958.status[3] << 24); | ||
786 | down(&chip->mixer_mutex); | ||
787 | if (chip->uer_bits != val) { | ||
788 | chip->uer_bits = val; | ||
789 | vx_set_iec958_status(chip, val); | ||
790 | up(&chip->mixer_mutex); | ||
791 | return 1; | ||
792 | } | ||
793 | up(&chip->mixer_mutex); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static snd_kcontrol_new_t vx_control_iec958_mask = { | ||
798 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
799 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
800 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), | ||
801 | .info = vx_iec958_info, /* shared */ | ||
802 | .get = vx_iec958_mask_get, | ||
803 | }; | ||
804 | |||
805 | static snd_kcontrol_new_t vx_control_iec958 = { | ||
806 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
807 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | ||
808 | .info = vx_iec958_info, | ||
809 | .get = vx_iec958_get, | ||
810 | .put = vx_iec958_put | ||
811 | }; | ||
812 | |||
813 | |||
814 | /* | ||
815 | * VU meter | ||
816 | */ | ||
817 | |||
818 | #define METER_MAX 0xff | ||
819 | #define METER_SHIFT 16 | ||
820 | |||
821 | static int vx_vu_meter_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
822 | { | ||
823 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
824 | uinfo->count = 2; | ||
825 | uinfo->value.integer.min = 0; | ||
826 | uinfo->value.integer.max = METER_MAX; | ||
827 | return 0; | ||
828 | } | ||
829 | |||
830 | static int vx_vu_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
831 | { | ||
832 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
833 | struct vx_vu_meter meter[2]; | ||
834 | int audio = kcontrol->private_value & 0xff; | ||
835 | int capture = (kcontrol->private_value >> 8) & 1; | ||
836 | |||
837 | vx_get_audio_vu_meter(chip, audio, capture, meter); | ||
838 | ucontrol->value.integer.value[0] = meter[0].vu_level >> METER_SHIFT; | ||
839 | ucontrol->value.integer.value[1] = meter[1].vu_level >> METER_SHIFT; | ||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static int vx_peak_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
844 | { | ||
845 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
846 | struct vx_vu_meter meter[2]; | ||
847 | int audio = kcontrol->private_value & 0xff; | ||
848 | int capture = (kcontrol->private_value >> 8) & 1; | ||
849 | |||
850 | vx_get_audio_vu_meter(chip, audio, capture, meter); | ||
851 | ucontrol->value.integer.value[0] = meter[0].peak_level >> METER_SHIFT; | ||
852 | ucontrol->value.integer.value[1] = meter[1].peak_level >> METER_SHIFT; | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | static int vx_saturation_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
857 | { | ||
858 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
859 | uinfo->count = 2; | ||
860 | uinfo->value.integer.min = 0; | ||
861 | uinfo->value.integer.max = 1; | ||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static int vx_saturation_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
866 | { | ||
867 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
868 | struct vx_vu_meter meter[2]; | ||
869 | int audio = kcontrol->private_value & 0xff; | ||
870 | |||
871 | vx_get_audio_vu_meter(chip, audio, 1, meter); /* capture only */ | ||
872 | ucontrol->value.integer.value[0] = meter[0].saturated; | ||
873 | ucontrol->value.integer.value[1] = meter[1].saturated; | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static snd_kcontrol_new_t vx_control_vu_meter = { | ||
878 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
879 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
880 | /* name will be filled later */ | ||
881 | .info = vx_vu_meter_info, | ||
882 | .get = vx_vu_meter_get, | ||
883 | }; | ||
884 | |||
885 | static snd_kcontrol_new_t vx_control_peak_meter = { | ||
886 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
887 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
888 | /* name will be filled later */ | ||
889 | .info = vx_vu_meter_info, /* shared */ | ||
890 | .get = vx_peak_meter_get, | ||
891 | }; | ||
892 | |||
893 | static snd_kcontrol_new_t vx_control_saturation = { | ||
894 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
895 | .name = "Input Saturation", | ||
896 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
897 | .info = vx_saturation_info, | ||
898 | .get = vx_saturation_get, | ||
899 | }; | ||
900 | |||
901 | |||
902 | |||
903 | /* | ||
904 | * | ||
905 | */ | ||
906 | |||
907 | int snd_vx_mixer_new(vx_core_t *chip) | ||
908 | { | ||
909 | unsigned int i, c; | ||
910 | int err; | ||
911 | snd_kcontrol_new_t temp; | ||
912 | snd_card_t *card = chip->card; | ||
913 | char name[32]; | ||
914 | |||
915 | strcpy(card->mixername, card->driver); | ||
916 | |||
917 | /* output level controls */ | ||
918 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
919 | temp = vx_control_output_level; | ||
920 | temp.index = i; | ||
921 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
922 | return err; | ||
923 | } | ||
924 | |||
925 | /* PCM volumes, switches, monitoring */ | ||
926 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
927 | int val = i * 2; | ||
928 | temp = vx_control_audio_gain; | ||
929 | temp.index = i; | ||
930 | temp.name = "PCM Playback Volume"; | ||
931 | temp.private_value = val; | ||
932 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
933 | return err; | ||
934 | temp = vx_control_output_switch; | ||
935 | temp.index = i; | ||
936 | temp.private_value = val; | ||
937 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
938 | return err; | ||
939 | temp = vx_control_monitor_gain; | ||
940 | temp.index = i; | ||
941 | temp.private_value = val; | ||
942 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
943 | return err; | ||
944 | temp = vx_control_monitor_switch; | ||
945 | temp.index = i; | ||
946 | temp.private_value = val; | ||
947 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
948 | return err; | ||
949 | } | ||
950 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
951 | temp = vx_control_audio_gain; | ||
952 | temp.index = i; | ||
953 | temp.name = "PCM Capture Volume"; | ||
954 | temp.private_value = (i * 2) | (1 << 8); | ||
955 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
956 | return err; | ||
957 | } | ||
958 | |||
959 | /* Audio source */ | ||
960 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0) | ||
961 | return err; | ||
962 | /* clock mode */ | ||
963 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0) | ||
964 | return err; | ||
965 | /* IEC958 controls */ | ||
966 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0) | ||
967 | return err; | ||
968 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0) | ||
969 | return err; | ||
970 | /* VU, peak, saturation meters */ | ||
971 | for (c = 0; c < 2; c++) { | ||
972 | static char *dir[2] = { "Output", "Input" }; | ||
973 | for (i = 0; i < chip->hw->num_ins; i++) { | ||
974 | int val = (i * 2) | (c << 8); | ||
975 | if (c == 1) { | ||
976 | temp = vx_control_saturation; | ||
977 | temp.index = i; | ||
978 | temp.private_value = val; | ||
979 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
980 | return err; | ||
981 | } | ||
982 | sprintf(name, "%s VU Meter", dir[c]); | ||
983 | temp = vx_control_vu_meter; | ||
984 | temp.index = i; | ||
985 | temp.name = name; | ||
986 | temp.private_value = val; | ||
987 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
988 | return err; | ||
989 | sprintf(name, "%s Peak Meter", dir[c]); | ||
990 | temp = vx_control_peak_meter; | ||
991 | temp.index = i; | ||
992 | temp.name = name; | ||
993 | temp.private_value = val; | ||
994 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
995 | return err; | ||
996 | } | ||
997 | } | ||
998 | vx_reset_audio_levels(chip); | ||
999 | return 0; | ||
1000 | } | ||
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c new file mode 100644 index 000000000000..98587176b327 --- /dev/null +++ b/sound/drivers/vx/vx_pcm.c | |||
@@ -0,0 +1,1312 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * PCM part | ||
5 | * | ||
6 | * Copyright (c) 2002,2003 by Takashi Iwai <tiwai@suse.de> | ||
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 | * STRATEGY | ||
24 | * for playback, we send series of "chunks", which size is equal with the | ||
25 | * IBL size, typically 126 samples. at each end of chunk, the end-of-buffer | ||
26 | * interrupt is notified, and the interrupt handler will feed the next chunk. | ||
27 | * | ||
28 | * the current position is calculated from the sample count RMH. | ||
29 | * pipe->transferred is the counter of data which has been already transferred. | ||
30 | * if this counter reaches to the period size, snd_pcm_period_elapsed() will | ||
31 | * be issued. | ||
32 | * | ||
33 | * for capture, the situation is much easier. | ||
34 | * to get a low latency response, we'll check the capture streams at each | ||
35 | * interrupt (capture stream has no EOB notification). if the pending | ||
36 | * data is accumulated to the period size, snd_pcm_period_elapsed() is | ||
37 | * called and the pointer is updated. | ||
38 | * | ||
39 | * the current point of read buffer is kept in pipe->hw_ptr. note that | ||
40 | * this is in bytes. | ||
41 | * | ||
42 | * | ||
43 | * TODO | ||
44 | * - linked trigger for full-duplex mode. | ||
45 | * - scheduled action on the stream. | ||
46 | */ | ||
47 | |||
48 | #include <sound/driver.h> | ||
49 | #include <linux/slab.h> | ||
50 | #include <linux/vmalloc.h> | ||
51 | #include <linux/delay.h> | ||
52 | #include <sound/core.h> | ||
53 | #include <sound/asoundef.h> | ||
54 | #include <sound/pcm.h> | ||
55 | #include <sound/vx_core.h> | ||
56 | #include "vx_cmd.h" | ||
57 | |||
58 | |||
59 | /* | ||
60 | * we use a vmalloc'ed (sg-)buffer | ||
61 | */ | ||
62 | |||
63 | /* get the physical page pointer on the given offset */ | ||
64 | static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) | ||
65 | { | ||
66 | void *pageptr = subs->runtime->dma_area + offset; | ||
67 | return vmalloc_to_page(pageptr); | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * allocate a buffer via vmalloc_32(). | ||
72 | * called from hw_params | ||
73 | * NOTE: this may be called not only once per pcm open! | ||
74 | */ | ||
75 | static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) | ||
76 | { | ||
77 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
78 | if (runtime->dma_area) { | ||
79 | /* already allocated */ | ||
80 | if (runtime->dma_bytes >= size) | ||
81 | return 0; /* already enough large */ | ||
82 | vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */ | ||
83 | } | ||
84 | runtime->dma_area = vmalloc_32(size); | ||
85 | if (! runtime->dma_area) | ||
86 | return -ENOMEM; | ||
87 | memset(runtime->dma_area, 0, size); | ||
88 | runtime->dma_bytes = size; | ||
89 | return 1; /* changed */ | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * free the buffer. | ||
94 | * called from hw_free callback | ||
95 | * NOTE: this may be called not only once per pcm open! | ||
96 | */ | ||
97 | static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) | ||
98 | { | ||
99 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
100 | if (runtime->dma_area) { | ||
101 | vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */ | ||
102 | runtime->dma_area = NULL; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | |||
108 | /* | ||
109 | * read three pending pcm bytes via inb() | ||
110 | */ | ||
111 | static void vx_pcm_read_per_bytes(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) | ||
112 | { | ||
113 | int offset = pipe->hw_ptr; | ||
114 | unsigned char *buf = (unsigned char *)(runtime->dma_area + offset); | ||
115 | *buf++ = vx_inb(chip, RXH); | ||
116 | if (++offset >= pipe->buffer_bytes) { | ||
117 | offset = 0; | ||
118 | buf = (unsigned char *)runtime->dma_area; | ||
119 | } | ||
120 | *buf++ = vx_inb(chip, RXM); | ||
121 | if (++offset >= pipe->buffer_bytes) { | ||
122 | offset = 0; | ||
123 | buf = (unsigned char *)runtime->dma_area; | ||
124 | } | ||
125 | *buf++ = vx_inb(chip, RXL); | ||
126 | if (++offset >= pipe->buffer_bytes) { | ||
127 | offset = 0; | ||
128 | buf = (unsigned char *)runtime->dma_area; | ||
129 | } | ||
130 | pipe->hw_ptr = offset; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * vx_set_pcx_time - convert from the PC time to the RMH status time. | ||
135 | * @pc_time: the pointer for the PC-time to set | ||
136 | * @dsp_time: the pointer for RMH status time array | ||
137 | */ | ||
138 | static void vx_set_pcx_time(vx_core_t *chip, pcx_time_t *pc_time, unsigned int *dsp_time) | ||
139 | { | ||
140 | dsp_time[0] = (unsigned int)((*pc_time) >> 24) & PCX_TIME_HI_MASK; | ||
141 | dsp_time[1] = (unsigned int)(*pc_time) & MASK_DSP_WORD; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * vx_set_differed_time - set the differed time if specified | ||
146 | * @rmh: the rmh record to modify | ||
147 | * @pipe: the pipe to be checked | ||
148 | * | ||
149 | * if the pipe is programmed with the differed time, set the DSP time | ||
150 | * on the rmh and changes its command length. | ||
151 | * | ||
152 | * returns the increase of the command length. | ||
153 | */ | ||
154 | static int vx_set_differed_time(vx_core_t *chip, struct vx_rmh *rmh, vx_pipe_t *pipe) | ||
155 | { | ||
156 | /* Update The length added to the RMH command by the timestamp */ | ||
157 | if (! (pipe->differed_type & DC_DIFFERED_DELAY)) | ||
158 | return 0; | ||
159 | |||
160 | /* Set the T bit */ | ||
161 | rmh->Cmd[0] |= DSP_DIFFERED_COMMAND_MASK; | ||
162 | |||
163 | /* Time stamp is the 1st following parameter */ | ||
164 | vx_set_pcx_time(chip, &pipe->pcx_time, &rmh->Cmd[1]); | ||
165 | |||
166 | /* Add the flags to a notified differed command */ | ||
167 | if (pipe->differed_type & DC_NOTIFY_DELAY) | ||
168 | rmh->Cmd[1] |= NOTIFY_MASK_TIME_HIGH ; | ||
169 | |||
170 | /* Add the flags to a multiple differed command */ | ||
171 | if (pipe->differed_type & DC_MULTIPLE_DELAY) | ||
172 | rmh->Cmd[1] |= MULTIPLE_MASK_TIME_HIGH; | ||
173 | |||
174 | /* Add the flags to a stream-time differed command */ | ||
175 | if (pipe->differed_type & DC_STREAM_TIME_DELAY) | ||
176 | rmh->Cmd[1] |= STREAM_MASK_TIME_HIGH; | ||
177 | |||
178 | rmh->LgCmd += 2; | ||
179 | return 2; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * vx_set_stream_format - send the stream format command | ||
184 | * @pipe: the affected pipe | ||
185 | * @data: format bitmask | ||
186 | */ | ||
187 | static int vx_set_stream_format(vx_core_t *chip, vx_pipe_t *pipe, unsigned int data) | ||
188 | { | ||
189 | struct vx_rmh rmh; | ||
190 | |||
191 | vx_init_rmh(&rmh, pipe->is_capture ? | ||
192 | CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT); | ||
193 | rmh.Cmd[0] |= pipe->number << FIELD_SIZE; | ||
194 | |||
195 | /* Command might be longer since we may have to add a timestamp */ | ||
196 | vx_set_differed_time(chip, &rmh, pipe); | ||
197 | |||
198 | rmh.Cmd[rmh.LgCmd] = (data & 0xFFFFFF00) >> 8; | ||
199 | rmh.Cmd[rmh.LgCmd + 1] = (data & 0xFF) << 16 /*| (datal & 0xFFFF00) >> 8*/; | ||
200 | rmh.LgCmd += 2; | ||
201 | |||
202 | return vx_send_msg(chip, &rmh); | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | * vx_set_format - set the format of a pipe | ||
208 | * @pipe: the affected pipe | ||
209 | * @runtime: pcm runtime instance to be referred | ||
210 | * | ||
211 | * returns 0 if successful, or a negative error code. | ||
212 | */ | ||
213 | static int vx_set_format(vx_core_t *chip, vx_pipe_t *pipe, | ||
214 | snd_pcm_runtime_t *runtime) | ||
215 | { | ||
216 | unsigned int header = HEADER_FMT_BASE; | ||
217 | |||
218 | if (runtime->channels == 1) | ||
219 | header |= HEADER_FMT_MONO; | ||
220 | if (snd_pcm_format_little_endian(runtime->format)) | ||
221 | header |= HEADER_FMT_INTEL; | ||
222 | if (runtime->rate < 32000 && runtime->rate > 11025) | ||
223 | header |= HEADER_FMT_UPTO32; | ||
224 | else if (runtime->rate <= 11025) | ||
225 | header |= HEADER_FMT_UPTO11; | ||
226 | |||
227 | switch (snd_pcm_format_physical_width(runtime->format)) { | ||
228 | // case 8: break; | ||
229 | case 16: header |= HEADER_FMT_16BITS; break; | ||
230 | case 24: header |= HEADER_FMT_24BITS; break; | ||
231 | default : | ||
232 | snd_BUG(); | ||
233 | return -EINVAL; | ||
234 | }; | ||
235 | |||
236 | return vx_set_stream_format(chip, pipe, header); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * set / query the IBL size | ||
241 | */ | ||
242 | static int vx_set_ibl(vx_core_t *chip, struct vx_ibl_info *info) | ||
243 | { | ||
244 | int err; | ||
245 | struct vx_rmh rmh; | ||
246 | |||
247 | vx_init_rmh(&rmh, CMD_IBL); | ||
248 | rmh.Cmd[0] |= info->size & 0x03ffff; | ||
249 | err = vx_send_msg(chip, &rmh); | ||
250 | if (err < 0) | ||
251 | return err; | ||
252 | info->size = rmh.Stat[0]; | ||
253 | info->max_size = rmh.Stat[1]; | ||
254 | info->min_size = rmh.Stat[2]; | ||
255 | info->granularity = rmh.Stat[3]; | ||
256 | snd_printdd(KERN_DEBUG "vx_set_ibl: size = %d, max = %d, min = %d, gran = %d\n", | ||
257 | info->size, info->max_size, info->min_size, info->granularity); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | |||
262 | /* | ||
263 | * vx_get_pipe_state - get the state of a pipe | ||
264 | * @pipe: the pipe to be checked | ||
265 | * @state: the pointer for the returned state | ||
266 | * | ||
267 | * checks the state of a given pipe, and stores the state (1 = running, | ||
268 | * 0 = paused) on the given pointer. | ||
269 | * | ||
270 | * called from trigger callback only | ||
271 | */ | ||
272 | static int vx_get_pipe_state(vx_core_t *chip, vx_pipe_t *pipe, int *state) | ||
273 | { | ||
274 | int err; | ||
275 | struct vx_rmh rmh; | ||
276 | |||
277 | vx_init_rmh(&rmh, CMD_PIPE_STATE); | ||
278 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
279 | err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
280 | if (! err) | ||
281 | *state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0; | ||
282 | return err; | ||
283 | } | ||
284 | |||
285 | |||
286 | /* | ||
287 | * vx_query_hbuffer_size - query available h-buffer size in bytes | ||
288 | * @pipe: the pipe to be checked | ||
289 | * | ||
290 | * return the available size on h-buffer in bytes, | ||
291 | * or a negative error code. | ||
292 | * | ||
293 | * NOTE: calling this function always switches to the stream mode. | ||
294 | * you'll need to disconnect the host to get back to the | ||
295 | * normal mode. | ||
296 | */ | ||
297 | static int vx_query_hbuffer_size(vx_core_t *chip, vx_pipe_t *pipe) | ||
298 | { | ||
299 | int result; | ||
300 | struct vx_rmh rmh; | ||
301 | |||
302 | vx_init_rmh(&rmh, CMD_SIZE_HBUFFER); | ||
303 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
304 | if (pipe->is_capture) | ||
305 | rmh.Cmd[0] |= 0x00000001; | ||
306 | result = vx_send_msg(chip, &rmh); | ||
307 | if (! result) | ||
308 | result = rmh.Stat[0] & 0xffff; | ||
309 | return result; | ||
310 | } | ||
311 | |||
312 | |||
313 | /* | ||
314 | * vx_pipe_can_start - query whether a pipe is ready for start | ||
315 | * @pipe: the pipe to be checked | ||
316 | * | ||
317 | * return 1 if ready, 0 if not ready, and negative value on error. | ||
318 | * | ||
319 | * called from trigger callback only | ||
320 | */ | ||
321 | static int vx_pipe_can_start(vx_core_t *chip, vx_pipe_t *pipe) | ||
322 | { | ||
323 | int err; | ||
324 | struct vx_rmh rmh; | ||
325 | |||
326 | vx_init_rmh(&rmh, CMD_CAN_START_PIPE); | ||
327 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
328 | rmh.Cmd[0] |= 1; | ||
329 | |||
330 | err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
331 | if (! err) { | ||
332 | if (rmh.Stat[0]) | ||
333 | err = 1; | ||
334 | } | ||
335 | return err; | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * vx_conf_pipe - tell the pipe to stand by and wait for IRQA. | ||
340 | * @pipe: the pipe to be configured | ||
341 | */ | ||
342 | static int vx_conf_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
343 | { | ||
344 | struct vx_rmh rmh; | ||
345 | |||
346 | vx_init_rmh(&rmh, CMD_CONF_PIPE); | ||
347 | if (pipe->is_capture) | ||
348 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
349 | rmh.Cmd[1] = 1 << pipe->number; | ||
350 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * vx_send_irqa - trigger IRQA | ||
355 | */ | ||
356 | static int vx_send_irqa(vx_core_t *chip) | ||
357 | { | ||
358 | struct vx_rmh rmh; | ||
359 | |||
360 | vx_init_rmh(&rmh, CMD_SEND_IRQA); | ||
361 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
362 | } | ||
363 | |||
364 | |||
365 | #define MAX_WAIT_FOR_DSP 250 | ||
366 | /* | ||
367 | * vx boards do not support inter-card sync, besides | ||
368 | * only 126 samples require to be prepared before a pipe can start | ||
369 | */ | ||
370 | #define CAN_START_DELAY 2 /* wait 2ms only before asking if the pipe is ready*/ | ||
371 | #define WAIT_STATE_DELAY 2 /* wait 2ms after irqA was requested and check if the pipe state toggled*/ | ||
372 | |||
373 | /* | ||
374 | * vx_toggle_pipe - start / pause a pipe | ||
375 | * @pipe: the pipe to be triggered | ||
376 | * @state: start = 1, pause = 0 | ||
377 | * | ||
378 | * called from trigger callback only | ||
379 | * | ||
380 | */ | ||
381 | static int vx_toggle_pipe(vx_core_t *chip, vx_pipe_t *pipe, int state) | ||
382 | { | ||
383 | int err, i, cur_state; | ||
384 | |||
385 | /* Check the pipe is not already in the requested state */ | ||
386 | if (vx_get_pipe_state(chip, pipe, &cur_state) < 0) | ||
387 | return -EBADFD; | ||
388 | if (state == cur_state) | ||
389 | return 0; | ||
390 | |||
391 | /* If a start is requested, ask the DSP to get prepared | ||
392 | * and wait for a positive acknowledge (when there are | ||
393 | * enough sound buffer for this pipe) | ||
394 | */ | ||
395 | if (state) { | ||
396 | for (i = 0 ; i < MAX_WAIT_FOR_DSP; i++) { | ||
397 | err = vx_pipe_can_start(chip, pipe); | ||
398 | if (err > 0) | ||
399 | break; | ||
400 | /* Wait for a few, before asking again | ||
401 | * to avoid flooding the DSP with our requests | ||
402 | */ | ||
403 | mdelay(1); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | if ((err = vx_conf_pipe(chip, pipe)) < 0) | ||
408 | return err; | ||
409 | |||
410 | if ((err = vx_send_irqa(chip)) < 0) | ||
411 | return err; | ||
412 | |||
413 | /* If it completes successfully, wait for the pipes | ||
414 | * reaching the expected state before returning | ||
415 | * Check one pipe only (since they are synchronous) | ||
416 | */ | ||
417 | for (i = 0; i < MAX_WAIT_FOR_DSP; i++) { | ||
418 | err = vx_get_pipe_state(chip, pipe, &cur_state); | ||
419 | if (err < 0 || cur_state == state) | ||
420 | break; | ||
421 | err = -EIO; | ||
422 | mdelay(1); | ||
423 | } | ||
424 | return err < 0 ? -EIO : 0; | ||
425 | } | ||
426 | |||
427 | |||
428 | /* | ||
429 | * vx_stop_pipe - stop a pipe | ||
430 | * @pipe: the pipe to be stopped | ||
431 | * | ||
432 | * called from trigger callback only | ||
433 | */ | ||
434 | static int vx_stop_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
435 | { | ||
436 | struct vx_rmh rmh; | ||
437 | vx_init_rmh(&rmh, CMD_STOP_PIPE); | ||
438 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
439 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
440 | } | ||
441 | |||
442 | |||
443 | /* | ||
444 | * vx_alloc_pipe - allocate a pipe and initialize the pipe instance | ||
445 | * @capture: 0 = playback, 1 = capture operation | ||
446 | * @audioid: the audio id to be assigned | ||
447 | * @num_audio: number of audio channels | ||
448 | * @pipep: the returned pipe instance | ||
449 | * | ||
450 | * return 0 on success, or a negative error code. | ||
451 | */ | ||
452 | static int vx_alloc_pipe(vx_core_t *chip, int capture, | ||
453 | int audioid, int num_audio, | ||
454 | vx_pipe_t **pipep) | ||
455 | { | ||
456 | int err; | ||
457 | vx_pipe_t *pipe; | ||
458 | struct vx_rmh rmh; | ||
459 | int data_mode; | ||
460 | |||
461 | *pipep = NULL; | ||
462 | vx_init_rmh(&rmh, CMD_RES_PIPE); | ||
463 | vx_set_pipe_cmd_params(&rmh, capture, audioid, num_audio); | ||
464 | #if 0 // NYI | ||
465 | if (underrun_skip_sound) | ||
466 | rmh.Cmd[0] |= BIT_SKIP_SOUND; | ||
467 | #endif // NYI | ||
468 | data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; | ||
469 | if (! capture && data_mode) | ||
470 | rmh.Cmd[0] |= BIT_DATA_MODE; | ||
471 | err = vx_send_msg(chip, &rmh); | ||
472 | if (err < 0) | ||
473 | return err; | ||
474 | |||
475 | /* initialize the pipe record */ | ||
476 | pipe = kcalloc(1, sizeof(*pipe), GFP_KERNEL); | ||
477 | if (! pipe) { | ||
478 | /* release the pipe */ | ||
479 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
480 | vx_set_pipe_cmd_params(&rmh, capture, audioid, 0); | ||
481 | vx_send_msg(chip, &rmh); | ||
482 | return -ENOMEM; | ||
483 | } | ||
484 | |||
485 | /* the pipe index should be identical with the audio index */ | ||
486 | pipe->number = audioid; | ||
487 | pipe->is_capture = capture; | ||
488 | pipe->channels = num_audio; | ||
489 | pipe->differed_type = 0; | ||
490 | pipe->pcx_time = 0; | ||
491 | pipe->data_mode = data_mode; | ||
492 | *pipep = pipe; | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | |||
498 | /* | ||
499 | * vx_free_pipe - release a pipe | ||
500 | * @pipe: pipe to be released | ||
501 | */ | ||
502 | static int vx_free_pipe(vx_core_t *chip, vx_pipe_t *pipe) | ||
503 | { | ||
504 | struct vx_rmh rmh; | ||
505 | |||
506 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
507 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
508 | vx_send_msg(chip, &rmh); | ||
509 | |||
510 | kfree(pipe); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | |||
515 | /* | ||
516 | * vx_start_stream - start the stream | ||
517 | * | ||
518 | * called from trigger callback only | ||
519 | */ | ||
520 | static int vx_start_stream(vx_core_t *chip, vx_pipe_t *pipe) | ||
521 | { | ||
522 | struct vx_rmh rmh; | ||
523 | |||
524 | vx_init_rmh(&rmh, CMD_START_ONE_STREAM); | ||
525 | vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); | ||
526 | vx_set_differed_time(chip, &rmh, pipe); | ||
527 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
528 | } | ||
529 | |||
530 | |||
531 | /* | ||
532 | * vx_stop_stream - stop the stream | ||
533 | * | ||
534 | * called from trigger callback only | ||
535 | */ | ||
536 | static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe) | ||
537 | { | ||
538 | struct vx_rmh rmh; | ||
539 | |||
540 | vx_init_rmh(&rmh, CMD_STOP_STREAM); | ||
541 | vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number); | ||
542 | return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ | ||
543 | } | ||
544 | |||
545 | |||
546 | /* | ||
547 | * playback hw information | ||
548 | */ | ||
549 | |||
550 | static snd_pcm_hardware_t vx_pcm_playback_hw = { | ||
551 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
552 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | | ||
553 | SNDRV_PCM_INFO_RESUME), | ||
554 | .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, | ||
555 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
556 | .rate_min = 5000, | ||
557 | .rate_max = 48000, | ||
558 | .channels_min = 1, | ||
559 | .channels_max = 2, | ||
560 | .buffer_bytes_max = (128*1024), | ||
561 | .period_bytes_min = 126, | ||
562 | .period_bytes_max = (128*1024), | ||
563 | .periods_min = 2, | ||
564 | .periods_max = VX_MAX_PERIODS, | ||
565 | .fifo_size = 126, | ||
566 | }; | ||
567 | |||
568 | |||
569 | static void vx_pcm_delayed_start(unsigned long arg); | ||
570 | |||
571 | /* | ||
572 | * vx_pcm_playback_open - open callback for playback | ||
573 | */ | ||
574 | static int vx_pcm_playback_open(snd_pcm_substream_t *subs) | ||
575 | { | ||
576 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
577 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
578 | vx_pipe_t *pipe = NULL; | ||
579 | unsigned int audio; | ||
580 | int err; | ||
581 | |||
582 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
583 | return -EBUSY; | ||
584 | |||
585 | audio = subs->pcm->device * 2; | ||
586 | snd_assert(audio < chip->audio_outs, return -EINVAL); | ||
587 | |||
588 | /* playback pipe may have been already allocated for monitoring */ | ||
589 | pipe = chip->playback_pipes[audio]; | ||
590 | if (! pipe) { | ||
591 | /* not allocated yet */ | ||
592 | err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */ | ||
593 | if (err < 0) | ||
594 | return err; | ||
595 | chip->playback_pipes[audio] = pipe; | ||
596 | } | ||
597 | /* open for playback */ | ||
598 | pipe->references++; | ||
599 | |||
600 | pipe->substream = subs; | ||
601 | tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); | ||
602 | chip->playback_pipes[audio] = pipe; | ||
603 | |||
604 | runtime->hw = vx_pcm_playback_hw; | ||
605 | runtime->hw.period_bytes_min = chip->ibl.size; | ||
606 | runtime->private_data = pipe; | ||
607 | |||
608 | /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ | ||
609 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); | ||
610 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | /* | ||
616 | * vx_pcm_playback_close - close callback for playback | ||
617 | */ | ||
618 | static int vx_pcm_playback_close(snd_pcm_substream_t *subs) | ||
619 | { | ||
620 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
621 | vx_pipe_t *pipe; | ||
622 | |||
623 | if (! subs->runtime->private_data) | ||
624 | return -EINVAL; | ||
625 | |||
626 | pipe = subs->runtime->private_data; | ||
627 | |||
628 | if (--pipe->references == 0) { | ||
629 | chip->playback_pipes[pipe->number] = NULL; | ||
630 | vx_free_pipe(chip, pipe); | ||
631 | } | ||
632 | |||
633 | return 0; | ||
634 | |||
635 | } | ||
636 | |||
637 | |||
638 | /* | ||
639 | * vx_notify_end_of_buffer - send "end-of-buffer" notifier at the given pipe | ||
640 | * @pipe: the pipe to notify | ||
641 | * | ||
642 | * NB: call with a certain lock. | ||
643 | */ | ||
644 | static int vx_notify_end_of_buffer(vx_core_t *chip, vx_pipe_t *pipe) | ||
645 | { | ||
646 | int err; | ||
647 | struct vx_rmh rmh; /* use a temporary rmh here */ | ||
648 | |||
649 | /* Toggle Dsp Host Interface into Message mode */ | ||
650 | vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); | ||
651 | vx_init_rmh(&rmh, CMD_NOTIFY_END_OF_BUFFER); | ||
652 | vx_set_stream_cmd_params(&rmh, 0, pipe->number); | ||
653 | err = vx_send_msg_nolock(chip, &rmh); | ||
654 | if (err < 0) | ||
655 | return err; | ||
656 | /* Toggle Dsp Host Interface back to sound transfer mode */ | ||
657 | vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT); | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * vx_pcm_playback_transfer_chunk - transfer a single chunk | ||
663 | * @subs: substream | ||
664 | * @pipe: the pipe to transfer | ||
665 | * @size: chunk size in bytes | ||
666 | * | ||
667 | * transfer a single buffer chunk. EOB notificaton is added after that. | ||
668 | * called from the interrupt handler, too. | ||
669 | * | ||
670 | * return 0 if ok. | ||
671 | */ | ||
672 | static int vx_pcm_playback_transfer_chunk(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe, int size) | ||
673 | { | ||
674 | int space, err = 0; | ||
675 | |||
676 | space = vx_query_hbuffer_size(chip, pipe); | ||
677 | if (space < 0) { | ||
678 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
679 | vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); | ||
680 | snd_printd("error hbuffer\n"); | ||
681 | return space; | ||
682 | } | ||
683 | if (space < size) { | ||
684 | vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT); | ||
685 | snd_printd("no enough hbuffer space %d\n", space); | ||
686 | return -EIO; /* XRUN */ | ||
687 | } | ||
688 | |||
689 | /* we don't need irqsave here, because this function | ||
690 | * is called from either trigger callback or irq handler | ||
691 | */ | ||
692 | spin_lock(&chip->lock); | ||
693 | vx_pseudo_dma_write(chip, runtime, pipe, size); | ||
694 | err = vx_notify_end_of_buffer(chip, pipe); | ||
695 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
696 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
697 | spin_unlock(&chip->lock); | ||
698 | return err; | ||
699 | } | ||
700 | |||
701 | /* | ||
702 | * update the position of the given pipe. | ||
703 | * pipe->position is updated and wrapped within the buffer size. | ||
704 | * pipe->transferred is updated, too, but the size is not wrapped, | ||
705 | * so that the caller can check the total transferred size later | ||
706 | * (to call snd_pcm_period_elapsed). | ||
707 | */ | ||
708 | static int vx_update_pipe_position(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) | ||
709 | { | ||
710 | struct vx_rmh rmh; | ||
711 | int err, update; | ||
712 | u64 count; | ||
713 | |||
714 | vx_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT); | ||
715 | vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); | ||
716 | err = vx_send_msg(chip, &rmh); | ||
717 | if (err < 0) | ||
718 | return err; | ||
719 | |||
720 | count = ((u64)(rmh.Stat[0] & 0xfffff) << 24) | (u64)rmh.Stat[1]; | ||
721 | update = (int)(count - pipe->cur_count); | ||
722 | pipe->cur_count = count; | ||
723 | pipe->position += update; | ||
724 | if (pipe->position >= (int)runtime->buffer_size) | ||
725 | pipe->position %= runtime->buffer_size; | ||
726 | pipe->transferred += update; | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | /* | ||
731 | * transfer the pending playback buffer data to DSP | ||
732 | * called from interrupt handler | ||
733 | */ | ||
734 | static void vx_pcm_playback_transfer(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe, int nchunks) | ||
735 | { | ||
736 | int i, err; | ||
737 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
738 | |||
739 | if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) | ||
740 | return; | ||
741 | for (i = 0; i < nchunks; i++) { | ||
742 | if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe, | ||
743 | chip->ibl.size)) < 0) | ||
744 | return; | ||
745 | } | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * update the playback position and call snd_pcm_period_elapsed() if necessary | ||
750 | * called from interrupt handler | ||
751 | */ | ||
752 | static void vx_pcm_playback_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe) | ||
753 | { | ||
754 | int err; | ||
755 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
756 | |||
757 | if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) { | ||
758 | if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0) | ||
759 | return; | ||
760 | if (pipe->transferred >= (int)runtime->period_size) { | ||
761 | pipe->transferred %= runtime->period_size; | ||
762 | snd_pcm_period_elapsed(subs); | ||
763 | } | ||
764 | } | ||
765 | } | ||
766 | |||
767 | /* | ||
768 | * start the stream and pipe. | ||
769 | * this function is called from tasklet, which is invoked by the trigger | ||
770 | * START callback. | ||
771 | */ | ||
772 | static void vx_pcm_delayed_start(unsigned long arg) | ||
773 | { | ||
774 | snd_pcm_substream_t *subs = (snd_pcm_substream_t *)arg; | ||
775 | vx_core_t *chip = subs->pcm->private_data; | ||
776 | vx_pipe_t *pipe = subs->runtime->private_data; | ||
777 | int err; | ||
778 | |||
779 | /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ | ||
780 | |||
781 | if ((err = vx_start_stream(chip, pipe)) < 0) { | ||
782 | snd_printk(KERN_ERR "vx: cannot start stream\n"); | ||
783 | return; | ||
784 | } | ||
785 | if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) { | ||
786 | snd_printk(KERN_ERR "vx: cannot start pipe\n"); | ||
787 | return; | ||
788 | } | ||
789 | /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/ | ||
790 | } | ||
791 | |||
792 | /* | ||
793 | * vx_pcm_playback_trigger - trigger callback for playback | ||
794 | */ | ||
795 | static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd) | ||
796 | { | ||
797 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
798 | vx_pipe_t *pipe = subs->runtime->private_data; | ||
799 | int err; | ||
800 | |||
801 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
802 | return -EBUSY; | ||
803 | |||
804 | switch (cmd) { | ||
805 | case SNDRV_PCM_TRIGGER_START: | ||
806 | case SNDRV_PCM_TRIGGER_RESUME: | ||
807 | if (! pipe->is_capture) | ||
808 | vx_pcm_playback_transfer(chip, subs, pipe, 2); | ||
809 | /* FIXME: | ||
810 | * we trigger the pipe using tasklet, so that the interrupts are | ||
811 | * issued surely after the trigger is completed. | ||
812 | */ | ||
813 | tasklet_hi_schedule(&pipe->start_tq); | ||
814 | chip->pcm_running++; | ||
815 | pipe->running = 1; | ||
816 | break; | ||
817 | case SNDRV_PCM_TRIGGER_STOP: | ||
818 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
819 | vx_toggle_pipe(chip, pipe, 0); | ||
820 | vx_stop_pipe(chip, pipe); | ||
821 | vx_stop_stream(chip, pipe); | ||
822 | chip->pcm_running--; | ||
823 | pipe->running = 0; | ||
824 | break; | ||
825 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
826 | if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0) | ||
827 | return err; | ||
828 | break; | ||
829 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
830 | if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) | ||
831 | return err; | ||
832 | break; | ||
833 | default: | ||
834 | return -EINVAL; | ||
835 | } | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * vx_pcm_playback_pointer - pointer callback for playback | ||
841 | */ | ||
842 | static snd_pcm_uframes_t vx_pcm_playback_pointer(snd_pcm_substream_t *subs) | ||
843 | { | ||
844 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
845 | vx_pipe_t *pipe = runtime->private_data; | ||
846 | return pipe->position; | ||
847 | } | ||
848 | |||
849 | /* | ||
850 | * vx_pcm_hw_params - hw_params callback for playback and capture | ||
851 | */ | ||
852 | static int vx_pcm_hw_params(snd_pcm_substream_t *subs, | ||
853 | snd_pcm_hw_params_t *hw_params) | ||
854 | { | ||
855 | return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * vx_pcm_hw_free - hw_free callback for playback and capture | ||
860 | */ | ||
861 | static int vx_pcm_hw_free(snd_pcm_substream_t *subs) | ||
862 | { | ||
863 | return snd_pcm_free_vmalloc_buffer(subs); | ||
864 | } | ||
865 | |||
866 | /* | ||
867 | * vx_pcm_prepare - prepare callback for playback and capture | ||
868 | */ | ||
869 | static int vx_pcm_prepare(snd_pcm_substream_t *subs) | ||
870 | { | ||
871 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
872 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
873 | vx_pipe_t *pipe = runtime->private_data; | ||
874 | int err, data_mode; | ||
875 | // int max_size, nchunks; | ||
876 | |||
877 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
878 | return -EBUSY; | ||
879 | |||
880 | data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0; | ||
881 | if (data_mode != pipe->data_mode && ! pipe->is_capture) { | ||
882 | /* IEC958 status (raw-mode) was changed */ | ||
883 | /* we reopen the pipe */ | ||
884 | struct vx_rmh rmh; | ||
885 | snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode); | ||
886 | vx_init_rmh(&rmh, CMD_FREE_PIPE); | ||
887 | vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0); | ||
888 | if ((err = vx_send_msg(chip, &rmh)) < 0) | ||
889 | return err; | ||
890 | vx_init_rmh(&rmh, CMD_RES_PIPE); | ||
891 | vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels); | ||
892 | if (data_mode) | ||
893 | rmh.Cmd[0] |= BIT_DATA_MODE; | ||
894 | if ((err = vx_send_msg(chip, &rmh)) < 0) | ||
895 | return err; | ||
896 | pipe->data_mode = data_mode; | ||
897 | } | ||
898 | |||
899 | if (chip->pcm_running && chip->freq != runtime->rate) { | ||
900 | snd_printk(KERN_ERR "vx: cannot set different clock %d from the current %d\n", runtime->rate, chip->freq); | ||
901 | return -EINVAL; | ||
902 | } | ||
903 | vx_set_clock(chip, runtime->rate); | ||
904 | |||
905 | if ((err = vx_set_format(chip, pipe, runtime)) < 0) | ||
906 | return err; | ||
907 | |||
908 | if (vx_is_pcmcia(chip)) { | ||
909 | pipe->align = 2; /* 16bit word */ | ||
910 | } else { | ||
911 | pipe->align = 4; /* 32bit word */ | ||
912 | } | ||
913 | |||
914 | pipe->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size); | ||
915 | pipe->period_bytes = frames_to_bytes(runtime, runtime->period_size); | ||
916 | pipe->hw_ptr = 0; | ||
917 | |||
918 | /* set the timestamp */ | ||
919 | vx_update_pipe_position(chip, runtime, pipe); | ||
920 | /* clear again */ | ||
921 | pipe->transferred = 0; | ||
922 | pipe->position = 0; | ||
923 | |||
924 | pipe->prepared = 1; | ||
925 | |||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | |||
930 | /* | ||
931 | * operators for PCM playback | ||
932 | */ | ||
933 | static snd_pcm_ops_t vx_pcm_playback_ops = { | ||
934 | .open = vx_pcm_playback_open, | ||
935 | .close = vx_pcm_playback_close, | ||
936 | .ioctl = snd_pcm_lib_ioctl, | ||
937 | .hw_params = vx_pcm_hw_params, | ||
938 | .hw_free = vx_pcm_hw_free, | ||
939 | .prepare = vx_pcm_prepare, | ||
940 | .trigger = vx_pcm_trigger, | ||
941 | .pointer = vx_pcm_playback_pointer, | ||
942 | .page = snd_pcm_get_vmalloc_page, | ||
943 | }; | ||
944 | |||
945 | |||
946 | /* | ||
947 | * playback hw information | ||
948 | */ | ||
949 | |||
950 | static snd_pcm_hardware_t vx_pcm_capture_hw = { | ||
951 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
952 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | | ||
953 | SNDRV_PCM_INFO_RESUME), | ||
954 | .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, | ||
955 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
956 | .rate_min = 5000, | ||
957 | .rate_max = 48000, | ||
958 | .channels_min = 1, | ||
959 | .channels_max = 2, | ||
960 | .buffer_bytes_max = (128*1024), | ||
961 | .period_bytes_min = 126, | ||
962 | .period_bytes_max = (128*1024), | ||
963 | .periods_min = 2, | ||
964 | .periods_max = VX_MAX_PERIODS, | ||
965 | .fifo_size = 126, | ||
966 | }; | ||
967 | |||
968 | |||
969 | /* | ||
970 | * vx_pcm_capture_open - open callback for capture | ||
971 | */ | ||
972 | static int vx_pcm_capture_open(snd_pcm_substream_t *subs) | ||
973 | { | ||
974 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
975 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
976 | vx_pipe_t *pipe; | ||
977 | vx_pipe_t *pipe_out_monitoring = NULL; | ||
978 | unsigned int audio; | ||
979 | int err; | ||
980 | |||
981 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
982 | return -EBUSY; | ||
983 | |||
984 | audio = subs->pcm->device * 2; | ||
985 | snd_assert(audio < chip->audio_ins, return -EINVAL); | ||
986 | err = vx_alloc_pipe(chip, 1, audio, 2, &pipe); | ||
987 | if (err < 0) | ||
988 | return err; | ||
989 | pipe->substream = subs; | ||
990 | tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs); | ||
991 | chip->capture_pipes[audio] = pipe; | ||
992 | |||
993 | /* check if monitoring is needed */ | ||
994 | if (chip->audio_monitor_active[audio]) { | ||
995 | pipe_out_monitoring = chip->playback_pipes[audio]; | ||
996 | if (! pipe_out_monitoring) { | ||
997 | /* allocate a pipe */ | ||
998 | err = vx_alloc_pipe(chip, 0, audio, 2, &pipe_out_monitoring); | ||
999 | if (err < 0) | ||
1000 | return err; | ||
1001 | chip->playback_pipes[audio] = pipe_out_monitoring; | ||
1002 | } | ||
1003 | pipe_out_monitoring->references++; | ||
1004 | /* | ||
1005 | if an output pipe is available, it's audios still may need to be | ||
1006 | unmuted. hence we'll have to call a mixer entry point. | ||
1007 | */ | ||
1008 | vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], chip->audio_monitor_active[audio]); | ||
1009 | /* assuming stereo */ | ||
1010 | vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], chip->audio_monitor_active[audio+1]); | ||
1011 | } | ||
1012 | |||
1013 | pipe->monitoring_pipe = pipe_out_monitoring; /* default value NULL */ | ||
1014 | |||
1015 | runtime->hw = vx_pcm_capture_hw; | ||
1016 | runtime->hw.period_bytes_min = chip->ibl.size; | ||
1017 | runtime->private_data = pipe; | ||
1018 | |||
1019 | /* align to 4 bytes (otherwise will be problematic when 24bit is used) */ | ||
1020 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); | ||
1021 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | /* | ||
1027 | * vx_pcm_capture_close - close callback for capture | ||
1028 | */ | ||
1029 | static int vx_pcm_capture_close(snd_pcm_substream_t *subs) | ||
1030 | { | ||
1031 | vx_core_t *chip = snd_pcm_substream_chip(subs); | ||
1032 | vx_pipe_t *pipe; | ||
1033 | vx_pipe_t *pipe_out_monitoring; | ||
1034 | |||
1035 | if (! subs->runtime->private_data) | ||
1036 | return -EINVAL; | ||
1037 | pipe = subs->runtime->private_data; | ||
1038 | chip->capture_pipes[pipe->number] = NULL; | ||
1039 | |||
1040 | pipe_out_monitoring = pipe->monitoring_pipe; | ||
1041 | |||
1042 | /* | ||
1043 | if an output pipe is attached to this input, | ||
1044 | check if it needs to be released. | ||
1045 | */ | ||
1046 | if (pipe_out_monitoring) { | ||
1047 | if (--pipe_out_monitoring->references == 0) { | ||
1048 | vx_free_pipe(chip, pipe_out_monitoring); | ||
1049 | chip->playback_pipes[pipe->number] = NULL; | ||
1050 | pipe->monitoring_pipe = NULL; | ||
1051 | } | ||
1052 | } | ||
1053 | |||
1054 | vx_free_pipe(chip, pipe); | ||
1055 | return 0; | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | |||
1060 | #define DMA_READ_ALIGN 6 /* hardware alignment for read */ | ||
1061 | |||
1062 | /* | ||
1063 | * vx_pcm_capture_update - update the capture buffer | ||
1064 | */ | ||
1065 | static void vx_pcm_capture_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe) | ||
1066 | { | ||
1067 | int size, space, count; | ||
1068 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
1069 | |||
1070 | if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) | ||
1071 | return; | ||
1072 | |||
1073 | size = runtime->buffer_size - snd_pcm_capture_avail(runtime); | ||
1074 | if (! size) | ||
1075 | return; | ||
1076 | size = frames_to_bytes(runtime, size); | ||
1077 | space = vx_query_hbuffer_size(chip, pipe); | ||
1078 | if (space < 0) | ||
1079 | goto _error; | ||
1080 | if (size > space) | ||
1081 | size = space; | ||
1082 | size = (size / 3) * 3; /* align to 3 bytes */ | ||
1083 | if (size < DMA_READ_ALIGN) | ||
1084 | goto _error; | ||
1085 | |||
1086 | /* keep the last 6 bytes, they will be read after disconnection */ | ||
1087 | count = size - DMA_READ_ALIGN; | ||
1088 | /* read bytes until the current pointer reaches to the aligned position | ||
1089 | * for word-transfer | ||
1090 | */ | ||
1091 | while (count > 0) { | ||
1092 | if ((pipe->hw_ptr % pipe->align) == 0) | ||
1093 | break; | ||
1094 | if (vx_wait_for_rx_full(chip) < 0) | ||
1095 | goto _error; | ||
1096 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1097 | count -= 3; | ||
1098 | } | ||
1099 | if (count > 0) { | ||
1100 | /* ok, let's accelerate! */ | ||
1101 | int align = pipe->align * 3; | ||
1102 | space = (count / align) * align; | ||
1103 | vx_pseudo_dma_read(chip, runtime, pipe, space); | ||
1104 | count -= space; | ||
1105 | } | ||
1106 | /* read the rest of bytes */ | ||
1107 | while (count > 0) { | ||
1108 | if (vx_wait_for_rx_full(chip) < 0) | ||
1109 | goto _error; | ||
1110 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1111 | count -= 3; | ||
1112 | } | ||
1113 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
1114 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
1115 | /* read the last pending 6 bytes */ | ||
1116 | count = DMA_READ_ALIGN; | ||
1117 | while (count > 0) { | ||
1118 | vx_pcm_read_per_bytes(chip, runtime, pipe); | ||
1119 | count -= 3; | ||
1120 | } | ||
1121 | /* update the position */ | ||
1122 | pipe->transferred += size; | ||
1123 | if (pipe->transferred >= pipe->period_bytes) { | ||
1124 | pipe->transferred %= pipe->period_bytes; | ||
1125 | snd_pcm_period_elapsed(subs); | ||
1126 | } | ||
1127 | return; | ||
1128 | |||
1129 | _error: | ||
1130 | /* disconnect the host, SIZE_HBUF command always switches to the stream mode */ | ||
1131 | vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT); | ||
1132 | return; | ||
1133 | } | ||
1134 | |||
1135 | /* | ||
1136 | * vx_pcm_capture_pointer - pointer callback for capture | ||
1137 | */ | ||
1138 | static snd_pcm_uframes_t vx_pcm_capture_pointer(snd_pcm_substream_t *subs) | ||
1139 | { | ||
1140 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
1141 | vx_pipe_t *pipe = runtime->private_data; | ||
1142 | return bytes_to_frames(runtime, pipe->hw_ptr); | ||
1143 | } | ||
1144 | |||
1145 | /* | ||
1146 | * operators for PCM capture | ||
1147 | */ | ||
1148 | static snd_pcm_ops_t vx_pcm_capture_ops = { | ||
1149 | .open = vx_pcm_capture_open, | ||
1150 | .close = vx_pcm_capture_close, | ||
1151 | .ioctl = snd_pcm_lib_ioctl, | ||
1152 | .hw_params = vx_pcm_hw_params, | ||
1153 | .hw_free = vx_pcm_hw_free, | ||
1154 | .prepare = vx_pcm_prepare, | ||
1155 | .trigger = vx_pcm_trigger, | ||
1156 | .pointer = vx_pcm_capture_pointer, | ||
1157 | .page = snd_pcm_get_vmalloc_page, | ||
1158 | }; | ||
1159 | |||
1160 | |||
1161 | /* | ||
1162 | * interrupt handler for pcm streams | ||
1163 | */ | ||
1164 | void vx_pcm_update_intr(vx_core_t *chip, unsigned int events) | ||
1165 | { | ||
1166 | unsigned int i; | ||
1167 | vx_pipe_t *pipe; | ||
1168 | |||
1169 | #define EVENT_MASK (END_OF_BUFFER_EVENTS_PENDING|ASYNC_EVENTS_PENDING) | ||
1170 | |||
1171 | if (events & EVENT_MASK) { | ||
1172 | vx_init_rmh(&chip->irq_rmh, CMD_ASYNC); | ||
1173 | if (events & ASYNC_EVENTS_PENDING) | ||
1174 | chip->irq_rmh.Cmd[0] |= 0x00000001; /* SEL_ASYNC_EVENTS */ | ||
1175 | if (events & END_OF_BUFFER_EVENTS_PENDING) | ||
1176 | chip->irq_rmh.Cmd[0] |= 0x00000002; /* SEL_END_OF_BUF_EVENTS */ | ||
1177 | |||
1178 | if (vx_send_msg(chip, &chip->irq_rmh) < 0) { | ||
1179 | snd_printdd(KERN_ERR "msg send error!!\n"); | ||
1180 | return; | ||
1181 | } | ||
1182 | |||
1183 | i = 1; | ||
1184 | while (i < chip->irq_rmh.LgStat) { | ||
1185 | int p, buf, capture, eob; | ||
1186 | p = chip->irq_rmh.Stat[i] & MASK_FIRST_FIELD; | ||
1187 | capture = (chip->irq_rmh.Stat[i] & 0x400000) ? 1 : 0; | ||
1188 | eob = (chip->irq_rmh.Stat[i] & 0x800000) ? 1 : 0; | ||
1189 | i++; | ||
1190 | if (events & ASYNC_EVENTS_PENDING) | ||
1191 | i++; | ||
1192 | buf = 1; /* force to transfer */ | ||
1193 | if (events & END_OF_BUFFER_EVENTS_PENDING) { | ||
1194 | if (eob) | ||
1195 | buf = chip->irq_rmh.Stat[i]; | ||
1196 | i++; | ||
1197 | } | ||
1198 | if (capture) | ||
1199 | continue; | ||
1200 | snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,); | ||
1201 | pipe = chip->playback_pipes[p]; | ||
1202 | if (pipe && pipe->substream) { | ||
1203 | vx_pcm_playback_update(chip, pipe->substream, pipe); | ||
1204 | vx_pcm_playback_transfer(chip, pipe->substream, pipe, buf); | ||
1205 | } | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | /* update the capture pcm pointers as frequently as possible */ | ||
1210 | for (i = 0; i < chip->audio_ins; i++) { | ||
1211 | pipe = chip->capture_pipes[i]; | ||
1212 | if (pipe && pipe->substream) | ||
1213 | vx_pcm_capture_update(chip, pipe->substream, pipe); | ||
1214 | } | ||
1215 | } | ||
1216 | |||
1217 | |||
1218 | /* | ||
1219 | * vx_init_audio_io - check the availabe audio i/o and allocate pipe arrays | ||
1220 | */ | ||
1221 | static int vx_init_audio_io(vx_core_t *chip) | ||
1222 | { | ||
1223 | struct vx_rmh rmh; | ||
1224 | int preferred; | ||
1225 | |||
1226 | vx_init_rmh(&rmh, CMD_SUPPORTED); | ||
1227 | if (vx_send_msg(chip, &rmh) < 0) { | ||
1228 | snd_printk(KERN_ERR "vx: cannot get the supported audio data\n"); | ||
1229 | return -ENXIO; | ||
1230 | } | ||
1231 | |||
1232 | chip->audio_outs = rmh.Stat[0] & MASK_FIRST_FIELD; | ||
1233 | chip->audio_ins = (rmh.Stat[0] >> (FIELD_SIZE*2)) & MASK_FIRST_FIELD; | ||
1234 | chip->audio_info = rmh.Stat[1]; | ||
1235 | |||
1236 | /* allocate pipes */ | ||
1237 | chip->playback_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_outs, GFP_KERNEL); | ||
1238 | chip->capture_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_ins, GFP_KERNEL); | ||
1239 | if (! chip->playback_pipes || ! chip->capture_pipes) | ||
1240 | return -ENOMEM; | ||
1241 | |||
1242 | memset(chip->playback_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_outs); | ||
1243 | memset(chip->capture_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_ins); | ||
1244 | |||
1245 | preferred = chip->ibl.size; | ||
1246 | chip->ibl.size = 0; | ||
1247 | vx_set_ibl(chip, &chip->ibl); /* query the info */ | ||
1248 | if (preferred > 0) { | ||
1249 | chip->ibl.size = ((preferred + chip->ibl.granularity - 1) / chip->ibl.granularity) * chip->ibl.granularity; | ||
1250 | if (chip->ibl.size > chip->ibl.max_size) | ||
1251 | chip->ibl.size = chip->ibl.max_size; | ||
1252 | } else | ||
1253 | chip->ibl.size = chip->ibl.min_size; /* set to the minimum */ | ||
1254 | vx_set_ibl(chip, &chip->ibl); | ||
1255 | |||
1256 | return 0; | ||
1257 | } | ||
1258 | |||
1259 | |||
1260 | /* | ||
1261 | * free callback for pcm | ||
1262 | */ | ||
1263 | static void snd_vx_pcm_free(snd_pcm_t *pcm) | ||
1264 | { | ||
1265 | vx_core_t *chip = pcm->private_data; | ||
1266 | chip->pcm[pcm->device] = NULL; | ||
1267 | if (chip->playback_pipes) { | ||
1268 | kfree(chip->playback_pipes); | ||
1269 | chip->playback_pipes = NULL; | ||
1270 | } | ||
1271 | if (chip->capture_pipes) { | ||
1272 | kfree(chip->capture_pipes); | ||
1273 | chip->capture_pipes = NULL; | ||
1274 | } | ||
1275 | } | ||
1276 | |||
1277 | /* | ||
1278 | * snd_vx_pcm_new - create and initialize a pcm | ||
1279 | */ | ||
1280 | int snd_vx_pcm_new(vx_core_t *chip) | ||
1281 | { | ||
1282 | snd_pcm_t *pcm; | ||
1283 | unsigned int i; | ||
1284 | int err; | ||
1285 | |||
1286 | if ((err = vx_init_audio_io(chip)) < 0) | ||
1287 | return err; | ||
1288 | |||
1289 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
1290 | unsigned int outs, ins; | ||
1291 | outs = chip->audio_outs > i * 2 ? 1 : 0; | ||
1292 | ins = chip->audio_ins > i * 2 ? 1 : 0; | ||
1293 | if (! outs && ! ins) | ||
1294 | break; | ||
1295 | err = snd_pcm_new(chip->card, "VX PCM", i, | ||
1296 | outs, ins, &pcm); | ||
1297 | if (err < 0) | ||
1298 | return err; | ||
1299 | if (outs) | ||
1300 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops); | ||
1301 | if (ins) | ||
1302 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops); | ||
1303 | |||
1304 | pcm->private_data = chip; | ||
1305 | pcm->private_free = snd_vx_pcm_free; | ||
1306 | pcm->info_flags = 0; | ||
1307 | strcpy(pcm->name, chip->card->shortname); | ||
1308 | chip->pcm[i] = pcm; | ||
1309 | } | ||
1310 | |||
1311 | return 0; | ||
1312 | } | ||
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c new file mode 100644 index 000000000000..18114713c3b3 --- /dev/null +++ b/sound/drivers/vx/vx_uer.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * IEC958 stuff | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
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 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | |||
30 | /* | ||
31 | * vx_modify_board_clock - tell the board that its clock has been modified | ||
32 | * @sync: DSP needs to resynchronize its FIFO | ||
33 | */ | ||
34 | static int vx_modify_board_clock(vx_core_t *chip, int sync) | ||
35 | { | ||
36 | struct vx_rmh rmh; | ||
37 | |||
38 | vx_init_rmh(&rmh, CMD_MODIFY_CLOCK); | ||
39 | /* Ask the DSP to resynchronize its FIFO. */ | ||
40 | if (sync) | ||
41 | rmh.Cmd[0] |= CMD_MODIFY_CLOCK_S_BIT; | ||
42 | return vx_send_msg(chip, &rmh); | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * vx_modify_board_inputs - resync audio inputs | ||
47 | */ | ||
48 | static int vx_modify_board_inputs(vx_core_t *chip) | ||
49 | { | ||
50 | struct vx_rmh rmh; | ||
51 | |||
52 | vx_init_rmh(&rmh, CMD_RESYNC_AUDIO_INPUTS); | ||
53 | rmh.Cmd[0] |= 1 << 0; /* reference: AUDIO 0 */ | ||
54 | return vx_send_msg(chip, &rmh); | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * vx_read_one_cbit - read one bit from UER config | ||
59 | * @index: the bit index | ||
60 | * returns 0 or 1. | ||
61 | */ | ||
62 | static int vx_read_one_cbit(vx_core_t *chip, int index) | ||
63 | { | ||
64 | unsigned long flags; | ||
65 | int val; | ||
66 | spin_lock_irqsave(&chip->lock, flags); | ||
67 | if (chip->type >= VX_TYPE_VXPOCKET) { | ||
68 | vx_outb(chip, CSUER, 1); /* read */ | ||
69 | vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); | ||
70 | val = (vx_inb(chip, RUER) >> 7) & 0x01; | ||
71 | } else { | ||
72 | vx_outl(chip, CSUER, 1); /* read */ | ||
73 | vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK); | ||
74 | val = (vx_inl(chip, RUER) >> 7) & 0x01; | ||
75 | } | ||
76 | spin_unlock_irqrestore(&chip->lock, flags); | ||
77 | return val; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * vx_write_one_cbit - write one bit to UER config | ||
82 | * @index: the bit index | ||
83 | * @val: bit value, 0 or 1 | ||
84 | */ | ||
85 | static void vx_write_one_cbit(vx_core_t *chip, int index, int val) | ||
86 | { | ||
87 | unsigned long flags; | ||
88 | val = !!val; /* 0 or 1 */ | ||
89 | spin_lock_irqsave(&chip->lock, flags); | ||
90 | if (vx_is_pcmcia(chip)) { | ||
91 | vx_outb(chip, CSUER, 0); /* write */ | ||
92 | vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); | ||
93 | } else { | ||
94 | vx_outl(chip, CSUER, 0); /* write */ | ||
95 | vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK)); | ||
96 | } | ||
97 | spin_unlock_irqrestore(&chip->lock, flags); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * vx_read_uer_status - read the current UER status | ||
102 | * @mode: pointer to store the UER mode, VX_UER_MODE_XXX | ||
103 | * | ||
104 | * returns the frequency of UER, or 0 if not sync, | ||
105 | * or a negative error code. | ||
106 | */ | ||
107 | static int vx_read_uer_status(vx_core_t *chip, int *mode) | ||
108 | { | ||
109 | int val, freq; | ||
110 | |||
111 | /* Default values */ | ||
112 | freq = 0; | ||
113 | |||
114 | /* Read UER status */ | ||
115 | if (vx_is_pcmcia(chip)) | ||
116 | val = vx_inb(chip, CSUER); | ||
117 | else | ||
118 | val = vx_inl(chip, CSUER); | ||
119 | if (val < 0) | ||
120 | return val; | ||
121 | /* If clock is present, read frequency */ | ||
122 | if (val & VX_SUER_CLOCK_PRESENT_MASK) { | ||
123 | switch (val & VX_SUER_FREQ_MASK) { | ||
124 | case VX_SUER_FREQ_32KHz_MASK: | ||
125 | freq = 32000; | ||
126 | break; | ||
127 | case VX_SUER_FREQ_44KHz_MASK: | ||
128 | freq = 44100; | ||
129 | break; | ||
130 | case VX_SUER_FREQ_48KHz_MASK: | ||
131 | freq = 48000; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | if (val & VX_SUER_DATA_PRESENT_MASK) | ||
136 | /* bit 0 corresponds to consumer/professional bit */ | ||
137 | *mode = vx_read_one_cbit(chip, 0) ? | ||
138 | VX_UER_MODE_PROFESSIONAL : VX_UER_MODE_CONSUMER; | ||
139 | else | ||
140 | *mode = VX_UER_MODE_NOT_PRESENT; | ||
141 | |||
142 | return freq; | ||
143 | } | ||
144 | |||
145 | |||
146 | /* | ||
147 | * compute the sample clock value from frequency | ||
148 | * | ||
149 | * The formula is as follows: | ||
150 | * | ||
151 | * HexFreq = (dword) ((double) ((double) 28224000 / (double) Frequency)) | ||
152 | * switch ( HexFreq & 0x00000F00 ) | ||
153 | * case 0x00000100: ; | ||
154 | * case 0x00000200: | ||
155 | * case 0x00000300: HexFreq -= 0x00000201 ; | ||
156 | * case 0x00000400: | ||
157 | * case 0x00000500: | ||
158 | * case 0x00000600: | ||
159 | * case 0x00000700: HexFreq = (dword) (((double) 28224000 / (double) (Frequency*2)) - 1) | ||
160 | * default : HexFreq = (dword) ((double) 28224000 / (double) (Frequency*4)) - 0x000001FF | ||
161 | */ | ||
162 | |||
163 | static int vx_calc_clock_from_freq(vx_core_t *chip, int freq) | ||
164 | { | ||
165 | #define XX_FECH48000 0x0000004B | ||
166 | #define XX_FECH32000 0x00000171 | ||
167 | #define XX_FECH24000 0x0000024B | ||
168 | #define XX_FECH16000 0x00000371 | ||
169 | #define XX_FECH12000 0x0000044B | ||
170 | #define XX_FECH8000 0x00000571 | ||
171 | #define XX_FECH44100 0x0000007F | ||
172 | #define XX_FECH29400 0x0000016F | ||
173 | #define XX_FECH22050 0x0000027F | ||
174 | #define XX_FECH14000 0x000003EF | ||
175 | #define XX_FECH11025 0x0000047F | ||
176 | #define XX_FECH7350 0x000005BF | ||
177 | |||
178 | switch (freq) { | ||
179 | case 48000: return XX_FECH48000; | ||
180 | case 44100: return XX_FECH44100; | ||
181 | case 32000: return XX_FECH32000; | ||
182 | case 29400: return XX_FECH29400; | ||
183 | case 24000: return XX_FECH24000; | ||
184 | case 22050: return XX_FECH22050; | ||
185 | case 16000: return XX_FECH16000; | ||
186 | case 14000: return XX_FECH14000; | ||
187 | case 12000: return XX_FECH12000; | ||
188 | case 11025: return XX_FECH11025; | ||
189 | case 8000: return XX_FECH8000; | ||
190 | case 7350: return XX_FECH7350; | ||
191 | default: return freq; /* The value is already correct */ | ||
192 | } | ||
193 | } | ||
194 | |||
195 | |||
196 | /* | ||
197 | * vx_change_clock_source - change the clock source | ||
198 | * @source: the new source | ||
199 | */ | ||
200 | static void vx_change_clock_source(vx_core_t *chip, int source) | ||
201 | { | ||
202 | unsigned long flags; | ||
203 | |||
204 | /* we mute DAC to prevent clicks */ | ||
205 | vx_toggle_dac_mute(chip, 1); | ||
206 | spin_lock_irqsave(&chip->lock, flags); | ||
207 | chip->ops->set_clock_source(chip, source); | ||
208 | chip->clock_source = source; | ||
209 | spin_unlock_irqrestore(&chip->lock, flags); | ||
210 | /* unmute */ | ||
211 | vx_toggle_dac_mute(chip, 0); | ||
212 | } | ||
213 | |||
214 | |||
215 | /* | ||
216 | * set the internal clock | ||
217 | */ | ||
218 | void vx_set_internal_clock(vx_core_t *chip, unsigned int freq) | ||
219 | { | ||
220 | int clock; | ||
221 | unsigned long flags; | ||
222 | /* Get real clock value */ | ||
223 | clock = vx_calc_clock_from_freq(chip, freq); | ||
224 | snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq); | ||
225 | spin_lock_irqsave(&chip->lock, flags); | ||
226 | if (vx_is_pcmcia(chip)) { | ||
227 | vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f); | ||
228 | vx_outb(chip, LOFREQ, clock & 0xff); | ||
229 | } else { | ||
230 | vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f); | ||
231 | vx_outl(chip, LOFREQ, clock & 0xff); | ||
232 | } | ||
233 | spin_unlock_irqrestore(&chip->lock, flags); | ||
234 | } | ||
235 | |||
236 | |||
237 | /* | ||
238 | * set the iec958 status bits | ||
239 | * @bits: 32-bit status bits | ||
240 | */ | ||
241 | void vx_set_iec958_status(vx_core_t *chip, unsigned int bits) | ||
242 | { | ||
243 | int i; | ||
244 | |||
245 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
246 | return; | ||
247 | |||
248 | for (i = 0; i < 32; i++) | ||
249 | vx_write_one_cbit(chip, i, bits & (1 << i)); | ||
250 | } | ||
251 | |||
252 | |||
253 | /* | ||
254 | * vx_set_clock - change the clock and audio source if necessary | ||
255 | */ | ||
256 | int vx_set_clock(vx_core_t *chip, unsigned int freq) | ||
257 | { | ||
258 | int src_changed = 0; | ||
259 | |||
260 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
261 | return 0; | ||
262 | |||
263 | /* change the audio source if possible */ | ||
264 | vx_sync_audio_source(chip); | ||
265 | |||
266 | if (chip->clock_mode == VX_CLOCK_MODE_EXTERNAL || | ||
267 | (chip->clock_mode == VX_CLOCK_MODE_AUTO && | ||
268 | chip->audio_source == VX_AUDIO_SRC_DIGITAL)) { | ||
269 | if (chip->clock_source != UER_SYNC) { | ||
270 | vx_change_clock_source(chip, UER_SYNC); | ||
271 | mdelay(6); | ||
272 | src_changed = 1; | ||
273 | } | ||
274 | } else if (chip->clock_mode == VX_CLOCK_MODE_INTERNAL || | ||
275 | (chip->clock_mode == VX_CLOCK_MODE_AUTO && | ||
276 | chip->audio_source != VX_AUDIO_SRC_DIGITAL)) { | ||
277 | if (chip->clock_source != INTERNAL_QUARTZ) { | ||
278 | vx_change_clock_source(chip, INTERNAL_QUARTZ); | ||
279 | src_changed = 1; | ||
280 | } | ||
281 | if (chip->freq == freq) | ||
282 | return 0; | ||
283 | vx_set_internal_clock(chip, freq); | ||
284 | if (src_changed) | ||
285 | vx_modify_board_inputs(chip); | ||
286 | } | ||
287 | if (chip->freq == freq) | ||
288 | return 0; | ||
289 | chip->freq = freq; | ||
290 | vx_modify_board_clock(chip, 1); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | |||
295 | /* | ||
296 | * vx_change_frequency - called from interrupt handler | ||
297 | */ | ||
298 | int vx_change_frequency(vx_core_t *chip) | ||
299 | { | ||
300 | int freq; | ||
301 | |||
302 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
303 | return 0; | ||
304 | |||
305 | if (chip->clock_source == INTERNAL_QUARTZ) | ||
306 | return 0; | ||
307 | /* | ||
308 | * Read the real UER board frequency | ||
309 | */ | ||
310 | freq = vx_read_uer_status(chip, &chip->uer_detected); | ||
311 | if (freq < 0) | ||
312 | return freq; | ||
313 | /* | ||
314 | * The frequency computed by the DSP is good and | ||
315 | * is different from the previous computed. | ||
316 | */ | ||
317 | if (freq == 48000 || freq == 44100 || freq == 32000) | ||
318 | chip->freq_detected = freq; | ||
319 | |||
320 | return 0; | ||
321 | } | ||