]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
c1017a4c | 2 | * Copyright (c) by Jaroslav Kysela <[email protected]> |
1da177e4 LT |
3 | * Routines for control of 16-bit SoundBlaster cards and clones |
4 | * Note: This is very ugly hardware which uses one 8-bit DMA channel and | |
5 | * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't | |
6 | * transfer 16-bit samples and 16-bit DMA channels can't transfer | |
7 | * 8-bit samples. This make full duplex more complicated than | |
8 | * can be... People, don't buy these soundcards for full 16-bit | |
9 | * duplex!!! | |
10 | * Note: 16-bit wide is assigned to first direction which made request. | |
11 | * With full duplex - playback is preferred with abstract layer. | |
12 | * | |
13 | * Note: Some chip revisions have hardware bug. Changing capture | |
14 | * channel from full-duplex 8bit DMA to 16bit DMA will block | |
15 | * 16bit DMA transfers from DSP chip (capture) until 8bit transfer | |
16 | * to DSP chip (playback) starts. This bug can be avoided with | |
17 | * "16bit DMA Allocation" setting set to Playback or Capture. | |
18 | * | |
19 | * | |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
33 | * | |
34 | */ | |
35 | ||
6cbbfe1c | 36 | #include <linux/io.h> |
1da177e4 LT |
37 | #include <asm/dma.h> |
38 | #include <linux/init.h> | |
39 | #include <linux/time.h> | |
da155d5b | 40 | #include <linux/module.h> |
1da177e4 LT |
41 | #include <sound/core.h> |
42 | #include <sound/sb.h> | |
43 | #include <sound/sb16_csp.h> | |
44 | #include <sound/mpu401.h> | |
45 | #include <sound/control.h> | |
46 | #include <sound/info.h> | |
47 | ||
c1017a4c | 48 | MODULE_AUTHOR("Jaroslav Kysela <[email protected]>"); |
1da177e4 LT |
49 | MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones"); |
50 | MODULE_LICENSE("GPL"); | |
51 | ||
52 | #ifdef CONFIG_SND_SB16_CSP | |
029d64b0 | 53 | static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_runtime *runtime) |
1da177e4 LT |
54 | { |
55 | if (chip->hardware == SB_HW_16CSP) { | |
029d64b0 | 56 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
57 | |
58 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | |
59 | /* manually loaded codec */ | |
60 | if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && | |
61 | ((1U << runtime->format) == csp->acc_format)) { | |
62 | /* Supported runtime PCM format for playback */ | |
63 | if (csp->ops.csp_use(csp) == 0) { | |
64 | /* If CSP was successfully acquired */ | |
65 | goto __start_CSP; | |
66 | } | |
67 | } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { | |
68 | /* QSound decoder is loaded and enabled */ | |
69 | if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | | |
70 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { | |
71 | /* Only for simple PCM formats */ | |
72 | if (csp->ops.csp_use(csp) == 0) { | |
73 | /* If CSP was successfully acquired */ | |
74 | goto __start_CSP; | |
75 | } | |
76 | } | |
77 | } | |
78 | } else if (csp->ops.csp_use(csp) == 0) { | |
79 | /* Acquire CSP and try to autoload hardware codec */ | |
80 | if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { | |
81 | /* Unsupported format, release CSP */ | |
82 | csp->ops.csp_unuse(csp); | |
83 | } else { | |
84 | __start_CSP: | |
85 | /* Try to start CSP */ | |
86 | if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? | |
87 | SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, | |
88 | (runtime->channels > 1) ? | |
89 | SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { | |
90 | /* Failed, release CSP */ | |
91 | csp->ops.csp_unuse(csp); | |
92 | } else { | |
93 | /* Success, CSP acquired and running */ | |
94 | chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; | |
95 | } | |
96 | } | |
97 | } | |
98 | } | |
99 | } | |
100 | ||
029d64b0 | 101 | static void snd_sb16_csp_capture_prepare(struct snd_sb *chip, struct snd_pcm_runtime *runtime) |
1da177e4 LT |
102 | { |
103 | if (chip->hardware == SB_HW_16CSP) { | |
029d64b0 | 104 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
105 | |
106 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | |
107 | /* manually loaded codec */ | |
108 | if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && | |
109 | ((1U << runtime->format) == csp->acc_format)) { | |
110 | /* Supported runtime PCM format for capture */ | |
111 | if (csp->ops.csp_use(csp) == 0) { | |
112 | /* If CSP was successfully acquired */ | |
113 | goto __start_CSP; | |
114 | } | |
115 | } | |
116 | } else if (csp->ops.csp_use(csp) == 0) { | |
117 | /* Acquire CSP and try to autoload hardware codec */ | |
118 | if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { | |
119 | /* Unsupported format, release CSP */ | |
120 | csp->ops.csp_unuse(csp); | |
121 | } else { | |
122 | __start_CSP: | |
123 | /* Try to start CSP */ | |
124 | if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? | |
125 | SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, | |
126 | (runtime->channels > 1) ? | |
127 | SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { | |
128 | /* Failed, release CSP */ | |
129 | csp->ops.csp_unuse(csp); | |
130 | } else { | |
131 | /* Success, CSP acquired and running */ | |
132 | chip->open = SNDRV_SB_CSP_MODE_DSP_READ; | |
133 | } | |
134 | } | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
029d64b0 | 139 | static void snd_sb16_csp_update(struct snd_sb *chip) |
1da177e4 LT |
140 | { |
141 | if (chip->hardware == SB_HW_16CSP) { | |
029d64b0 | 142 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
143 | |
144 | if (csp->qpos_changed) { | |
145 | spin_lock(&chip->reg_lock); | |
146 | csp->ops.csp_qsound_transfer (csp); | |
147 | spin_unlock(&chip->reg_lock); | |
148 | } | |
149 | } | |
150 | } | |
151 | ||
029d64b0 | 152 | static void snd_sb16_csp_playback_open(struct snd_sb *chip, struct snd_pcm_runtime *runtime) |
1da177e4 LT |
153 | { |
154 | /* CSP decoders (QSound excluded) support only 16bit transfers */ | |
155 | if (chip->hardware == SB_HW_16CSP) { | |
029d64b0 | 156 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
157 | |
158 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | |
159 | /* manually loaded codec */ | |
160 | if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { | |
161 | runtime->hw.formats |= csp->acc_format; | |
162 | } | |
163 | } else { | |
164 | /* autoloaded codecs */ | |
165 | runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | |
166 | SNDRV_PCM_FMTBIT_IMA_ADPCM; | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
029d64b0 | 171 | static void snd_sb16_csp_playback_close(struct snd_sb *chip) |
1da177e4 LT |
172 | { |
173 | if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { | |
029d64b0 | 174 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
175 | |
176 | if (csp->ops.csp_stop(csp) == 0) { | |
177 | csp->ops.csp_unuse(csp); | |
178 | chip->open = 0; | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
029d64b0 | 183 | static void snd_sb16_csp_capture_open(struct snd_sb *chip, struct snd_pcm_runtime *runtime) |
1da177e4 LT |
184 | { |
185 | /* CSP coders support only 16bit transfers */ | |
186 | if (chip->hardware == SB_HW_16CSP) { | |
029d64b0 | 187 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
188 | |
189 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | |
190 | /* manually loaded codec */ | |
191 | if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { | |
192 | runtime->hw.formats |= csp->acc_format; | |
193 | } | |
194 | } else { | |
195 | /* autoloaded codecs */ | |
196 | runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | |
197 | SNDRV_PCM_FMTBIT_IMA_ADPCM; | |
198 | } | |
199 | } | |
200 | } | |
201 | ||
029d64b0 | 202 | static void snd_sb16_csp_capture_close(struct snd_sb *chip) |
1da177e4 LT |
203 | { |
204 | if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { | |
029d64b0 | 205 | struct snd_sb_csp *csp = chip->csp; |
1da177e4 LT |
206 | |
207 | if (csp->ops.csp_stop(csp) == 0) { | |
208 | csp->ops.csp_unuse(csp); | |
209 | chip->open = 0; | |
210 | } | |
211 | } | |
212 | } | |
213 | #else | |
214 | #define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ | |
215 | #define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ | |
216 | #define snd_sb16_csp_update(chip) /*nop*/ | |
217 | #define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ | |
218 | #define snd_sb16_csp_playback_close(chip) /*nop*/ | |
219 | #define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ | |
220 | #define snd_sb16_csp_capture_close(chip) /*nop*/ | |
221 | #endif | |
222 | ||
223 | ||
029d64b0 | 224 | static void snd_sb16_setup_rate(struct snd_sb *chip, |
1da177e4 LT |
225 | unsigned short rate, |
226 | int channel) | |
227 | { | |
228 | unsigned long flags; | |
229 | ||
230 | spin_lock_irqsave(&chip->reg_lock, flags); | |
231 | if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) | |
232 | snd_sb_ack_16bit(chip); | |
233 | else | |
234 | snd_sb_ack_8bit(chip); | |
235 | if (!(chip->mode & SB_RATE_LOCK)) { | |
236 | chip->locked_rate = rate; | |
237 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); | |
238 | snd_sbdsp_command(chip, rate >> 8); | |
239 | snd_sbdsp_command(chip, rate & 0xff); | |
240 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); | |
241 | snd_sbdsp_command(chip, rate >> 8); | |
242 | snd_sbdsp_command(chip, rate & 0xff); | |
243 | } | |
244 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
245 | } | |
246 | ||
029d64b0 TI |
247 | static int snd_sb16_hw_params(struct snd_pcm_substream *substream, |
248 | struct snd_pcm_hw_params *hw_params) | |
1da177e4 LT |
249 | { |
250 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | |
251 | } | |
252 | ||
029d64b0 | 253 | static int snd_sb16_hw_free(struct snd_pcm_substream *substream) |
1da177e4 LT |
254 | { |
255 | snd_pcm_lib_free_pages(substream); | |
256 | return 0; | |
257 | } | |
258 | ||
029d64b0 | 259 | static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream) |
1da177e4 LT |
260 | { |
261 | unsigned long flags; | |
029d64b0 TI |
262 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
263 | struct snd_pcm_runtime *runtime = substream->runtime; | |
1da177e4 LT |
264 | unsigned char format; |
265 | unsigned int size, count, dma; | |
266 | ||
267 | snd_sb16_csp_playback_prepare(chip, runtime); | |
268 | if (snd_pcm_format_unsigned(runtime->format) > 0) { | |
269 | format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; | |
270 | } else { | |
271 | format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; | |
272 | } | |
273 | ||
274 | snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); | |
275 | size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); | |
276 | dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; | |
277 | snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | |
278 | ||
279 | count = snd_pcm_lib_period_bytes(substream); | |
280 | spin_lock_irqsave(&chip->reg_lock, flags); | |
281 | if (chip->mode & SB_MODE_PLAYBACK_16) { | |
282 | count >>= 1; | |
283 | count--; | |
284 | snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); | |
285 | snd_sbdsp_command(chip, format); | |
286 | snd_sbdsp_command(chip, count & 0xff); | |
287 | snd_sbdsp_command(chip, count >> 8); | |
288 | snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); | |
289 | } else { | |
290 | count--; | |
291 | snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); | |
292 | snd_sbdsp_command(chip, format); | |
293 | snd_sbdsp_command(chip, count & 0xff); | |
294 | snd_sbdsp_command(chip, count >> 8); | |
295 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | |
296 | } | |
297 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
298 | return 0; | |
299 | } | |
300 | ||
029d64b0 | 301 | static int snd_sb16_playback_trigger(struct snd_pcm_substream *substream, |
1da177e4 LT |
302 | int cmd) |
303 | { | |
029d64b0 | 304 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
305 | int result = 0; |
306 | ||
307 | spin_lock(&chip->reg_lock); | |
308 | switch (cmd) { | |
309 | case SNDRV_PCM_TRIGGER_START: | |
5bdb6a16 | 310 | case SNDRV_PCM_TRIGGER_RESUME: |
1da177e4 LT |
311 | chip->mode |= SB_RATE_LOCK_PLAYBACK; |
312 | snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | |
313 | break; | |
314 | case SNDRV_PCM_TRIGGER_STOP: | |
5bdb6a16 | 315 | case SNDRV_PCM_TRIGGER_SUSPEND: |
1da177e4 LT |
316 | snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); |
317 | /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ | |
318 | if (chip->mode & SB_RATE_LOCK_CAPTURE) | |
319 | snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | |
320 | chip->mode &= ~SB_RATE_LOCK_PLAYBACK; | |
321 | break; | |
322 | default: | |
323 | result = -EINVAL; | |
324 | } | |
325 | spin_unlock(&chip->reg_lock); | |
326 | return result; | |
327 | } | |
328 | ||
029d64b0 | 329 | static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream) |
1da177e4 LT |
330 | { |
331 | unsigned long flags; | |
029d64b0 TI |
332 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
333 | struct snd_pcm_runtime *runtime = substream->runtime; | |
1da177e4 LT |
334 | unsigned char format; |
335 | unsigned int size, count, dma; | |
336 | ||
337 | snd_sb16_csp_capture_prepare(chip, runtime); | |
338 | if (snd_pcm_format_unsigned(runtime->format) > 0) { | |
339 | format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; | |
340 | } else { | |
341 | format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; | |
342 | } | |
343 | snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); | |
344 | size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); | |
345 | dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; | |
346 | snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | |
347 | ||
348 | count = snd_pcm_lib_period_bytes(substream); | |
349 | spin_lock_irqsave(&chip->reg_lock, flags); | |
350 | if (chip->mode & SB_MODE_CAPTURE_16) { | |
351 | count >>= 1; | |
352 | count--; | |
353 | snd_sbdsp_command(chip, SB_DSP4_IN16_AI); | |
354 | snd_sbdsp_command(chip, format); | |
355 | snd_sbdsp_command(chip, count & 0xff); | |
356 | snd_sbdsp_command(chip, count >> 8); | |
357 | snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); | |
358 | } else { | |
359 | count--; | |
360 | snd_sbdsp_command(chip, SB_DSP4_IN8_AI); | |
361 | snd_sbdsp_command(chip, format); | |
362 | snd_sbdsp_command(chip, count & 0xff); | |
363 | snd_sbdsp_command(chip, count >> 8); | |
364 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | |
365 | } | |
366 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
367 | return 0; | |
368 | } | |
369 | ||
029d64b0 | 370 | static int snd_sb16_capture_trigger(struct snd_pcm_substream *substream, |
1da177e4 LT |
371 | int cmd) |
372 | { | |
029d64b0 | 373 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
374 | int result = 0; |
375 | ||
376 | spin_lock(&chip->reg_lock); | |
377 | switch (cmd) { | |
378 | case SNDRV_PCM_TRIGGER_START: | |
5bdb6a16 | 379 | case SNDRV_PCM_TRIGGER_RESUME: |
1da177e4 LT |
380 | chip->mode |= SB_RATE_LOCK_CAPTURE; |
381 | snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | |
382 | break; | |
383 | case SNDRV_PCM_TRIGGER_STOP: | |
5bdb6a16 | 384 | case SNDRV_PCM_TRIGGER_SUSPEND: |
1da177e4 LT |
385 | snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); |
386 | /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ | |
387 | if (chip->mode & SB_RATE_LOCK_PLAYBACK) | |
388 | snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | |
389 | chip->mode &= ~SB_RATE_LOCK_CAPTURE; | |
390 | break; | |
391 | default: | |
392 | result = -EINVAL; | |
393 | } | |
394 | spin_unlock(&chip->reg_lock); | |
395 | return result; | |
396 | } | |
397 | ||
7d12e780 | 398 | irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id) |
1da177e4 | 399 | { |
029d64b0 | 400 | struct snd_sb *chip = dev_id; |
1da177e4 LT |
401 | unsigned char status; |
402 | int ok; | |
403 | ||
404 | spin_lock(&chip->mixer_lock); | |
405 | status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); | |
406 | spin_unlock(&chip->mixer_lock); | |
407 | if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback) | |
7d12e780 | 408 | chip->rmidi_callback(irq, chip->rmidi->private_data); |
1da177e4 LT |
409 | if (status & SB_IRQTYPE_8BIT) { |
410 | ok = 0; | |
411 | if (chip->mode & SB_MODE_PLAYBACK_8) { | |
412 | snd_pcm_period_elapsed(chip->playback_substream); | |
413 | snd_sb16_csp_update(chip); | |
414 | ok++; | |
415 | } | |
416 | if (chip->mode & SB_MODE_CAPTURE_8) { | |
417 | snd_pcm_period_elapsed(chip->capture_substream); | |
418 | ok++; | |
419 | } | |
420 | spin_lock(&chip->reg_lock); | |
421 | if (!ok) | |
422 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | |
423 | snd_sb_ack_8bit(chip); | |
424 | spin_unlock(&chip->reg_lock); | |
425 | } | |
426 | if (status & SB_IRQTYPE_16BIT) { | |
427 | ok = 0; | |
428 | if (chip->mode & SB_MODE_PLAYBACK_16) { | |
429 | snd_pcm_period_elapsed(chip->playback_substream); | |
430 | snd_sb16_csp_update(chip); | |
431 | ok++; | |
432 | } | |
433 | if (chip->mode & SB_MODE_CAPTURE_16) { | |
434 | snd_pcm_period_elapsed(chip->capture_substream); | |
435 | ok++; | |
436 | } | |
437 | spin_lock(&chip->reg_lock); | |
438 | if (!ok) | |
439 | snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); | |
440 | snd_sb_ack_16bit(chip); | |
441 | spin_unlock(&chip->reg_lock); | |
442 | } | |
443 | return IRQ_HANDLED; | |
444 | } | |
445 | ||
446 | /* | |
447 | ||
448 | */ | |
449 | ||
029d64b0 | 450 | static snd_pcm_uframes_t snd_sb16_playback_pointer(struct snd_pcm_substream *substream) |
1da177e4 | 451 | { |
029d64b0 | 452 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
453 | unsigned int dma; |
454 | size_t ptr; | |
455 | ||
456 | dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; | |
457 | ptr = snd_dma_pointer(dma, chip->p_dma_size); | |
458 | return bytes_to_frames(substream->runtime, ptr); | |
459 | } | |
460 | ||
029d64b0 | 461 | static snd_pcm_uframes_t snd_sb16_capture_pointer(struct snd_pcm_substream *substream) |
1da177e4 | 462 | { |
029d64b0 | 463 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
464 | unsigned int dma; |
465 | size_t ptr; | |
466 | ||
467 | dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; | |
468 | ptr = snd_dma_pointer(dma, chip->c_dma_size); | |
469 | return bytes_to_frames(substream->runtime, ptr); | |
470 | } | |
471 | ||
472 | /* | |
473 | ||
474 | */ | |
475 | ||
029d64b0 | 476 | static struct snd_pcm_hardware snd_sb16_playback = |
1da177e4 LT |
477 | { |
478 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | |
479 | SNDRV_PCM_INFO_MMAP_VALID), | |
480 | .formats = 0, | |
481 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, | |
482 | .rate_min = 4000, | |
483 | .rate_max = 44100, | |
484 | .channels_min = 1, | |
485 | .channels_max = 2, | |
486 | .buffer_bytes_max = (128*1024), | |
487 | .period_bytes_min = 64, | |
488 | .period_bytes_max = (128*1024), | |
489 | .periods_min = 1, | |
490 | .periods_max = 1024, | |
491 | .fifo_size = 0, | |
492 | }; | |
493 | ||
029d64b0 | 494 | static struct snd_pcm_hardware snd_sb16_capture = |
1da177e4 LT |
495 | { |
496 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | |
497 | SNDRV_PCM_INFO_MMAP_VALID), | |
498 | .formats = 0, | |
499 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, | |
500 | .rate_min = 4000, | |
501 | .rate_max = 44100, | |
502 | .channels_min = 1, | |
503 | .channels_max = 2, | |
504 | .buffer_bytes_max = (128*1024), | |
505 | .period_bytes_min = 64, | |
506 | .period_bytes_max = (128*1024), | |
507 | .periods_min = 1, | |
508 | .periods_max = 1024, | |
509 | .fifo_size = 0, | |
510 | }; | |
511 | ||
512 | /* | |
513 | * open/close | |
514 | */ | |
515 | ||
029d64b0 | 516 | static int snd_sb16_playback_open(struct snd_pcm_substream *substream) |
1da177e4 LT |
517 | { |
518 | unsigned long flags; | |
029d64b0 TI |
519 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
520 | struct snd_pcm_runtime *runtime = substream->runtime; | |
1da177e4 LT |
521 | |
522 | spin_lock_irqsave(&chip->open_lock, flags); | |
523 | if (chip->mode & SB_MODE_PLAYBACK) { | |
524 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
525 | return -EAGAIN; | |
526 | } | |
527 | runtime->hw = snd_sb16_playback; | |
528 | ||
529 | /* skip if 16 bit DMA was reserved for capture */ | |
530 | if (chip->force_mode16 & SB_MODE_CAPTURE_16) | |
531 | goto __skip_16bit; | |
532 | ||
533 | if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) { | |
534 | chip->mode |= SB_MODE_PLAYBACK_16; | |
535 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | |
536 | /* Vibra16X hack */ | |
537 | if (chip->dma16 <= 3) { | |
538 | runtime->hw.buffer_bytes_max = | |
539 | runtime->hw.period_bytes_max = 64 * 1024; | |
540 | } else { | |
541 | snd_sb16_csp_playback_open(chip, runtime); | |
542 | } | |
543 | goto __open_ok; | |
544 | } | |
545 | ||
546 | __skip_16bit: | |
547 | if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) { | |
548 | chip->mode |= SB_MODE_PLAYBACK_8; | |
549 | /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ | |
550 | if (chip->dma16 < 0) { | |
551 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | |
552 | chip->mode |= SB_MODE_PLAYBACK_16; | |
553 | } else { | |
554 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; | |
555 | } | |
556 | runtime->hw.buffer_bytes_max = | |
557 | runtime->hw.period_bytes_max = 64 * 1024; | |
558 | goto __open_ok; | |
559 | } | |
560 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
561 | return -EAGAIN; | |
562 | ||
563 | __open_ok: | |
564 | if (chip->hardware == SB_HW_ALS100) | |
565 | runtime->hw.rate_max = 48000; | |
621887ae TI |
566 | if (chip->hardware == SB_HW_CS5530) { |
567 | runtime->hw.buffer_bytes_max = 32 * 1024; | |
568 | runtime->hw.periods_min = 2; | |
569 | runtime->hw.rate_min = 44100; | |
570 | } | |
1da177e4 LT |
571 | if (chip->mode & SB_RATE_LOCK) |
572 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | |
573 | chip->playback_substream = substream; | |
574 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
575 | return 0; | |
576 | } | |
577 | ||
029d64b0 | 578 | static int snd_sb16_playback_close(struct snd_pcm_substream *substream) |
1da177e4 LT |
579 | { |
580 | unsigned long flags; | |
029d64b0 | 581 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
582 | |
583 | snd_sb16_csp_playback_close(chip); | |
584 | spin_lock_irqsave(&chip->open_lock, flags); | |
585 | chip->playback_substream = NULL; | |
586 | chip->mode &= ~SB_MODE_PLAYBACK; | |
587 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
588 | return 0; | |
589 | } | |
590 | ||
029d64b0 | 591 | static int snd_sb16_capture_open(struct snd_pcm_substream *substream) |
1da177e4 LT |
592 | { |
593 | unsigned long flags; | |
029d64b0 TI |
594 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
595 | struct snd_pcm_runtime *runtime = substream->runtime; | |
1da177e4 LT |
596 | |
597 | spin_lock_irqsave(&chip->open_lock, flags); | |
598 | if (chip->mode & SB_MODE_CAPTURE) { | |
599 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
600 | return -EAGAIN; | |
601 | } | |
602 | runtime->hw = snd_sb16_capture; | |
603 | ||
604 | /* skip if 16 bit DMA was reserved for playback */ | |
605 | if (chip->force_mode16 & SB_MODE_PLAYBACK_16) | |
606 | goto __skip_16bit; | |
607 | ||
608 | if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { | |
609 | chip->mode |= SB_MODE_CAPTURE_16; | |
610 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | |
611 | /* Vibra16X hack */ | |
612 | if (chip->dma16 <= 3) { | |
613 | runtime->hw.buffer_bytes_max = | |
614 | runtime->hw.period_bytes_max = 64 * 1024; | |
615 | } else { | |
616 | snd_sb16_csp_capture_open(chip, runtime); | |
617 | } | |
618 | goto __open_ok; | |
619 | } | |
620 | ||
621 | __skip_16bit: | |
622 | if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { | |
623 | chip->mode |= SB_MODE_CAPTURE_8; | |
624 | /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ | |
625 | if (chip->dma16 < 0) { | |
626 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | |
627 | chip->mode |= SB_MODE_CAPTURE_16; | |
628 | } else { | |
629 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; | |
630 | } | |
631 | runtime->hw.buffer_bytes_max = | |
632 | runtime->hw.period_bytes_max = 64 * 1024; | |
633 | goto __open_ok; | |
634 | } | |
635 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
636 | return -EAGAIN; | |
637 | ||
638 | __open_ok: | |
639 | if (chip->hardware == SB_HW_ALS100) | |
640 | runtime->hw.rate_max = 48000; | |
621887ae TI |
641 | if (chip->hardware == SB_HW_CS5530) { |
642 | runtime->hw.buffer_bytes_max = 32 * 1024; | |
643 | runtime->hw.periods_min = 2; | |
644 | runtime->hw.rate_min = 44100; | |
645 | } | |
1da177e4 LT |
646 | if (chip->mode & SB_RATE_LOCK) |
647 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | |
648 | chip->capture_substream = substream; | |
649 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
650 | return 0; | |
651 | } | |
652 | ||
029d64b0 | 653 | static int snd_sb16_capture_close(struct snd_pcm_substream *substream) |
1da177e4 LT |
654 | { |
655 | unsigned long flags; | |
029d64b0 | 656 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
657 | |
658 | snd_sb16_csp_capture_close(chip); | |
659 | spin_lock_irqsave(&chip->open_lock, flags); | |
660 | chip->capture_substream = NULL; | |
661 | chip->mode &= ~SB_MODE_CAPTURE; | |
662 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
663 | return 0; | |
664 | } | |
665 | ||
666 | /* | |
667 | * DMA control interface | |
668 | */ | |
669 | ||
029d64b0 | 670 | static int snd_sb16_set_dma_mode(struct snd_sb *chip, int what) |
1da177e4 LT |
671 | { |
672 | if (chip->dma8 < 0 || chip->dma16 < 0) { | |
622207dc TI |
673 | if (snd_BUG_ON(what)) |
674 | return -EINVAL; | |
1da177e4 LT |
675 | return 0; |
676 | } | |
677 | if (what == 0) { | |
678 | chip->force_mode16 = 0; | |
679 | } else if (what == 1) { | |
680 | chip->force_mode16 = SB_MODE_PLAYBACK_16; | |
681 | } else if (what == 2) { | |
682 | chip->force_mode16 = SB_MODE_CAPTURE_16; | |
683 | } else { | |
684 | return -EINVAL; | |
685 | } | |
686 | return 0; | |
687 | } | |
688 | ||
029d64b0 | 689 | static int snd_sb16_get_dma_mode(struct snd_sb *chip) |
1da177e4 LT |
690 | { |
691 | if (chip->dma8 < 0 || chip->dma16 < 0) | |
692 | return 0; | |
693 | switch (chip->force_mode16) { | |
694 | case SB_MODE_PLAYBACK_16: | |
695 | return 1; | |
696 | case SB_MODE_CAPTURE_16: | |
697 | return 2; | |
698 | default: | |
699 | return 0; | |
700 | } | |
701 | } | |
702 | ||
029d64b0 | 703 | static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
1da177e4 | 704 | { |
6b9e1288 | 705 | static const char * const texts[3] = { |
1da177e4 LT |
706 | "Auto", "Playback", "Capture" |
707 | }; | |
708 | ||
6b9e1288 | 709 | return snd_ctl_enum_info(uinfo, 1, 3, texts); |
1da177e4 LT |
710 | } |
711 | ||
029d64b0 | 712 | static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 | 713 | { |
029d64b0 | 714 | struct snd_sb *chip = snd_kcontrol_chip(kcontrol); |
1da177e4 LT |
715 | unsigned long flags; |
716 | ||
717 | spin_lock_irqsave(&chip->reg_lock, flags); | |
718 | ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); | |
719 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
720 | return 0; | |
721 | } | |
722 | ||
029d64b0 | 723 | static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 | 724 | { |
029d64b0 | 725 | struct snd_sb *chip = snd_kcontrol_chip(kcontrol); |
1da177e4 LT |
726 | unsigned long flags; |
727 | unsigned char nval, oval; | |
728 | int change; | |
729 | ||
730 | if ((nval = ucontrol->value.enumerated.item[0]) > 2) | |
731 | return -EINVAL; | |
732 | spin_lock_irqsave(&chip->reg_lock, flags); | |
733 | oval = snd_sb16_get_dma_mode(chip); | |
734 | change = nval != oval; | |
735 | snd_sb16_set_dma_mode(chip, nval); | |
736 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
737 | return change; | |
738 | } | |
739 | ||
029d64b0 | 740 | static struct snd_kcontrol_new snd_sb16_dma_control = { |
67ed4161 | 741 | .iface = SNDRV_CTL_ELEM_IFACE_CARD, |
1da177e4 LT |
742 | .name = "16-bit DMA Allocation", |
743 | .info = snd_sb16_dma_control_info, | |
744 | .get = snd_sb16_dma_control_get, | |
745 | .put = snd_sb16_dma_control_put | |
746 | }; | |
747 | ||
748 | /* | |
749 | * Initialization part | |
750 | */ | |
751 | ||
029d64b0 | 752 | int snd_sb16dsp_configure(struct snd_sb * chip) |
1da177e4 LT |
753 | { |
754 | unsigned long flags; | |
755 | unsigned char irqreg = 0, dmareg = 0, mpureg; | |
756 | unsigned char realirq, realdma, realmpureg; | |
757 | /* note: mpu register should be present only on SB16 Vibra soundcards */ | |
758 | ||
99b359ba | 759 | // printk(KERN_DEBUG "codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); |
1da177e4 LT |
760 | spin_lock_irqsave(&chip->mixer_lock, flags); |
761 | mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; | |
762 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | |
763 | switch (chip->irq) { | |
764 | case 2: | |
765 | case 9: | |
766 | irqreg |= SB_IRQSETUP_IRQ9; | |
767 | break; | |
768 | case 5: | |
769 | irqreg |= SB_IRQSETUP_IRQ5; | |
770 | break; | |
771 | case 7: | |
772 | irqreg |= SB_IRQSETUP_IRQ7; | |
773 | break; | |
774 | case 10: | |
775 | irqreg |= SB_IRQSETUP_IRQ10; | |
776 | break; | |
777 | default: | |
778 | return -EINVAL; | |
779 | } | |
780 | if (chip->dma8 >= 0) { | |
781 | switch (chip->dma8) { | |
782 | case 0: | |
783 | dmareg |= SB_DMASETUP_DMA0; | |
784 | break; | |
785 | case 1: | |
786 | dmareg |= SB_DMASETUP_DMA1; | |
787 | break; | |
788 | case 3: | |
789 | dmareg |= SB_DMASETUP_DMA3; | |
790 | break; | |
791 | default: | |
792 | return -EINVAL; | |
793 | } | |
794 | } | |
795 | if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { | |
796 | switch (chip->dma16) { | |
797 | case 5: | |
798 | dmareg |= SB_DMASETUP_DMA5; | |
799 | break; | |
800 | case 6: | |
801 | dmareg |= SB_DMASETUP_DMA6; | |
802 | break; | |
803 | case 7: | |
804 | dmareg |= SB_DMASETUP_DMA7; | |
805 | break; | |
806 | default: | |
807 | return -EINVAL; | |
808 | } | |
809 | } | |
810 | switch (chip->mpu_port) { | |
811 | case 0x300: | |
812 | mpureg |= 0x04; | |
813 | break; | |
814 | case 0x330: | |
815 | mpureg |= 0x00; | |
816 | break; | |
817 | default: | |
818 | mpureg |= 0x02; /* disable MPU */ | |
819 | } | |
820 | spin_lock_irqsave(&chip->mixer_lock, flags); | |
821 | ||
822 | snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); | |
823 | realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); | |
824 | ||
825 | snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); | |
826 | realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); | |
827 | ||
828 | snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); | |
829 | realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); | |
830 | ||
831 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | |
832 | if ((~realirq) & irqreg || (~realdma) & dmareg) { | |
99b359ba TI |
833 | snd_printk(KERN_ERR "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); |
834 | snd_printk(KERN_ERR "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); | |
835 | snd_printk(KERN_ERR "SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg); | |
1da177e4 LT |
836 | return -ENODEV; |
837 | } | |
838 | return 0; | |
839 | } | |
840 | ||
029d64b0 | 841 | static struct snd_pcm_ops snd_sb16_playback_ops = { |
1da177e4 LT |
842 | .open = snd_sb16_playback_open, |
843 | .close = snd_sb16_playback_close, | |
844 | .ioctl = snd_pcm_lib_ioctl, | |
845 | .hw_params = snd_sb16_hw_params, | |
846 | .hw_free = snd_sb16_hw_free, | |
847 | .prepare = snd_sb16_playback_prepare, | |
848 | .trigger = snd_sb16_playback_trigger, | |
849 | .pointer = snd_sb16_playback_pointer, | |
850 | }; | |
851 | ||
029d64b0 | 852 | static struct snd_pcm_ops snd_sb16_capture_ops = { |
1da177e4 LT |
853 | .open = snd_sb16_capture_open, |
854 | .close = snd_sb16_capture_close, | |
855 | .ioctl = snd_pcm_lib_ioctl, | |
856 | .hw_params = snd_sb16_hw_params, | |
857 | .hw_free = snd_sb16_hw_free, | |
858 | .prepare = snd_sb16_capture_prepare, | |
859 | .trigger = snd_sb16_capture_trigger, | |
860 | .pointer = snd_sb16_capture_pointer, | |
861 | }; | |
862 | ||
92533f18 | 863 | int snd_sb16dsp_pcm(struct snd_sb *chip, int device) |
1da177e4 | 864 | { |
029d64b0 TI |
865 | struct snd_card *card = chip->card; |
866 | struct snd_pcm *pcm; | |
1da177e4 LT |
867 | int err; |
868 | ||
1da177e4 LT |
869 | if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) |
870 | return err; | |
871 | sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); | |
872 | pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; | |
873 | pcm->private_data = chip; | |
92533f18 | 874 | chip->pcm = pcm; |
1da177e4 LT |
875 | |
876 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); | |
877 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); | |
878 | ||
879 | if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) | |
880 | snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); | |
881 | else | |
882 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | |
883 | ||
884 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | |
885 | snd_dma_isa_data(), | |
886 | 64*1024, 128*1024); | |
1da177e4 LT |
887 | return 0; |
888 | } | |
889 | ||
029d64b0 | 890 | const struct snd_pcm_ops *snd_sb16dsp_get_pcm_ops(int direction) |
1da177e4 LT |
891 | { |
892 | return direction == SNDRV_PCM_STREAM_PLAYBACK ? | |
893 | &snd_sb16_playback_ops : &snd_sb16_capture_ops; | |
894 | } | |
895 | ||
896 | EXPORT_SYMBOL(snd_sb16dsp_pcm); | |
897 | EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); | |
898 | EXPORT_SYMBOL(snd_sb16dsp_configure); | |
899 | EXPORT_SYMBOL(snd_sb16dsp_interrupt); | |
900 | ||
901 | /* | |
902 | * INIT part | |
903 | */ | |
904 | ||
905 | static int __init alsa_sb16_init(void) | |
906 | { | |
907 | return 0; | |
908 | } | |
909 | ||
910 | static void __exit alsa_sb16_exit(void) | |
911 | { | |
912 | } | |
913 | ||
914 | module_init(alsa_sb16_init) | |
915 | module_exit(alsa_sb16_exit) |