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