]>
Commit | Line | Data |
---|---|---|
d0ce9946 CL |
1 | /* |
2 | * C-Media CMI8788 driver - PCM code | |
3 | * | |
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | * | |
6 | * | |
7 | * This driver is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License, version 2. | |
9 | * | |
10 | * This driver is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this driver; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | ||
d0ce9946 CL |
20 | #include <linux/pci.h> |
21 | #include <sound/control.h> | |
22 | #include <sound/core.h> | |
23 | #include <sound/pcm.h> | |
24 | #include <sound/pcm_params.h> | |
25 | #include "oxygen.h" | |
26 | ||
c57cccc0 CL |
27 | static const struct snd_pcm_hardware oxygen_stereo_hardware = { |
28 | .info = SNDRV_PCM_INFO_MMAP | | |
29 | SNDRV_PCM_INFO_MMAP_VALID | | |
30 | SNDRV_PCM_INFO_INTERLEAVED | | |
31 | SNDRV_PCM_INFO_PAUSE | | |
32 | SNDRV_PCM_INFO_SYNC_START, | |
33 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
34 | SNDRV_PCM_FMTBIT_S32_LE, | |
35 | .rates = SNDRV_PCM_RATE_32000 | | |
36 | SNDRV_PCM_RATE_44100 | | |
37 | SNDRV_PCM_RATE_48000 | | |
38 | SNDRV_PCM_RATE_64000 | | |
39 | SNDRV_PCM_RATE_88200 | | |
40 | SNDRV_PCM_RATE_96000 | | |
41 | SNDRV_PCM_RATE_176400 | | |
42 | SNDRV_PCM_RATE_192000, | |
43 | .rate_min = 32000, | |
44 | .rate_max = 192000, | |
45 | .channels_min = 2, | |
46 | .channels_max = 2, | |
47 | .buffer_bytes_max = 256 * 1024, | |
48 | .period_bytes_min = 128, | |
49 | .period_bytes_max = 128 * 1024, | |
50 | .periods_min = 2, | |
51 | .periods_max = 2048, | |
52 | }; | |
53 | static const struct snd_pcm_hardware oxygen_multichannel_hardware = { | |
54 | .info = SNDRV_PCM_INFO_MMAP | | |
55 | SNDRV_PCM_INFO_MMAP_VALID | | |
56 | SNDRV_PCM_INFO_INTERLEAVED | | |
57 | SNDRV_PCM_INFO_PAUSE | | |
58 | SNDRV_PCM_INFO_SYNC_START, | |
59 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
60 | SNDRV_PCM_FMTBIT_S32_LE, | |
61 | .rates = SNDRV_PCM_RATE_32000 | | |
62 | SNDRV_PCM_RATE_44100 | | |
63 | SNDRV_PCM_RATE_48000 | | |
64 | SNDRV_PCM_RATE_64000 | | |
65 | SNDRV_PCM_RATE_88200 | | |
66 | SNDRV_PCM_RATE_96000 | | |
67 | SNDRV_PCM_RATE_176400 | | |
68 | SNDRV_PCM_RATE_192000, | |
69 | .rate_min = 32000, | |
70 | .rate_max = 192000, | |
71 | .channels_min = 2, | |
72 | .channels_max = 8, | |
73 | .buffer_bytes_max = 2048 * 1024, | |
74 | .period_bytes_min = 128, | |
75 | .period_bytes_max = 256 * 1024, | |
76 | .periods_min = 2, | |
77 | .periods_max = 16384, | |
78 | }; | |
79 | static const struct snd_pcm_hardware oxygen_ac97_hardware = { | |
80 | .info = SNDRV_PCM_INFO_MMAP | | |
81 | SNDRV_PCM_INFO_MMAP_VALID | | |
82 | SNDRV_PCM_INFO_INTERLEAVED | | |
83 | SNDRV_PCM_INFO_PAUSE | | |
84 | SNDRV_PCM_INFO_SYNC_START, | |
85 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
86 | .rates = SNDRV_PCM_RATE_48000, | |
87 | .rate_min = 48000, | |
88 | .rate_max = 48000, | |
89 | .channels_min = 2, | |
90 | .channels_max = 2, | |
91 | .buffer_bytes_max = 256 * 1024, | |
92 | .period_bytes_min = 128, | |
93 | .period_bytes_max = 128 * 1024, | |
94 | .periods_min = 2, | |
95 | .periods_max = 2048, | |
96 | }; | |
97 | ||
98 | static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = { | |
99 | [PCM_A] = &oxygen_stereo_hardware, | |
100 | [PCM_B] = &oxygen_stereo_hardware, | |
101 | [PCM_C] = &oxygen_stereo_hardware, | |
102 | [PCM_SPDIF] = &oxygen_stereo_hardware, | |
103 | [PCM_MULTICH] = &oxygen_multichannel_hardware, | |
104 | [PCM_AC97] = &oxygen_ac97_hardware, | |
d0ce9946 CL |
105 | }; |
106 | ||
740eb835 CL |
107 | static inline unsigned int |
108 | oxygen_substream_channel(struct snd_pcm_substream *substream) | |
109 | { | |
110 | return (unsigned int)(uintptr_t)substream->runtime->private_data; | |
111 | } | |
112 | ||
d0ce9946 CL |
113 | static int oxygen_open(struct snd_pcm_substream *substream, |
114 | unsigned int channel) | |
115 | { | |
116 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
117 | struct snd_pcm_runtime *runtime = substream->runtime; | |
118 | int err; | |
119 | ||
740eb835 | 120 | runtime->private_data = (void *)(uintptr_t)channel; |
5f7b9b45 CL |
121 | if (channel == PCM_B && chip->has_ac97_1 && |
122 | (chip->model->used_channels & OXYGEN_CHANNEL_AC97)) | |
123 | runtime->hw = oxygen_ac97_hardware; | |
124 | else | |
125 | runtime->hw = *oxygen_hardware[channel]; | |
976cd627 CL |
126 | switch (channel) { |
127 | case PCM_C: | |
33c646e4 CL |
128 | runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 | |
129 | SNDRV_PCM_RATE_64000); | |
130 | runtime->hw.rate_min = 44100; | |
976cd627 CL |
131 | break; |
132 | case PCM_MULTICH: | |
133 | runtime->hw.channels_max = chip->model->dac_channels; | |
134 | break; | |
33c646e4 | 135 | } |
747c6016 CL |
136 | if (chip->model->pcm_hardware_filter) |
137 | chip->model->pcm_hardware_filter(channel, &runtime->hw); | |
d0ce9946 CL |
138 | err = snd_pcm_hw_constraint_step(runtime, 0, |
139 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); | |
140 | if (err < 0) | |
141 | return err; | |
142 | err = snd_pcm_hw_constraint_step(runtime, 0, | |
143 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); | |
144 | if (err < 0) | |
145 | return err; | |
146 | if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) { | |
147 | err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); | |
148 | if (err < 0) | |
149 | return err; | |
150 | } | |
151 | if (runtime->hw.channels_max > 2) { | |
152 | err = snd_pcm_hw_constraint_step(runtime, 0, | |
153 | SNDRV_PCM_HW_PARAM_CHANNELS, | |
154 | 2); | |
155 | if (err < 0) | |
156 | return err; | |
157 | } | |
158 | snd_pcm_set_sync(substream); | |
159 | chip->streams[channel] = substream; | |
160 | ||
161 | mutex_lock(&chip->mutex); | |
162 | chip->pcm_active |= 1 << channel; | |
163 | if (channel == PCM_SPDIF) { | |
164 | chip->spdif_pcm_bits = chip->spdif_bits; | |
01a3affb | 165 | chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &= |
d0ce9946 CL |
166 | ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; |
167 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | | |
168 | SNDRV_CTL_EVENT_MASK_INFO, | |
01a3affb | 169 | &chip->controls[CONTROL_SPDIF_PCM]->id); |
d0ce9946 CL |
170 | } |
171 | mutex_unlock(&chip->mutex); | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static int oxygen_rec_a_open(struct snd_pcm_substream *substream) | |
177 | { | |
178 | return oxygen_open(substream, PCM_A); | |
179 | } | |
180 | ||
181 | static int oxygen_rec_b_open(struct snd_pcm_substream *substream) | |
182 | { | |
183 | return oxygen_open(substream, PCM_B); | |
184 | } | |
185 | ||
186 | static int oxygen_rec_c_open(struct snd_pcm_substream *substream) | |
187 | { | |
188 | return oxygen_open(substream, PCM_C); | |
189 | } | |
190 | ||
191 | static int oxygen_spdif_open(struct snd_pcm_substream *substream) | |
192 | { | |
193 | return oxygen_open(substream, PCM_SPDIF); | |
194 | } | |
195 | ||
196 | static int oxygen_multich_open(struct snd_pcm_substream *substream) | |
197 | { | |
198 | return oxygen_open(substream, PCM_MULTICH); | |
199 | } | |
200 | ||
201 | static int oxygen_ac97_open(struct snd_pcm_substream *substream) | |
202 | { | |
203 | return oxygen_open(substream, PCM_AC97); | |
204 | } | |
205 | ||
206 | static int oxygen_close(struct snd_pcm_substream *substream) | |
207 | { | |
208 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
740eb835 | 209 | unsigned int channel = oxygen_substream_channel(substream); |
d0ce9946 CL |
210 | |
211 | mutex_lock(&chip->mutex); | |
212 | chip->pcm_active &= ~(1 << channel); | |
213 | if (channel == PCM_SPDIF) { | |
01a3affb | 214 | chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |= |
d0ce9946 CL |
215 | SNDRV_CTL_ELEM_ACCESS_INACTIVE; |
216 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | | |
217 | SNDRV_CTL_EVENT_MASK_INFO, | |
01a3affb | 218 | &chip->controls[CONTROL_SPDIF_PCM]->id); |
d0ce9946 CL |
219 | } |
220 | if (channel == PCM_SPDIF || channel == PCM_MULTICH) | |
221 | oxygen_update_spdif_source(chip); | |
222 | mutex_unlock(&chip->mutex); | |
223 | ||
224 | chip->streams[channel] = NULL; | |
225 | return 0; | |
226 | } | |
227 | ||
228 | static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params) | |
229 | { | |
230 | if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) | |
231 | return OXYGEN_FORMAT_24; | |
232 | else | |
233 | return OXYGEN_FORMAT_16; | |
234 | } | |
235 | ||
236 | static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params) | |
237 | { | |
238 | switch (params_rate(hw_params)) { | |
239 | case 32000: | |
240 | return OXYGEN_RATE_32000; | |
241 | case 44100: | |
242 | return OXYGEN_RATE_44100; | |
243 | default: /* 48000 */ | |
244 | return OXYGEN_RATE_48000; | |
245 | case 64000: | |
246 | return OXYGEN_RATE_64000; | |
247 | case 88200: | |
248 | return OXYGEN_RATE_88200; | |
249 | case 96000: | |
250 | return OXYGEN_RATE_96000; | |
251 | case 176400: | |
252 | return OXYGEN_RATE_176400; | |
253 | case 192000: | |
254 | return OXYGEN_RATE_192000; | |
255 | } | |
256 | } | |
257 | ||
c2353a08 | 258 | static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params) |
d0ce9946 | 259 | { |
b78e3dbb CL |
260 | if (params_rate(hw_params) <= 96000) |
261 | return OXYGEN_I2S_MCLK_256; | |
262 | else | |
263 | return OXYGEN_I2S_MCLK_128; | |
d0ce9946 CL |
264 | } |
265 | ||
05855ba3 | 266 | static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params) |
d0ce9946 CL |
267 | { |
268 | if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) | |
05855ba3 | 269 | return OXYGEN_I2S_BITS_24; |
d0ce9946 | 270 | else |
05855ba3 | 271 | return OXYGEN_I2S_BITS_16; |
d0ce9946 CL |
272 | } |
273 | ||
274 | static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params) | |
275 | { | |
276 | switch (params_channels(hw_params)) { | |
277 | default: /* 2 */ | |
278 | return OXYGEN_PLAY_CHANNELS_2; | |
279 | case 4: | |
280 | return OXYGEN_PLAY_CHANNELS_4; | |
281 | case 6: | |
282 | return OXYGEN_PLAY_CHANNELS_6; | |
283 | case 8: | |
284 | return OXYGEN_PLAY_CHANNELS_8; | |
285 | } | |
286 | } | |
287 | ||
288 | static const unsigned int channel_base_registers[PCM_COUNT] = { | |
289 | [PCM_A] = OXYGEN_DMA_A_ADDRESS, | |
290 | [PCM_B] = OXYGEN_DMA_B_ADDRESS, | |
291 | [PCM_C] = OXYGEN_DMA_C_ADDRESS, | |
292 | [PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS, | |
293 | [PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS, | |
294 | [PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS, | |
295 | }; | |
296 | ||
297 | static int oxygen_hw_params(struct snd_pcm_substream *substream, | |
298 | struct snd_pcm_hw_params *hw_params) | |
299 | { | |
300 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
740eb835 | 301 | unsigned int channel = oxygen_substream_channel(substream); |
d0ce9946 CL |
302 | int err; |
303 | ||
304 | err = snd_pcm_lib_malloc_pages(substream, | |
305 | params_buffer_bytes(hw_params)); | |
306 | if (err < 0) | |
307 | return err; | |
308 | ||
309 | oxygen_write32(chip, channel_base_registers[channel], | |
310 | (u32)substream->runtime->dma_addr); | |
311 | if (channel == PCM_MULTICH) { | |
312 | oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT, | |
313 | params_buffer_bytes(hw_params) / 4 - 1); | |
314 | oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT, | |
315 | params_period_bytes(hw_params) / 4 - 1); | |
316 | } else { | |
317 | oxygen_write16(chip, channel_base_registers[channel] + 4, | |
318 | params_buffer_bytes(hw_params) / 4 - 1); | |
319 | oxygen_write16(chip, channel_base_registers[channel] + 6, | |
320 | params_period_bytes(hw_params) / 4 - 1); | |
321 | } | |
322 | return 0; | |
323 | } | |
324 | ||
325 | static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream, | |
326 | struct snd_pcm_hw_params *hw_params) | |
327 | { | |
328 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
329 | int err; | |
330 | ||
331 | err = oxygen_hw_params(substream, hw_params); | |
332 | if (err < 0) | |
333 | return err; | |
334 | ||
335 | spin_lock_irq(&chip->reg_lock); | |
336 | oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, | |
337 | oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT, | |
338 | OXYGEN_REC_FORMAT_A_MASK); | |
05855ba3 CL |
339 | oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, |
340 | oxygen_rate(hw_params) | | |
c2353a08 | 341 | oxygen_i2s_mclk(hw_params) | |
05855ba3 CL |
342 | chip->model->adc_i2s_format | |
343 | oxygen_i2s_bits(hw_params), | |
344 | OXYGEN_I2S_RATE_MASK | | |
345 | OXYGEN_I2S_FORMAT_MASK | | |
c2353a08 | 346 | OXYGEN_I2S_MCLK_MASK | |
05855ba3 | 347 | OXYGEN_I2S_BITS_MASK); |
d0ce9946 CL |
348 | spin_unlock_irq(&chip->reg_lock); |
349 | ||
350 | mutex_lock(&chip->mutex); | |
351 | chip->model->set_adc_params(chip, hw_params); | |
352 | mutex_unlock(&chip->mutex); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, | |
357 | struct snd_pcm_hw_params *hw_params) | |
358 | { | |
359 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
5f7b9b45 | 360 | int is_ac97; |
d0ce9946 CL |
361 | int err; |
362 | ||
363 | err = oxygen_hw_params(substream, hw_params); | |
364 | if (err < 0) | |
365 | return err; | |
366 | ||
5f7b9b45 CL |
367 | is_ac97 = chip->has_ac97_1 && |
368 | (chip->model->used_channels & OXYGEN_CHANNEL_AC97); | |
369 | ||
d0ce9946 CL |
370 | spin_lock_irq(&chip->reg_lock); |
371 | oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, | |
372 | oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT, | |
373 | OXYGEN_REC_FORMAT_B_MASK); | |
5f7b9b45 CL |
374 | if (!is_ac97) |
375 | oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, | |
376 | oxygen_rate(hw_params) | | |
377 | oxygen_i2s_mclk(hw_params) | | |
378 | chip->model->adc_i2s_format | | |
379 | oxygen_i2s_bits(hw_params), | |
380 | OXYGEN_I2S_RATE_MASK | | |
381 | OXYGEN_I2S_FORMAT_MASK | | |
382 | OXYGEN_I2S_MCLK_MASK | | |
383 | OXYGEN_I2S_BITS_MASK); | |
d0ce9946 CL |
384 | spin_unlock_irq(&chip->reg_lock); |
385 | ||
5f7b9b45 CL |
386 | if (!is_ac97) { |
387 | mutex_lock(&chip->mutex); | |
388 | chip->model->set_adc_params(chip, hw_params); | |
389 | mutex_unlock(&chip->mutex); | |
390 | } | |
d0ce9946 CL |
391 | return 0; |
392 | } | |
393 | ||
394 | static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream, | |
395 | struct snd_pcm_hw_params *hw_params) | |
396 | { | |
397 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
398 | int err; | |
399 | ||
400 | err = oxygen_hw_params(substream, hw_params); | |
401 | if (err < 0) | |
402 | return err; | |
403 | ||
404 | spin_lock_irq(&chip->reg_lock); | |
405 | oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, | |
406 | oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT, | |
407 | OXYGEN_REC_FORMAT_C_MASK); | |
d0ce9946 CL |
408 | spin_unlock_irq(&chip->reg_lock); |
409 | return 0; | |
410 | } | |
411 | ||
412 | static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, | |
413 | struct snd_pcm_hw_params *hw_params) | |
414 | { | |
415 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
416 | int err; | |
417 | ||
418 | err = oxygen_hw_params(substream, hw_params); | |
419 | if (err < 0) | |
420 | return err; | |
421 | ||
422 | spin_lock_irq(&chip->reg_lock); | |
423 | oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, | |
424 | OXYGEN_SPDIF_OUT_ENABLE); | |
425 | oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT, | |
426 | oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT, | |
427 | OXYGEN_SPDIF_FORMAT_MASK); | |
428 | oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, | |
429 | oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT, | |
430 | OXYGEN_SPDIF_OUT_RATE_MASK); | |
431 | oxygen_update_spdif_source(chip); | |
432 | spin_unlock_irq(&chip->reg_lock); | |
433 | return 0; | |
434 | } | |
435 | ||
436 | static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, | |
437 | struct snd_pcm_hw_params *hw_params) | |
438 | { | |
439 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
440 | int err; | |
441 | ||
442 | err = oxygen_hw_params(substream, hw_params); | |
443 | if (err < 0) | |
444 | return err; | |
445 | ||
446 | spin_lock_irq(&chip->reg_lock); | |
447 | oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS, | |
448 | oxygen_play_channels(hw_params), | |
449 | OXYGEN_PLAY_CHANNELS_MASK); | |
450 | oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT, | |
451 | oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT, | |
452 | OXYGEN_MULTICH_FORMAT_MASK); | |
453 | oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, | |
05855ba3 CL |
454 | oxygen_rate(hw_params) | |
455 | chip->model->dac_i2s_format | | |
456 | oxygen_i2s_bits(hw_params), | |
457 | OXYGEN_I2S_RATE_MASK | | |
458 | OXYGEN_I2S_FORMAT_MASK | | |
459 | OXYGEN_I2S_BITS_MASK); | |
d0ce9946 CL |
460 | oxygen_update_dac_routing(chip); |
461 | oxygen_update_spdif_source(chip); | |
462 | spin_unlock_irq(&chip->reg_lock); | |
463 | ||
464 | mutex_lock(&chip->mutex); | |
465 | chip->model->set_dac_params(chip, hw_params); | |
466 | mutex_unlock(&chip->mutex); | |
467 | return 0; | |
468 | } | |
469 | ||
d0ce9946 CL |
470 | static int oxygen_hw_free(struct snd_pcm_substream *substream) |
471 | { | |
472 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
740eb835 | 473 | unsigned int channel = oxygen_substream_channel(substream); |
d0ce9946 CL |
474 | |
475 | spin_lock_irq(&chip->reg_lock); | |
476 | chip->interrupt_mask &= ~(1 << channel); | |
477 | oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); | |
478 | spin_unlock_irq(&chip->reg_lock); | |
479 | ||
480 | return snd_pcm_lib_free_pages(substream); | |
481 | } | |
482 | ||
483 | static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream) | |
484 | { | |
485 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
486 | ||
487 | spin_lock_irq(&chip->reg_lock); | |
488 | oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, | |
489 | OXYGEN_SPDIF_OUT_ENABLE); | |
490 | spin_unlock_irq(&chip->reg_lock); | |
491 | return oxygen_hw_free(substream); | |
492 | } | |
493 | ||
494 | static int oxygen_prepare(struct snd_pcm_substream *substream) | |
495 | { | |
496 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
740eb835 | 497 | unsigned int channel = oxygen_substream_channel(substream); |
d0ce9946 CL |
498 | unsigned int channel_mask = 1 << channel; |
499 | ||
500 | spin_lock_irq(&chip->reg_lock); | |
501 | oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); | |
502 | oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); | |
503 | ||
504 | chip->interrupt_mask |= channel_mask; | |
505 | oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); | |
506 | spin_unlock_irq(&chip->reg_lock); | |
507 | return 0; | |
508 | } | |
509 | ||
510 | static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd) | |
511 | { | |
512 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
513 | struct snd_pcm_substream *s; | |
514 | unsigned int mask = 0; | |
db2396d4 | 515 | int pausing; |
d0ce9946 CL |
516 | |
517 | switch (cmd) { | |
518 | case SNDRV_PCM_TRIGGER_STOP: | |
d0ce9946 | 519 | case SNDRV_PCM_TRIGGER_START: |
db2396d4 CL |
520 | pausing = 0; |
521 | break; | |
522 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
d0ce9946 | 523 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
db2396d4 | 524 | pausing = 1; |
d0ce9946 CL |
525 | break; |
526 | default: | |
527 | return -EINVAL; | |
528 | } | |
529 | ||
530 | snd_pcm_group_for_each_entry(s, substream) { | |
531 | if (snd_pcm_substream_chip(s) == chip) { | |
740eb835 | 532 | mask |= 1 << oxygen_substream_channel(s); |
d0ce9946 CL |
533 | snd_pcm_trigger_done(s, substream); |
534 | } | |
535 | } | |
536 | ||
537 | spin_lock(&chip->reg_lock); | |
db2396d4 CL |
538 | if (!pausing) { |
539 | if (cmd == SNDRV_PCM_TRIGGER_START) | |
540 | chip->pcm_running |= mask; | |
541 | else | |
542 | chip->pcm_running &= ~mask; | |
543 | oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running); | |
544 | } else { | |
545 | if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) | |
546 | oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask); | |
547 | else | |
548 | oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask); | |
549 | } | |
d0ce9946 CL |
550 | spin_unlock(&chip->reg_lock); |
551 | return 0; | |
552 | } | |
553 | ||
554 | static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream) | |
555 | { | |
556 | struct oxygen *chip = snd_pcm_substream_chip(substream); | |
557 | struct snd_pcm_runtime *runtime = substream->runtime; | |
740eb835 | 558 | unsigned int channel = oxygen_substream_channel(substream); |
d0ce9946 CL |
559 | u32 curr_addr; |
560 | ||
561 | /* no spinlock, this read should be atomic */ | |
562 | curr_addr = oxygen_read32(chip, channel_base_registers[channel]); | |
563 | return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr); | |
564 | } | |
565 | ||
566 | static struct snd_pcm_ops oxygen_rec_a_ops = { | |
567 | .open = oxygen_rec_a_open, | |
568 | .close = oxygen_close, | |
569 | .ioctl = snd_pcm_lib_ioctl, | |
570 | .hw_params = oxygen_rec_a_hw_params, | |
571 | .hw_free = oxygen_hw_free, | |
572 | .prepare = oxygen_prepare, | |
573 | .trigger = oxygen_trigger, | |
574 | .pointer = oxygen_pointer, | |
575 | }; | |
576 | ||
577 | static struct snd_pcm_ops oxygen_rec_b_ops = { | |
578 | .open = oxygen_rec_b_open, | |
579 | .close = oxygen_close, | |
580 | .ioctl = snd_pcm_lib_ioctl, | |
581 | .hw_params = oxygen_rec_b_hw_params, | |
582 | .hw_free = oxygen_hw_free, | |
583 | .prepare = oxygen_prepare, | |
584 | .trigger = oxygen_trigger, | |
585 | .pointer = oxygen_pointer, | |
586 | }; | |
587 | ||
588 | static struct snd_pcm_ops oxygen_rec_c_ops = { | |
589 | .open = oxygen_rec_c_open, | |
590 | .close = oxygen_close, | |
591 | .ioctl = snd_pcm_lib_ioctl, | |
592 | .hw_params = oxygen_rec_c_hw_params, | |
593 | .hw_free = oxygen_hw_free, | |
594 | .prepare = oxygen_prepare, | |
595 | .trigger = oxygen_trigger, | |
596 | .pointer = oxygen_pointer, | |
597 | }; | |
598 | ||
599 | static struct snd_pcm_ops oxygen_spdif_ops = { | |
600 | .open = oxygen_spdif_open, | |
601 | .close = oxygen_close, | |
602 | .ioctl = snd_pcm_lib_ioctl, | |
603 | .hw_params = oxygen_spdif_hw_params, | |
604 | .hw_free = oxygen_spdif_hw_free, | |
605 | .prepare = oxygen_prepare, | |
606 | .trigger = oxygen_trigger, | |
607 | .pointer = oxygen_pointer, | |
608 | }; | |
609 | ||
610 | static struct snd_pcm_ops oxygen_multich_ops = { | |
611 | .open = oxygen_multich_open, | |
612 | .close = oxygen_close, | |
613 | .ioctl = snd_pcm_lib_ioctl, | |
614 | .hw_params = oxygen_multich_hw_params, | |
615 | .hw_free = oxygen_hw_free, | |
616 | .prepare = oxygen_prepare, | |
617 | .trigger = oxygen_trigger, | |
618 | .pointer = oxygen_pointer, | |
619 | }; | |
620 | ||
621 | static struct snd_pcm_ops oxygen_ac97_ops = { | |
622 | .open = oxygen_ac97_open, | |
623 | .close = oxygen_close, | |
624 | .ioctl = snd_pcm_lib_ioctl, | |
c2353a08 | 625 | .hw_params = oxygen_hw_params, |
d0ce9946 CL |
626 | .hw_free = oxygen_hw_free, |
627 | .prepare = oxygen_prepare, | |
628 | .trigger = oxygen_trigger, | |
629 | .pointer = oxygen_pointer, | |
630 | }; | |
631 | ||
632 | static void oxygen_pcm_free(struct snd_pcm *pcm) | |
633 | { | |
634 | snd_pcm_lib_preallocate_free_for_all(pcm); | |
635 | } | |
636 | ||
f007dc04 | 637 | int oxygen_pcm_init(struct oxygen *chip) |
d0ce9946 CL |
638 | { |
639 | struct snd_pcm *pcm; | |
e85e0925 | 640 | int outs, ins; |
d0ce9946 CL |
641 | int err; |
642 | ||
e85e0925 CL |
643 | outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */ |
644 | ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A | | |
645 | OXYGEN_CHANNEL_B)); | |
646 | err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); | |
d0ce9946 CL |
647 | if (err < 0) |
648 | return err; | |
649 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops); | |
e85e0925 CL |
650 | if (chip->model->used_channels & OXYGEN_CHANNEL_A) |
651 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
652 | &oxygen_rec_a_ops); | |
653 | else if (chip->model->used_channels & OXYGEN_CHANNEL_B) | |
654 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
655 | &oxygen_rec_b_ops); | |
d0ce9946 CL |
656 | pcm->private_data = chip; |
657 | pcm->private_free = oxygen_pcm_free; | |
658 | strcpy(pcm->name, "Analog"); | |
659 | snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, | |
660 | SNDRV_DMA_TYPE_DEV, | |
661 | snd_dma_pci_data(chip->pci), | |
662 | 512 * 1024, 2048 * 1024); | |
e85e0925 CL |
663 | if (ins) |
664 | snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, | |
665 | SNDRV_DMA_TYPE_DEV, | |
d0ce9946 CL |
666 | snd_dma_pci_data(chip->pci), |
667 | 128 * 1024, 256 * 1024); | |
668 | ||
e85e0925 CL |
669 | outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF); |
670 | ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); | |
671 | if (outs | ins) { | |
672 | err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm); | |
673 | if (err < 0) | |
674 | return err; | |
675 | if (outs) | |
676 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | |
677 | &oxygen_spdif_ops); | |
678 | if (ins) | |
679 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
680 | &oxygen_rec_c_ops); | |
681 | pcm->private_data = chip; | |
682 | pcm->private_free = oxygen_pcm_free; | |
683 | strcpy(pcm->name, "Digital"); | |
684 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | |
685 | snd_dma_pci_data(chip->pci), | |
686 | 128 * 1024, 256 * 1024); | |
687 | } | |
688 | ||
689 | outs = chip->has_ac97_1 && | |
690 | (chip->model->used_channels & OXYGEN_CHANNEL_AC97); | |
5f7b9b45 CL |
691 | ins = outs || |
692 | (chip->model->used_channels & (OXYGEN_CHANNEL_A | | |
693 | OXYGEN_CHANNEL_B)) | |
e85e0925 CL |
694 | == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B); |
695 | if (outs | ins) { | |
5f7b9b45 | 696 | err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", |
e85e0925 | 697 | 2, outs, ins, &pcm); |
d0ce9946 CL |
698 | if (err < 0) |
699 | return err; | |
5f7b9b45 | 700 | if (outs) { |
e85e0925 CL |
701 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, |
702 | &oxygen_ac97_ops); | |
5f7b9b45 CL |
703 | oxygen_write8_masked(chip, OXYGEN_REC_ROUTING, |
704 | OXYGEN_REC_B_ROUTE_AC97_1, | |
705 | OXYGEN_REC_B_ROUTE_MASK); | |
706 | } | |
e85e0925 CL |
707 | if (ins) |
708 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
709 | &oxygen_rec_b_ops); | |
d0ce9946 CL |
710 | pcm->private_data = chip; |
711 | pcm->private_free = oxygen_pcm_free; | |
5f7b9b45 | 712 | strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); |
d0ce9946 CL |
713 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, |
714 | snd_dma_pci_data(chip->pci), | |
715 | 128 * 1024, 256 * 1024); | |
716 | } | |
717 | return 0; | |
718 | } |