Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License as published by | |
4 | * the Free Software Foundation; either version 2 of the License, or | |
5 | * (at your option) any later version. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU Library General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
15 | */ | |
16 | ||
17 | /* | |
18 | * Vortex PCM ALSA driver. | |
19 | * | |
20 | * Supports ADB and WT DMA. Unfortunately, WT channels do not run yet. | |
21 | * It remains stuck,and DMA transfers do not happen. | |
22 | */ | |
23 | #include <sound/asoundef.h> | |
1da177e4 LT |
24 | #include <linux/time.h> |
25 | #include <sound/core.h> | |
26 | #include <sound/pcm.h> | |
27 | #include <sound/pcm_params.h> | |
28 | #include "au88x0.h" | |
29 | ||
30 | #define VORTEX_PCM_TYPE(x) (x->name[40]) | |
31 | ||
32 | /* hardware definition */ | |
2fd16874 | 33 | static struct snd_pcm_hardware snd_vortex_playback_hw_adb = { |
1da177e4 | 34 | .info = |
41e4845c | 35 | (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */ |
1da177e4 LT |
36 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED | |
37 | SNDRV_PCM_INFO_MMAP_VALID), | |
38 | .formats = | |
39 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 | | |
40 | SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, | |
41 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | |
42 | .rate_min = 5000, | |
43 | .rate_max = 48000, | |
44 | .channels_min = 1, | |
1da177e4 | 45 | .channels_max = 2, |
1da177e4 | 46 | .buffer_bytes_max = 0x10000, |
54a96dad | 47 | .period_bytes_min = 0x20, |
1da177e4 LT |
48 | .period_bytes_max = 0x1000, |
49 | .periods_min = 2, | |
54a96dad | 50 | .periods_max = 1024, |
1da177e4 LT |
51 | }; |
52 | ||
53 | #ifndef CHIP_AU8820 | |
2fd16874 | 54 | static struct snd_pcm_hardware snd_vortex_playback_hw_a3d = { |
1da177e4 | 55 | .info = |
41e4845c | 56 | (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */ |
1da177e4 LT |
57 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED | |
58 | SNDRV_PCM_INFO_MMAP_VALID), | |
59 | .formats = | |
60 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 | | |
61 | SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, | |
62 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | |
63 | .rate_min = 5000, | |
64 | .rate_max = 48000, | |
65 | .channels_min = 1, | |
66 | .channels_max = 1, | |
67 | .buffer_bytes_max = 0x10000, | |
68 | .period_bytes_min = 0x100, | |
69 | .period_bytes_max = 0x1000, | |
70 | .periods_min = 2, | |
71 | .periods_max = 64, | |
72 | }; | |
73 | #endif | |
2fd16874 | 74 | static struct snd_pcm_hardware snd_vortex_playback_hw_spdif = { |
1da177e4 | 75 | .info = |
41e4845c | 76 | (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */ |
1da177e4 LT |
77 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED | |
78 | SNDRV_PCM_INFO_MMAP_VALID), | |
79 | .formats = | |
80 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 | | |
81 | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | SNDRV_PCM_FMTBIT_MU_LAW | | |
82 | SNDRV_PCM_FMTBIT_A_LAW, | |
83 | .rates = | |
84 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, | |
85 | .rate_min = 32000, | |
86 | .rate_max = 48000, | |
87 | .channels_min = 1, | |
88 | .channels_max = 2, | |
89 | .buffer_bytes_max = 0x10000, | |
90 | .period_bytes_min = 0x100, | |
91 | .period_bytes_max = 0x1000, | |
92 | .periods_min = 2, | |
93 | .periods_max = 64, | |
94 | }; | |
95 | ||
96 | #ifndef CHIP_AU8810 | |
2fd16874 | 97 | static struct snd_pcm_hardware snd_vortex_playback_hw_wt = { |
1da177e4 LT |
98 | .info = (SNDRV_PCM_INFO_MMAP | |
99 | SNDRV_PCM_INFO_INTERLEAVED | | |
100 | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), | |
101 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
102 | .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, // SNDRV_PCM_RATE_48000, | |
103 | .rate_min = 8000, | |
104 | .rate_max = 48000, | |
105 | .channels_min = 1, | |
106 | .channels_max = 2, | |
107 | .buffer_bytes_max = 0x10000, | |
108 | .period_bytes_min = 0x0400, | |
109 | .period_bytes_max = 0x1000, | |
110 | .periods_min = 2, | |
111 | .periods_max = 64, | |
112 | }; | |
113 | #endif | |
d9ab3443 RY |
114 | #ifdef CHIP_AU8830 |
115 | static unsigned int au8830_channels[3] = { | |
116 | 1, 2, 4, | |
117 | }; | |
118 | ||
119 | static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { | |
120 | .count = ARRAY_SIZE(au8830_channels), | |
121 | .list = au8830_channels, | |
122 | .mask = 0, | |
123 | }; | |
124 | #endif | |
bb92b7c4 RY |
125 | |
126 | static void vortex_notify_pcm_vol_change(struct snd_card *card, | |
127 | struct snd_kcontrol *kctl, int activate) | |
128 | { | |
129 | if (activate) | |
130 | kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; | |
131 | else | |
132 | kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | |
133 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | | |
134 | SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id)); | |
135 | } | |
136 | ||
1da177e4 | 137 | /* open callback */ |
2fd16874 | 138 | static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) |
1da177e4 LT |
139 | { |
140 | vortex_t *vortex = snd_pcm_substream_chip(substream); | |
2fd16874 | 141 | struct snd_pcm_runtime *runtime = substream->runtime; |
1da177e4 LT |
142 | int err; |
143 | ||
144 | /* Force equal size periods */ | |
145 | if ((err = | |
146 | snd_pcm_hw_constraint_integer(runtime, | |
147 | SNDRV_PCM_HW_PARAM_PERIODS)) < 0) | |
148 | return err; | |
149 | /* Avoid PAGE_SIZE boundary to fall inside of a period. */ | |
150 | if ((err = | |
151 | snd_pcm_hw_constraint_pow2(runtime, 0, | |
152 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) | |
153 | return err; | |
154 | ||
54a96dad RY |
155 | snd_pcm_hw_constraint_step(runtime, 0, |
156 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 64); | |
157 | ||
1da177e4 LT |
158 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { |
159 | #ifndef CHIP_AU8820 | |
160 | if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) { | |
161 | runtime->hw = snd_vortex_playback_hw_a3d; | |
162 | } | |
163 | #endif | |
164 | if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_SPDIF) { | |
165 | runtime->hw = snd_vortex_playback_hw_spdif; | |
166 | switch (vortex->spdif_sr) { | |
167 | case 32000: | |
168 | runtime->hw.rates = SNDRV_PCM_RATE_32000; | |
169 | break; | |
170 | case 44100: | |
171 | runtime->hw.rates = SNDRV_PCM_RATE_44100; | |
172 | break; | |
173 | case 48000: | |
174 | runtime->hw.rates = SNDRV_PCM_RATE_48000; | |
175 | break; | |
176 | } | |
177 | } | |
178 | if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB | |
179 | || VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_I2S) | |
180 | runtime->hw = snd_vortex_playback_hw_adb; | |
d9ab3443 RY |
181 | #ifdef CHIP_AU8830 |
182 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | |
7b32486c | 183 | VORTEX_IS_QUAD(vortex) && |
d9ab3443 RY |
184 | VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { |
185 | runtime->hw.channels_max = 4; | |
186 | snd_pcm_hw_constraint_list(runtime, 0, | |
187 | SNDRV_PCM_HW_PARAM_CHANNELS, | |
188 | &hw_constraints_au8830_channels); | |
189 | } | |
190 | #endif | |
1da177e4 LT |
191 | substream->runtime->private_data = NULL; |
192 | } | |
193 | #ifndef CHIP_AU8810 | |
194 | else { | |
195 | runtime->hw = snd_vortex_playback_hw_wt; | |
196 | substream->runtime->private_data = NULL; | |
197 | } | |
198 | #endif | |
199 | return 0; | |
200 | } | |
201 | ||
202 | /* close callback */ | |
2fd16874 | 203 | static int snd_vortex_pcm_close(struct snd_pcm_substream *substream) |
1da177e4 LT |
204 | { |
205 | //vortex_t *chip = snd_pcm_substream_chip(substream); | |
206 | stream_t *stream = (stream_t *) substream->runtime->private_data; | |
207 | ||
208 | // the hardware-specific codes will be here | |
209 | if (stream != NULL) { | |
210 | stream->substream = NULL; | |
211 | stream->nr_ch = 0; | |
212 | } | |
213 | substream->runtime->private_data = NULL; | |
214 | return 0; | |
215 | } | |
216 | ||
217 | /* hw_params callback */ | |
218 | static int | |
2fd16874 TI |
219 | snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, |
220 | struct snd_pcm_hw_params *hw_params) | |
1da177e4 LT |
221 | { |
222 | vortex_t *chip = snd_pcm_substream_chip(substream); | |
223 | stream_t *stream = (stream_t *) (substream->runtime->private_data); | |
1da177e4 LT |
224 | int err; |
225 | ||
226 | // Alloc buffer memory. | |
227 | err = | |
228 | snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | |
229 | if (err < 0) { | |
70c84418 | 230 | dev_err(chip->card->dev, "Vortex: pcm page alloc failed!\n"); |
1da177e4 LT |
231 | return err; |
232 | } | |
1da177e4 | 233 | /* |
e7e69265 | 234 | pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params), |
1da177e4 LT |
235 | params_period_bytes(hw_params), params_channels(hw_params)); |
236 | */ | |
237 | spin_lock_irq(&chip->lock); | |
238 | // Make audio routes and config buffer DMA. | |
239 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { | |
240 | int dma, type = VORTEX_PCM_TYPE(substream->pcm); | |
241 | /* Dealloc any routes. */ | |
242 | if (stream != NULL) | |
243 | vortex_adb_allocroute(chip, stream->dma, | |
244 | stream->nr_ch, stream->dir, | |
bb92b7c4 RY |
245 | stream->type, |
246 | substream->number); | |
1da177e4 LT |
247 | /* Alloc routes. */ |
248 | dma = | |
249 | vortex_adb_allocroute(chip, -1, | |
250 | params_channels(hw_params), | |
bb92b7c4 RY |
251 | substream->stream, type, |
252 | substream->number); | |
a278655f TI |
253 | if (dma < 0) { |
254 | spin_unlock_irq(&chip->lock); | |
1da177e4 | 255 | return dma; |
a278655f | 256 | } |
1da177e4 LT |
257 | stream = substream->runtime->private_data = &chip->dma_adb[dma]; |
258 | stream->substream = substream; | |
259 | /* Setup Buffers. */ | |
77a23f26 | 260 | vortex_adbdma_setbuffers(chip, dma, |
1da177e4 LT |
261 | params_period_bytes(hw_params), |
262 | params_periods(hw_params)); | |
bb92b7c4 RY |
263 | if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { |
264 | chip->pcm_vol[substream->number].active = 1; | |
265 | vortex_notify_pcm_vol_change(chip->card, | |
266 | chip->pcm_vol[substream->number].kctl, 1); | |
267 | } | |
1da177e4 LT |
268 | } |
269 | #ifndef CHIP_AU8810 | |
270 | else { | |
271 | /* if (stream != NULL) | |
272 | vortex_wt_allocroute(chip, substream->number, 0); */ | |
273 | vortex_wt_allocroute(chip, substream->number, | |
274 | params_channels(hw_params)); | |
275 | stream = substream->runtime->private_data = | |
276 | &chip->dma_wt[substream->number]; | |
277 | stream->dma = substream->number; | |
278 | stream->substream = substream; | |
77a23f26 | 279 | vortex_wtdma_setbuffers(chip, substream->number, |
1da177e4 LT |
280 | params_period_bytes(hw_params), |
281 | params_periods(hw_params)); | |
282 | } | |
283 | #endif | |
284 | spin_unlock_irq(&chip->lock); | |
285 | return 0; | |
286 | } | |
287 | ||
288 | /* hw_free callback */ | |
2fd16874 | 289 | static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream) |
1da177e4 LT |
290 | { |
291 | vortex_t *chip = snd_pcm_substream_chip(substream); | |
292 | stream_t *stream = (stream_t *) (substream->runtime->private_data); | |
293 | ||
294 | spin_lock_irq(&chip->lock); | |
295 | // Delete audio routes. | |
296 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { | |
bb92b7c4 RY |
297 | if (stream != NULL) { |
298 | if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { | |
299 | chip->pcm_vol[substream->number].active = 0; | |
300 | vortex_notify_pcm_vol_change(chip->card, | |
301 | chip->pcm_vol[substream->number].kctl, | |
302 | 0); | |
303 | } | |
1da177e4 LT |
304 | vortex_adb_allocroute(chip, stream->dma, |
305 | stream->nr_ch, stream->dir, | |
bb92b7c4 RY |
306 | stream->type, |
307 | substream->number); | |
308 | } | |
1da177e4 LT |
309 | } |
310 | #ifndef CHIP_AU8810 | |
311 | else { | |
312 | if (stream != NULL) | |
313 | vortex_wt_allocroute(chip, stream->dma, 0); | |
314 | } | |
315 | #endif | |
316 | substream->runtime->private_data = NULL; | |
317 | spin_unlock_irq(&chip->lock); | |
318 | ||
319 | return snd_pcm_lib_free_pages(substream); | |
320 | } | |
321 | ||
322 | /* prepare callback */ | |
2fd16874 | 323 | static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream) |
1da177e4 LT |
324 | { |
325 | vortex_t *chip = snd_pcm_substream_chip(substream); | |
2fd16874 | 326 | struct snd_pcm_runtime *runtime = substream->runtime; |
1da177e4 LT |
327 | stream_t *stream = (stream_t *) substream->runtime->private_data; |
328 | int dma = stream->dma, fmt, dir; | |
329 | ||
330 | // set up the hardware with the current configuration. | |
331 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
332 | dir = 1; | |
333 | else | |
334 | dir = 0; | |
6a40dc5a | 335 | fmt = vortex_alsafmt_aspfmt(runtime->format, chip); |
1da177e4 LT |
336 | spin_lock_irq(&chip->lock); |
337 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { | |
3ae4e1f7 RY |
338 | vortex_adbdma_setmode(chip, dma, 1, dir, fmt, |
339 | runtime->channels == 1 ? 0 : 1, 0); | |
1da177e4 LT |
340 | vortex_adbdma_setstartbuffer(chip, dma, 0); |
341 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF) | |
342 | vortex_adb_setsrc(chip, dma, runtime->rate, dir); | |
343 | } | |
344 | #ifndef CHIP_AU8810 | |
345 | else { | |
346 | vortex_wtdma_setmode(chip, dma, 1, fmt, 0, 0); | |
347 | // FIXME: Set rate (i guess using vortex_wt_writereg() somehow). | |
348 | vortex_wtdma_setstartbuffer(chip, dma, 0); | |
349 | } | |
350 | #endif | |
351 | spin_unlock_irq(&chip->lock); | |
352 | return 0; | |
353 | } | |
354 | ||
355 | /* trigger callback */ | |
2fd16874 | 356 | static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |
1da177e4 LT |
357 | { |
358 | vortex_t *chip = snd_pcm_substream_chip(substream); | |
359 | stream_t *stream = (stream_t *) substream->runtime->private_data; | |
360 | int dma = stream->dma; | |
361 | ||
362 | spin_lock(&chip->lock); | |
363 | switch (cmd) { | |
364 | case SNDRV_PCM_TRIGGER_START: | |
365 | // do something to start the PCM engine | |
366 | //printk(KERN_INFO "vortex: start %d\n", dma); | |
367 | stream->fifo_enabled = 1; | |
368 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { | |
369 | vortex_adbdma_resetup(chip, dma); | |
370 | vortex_adbdma_startfifo(chip, dma); | |
371 | } | |
372 | #ifndef CHIP_AU8810 | |
373 | else { | |
70c84418 | 374 | dev_info(chip->card->dev, "wt start %d\n", dma); |
1da177e4 LT |
375 | vortex_wtdma_startfifo(chip, dma); |
376 | } | |
377 | #endif | |
378 | break; | |
379 | case SNDRV_PCM_TRIGGER_STOP: | |
380 | // do something to stop the PCM engine | |
381 | //printk(KERN_INFO "vortex: stop %d\n", dma); | |
382 | stream->fifo_enabled = 0; | |
383 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) | |
fb65c2df | 384 | vortex_adbdma_stopfifo(chip, dma); |
1da177e4 LT |
385 | #ifndef CHIP_AU8810 |
386 | else { | |
70c84418 | 387 | dev_info(chip->card->dev, "wt stop %d\n", dma); |
1da177e4 LT |
388 | vortex_wtdma_stopfifo(chip, dma); |
389 | } | |
390 | #endif | |
391 | break; | |
392 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
393 | //printk(KERN_INFO "vortex: pause %d\n", dma); | |
394 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) | |
395 | vortex_adbdma_pausefifo(chip, dma); | |
396 | #ifndef CHIP_AU8810 | |
397 | else | |
398 | vortex_wtdma_pausefifo(chip, dma); | |
399 | #endif | |
400 | break; | |
401 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
402 | //printk(KERN_INFO "vortex: resume %d\n", dma); | |
403 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) | |
404 | vortex_adbdma_resumefifo(chip, dma); | |
405 | #ifndef CHIP_AU8810 | |
406 | else | |
407 | vortex_wtdma_resumefifo(chip, dma); | |
408 | #endif | |
409 | break; | |
410 | default: | |
411 | spin_unlock(&chip->lock); | |
412 | return -EINVAL; | |
413 | } | |
414 | spin_unlock(&chip->lock); | |
415 | return 0; | |
416 | } | |
417 | ||
418 | /* pointer callback */ | |
2fd16874 | 419 | static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substream) |
1da177e4 LT |
420 | { |
421 | vortex_t *chip = snd_pcm_substream_chip(substream); | |
422 | stream_t *stream = (stream_t *) substream->runtime->private_data; | |
423 | int dma = stream->dma; | |
424 | snd_pcm_uframes_t current_ptr = 0; | |
425 | ||
426 | spin_lock(&chip->lock); | |
427 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) | |
428 | current_ptr = vortex_adbdma_getlinearpos(chip, dma); | |
429 | #ifndef CHIP_AU8810 | |
430 | else | |
431 | current_ptr = vortex_wtdma_getlinearpos(chip, dma); | |
432 | #endif | |
433 | //printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr); | |
434 | spin_unlock(&chip->lock); | |
435 | return (bytes_to_frames(substream->runtime, current_ptr)); | |
436 | } | |
437 | ||
1da177e4 | 438 | /* operators */ |
2fd16874 | 439 | static struct snd_pcm_ops snd_vortex_playback_ops = { |
1da177e4 LT |
440 | .open = snd_vortex_pcm_open, |
441 | .close = snd_vortex_pcm_close, | |
442 | .ioctl = snd_pcm_lib_ioctl, | |
443 | .hw_params = snd_vortex_pcm_hw_params, | |
444 | .hw_free = snd_vortex_pcm_hw_free, | |
445 | .prepare = snd_vortex_pcm_prepare, | |
446 | .trigger = snd_vortex_pcm_trigger, | |
447 | .pointer = snd_vortex_pcm_pointer, | |
448 | .page = snd_pcm_sgbuf_ops_page, | |
449 | }; | |
450 | ||
451 | /* | |
452 | * definitions of capture are omitted here... | |
453 | */ | |
454 | ||
455 | static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = { | |
13eb4ab8 RY |
456 | CARD_NAME " ADB", |
457 | CARD_NAME " SPDIF", | |
458 | CARD_NAME " A3D", | |
459 | CARD_NAME " WT", | |
460 | CARD_NAME " I2S", | |
1da177e4 LT |
461 | }; |
462 | static char *vortex_pcm_name[VORTEX_PCM_LAST] = { | |
463 | "adb", | |
464 | "spdif", | |
465 | "a3d", | |
466 | "wt", | |
467 | "i2s", | |
468 | }; | |
469 | ||
470 | /* SPDIF kcontrol */ | |
471 | ||
2fd16874 | 472 | static int snd_vortex_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
1da177e4 LT |
473 | { |
474 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | |
475 | uinfo->count = 1; | |
476 | return 0; | |
477 | } | |
478 | ||
2fd16874 | 479 | static int snd_vortex_spdif_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 LT |
480 | { |
481 | ucontrol->value.iec958.status[0] = 0xff; | |
482 | ucontrol->value.iec958.status[1] = 0xff; | |
483 | ucontrol->value.iec958.status[2] = 0xff; | |
484 | ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; | |
485 | return 0; | |
486 | } | |
487 | ||
2fd16874 | 488 | static int snd_vortex_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 LT |
489 | { |
490 | vortex_t *vortex = snd_kcontrol_chip(kcontrol); | |
491 | ucontrol->value.iec958.status[0] = 0x00; | |
492 | ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL|IEC958_AES1_CON_DIGDIGCONV_ID; | |
493 | ucontrol->value.iec958.status[2] = 0x00; | |
494 | switch (vortex->spdif_sr) { | |
495 | case 32000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_32000; break; | |
496 | case 44100: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_44100; break; | |
497 | case 48000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; break; | |
498 | } | |
499 | return 0; | |
500 | } | |
501 | ||
2fd16874 | 502 | static int snd_vortex_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
1da177e4 LT |
503 | { |
504 | vortex_t *vortex = snd_kcontrol_chip(kcontrol); | |
505 | int spdif_sr = 48000; | |
506 | switch (ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) { | |
507 | case IEC958_AES3_CON_FS_32000: spdif_sr = 32000; break; | |
508 | case IEC958_AES3_CON_FS_44100: spdif_sr = 44100; break; | |
509 | case IEC958_AES3_CON_FS_48000: spdif_sr = 48000; break; | |
510 | } | |
511 | if (spdif_sr == vortex->spdif_sr) | |
512 | return 0; | |
513 | vortex->spdif_sr = spdif_sr; | |
514 | vortex_spdif_init(vortex, vortex->spdif_sr, 1); | |
515 | return 1; | |
516 | } | |
517 | ||
518 | /* spdif controls */ | |
e23e7a14 | 519 | static struct snd_kcontrol_new snd_vortex_mixer_spdif[] = { |
1da177e4 LT |
520 | { |
521 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
522 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | |
523 | .info = snd_vortex_spdif_info, | |
524 | .get = snd_vortex_spdif_get, | |
525 | .put = snd_vortex_spdif_put, | |
526 | }, | |
527 | { | |
528 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | |
529 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
530 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), | |
531 | .info = snd_vortex_spdif_info, | |
532 | .get = snd_vortex_spdif_mask_get | |
533 | }, | |
534 | }; | |
535 | ||
bb92b7c4 RY |
536 | /* subdevice PCM Volume control */ |
537 | ||
538 | static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol, | |
539 | struct snd_ctl_elem_info *uinfo) | |
540 | { | |
541 | vortex_t *vortex = snd_kcontrol_chip(kcontrol); | |
542 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
543 | uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2); | |
544 | uinfo->value.integer.min = -128; | |
545 | uinfo->value.integer.max = 32; | |
546 | return 0; | |
547 | } | |
548 | ||
549 | static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol, | |
550 | struct snd_ctl_elem_value *ucontrol) | |
551 | { | |
552 | int i; | |
553 | vortex_t *vortex = snd_kcontrol_chip(kcontrol); | |
554 | int subdev = kcontrol->id.subdevice; | |
555 | struct pcm_vol *p = &vortex->pcm_vol[subdev]; | |
556 | int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2); | |
557 | for (i = 0; i < max_chn; i++) | |
558 | ucontrol->value.integer.value[i] = p->vol[i]; | |
559 | return 0; | |
560 | } | |
561 | ||
562 | static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol, | |
563 | struct snd_ctl_elem_value *ucontrol) | |
564 | { | |
565 | int i; | |
566 | int changed = 0; | |
567 | int mixin; | |
568 | unsigned char vol; | |
569 | vortex_t *vortex = snd_kcontrol_chip(kcontrol); | |
570 | int subdev = kcontrol->id.subdevice; | |
571 | struct pcm_vol *p = &vortex->pcm_vol[subdev]; | |
572 | int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2); | |
573 | for (i = 0; i < max_chn; i++) { | |
574 | if (p->vol[i] != ucontrol->value.integer.value[i]) { | |
575 | p->vol[i] = ucontrol->value.integer.value[i]; | |
576 | if (p->active) { | |
577 | switch (vortex->dma_adb[p->dma].nr_ch) { | |
578 | case 1: | |
579 | mixin = p->mixin[0]; | |
580 | break; | |
581 | case 2: | |
582 | default: | |
583 | mixin = p->mixin[(i < 2) ? i : (i - 2)]; | |
584 | break; | |
585 | case 4: | |
586 | mixin = p->mixin[i]; | |
587 | break; | |
395d9dd5 | 588 | } |
bb92b7c4 RY |
589 | vol = p->vol[i]; |
590 | vortex_mix_setinputvolumebyte(vortex, | |
591 | vortex->mixplayb[i], mixin, vol); | |
592 | } | |
593 | changed = 1; | |
594 | } | |
595 | } | |
596 | return changed; | |
597 | } | |
598 | ||
599 | static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400); | |
600 | ||
e23e7a14 | 601 | static struct snd_kcontrol_new snd_vortex_pcm_vol = { |
bb92b7c4 RY |
602 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
603 | .name = "PCM Playback Volume", | |
604 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | |
605 | SNDRV_CTL_ELEM_ACCESS_TLV_READ | | |
606 | SNDRV_CTL_ELEM_ACCESS_INACTIVE, | |
607 | .info = snd_vortex_pcm_vol_info, | |
608 | .get = snd_vortex_pcm_vol_get, | |
609 | .put = snd_vortex_pcm_vol_put, | |
610 | .tlv = { .p = vortex_pcm_vol_db_scale }, | |
611 | }; | |
612 | ||
1da177e4 | 613 | /* create a pcm device */ |
e23e7a14 | 614 | static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) |
1da177e4 | 615 | { |
2fd16874 TI |
616 | struct snd_pcm *pcm; |
617 | struct snd_kcontrol *kctl; | |
1da177e4 LT |
618 | int i; |
619 | int err, nr_capt; | |
620 | ||
3fa4a907 | 621 | if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST) |
1da177e4 LT |
622 | return -ENODEV; |
623 | ||
624 | /* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the | |
25985edc | 625 | * same dma engine. WT uses it own separate dma engine which can't capture. */ |
1da177e4 LT |
626 | if (idx == VORTEX_PCM_ADB) |
627 | nr_capt = nr; | |
628 | else | |
629 | nr_capt = 0; | |
3fa4a907 HH |
630 | err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, |
631 | nr_capt, &pcm); | |
632 | if (err < 0) | |
1da177e4 | 633 | return err; |
13eb4ab8 RY |
634 | snprintf(pcm->name, sizeof(pcm->name), |
635 | "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]); | |
1da177e4 LT |
636 | chip->pcm[idx] = pcm; |
637 | // This is an evil hack, but it saves a lot of duplicated code. | |
638 | VORTEX_PCM_TYPE(pcm) = idx; | |
639 | pcm->private_data = chip; | |
640 | /* set operators */ | |
641 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | |
642 | &snd_vortex_playback_ops); | |
643 | if (idx == VORTEX_PCM_ADB) | |
644 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
645 | &snd_vortex_playback_ops); | |
646 | ||
647 | /* pre-allocation of Scatter-Gather buffers */ | |
648 | ||
649 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, | |
650 | snd_dma_pci_data(chip->pci_dev), | |
651 | 0x10000, 0x10000); | |
652 | ||
f3c90242 RY |
653 | switch (VORTEX_PCM_TYPE(pcm)) { |
654 | case VORTEX_PCM_ADB: | |
655 | err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, | |
656 | snd_pcm_std_chmaps, | |
657 | VORTEX_IS_QUAD(chip) ? 4 : 2, | |
658 | 0, NULL); | |
659 | if (err < 0) | |
660 | return err; | |
661 | err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
662 | snd_pcm_std_chmaps, 2, 0, NULL); | |
663 | if (err < 0) | |
664 | return err; | |
665 | break; | |
666 | #ifdef CHIP_AU8830 | |
667 | case VORTEX_PCM_A3D: | |
668 | err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, | |
669 | snd_pcm_std_chmaps, 1, 0, NULL); | |
670 | if (err < 0) | |
671 | return err; | |
672 | break; | |
673 | #endif | |
1d198f26 | 674 | } |
f3c90242 | 675 | |
1da177e4 LT |
676 | if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) { |
677 | for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) { | |
678 | kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip); | |
679 | if (!kctl) | |
680 | return -ENOMEM; | |
681 | if ((err = snd_ctl_add(chip->card, kctl)) < 0) | |
682 | return err; | |
683 | } | |
684 | } | |
bb92b7c4 RY |
685 | if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) { |
686 | for (i = 0; i < NR_PCM; i++) { | |
687 | chip->pcm_vol[i].active = 0; | |
688 | chip->pcm_vol[i].dma = -1; | |
689 | kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip); | |
690 | if (!kctl) | |
691 | return -ENOMEM; | |
692 | chip->pcm_vol[i].kctl = kctl; | |
693 | kctl->id.device = 0; | |
694 | kctl->id.subdevice = i; | |
695 | err = snd_ctl_add(chip->card, kctl); | |
696 | if (err < 0) | |
697 | return err; | |
698 | } | |
699 | } | |
1da177e4 LT |
700 | return 0; |
701 | } |