Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
1da177e4 LT |
337 | snd_sb_ack_8bit(chip); |
338 | switch (chip->mode) { | |
339 | case SB_MODE_PLAYBACK_8: /* ok.. playback is active */ | |
340 | substream = chip->playback_substream; | |
341 | runtime = substream->runtime; | |
342 | if (chip->playback_format == SB_DSP_OUTPUT) | |
343 | snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); | |
344 | snd_pcm_period_elapsed(substream); | |
345 | break; | |
346 | case SB_MODE_CAPTURE_8: | |
347 | substream = chip->capture_substream; | |
348 | runtime = substream->runtime; | |
349 | if (chip->capture_format == SB_DSP_INPUT) | |
350 | snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); | |
351 | snd_pcm_period_elapsed(substream); | |
352 | break; | |
353 | } | |
354 | return IRQ_HANDLED; | |
355 | } | |
356 | ||
357 | static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream) | |
358 | { | |
359 | sb_t *chip = snd_pcm_substream_chip(substream); | |
360 | size_t ptr; | |
361 | ||
362 | if (chip->mode != SB_MODE_PLAYBACK_8) | |
363 | return 0; | |
364 | ptr = snd_dma_pointer(chip->dma8, chip->p_dma_size); | |
365 | return bytes_to_frames(substream->runtime, ptr); | |
366 | } | |
367 | ||
368 | static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream) | |
369 | { | |
370 | sb_t *chip = snd_pcm_substream_chip(substream); | |
371 | size_t ptr; | |
372 | ||
373 | if (chip->mode != SB_MODE_CAPTURE_8) | |
374 | return 0; | |
375 | ptr = snd_dma_pointer(chip->dma8, chip->c_dma_size); | |
376 | return bytes_to_frames(substream->runtime, ptr); | |
377 | } | |
378 | ||
379 | /* | |
380 | ||
381 | */ | |
382 | ||
383 | static snd_pcm_hardware_t snd_sb8_playback = | |
384 | { | |
385 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | |
386 | SNDRV_PCM_INFO_MMAP_VALID), | |
387 | .formats = SNDRV_PCM_FMTBIT_U8, | |
388 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | | |
389 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), | |
390 | .rate_min = 4000, | |
391 | .rate_max = 23000, | |
392 | .channels_min = 1, | |
393 | .channels_max = 1, | |
394 | .buffer_bytes_max = 65536, | |
395 | .period_bytes_min = 64, | |
396 | .period_bytes_max = 65536, | |
397 | .periods_min = 1, | |
398 | .periods_max = 1024, | |
399 | .fifo_size = 0, | |
400 | }; | |
401 | ||
402 | static snd_pcm_hardware_t snd_sb8_capture = | |
403 | { | |
404 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | |
405 | SNDRV_PCM_INFO_MMAP_VALID), | |
406 | .formats = SNDRV_PCM_FMTBIT_U8, | |
407 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | | |
408 | SNDRV_PCM_RATE_11025), | |
409 | .rate_min = 4000, | |
410 | .rate_max = 13000, | |
411 | .channels_min = 1, | |
412 | .channels_max = 1, | |
413 | .buffer_bytes_max = 65536, | |
414 | .period_bytes_min = 64, | |
415 | .period_bytes_max = 65536, | |
416 | .periods_min = 1, | |
417 | .periods_max = 1024, | |
418 | .fifo_size = 0, | |
419 | }; | |
420 | ||
421 | /* | |
422 | * | |
423 | */ | |
424 | ||
425 | static int snd_sb8_open(snd_pcm_substream_t *substream) | |
426 | { | |
427 | sb_t *chip = snd_pcm_substream_chip(substream); | |
428 | snd_pcm_runtime_t *runtime = substream->runtime; | |
429 | unsigned long flags; | |
430 | ||
431 | spin_lock_irqsave(&chip->open_lock, flags); | |
432 | if (chip->open) { | |
433 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
434 | return -EAGAIN; | |
435 | } | |
436 | chip->open |= SB_OPEN_PCM; | |
437 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
438 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
439 | chip->playback_substream = substream; | |
440 | runtime->hw = snd_sb8_playback; | |
441 | } else { | |
442 | chip->capture_substream = substream; | |
443 | runtime->hw = snd_sb8_capture; | |
444 | } | |
445 | switch (chip->hardware) { | |
446 | case SB_HW_PRO: | |
447 | runtime->hw.rate_max = 44100; | |
448 | runtime->hw.channels_max = 2; | |
449 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | |
450 | snd_sb8_hw_constraint_rate_channels, NULL, | |
451 | SNDRV_PCM_HW_PARAM_CHANNELS, | |
452 | SNDRV_PCM_HW_PARAM_RATE, -1); | |
453 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | |
454 | snd_sb8_hw_constraint_channels_rate, NULL, | |
455 | SNDRV_PCM_HW_PARAM_RATE, -1); | |
456 | break; | |
457 | case SB_HW_201: | |
458 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
459 | runtime->hw.rate_max = 44100; | |
460 | } else { | |
461 | runtime->hw.rate_max = 15000; | |
462 | } | |
463 | default: | |
464 | break; | |
465 | } | |
466 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | |
467 | &hw_constraints_clock); | |
468 | return 0; | |
469 | } | |
470 | ||
471 | static int snd_sb8_close(snd_pcm_substream_t *substream) | |
472 | { | |
473 | unsigned long flags; | |
474 | sb_t *chip = snd_pcm_substream_chip(substream); | |
475 | ||
476 | chip->playback_substream = NULL; | |
477 | chip->capture_substream = NULL; | |
478 | spin_lock_irqsave(&chip->open_lock, flags); | |
479 | chip->open &= ~SB_OPEN_PCM; | |
480 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
481 | return 0; | |
482 | } | |
483 | ||
484 | /* | |
485 | * Initialization part | |
486 | */ | |
487 | ||
488 | static snd_pcm_ops_t snd_sb8_playback_ops = { | |
489 | .open = snd_sb8_open, | |
490 | .close = snd_sb8_close, | |
491 | .ioctl = snd_pcm_lib_ioctl, | |
492 | .hw_params = snd_sb8_hw_params, | |
493 | .hw_free = snd_sb8_hw_free, | |
494 | .prepare = snd_sb8_playback_prepare, | |
495 | .trigger = snd_sb8_playback_trigger, | |
496 | .pointer = snd_sb8_playback_pointer, | |
497 | }; | |
498 | ||
499 | static snd_pcm_ops_t snd_sb8_capture_ops = { | |
500 | .open = snd_sb8_open, | |
501 | .close = snd_sb8_close, | |
502 | .ioctl = snd_pcm_lib_ioctl, | |
503 | .hw_params = snd_sb8_hw_params, | |
504 | .hw_free = snd_sb8_hw_free, | |
505 | .prepare = snd_sb8_capture_prepare, | |
506 | .trigger = snd_sb8_capture_trigger, | |
507 | .pointer = snd_sb8_capture_pointer, | |
508 | }; | |
509 | ||
1da177e4 LT |
510 | int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm) |
511 | { | |
512 | snd_card_t *card = chip->card; | |
513 | snd_pcm_t *pcm; | |
514 | int err; | |
515 | ||
516 | if (rpcm) | |
517 | *rpcm = NULL; | |
518 | if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) | |
519 | return err; | |
520 | sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); | |
521 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | |
522 | pcm->private_data = chip; | |
1da177e4 LT |
523 | |
524 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); | |
525 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); | |
526 | ||
527 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | |
528 | snd_dma_isa_data(), | |
529 | 64*1024, 64*1024); | |
530 | ||
531 | if (rpcm) | |
532 | *rpcm = pcm; | |
533 | return 0; | |
534 | } | |
535 | ||
536 | EXPORT_SYMBOL(snd_sb8dsp_pcm); | |
537 | EXPORT_SYMBOL(snd_sb8dsp_interrupt); | |
538 | /* sb8_midi.c */ | |
539 | EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); | |
540 | EXPORT_SYMBOL(snd_sb8dsp_midi); | |
541 | ||
542 | /* | |
543 | * INIT part | |
544 | */ | |
545 | ||
546 | static int __init alsa_sb8_init(void) | |
547 | { | |
548 | return 0; | |
549 | } | |
550 | ||
551 | static void __exit alsa_sb8_exit(void) | |
552 | { | |
553 | } | |
554 | ||
555 | module_init(alsa_sb8_init) | |
556 | module_exit(alsa_sb8_exit) |