diff options
Diffstat (limited to 'sound/isa/sb/sb8_main.c')
-rw-r--r-- | sound/isa/sb/sb8_main.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c new file mode 100644 index 000000000000..87c9b1ba06cf --- /dev/null +++ b/sound/isa/sb/sb8_main.c | |||
@@ -0,0 +1,565 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Uros Bizjak <uros@kss-loka.si> | ||
4 | * | ||
5 | * Routines for control of 8-bit SoundBlaster cards and clones | ||
6 | * Please note: I don't have access to old SB8 soundcards. | ||
7 | * | ||
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 | * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> | ||
26 | * DSP can't respond to commands whilst in "high speed" mode. Caused | ||
27 | * glitching during playback. Fixed. | ||
28 | * | ||
29 | * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si> | ||
30 | * Cleaned up and rewrote lowlevel routines. | ||
31 | */ | ||
32 | |||
33 | #include <sound/driver.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <asm/dma.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/time.h> | ||
38 | #include <sound/core.h> | ||
39 | #include <sound/sb.h> | ||
40 | |||
41 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Uros Bizjak <uros@kss-loka.si>"); | ||
42 | MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones"); | ||
43 | MODULE_LICENSE("GPL"); | ||
44 | |||
45 | #define SB8_CLOCK 1000000 | ||
46 | #define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) | ||
47 | #define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) | ||
48 | |||
49 | static ratnum_t clock = { | ||
50 | .num = SB8_CLOCK, | ||
51 | .den_min = 1, | ||
52 | .den_max = 256, | ||
53 | .den_step = 1, | ||
54 | }; | ||
55 | |||
56 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = { | ||
57 | .nrats = 1, | ||
58 | .rats = &clock, | ||
59 | }; | ||
60 | |||
61 | static ratnum_t stereo_clocks[] = { | ||
62 | { | ||
63 | .num = SB8_CLOCK, | ||
64 | .den_min = SB8_DEN(22050), | ||
65 | .den_max = SB8_DEN(22050), | ||
66 | .den_step = 1, | ||
67 | }, | ||
68 | { | ||
69 | .num = SB8_CLOCK, | ||
70 | .den_min = SB8_DEN(11025), | ||
71 | .den_max = SB8_DEN(11025), | ||
72 | .den_step = 1, | ||
73 | } | ||
74 | }; | ||
75 | |||
76 | static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params, | ||
77 | snd_pcm_hw_rule_t *rule) | ||
78 | { | ||
79 | snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
80 | if (c->min > 1) { | ||
81 | unsigned int num = 0, den = 0; | ||
82 | int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), | ||
83 | 2, stereo_clocks, &num, &den); | ||
84 | if (err >= 0 && den) { | ||
85 | params->rate_num = num; | ||
86 | params->rate_den = den; | ||
87 | } | ||
88 | return err; | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params, | ||
94 | snd_pcm_hw_rule_t *rule) | ||
95 | { | ||
96 | snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
97 | if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { | ||
98 | snd_interval_t t = { .min = 1, .max = 1 }; | ||
99 | return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream) | ||
105 | { | ||
106 | unsigned long flags; | ||
107 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
108 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
109 | unsigned int mixreg, rate, size, count; | ||
110 | |||
111 | rate = runtime->rate; | ||
112 | switch (chip->hardware) { | ||
113 | case SB_HW_PRO: | ||
114 | if (runtime->channels > 1) { | ||
115 | snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); | ||
116 | chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; | ||
117 | break; | ||
118 | } | ||
119 | /* fallthru */ | ||
120 | case SB_HW_201: | ||
121 | if (rate > 23000) { | ||
122 | chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; | ||
123 | break; | ||
124 | } | ||
125 | /* fallthru */ | ||
126 | case SB_HW_20: | ||
127 | chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; | ||
128 | break; | ||
129 | case SB_HW_10: | ||
130 | chip->playback_format = SB_DSP_OUTPUT; | ||
131 | break; | ||
132 | default: | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
136 | count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); | ||
137 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
138 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); | ||
139 | if (runtime->channels > 1) { | ||
140 | /* set playback stereo mode */ | ||
141 | spin_lock(&chip->mixer_lock); | ||
142 | mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); | ||
143 | snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); | ||
144 | spin_unlock(&chip->mixer_lock); | ||
145 | |||
146 | /* Soundblaster hardware programming reference guide, 3-23 */ | ||
147 | snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); | ||
148 | runtime->dma_area[0] = 0x80; | ||
149 | snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE); | ||
150 | /* force interrupt */ | ||
151 | chip->mode = SB_MODE_HALT; | ||
152 | snd_sbdsp_command(chip, SB_DSP_OUTPUT); | ||
153 | snd_sbdsp_command(chip, 0); | ||
154 | snd_sbdsp_command(chip, 0); | ||
155 | } | ||
156 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); | ||
157 | if (runtime->channels > 1) { | ||
158 | snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); | ||
159 | spin_lock(&chip->mixer_lock); | ||
160 | /* save output filter status and turn it off */ | ||
161 | mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); | ||
162 | snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); | ||
163 | spin_unlock(&chip->mixer_lock); | ||
164 | /* just use force_mode16 for temporary storate... */ | ||
165 | chip->force_mode16 = mixreg; | ||
166 | } else { | ||
167 | snd_sbdsp_command(chip, 256 - runtime->rate_den); | ||
168 | } | ||
169 | if (chip->playback_format != SB_DSP_OUTPUT) { | ||
170 | count--; | ||
171 | snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); | ||
172 | snd_sbdsp_command(chip, count & 0xff); | ||
173 | snd_sbdsp_command(chip, count >> 8); | ||
174 | } | ||
175 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
176 | snd_dma_program(chip->dma8, runtime->dma_addr, | ||
177 | size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream, | ||
182 | int cmd) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
186 | unsigned int count; | ||
187 | |||
188 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
189 | switch (cmd) { | ||
190 | case SNDRV_PCM_TRIGGER_START: | ||
191 | snd_sbdsp_command(chip, chip->playback_format); | ||
192 | if (chip->playback_format == SB_DSP_OUTPUT) { | ||
193 | count = chip->p_period_size - 1; | ||
194 | snd_sbdsp_command(chip, count & 0xff); | ||
195 | snd_sbdsp_command(chip, count >> 8); | ||
196 | } | ||
197 | break; | ||
198 | case SNDRV_PCM_TRIGGER_STOP: | ||
199 | if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) { | ||
200 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
201 | snd_sbdsp_reset(chip); | ||
202 | if (runtime->channels > 1) { | ||
203 | spin_lock(&chip->mixer_lock); | ||
204 | /* restore output filter and set hardware to mono mode */ | ||
205 | snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02); | ||
206 | spin_unlock(&chip->mixer_lock); | ||
207 | } | ||
208 | } else { | ||
209 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
210 | } | ||
211 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); | ||
212 | } | ||
213 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
214 | chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT; | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int snd_sb8_hw_params(snd_pcm_substream_t * substream, | ||
219 | snd_pcm_hw_params_t * hw_params) | ||
220 | { | ||
221 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
222 | } | ||
223 | |||
224 | static int snd_sb8_hw_free(snd_pcm_substream_t * substream) | ||
225 | { | ||
226 | snd_pcm_lib_free_pages(substream); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream) | ||
231 | { | ||
232 | unsigned long flags; | ||
233 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
234 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
235 | unsigned int mixreg, rate, size, count; | ||
236 | |||
237 | rate = runtime->rate; | ||
238 | switch (chip->hardware) { | ||
239 | case SB_HW_PRO: | ||
240 | if (runtime->channels > 1) { | ||
241 | snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); | ||
242 | chip->capture_format = SB_DSP_HI_INPUT_AUTO; | ||
243 | break; | ||
244 | } | ||
245 | chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO; | ||
246 | break; | ||
247 | case SB_HW_201: | ||
248 | if (rate > 13000) { | ||
249 | chip->capture_format = SB_DSP_HI_INPUT_AUTO; | ||
250 | break; | ||
251 | } | ||
252 | /* fallthru */ | ||
253 | case SB_HW_20: | ||
254 | chip->capture_format = SB_DSP_LO_INPUT_AUTO; | ||
255 | break; | ||
256 | case SB_HW_10: | ||
257 | chip->capture_format = SB_DSP_INPUT; | ||
258 | break; | ||
259 | default: | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
263 | count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); | ||
264 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
265 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); | ||
266 | if (runtime->channels > 1) | ||
267 | snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); | ||
268 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); | ||
269 | if (runtime->channels > 1) { | ||
270 | snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); | ||
271 | spin_lock(&chip->mixer_lock); | ||
272 | /* save input filter status and turn it off */ | ||
273 | mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT); | ||
274 | snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20); | ||
275 | spin_unlock(&chip->mixer_lock); | ||
276 | /* just use force_mode16 for temporary storate... */ | ||
277 | chip->force_mode16 = mixreg; | ||
278 | } else { | ||
279 | snd_sbdsp_command(chip, 256 - runtime->rate_den); | ||
280 | } | ||
281 | if (chip->capture_format != SB_DSP_OUTPUT) { | ||
282 | count--; | ||
283 | snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); | ||
284 | snd_sbdsp_command(chip, count & 0xff); | ||
285 | snd_sbdsp_command(chip, count >> 8); | ||
286 | } | ||
287 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
288 | snd_dma_program(chip->dma8, runtime->dma_addr, | ||
289 | size, DMA_MODE_READ | DMA_AUTOINIT); | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream, | ||
294 | int cmd) | ||
295 | { | ||
296 | unsigned long flags; | ||
297 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
298 | unsigned int count; | ||
299 | |||
300 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
301 | switch (cmd) { | ||
302 | case SNDRV_PCM_TRIGGER_START: | ||
303 | snd_sbdsp_command(chip, chip->capture_format); | ||
304 | if (chip->capture_format == SB_DSP_INPUT) { | ||
305 | count = chip->c_period_size - 1; | ||
306 | snd_sbdsp_command(chip, count & 0xff); | ||
307 | snd_sbdsp_command(chip, count >> 8); | ||
308 | } | ||
309 | break; | ||
310 | case SNDRV_PCM_TRIGGER_STOP: | ||
311 | if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) { | ||
312 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
313 | snd_sbdsp_reset(chip); | ||
314 | if (runtime->channels > 1) { | ||
315 | /* restore input filter status */ | ||
316 | spin_lock(&chip->mixer_lock); | ||
317 | snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16); | ||
318 | spin_unlock(&chip->mixer_lock); | ||
319 | /* set hardware to mono mode */ | ||
320 | snd_sbdsp_command(chip, SB_DSP_MONO_8BIT); | ||
321 | } | ||
322 | } else { | ||
323 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
324 | } | ||
325 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); | ||
326 | } | ||
327 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
328 | chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | irqreturn_t snd_sb8dsp_interrupt(sb_t *chip) | ||
333 | { | ||
334 | snd_pcm_substream_t *substream; | ||
335 | snd_pcm_runtime_t *runtime; | ||
336 | |||
337 | #if 0 | ||
338 | snd_printk("sb8: interrupt\n"); | ||
339 | #endif | ||
340 | snd_sb_ack_8bit(chip); | ||
341 | switch (chip->mode) { | ||
342 | case SB_MODE_PLAYBACK_8: /* ok.. playback is active */ | ||
343 | substream = chip->playback_substream; | ||
344 | runtime = substream->runtime; | ||
345 | if (chip->playback_format == SB_DSP_OUTPUT) | ||
346 | snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); | ||
347 | snd_pcm_period_elapsed(substream); | ||
348 | break; | ||
349 | case SB_MODE_CAPTURE_8: | ||
350 | substream = chip->capture_substream; | ||
351 | runtime = substream->runtime; | ||
352 | if (chip->capture_format == SB_DSP_INPUT) | ||
353 | snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); | ||
354 | snd_pcm_period_elapsed(substream); | ||
355 | break; | ||
356 | } | ||
357 | return IRQ_HANDLED; | ||
358 | } | ||
359 | |||
360 | static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream) | ||
361 | { | ||
362 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
363 | size_t ptr; | ||
364 | |||
365 | if (chip->mode != SB_MODE_PLAYBACK_8) | ||
366 | return 0; | ||
367 | ptr = snd_dma_pointer(chip->dma8, chip->p_dma_size); | ||
368 | return bytes_to_frames(substream->runtime, ptr); | ||
369 | } | ||
370 | |||
371 | static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream) | ||
372 | { | ||
373 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
374 | size_t ptr; | ||
375 | |||
376 | if (chip->mode != SB_MODE_CAPTURE_8) | ||
377 | return 0; | ||
378 | ptr = snd_dma_pointer(chip->dma8, chip->c_dma_size); | ||
379 | return bytes_to_frames(substream->runtime, ptr); | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | |||
384 | */ | ||
385 | |||
386 | static snd_pcm_hardware_t snd_sb8_playback = | ||
387 | { | ||
388 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
389 | SNDRV_PCM_INFO_MMAP_VALID), | ||
390 | .formats = SNDRV_PCM_FMTBIT_U8, | ||
391 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | | ||
392 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), | ||
393 | .rate_min = 4000, | ||
394 | .rate_max = 23000, | ||
395 | .channels_min = 1, | ||
396 | .channels_max = 1, | ||
397 | .buffer_bytes_max = 65536, | ||
398 | .period_bytes_min = 64, | ||
399 | .period_bytes_max = 65536, | ||
400 | .periods_min = 1, | ||
401 | .periods_max = 1024, | ||
402 | .fifo_size = 0, | ||
403 | }; | ||
404 | |||
405 | static snd_pcm_hardware_t snd_sb8_capture = | ||
406 | { | ||
407 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
408 | SNDRV_PCM_INFO_MMAP_VALID), | ||
409 | .formats = SNDRV_PCM_FMTBIT_U8, | ||
410 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | | ||
411 | SNDRV_PCM_RATE_11025), | ||
412 | .rate_min = 4000, | ||
413 | .rate_max = 13000, | ||
414 | .channels_min = 1, | ||
415 | .channels_max = 1, | ||
416 | .buffer_bytes_max = 65536, | ||
417 | .period_bytes_min = 64, | ||
418 | .period_bytes_max = 65536, | ||
419 | .periods_min = 1, | ||
420 | .periods_max = 1024, | ||
421 | .fifo_size = 0, | ||
422 | }; | ||
423 | |||
424 | /* | ||
425 | * | ||
426 | */ | ||
427 | |||
428 | static int snd_sb8_open(snd_pcm_substream_t *substream) | ||
429 | { | ||
430 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
431 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
432 | unsigned long flags; | ||
433 | |||
434 | spin_lock_irqsave(&chip->open_lock, flags); | ||
435 | if (chip->open) { | ||
436 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
437 | return -EAGAIN; | ||
438 | } | ||
439 | chip->open |= SB_OPEN_PCM; | ||
440 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
441 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
442 | chip->playback_substream = substream; | ||
443 | runtime->hw = snd_sb8_playback; | ||
444 | } else { | ||
445 | chip->capture_substream = substream; | ||
446 | runtime->hw = snd_sb8_capture; | ||
447 | } | ||
448 | switch (chip->hardware) { | ||
449 | case SB_HW_PRO: | ||
450 | runtime->hw.rate_max = 44100; | ||
451 | runtime->hw.channels_max = 2; | ||
452 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
453 | snd_sb8_hw_constraint_rate_channels, NULL, | ||
454 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
455 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
456 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
457 | snd_sb8_hw_constraint_channels_rate, NULL, | ||
458 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
459 | break; | ||
460 | case SB_HW_201: | ||
461 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
462 | runtime->hw.rate_max = 44100; | ||
463 | } else { | ||
464 | runtime->hw.rate_max = 15000; | ||
465 | } | ||
466 | default: | ||
467 | break; | ||
468 | } | ||
469 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
470 | &hw_constraints_clock); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int snd_sb8_close(snd_pcm_substream_t *substream) | ||
475 | { | ||
476 | unsigned long flags; | ||
477 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
478 | |||
479 | chip->playback_substream = NULL; | ||
480 | chip->capture_substream = NULL; | ||
481 | spin_lock_irqsave(&chip->open_lock, flags); | ||
482 | chip->open &= ~SB_OPEN_PCM; | ||
483 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | /* | ||
488 | * Initialization part | ||
489 | */ | ||
490 | |||
491 | static snd_pcm_ops_t snd_sb8_playback_ops = { | ||
492 | .open = snd_sb8_open, | ||
493 | .close = snd_sb8_close, | ||
494 | .ioctl = snd_pcm_lib_ioctl, | ||
495 | .hw_params = snd_sb8_hw_params, | ||
496 | .hw_free = snd_sb8_hw_free, | ||
497 | .prepare = snd_sb8_playback_prepare, | ||
498 | .trigger = snd_sb8_playback_trigger, | ||
499 | .pointer = snd_sb8_playback_pointer, | ||
500 | }; | ||
501 | |||
502 | static snd_pcm_ops_t snd_sb8_capture_ops = { | ||
503 | .open = snd_sb8_open, | ||
504 | .close = snd_sb8_close, | ||
505 | .ioctl = snd_pcm_lib_ioctl, | ||
506 | .hw_params = snd_sb8_hw_params, | ||
507 | .hw_free = snd_sb8_hw_free, | ||
508 | .prepare = snd_sb8_capture_prepare, | ||
509 | .trigger = snd_sb8_capture_trigger, | ||
510 | .pointer = snd_sb8_capture_pointer, | ||
511 | }; | ||
512 | |||
513 | static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm) | ||
514 | { | ||
515 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
516 | } | ||
517 | |||
518 | int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm) | ||
519 | { | ||
520 | snd_card_t *card = chip->card; | ||
521 | snd_pcm_t *pcm; | ||
522 | int err; | ||
523 | |||
524 | if (rpcm) | ||
525 | *rpcm = NULL; | ||
526 | if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) | ||
527 | return err; | ||
528 | sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); | ||
529 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
530 | pcm->private_data = chip; | ||
531 | pcm->private_free = snd_sb8dsp_pcm_free; | ||
532 | |||
533 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); | ||
534 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); | ||
535 | |||
536 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
537 | snd_dma_isa_data(), | ||
538 | 64*1024, 64*1024); | ||
539 | |||
540 | if (rpcm) | ||
541 | *rpcm = pcm; | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | EXPORT_SYMBOL(snd_sb8dsp_pcm); | ||
546 | EXPORT_SYMBOL(snd_sb8dsp_interrupt); | ||
547 | /* sb8_midi.c */ | ||
548 | EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); | ||
549 | EXPORT_SYMBOL(snd_sb8dsp_midi); | ||
550 | |||
551 | /* | ||
552 | * INIT part | ||
553 | */ | ||
554 | |||
555 | static int __init alsa_sb8_init(void) | ||
556 | { | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | static void __exit alsa_sb8_exit(void) | ||
561 | { | ||
562 | } | ||
563 | |||
564 | module_init(alsa_sb8_init) | ||
565 | module_exit(alsa_sb8_exit) | ||