Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | |
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 | ||
36 | #include <sound/driver.h> | |
37 | #include <asm/io.h> | |
38 | #include <asm/dma.h> | |
39 | #include <linux/init.h> | |
40 | #include <linux/time.h> | |
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 | ||
48 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | |
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; | |
566 | if (chip->mode & SB_RATE_LOCK) | |
567 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | |
568 | chip->playback_substream = substream; | |
569 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
570 | return 0; | |
571 | } | |
572 | ||
029d64b0 | 573 | static int snd_sb16_playback_close(struct snd_pcm_substream *substream) |
1da177e4 LT |
574 | { |
575 | unsigned long flags; | |
029d64b0 | 576 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
577 | |
578 | snd_sb16_csp_playback_close(chip); | |
579 | spin_lock_irqsave(&chip->open_lock, flags); | |
580 | chip->playback_substream = NULL; | |
581 | chip->mode &= ~SB_MODE_PLAYBACK; | |
582 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
583 | return 0; | |
584 | } | |
585 | ||
029d64b0 | 586 | static int snd_sb16_capture_open(struct snd_pcm_substream *substream) |
1da177e4 LT |
587 | { |
588 | unsigned long flags; | |
029d64b0 TI |
589 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
590 | struct snd_pcm_runtime *runtime = substream->runtime; | |
1da177e4 LT |
591 | |
592 | spin_lock_irqsave(&chip->open_lock, flags); | |
593 | if (chip->mode & SB_MODE_CAPTURE) { | |
594 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
595 | return -EAGAIN; | |
596 | } | |
597 | runtime->hw = snd_sb16_capture; | |
598 | ||
599 | /* skip if 16 bit DMA was reserved for playback */ | |
600 | if (chip->force_mode16 & SB_MODE_PLAYBACK_16) | |
601 | goto __skip_16bit; | |
602 | ||
603 | if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { | |
604 | chip->mode |= SB_MODE_CAPTURE_16; | |
605 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | |
606 | /* Vibra16X hack */ | |
607 | if (chip->dma16 <= 3) { | |
608 | runtime->hw.buffer_bytes_max = | |
609 | runtime->hw.period_bytes_max = 64 * 1024; | |
610 | } else { | |
611 | snd_sb16_csp_capture_open(chip, runtime); | |
612 | } | |
613 | goto __open_ok; | |
614 | } | |
615 | ||
616 | __skip_16bit: | |
617 | if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { | |
618 | chip->mode |= SB_MODE_CAPTURE_8; | |
619 | /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ | |
620 | if (chip->dma16 < 0) { | |
621 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | |
622 | chip->mode |= SB_MODE_CAPTURE_16; | |
623 | } else { | |
624 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; | |
625 | } | |
626 | runtime->hw.buffer_bytes_max = | |
627 | runtime->hw.period_bytes_max = 64 * 1024; | |
628 | goto __open_ok; | |
629 | } | |
630 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
631 | return -EAGAIN; | |
632 | ||
633 | __open_ok: | |
634 | if (chip->hardware == SB_HW_ALS100) | |
635 | runtime->hw.rate_max = 48000; | |
636 | if (chip->mode & SB_RATE_LOCK) | |
637 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | |
638 | chip->capture_substream = substream; | |
639 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
640 | return 0; | |
641 | } | |
642 | ||
029d64b0 | 643 | static int snd_sb16_capture_close(struct snd_pcm_substream *substream) |
1da177e4 LT |
644 | { |
645 | unsigned long flags; | |
029d64b0 | 646 | struct snd_sb *chip = snd_pcm_substream_chip(substream); |
1da177e4 LT |
647 | |
648 | snd_sb16_csp_capture_close(chip); | |
649 | spin_lock_irqsave(&chip->open_lock, flags); | |
650 | chip->capture_substream = NULL; | |
651 | chip->mode &= ~SB_MODE_CAPTURE; | |
652 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
653 | return 0; | |
654 | } | |
655 | ||
656 | /* | |
657 | * DMA control interface | |
658 | */ | |
659 | ||
029d64b0 | 660 | static int snd_sb16_set_dma_mode(struct snd_sb *chip, int what) |
1da177e4 LT |
661 | { |
662 | if (chip->dma8 < 0 || chip->dma16 < 0) { | |
663 | snd_assert(what == 0, return -EINVAL); | |
664 | return 0; | |
665 | } | |
666 | if (what == 0) { | |
667 | chip->force_mode16 = 0; | |
668 | } else if (what == 1) { | |
669 | chip->force_mode16 = SB_MODE_PLAYBACK_16; | |
670 | } else if (what == 2) { | |
671 | chip->force_mode16 = SB_MODE_CAPTURE_16; | |
672 | } else { | |
673 | return -EINVAL; | |
674 | } | |
675 | return 0; | |
676 | } | |
677 | ||
029d64b0 | 678 | static int snd_sb16_get_dma_mode(struct snd_sb *chip) |
1da177e4 LT |
679 | { |
680 | if (chip->dma8 < 0 || chip->dma16 < 0) | |
681 | return 0; | |
682 | switch (chip->force_mode16) { | |
683 | case SB_MODE_PLAYBACK_16: | |
684 | return 1; | |
685 | case SB_MODE_CAPTURE_16: | |
686 | return 2; | |
687 | default: | |
688 | return 0; | |
689 | } | |
690 | } | |
691 | ||
029d64b0 | 692 | static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
1da177e4 LT |
693 | { |
694 | static char *texts[3] = { | |
695 | "Auto", "Playback", "Capture" | |
696 | }; | |
697 | ||
698 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | |
699 | uinfo->count = 1; | |
700 | uinfo->value.enumerated.items = 3; | |
701 | if (uinfo->value.enumerated.item > 2) | |
702 | uinfo->value.enumerated.item = 2; | |
703 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | |
704 | return 0; | |
705 | } | |
706 | ||
029d64b0 | 707 | static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 | 708 | { |
029d64b0 | 709 | struct snd_sb *chip = snd_kcontrol_chip(kcontrol); |
1da177e4 LT |
710 | unsigned long flags; |
711 | ||
712 | spin_lock_irqsave(&chip->reg_lock, flags); | |
713 | ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); | |
714 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
715 | return 0; | |
716 | } | |
717 | ||
029d64b0 | 718 | static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 | 719 | { |
029d64b0 | 720 | struct snd_sb *chip = snd_kcontrol_chip(kcontrol); |
1da177e4 LT |
721 | unsigned long flags; |
722 | unsigned char nval, oval; | |
723 | int change; | |
724 | ||
725 | if ((nval = ucontrol->value.enumerated.item[0]) > 2) | |
726 | return -EINVAL; | |
727 | spin_lock_irqsave(&chip->reg_lock, flags); | |
728 | oval = snd_sb16_get_dma_mode(chip); | |
729 | change = nval != oval; | |
730 | snd_sb16_set_dma_mode(chip, nval); | |
731 | spin_unlock_irqrestore(&chip->reg_lock, flags); | |
732 | return change; | |
733 | } | |
734 | ||
029d64b0 | 735 | static struct snd_kcontrol_new snd_sb16_dma_control = { |
67ed4161 | 736 | .iface = SNDRV_CTL_ELEM_IFACE_CARD, |
1da177e4 LT |
737 | .name = "16-bit DMA Allocation", |
738 | .info = snd_sb16_dma_control_info, | |
739 | .get = snd_sb16_dma_control_get, | |
740 | .put = snd_sb16_dma_control_put | |
741 | }; | |
742 | ||
743 | /* | |
744 | * Initialization part | |
745 | */ | |
746 | ||
029d64b0 | 747 | int snd_sb16dsp_configure(struct snd_sb * chip) |
1da177e4 LT |
748 | { |
749 | unsigned long flags; | |
750 | unsigned char irqreg = 0, dmareg = 0, mpureg; | |
751 | unsigned char realirq, realdma, realmpureg; | |
752 | /* note: mpu register should be present only on SB16 Vibra soundcards */ | |
753 | ||
99b359ba | 754 | // printk(KERN_DEBUG "codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); |
1da177e4 LT |
755 | spin_lock_irqsave(&chip->mixer_lock, flags); |
756 | mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; | |
757 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | |
758 | switch (chip->irq) { | |
759 | case 2: | |
760 | case 9: | |
761 | irqreg |= SB_IRQSETUP_IRQ9; | |
762 | break; | |
763 | case 5: | |
764 | irqreg |= SB_IRQSETUP_IRQ5; | |
765 | break; | |
766 | case 7: | |
767 | irqreg |= SB_IRQSETUP_IRQ7; | |
768 | break; | |
769 | case 10: | |
770 | irqreg |= SB_IRQSETUP_IRQ10; | |
771 | break; | |
772 | default: | |
773 | return -EINVAL; | |
774 | } | |
775 | if (chip->dma8 >= 0) { | |
776 | switch (chip->dma8) { | |
777 | case 0: | |
778 | dmareg |= SB_DMASETUP_DMA0; | |
779 | break; | |
780 | case 1: | |
781 | dmareg |= SB_DMASETUP_DMA1; | |
782 | break; | |
783 | case 3: | |
784 | dmareg |= SB_DMASETUP_DMA3; | |
785 | break; | |
786 | default: | |
787 | return -EINVAL; | |
788 | } | |
789 | } | |
790 | if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { | |
791 | switch (chip->dma16) { | |
792 | case 5: | |
793 | dmareg |= SB_DMASETUP_DMA5; | |
794 | break; | |
795 | case 6: | |
796 | dmareg |= SB_DMASETUP_DMA6; | |
797 | break; | |
798 | case 7: | |
799 | dmareg |= SB_DMASETUP_DMA7; | |
800 | break; | |
801 | default: | |
802 | return -EINVAL; | |
803 | } | |
804 | } | |
805 | switch (chip->mpu_port) { | |
806 | case 0x300: | |
807 | mpureg |= 0x04; | |
808 | break; | |
809 | case 0x330: | |
810 | mpureg |= 0x00; | |
811 | break; | |
812 | default: | |
813 | mpureg |= 0x02; /* disable MPU */ | |
814 | } | |
815 | spin_lock_irqsave(&chip->mixer_lock, flags); | |
816 | ||
817 | snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); | |
818 | realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); | |
819 | ||
820 | snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); | |
821 | realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); | |
822 | ||
823 | snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); | |
824 | realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); | |
825 | ||
826 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | |
827 | if ((~realirq) & irqreg || (~realdma) & dmareg) { | |
99b359ba TI |
828 | snd_printk(KERN_ERR "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); |
829 | snd_printk(KERN_ERR "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); | |
830 | 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 |
831 | return -ENODEV; |
832 | } | |
833 | return 0; | |
834 | } | |
835 | ||
029d64b0 | 836 | static struct snd_pcm_ops snd_sb16_playback_ops = { |
1da177e4 LT |
837 | .open = snd_sb16_playback_open, |
838 | .close = snd_sb16_playback_close, | |
839 | .ioctl = snd_pcm_lib_ioctl, | |
840 | .hw_params = snd_sb16_hw_params, | |
841 | .hw_free = snd_sb16_hw_free, | |
842 | .prepare = snd_sb16_playback_prepare, | |
843 | .trigger = snd_sb16_playback_trigger, | |
844 | .pointer = snd_sb16_playback_pointer, | |
845 | }; | |
846 | ||
029d64b0 | 847 | static struct snd_pcm_ops snd_sb16_capture_ops = { |
1da177e4 LT |
848 | .open = snd_sb16_capture_open, |
849 | .close = snd_sb16_capture_close, | |
850 | .ioctl = snd_pcm_lib_ioctl, | |
851 | .hw_params = snd_sb16_hw_params, | |
852 | .hw_free = snd_sb16_hw_free, | |
853 | .prepare = snd_sb16_capture_prepare, | |
854 | .trigger = snd_sb16_capture_trigger, | |
855 | .pointer = snd_sb16_capture_pointer, | |
856 | }; | |
857 | ||
029d64b0 | 858 | int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm) |
1da177e4 | 859 | { |
029d64b0 TI |
860 | struct snd_card *card = chip->card; |
861 | struct snd_pcm *pcm; | |
1da177e4 LT |
862 | int err; |
863 | ||
864 | if (rpcm) | |
865 | *rpcm = NULL; | |
866 | if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) | |
867 | return err; | |
868 | sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); | |
869 | pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; | |
870 | pcm->private_data = chip; | |
1da177e4 LT |
871 | |
872 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); | |
873 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); | |
874 | ||
875 | if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) | |
876 | snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); | |
877 | else | |
878 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | |
879 | ||
880 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | |
881 | snd_dma_isa_data(), | |
882 | 64*1024, 128*1024); | |
883 | ||
884 | if (rpcm) | |
885 | *rpcm = pcm; | |
886 | return 0; | |
887 | } | |
888 | ||
029d64b0 | 889 | const struct snd_pcm_ops *snd_sb16dsp_get_pcm_ops(int direction) |
1da177e4 LT |
890 | { |
891 | return direction == SNDRV_PCM_STREAM_PLAYBACK ? | |
892 | &snd_sb16_playback_ops : &snd_sb16_capture_ops; | |
893 | } | |
894 | ||
895 | EXPORT_SYMBOL(snd_sb16dsp_pcm); | |
896 | EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); | |
897 | EXPORT_SYMBOL(snd_sb16dsp_configure); | |
898 | EXPORT_SYMBOL(snd_sb16dsp_interrupt); | |
899 | ||
900 | /* | |
901 | * INIT part | |
902 | */ | |
903 | ||
904 | static int __init alsa_sb16_init(void) | |
905 | { | |
906 | return 0; | |
907 | } | |
908 | ||
909 | static void __exit alsa_sb16_exit(void) | |
910 | { | |
911 | } | |
912 | ||
913 | module_init(alsa_sb16_init) | |
914 | module_exit(alsa_sb16_exit) |