diff options
author | Raymond Yau <superquad.vortex2@gmail.com> | 2012-01-16 22:32:17 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-01-24 09:25:08 -0500 |
commit | bb92b7c4ed4f7d5102bb1623cc8a1a9960ddfc08 (patch) | |
tree | d92a7641af16f2b7952a4c091756e067650c11b2 /sound/pci/au88x0 | |
parent | dcd6c92267155e70a94b3927bce681ce74b80d1f (diff) |
ALSA: Au88x0 - Implement subdevice volume controls
- add "PCM Playback Volume" controls for 16 playback subdevices
This allow application to change the volume of each subdevice
by using hardware mixer of au88x0 and default is zero gain/attenunation.
Signed-off-by: Raymond Yau <superquad.vortex2@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/au88x0')
-rw-r--r-- | sound/pci/au88x0/au88x0.h | 13 | ||||
-rw-r--r-- | sound/pci/au88x0/au88x0_core.c | 18 | ||||
-rw-r--r-- | sound/pci/au88x0/au88x0_pcm.c | 127 |
3 files changed, 145 insertions, 13 deletions
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index bb938153a964..466a5c8e8354 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h | |||
@@ -26,7 +26,7 @@ | |||
26 | #include <sound/mpu401.h> | 26 | #include <sound/mpu401.h> |
27 | #include <sound/hwdep.h> | 27 | #include <sound/hwdep.h> |
28 | #include <sound/ac97_codec.h> | 28 | #include <sound/ac97_codec.h> |
29 | 29 | #include <sound/tlv.h> | |
30 | #endif | 30 | #endif |
31 | 31 | ||
32 | #ifndef CHIP_AU8820 | 32 | #ifndef CHIP_AU8820 |
@@ -107,6 +107,14 @@ | |||
107 | #define NR_WTPB 0x20 /* WT channels per each bank. */ | 107 | #define NR_WTPB 0x20 /* WT channels per each bank. */ |
108 | #define NR_PCM 0x10 | 108 | #define NR_PCM 0x10 |
109 | 109 | ||
110 | struct pcm_vol { | ||
111 | struct snd_kcontrol *kctl; | ||
112 | int active; | ||
113 | int dma; | ||
114 | int mixin[4]; | ||
115 | int vol[4]; | ||
116 | }; | ||
117 | |||
110 | /* Structs */ | 118 | /* Structs */ |
111 | typedef struct { | 119 | typedef struct { |
112 | //int this_08; /* Still unknown */ | 120 | //int this_08; /* Still unknown */ |
@@ -168,6 +176,7 @@ struct snd_vortex { | |||
168 | /* Xtalk canceler */ | 176 | /* Xtalk canceler */ |
169 | int xt_mode; /* 1: speakers, 0:headphones. */ | 177 | int xt_mode; /* 1: speakers, 0:headphones. */ |
170 | #endif | 178 | #endif |
179 | struct pcm_vol pcm_vol[NR_PCM]; | ||
171 | 180 | ||
172 | int isquad; /* cache of extended ID codec flag. */ | 181 | int isquad; /* cache of extended ID codec flag. */ |
173 | 182 | ||
@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt); | |||
239 | /* Connection stuff. */ | 248 | /* Connection stuff. */ |
240 | static void vortex_connect_default(vortex_t * vortex, int en); | 249 | static void vortex_connect_default(vortex_t * vortex, int en); |
241 | static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, | 250 | static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, |
242 | int dir, int type); | 251 | int dir, int type, int subdev); |
243 | static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, | 252 | static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, |
244 | int restype); | 253 | int restype); |
245 | #ifndef CHIP_AU8810 | 254 | #ifndef CHIP_AU8810 |
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 6933a27a5d76..1181c5ec2d4f 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c | |||
@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) | |||
2050 | } | 2050 | } |
2051 | 2051 | ||
2052 | /* Default Connections */ | 2052 | /* Default Connections */ |
2053 | static int | ||
2054 | vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type); | ||
2055 | 2053 | ||
2056 | static void vortex_connect_default(vortex_t * vortex, int en) | 2054 | static void vortex_connect_default(vortex_t * vortex, int en) |
2057 | { | 2055 | { |
@@ -2111,15 +2109,13 @@ static void vortex_connect_default(vortex_t * vortex, int en) | |||
2111 | Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0. | 2109 | Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0. |
2112 | */ | 2110 | */ |
2113 | static int | 2111 | static int |
2114 | vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) | 2112 | vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, |
2113 | int type, int subdev) | ||
2115 | { | 2114 | { |
2116 | stream_t *stream; | 2115 | stream_t *stream; |
2117 | int i, en; | 2116 | int i, en; |
2117 | struct pcm_vol *p; | ||
2118 | 2118 | ||
2119 | if ((nr_ch == 3) | ||
2120 | || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2))) | ||
2121 | return -EBUSY; | ||
2122 | |||
2123 | if (dma >= 0) { | 2119 | if (dma >= 0) { |
2124 | en = 0; | 2120 | en = 0; |
2125 | vortex_adb_checkinout(vortex, | 2121 | vortex_adb_checkinout(vortex, |
@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) | |||
2250 | MIX_DEFIGAIN); | 2246 | MIX_DEFIGAIN); |
2251 | #endif | 2247 | #endif |
2252 | } | 2248 | } |
2249 | if (stream->type == VORTEX_PCM_ADB && en) { | ||
2250 | p = &vortex->pcm_vol[subdev]; | ||
2251 | p->dma = dma; | ||
2252 | for (i = 0; i < nr_ch; i++) | ||
2253 | p->mixin[i] = mix[i]; | ||
2254 | for (i = 0; i < ch_top; i++) | ||
2255 | p->vol[i] = 0; | ||
2256 | } | ||
2253 | } | 2257 | } |
2254 | #ifndef CHIP_AU8820 | 2258 | #ifndef CHIP_AU8820 |
2255 | else { | 2259 | else { |
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 0ef2f9712208..e59f120742a4 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c | |||
@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { | |||
122 | .mask = 0, | 122 | .mask = 0, |
123 | }; | 123 | }; |
124 | #endif | 124 | #endif |
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 | |||
125 | /* open callback */ | 137 | /* open callback */ |
126 | static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) | 138 | static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) |
127 | { | 139 | { |
@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, | |||
230 | if (stream != NULL) | 242 | if (stream != NULL) |
231 | vortex_adb_allocroute(chip, stream->dma, | 243 | vortex_adb_allocroute(chip, stream->dma, |
232 | stream->nr_ch, stream->dir, | 244 | stream->nr_ch, stream->dir, |
233 | stream->type); | 245 | stream->type, |
246 | substream->number); | ||
234 | /* Alloc routes. */ | 247 | /* Alloc routes. */ |
235 | dma = | 248 | dma = |
236 | vortex_adb_allocroute(chip, -1, | 249 | vortex_adb_allocroute(chip, -1, |
237 | params_channels(hw_params), | 250 | params_channels(hw_params), |
238 | substream->stream, type); | 251 | substream->stream, type, |
252 | substream->number); | ||
239 | if (dma < 0) { | 253 | if (dma < 0) { |
240 | spin_unlock_irq(&chip->lock); | 254 | spin_unlock_irq(&chip->lock); |
241 | return dma; | 255 | return dma; |
@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, | |||
246 | vortex_adbdma_setbuffers(chip, dma, | 260 | vortex_adbdma_setbuffers(chip, dma, |
247 | params_period_bytes(hw_params), | 261 | params_period_bytes(hw_params), |
248 | params_periods(hw_params)); | 262 | params_periods(hw_params)); |
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 | } | ||
249 | } | 268 | } |
250 | #ifndef CHIP_AU8810 | 269 | #ifndef CHIP_AU8810 |
251 | else { | 270 | else { |
@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream) | |||
275 | spin_lock_irq(&chip->lock); | 294 | spin_lock_irq(&chip->lock); |
276 | // Delete audio routes. | 295 | // Delete audio routes. |
277 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { | 296 | if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { |
278 | if (stream != NULL) | 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 | } | ||
279 | vortex_adb_allocroute(chip, stream->dma, | 304 | vortex_adb_allocroute(chip, stream->dma, |
280 | stream->nr_ch, stream->dir, | 305 | stream->nr_ch, stream->dir, |
281 | stream->type); | 306 | stream->type, |
307 | substream->number); | ||
308 | } | ||
282 | } | 309 | } |
283 | #ifndef CHIP_AU8810 | 310 | #ifndef CHIP_AU8810 |
284 | else { | 311 | else { |
@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { | |||
506 | }, | 533 | }, |
507 | }; | 534 | }; |
508 | 535 | ||
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; | ||
588 | }; | ||
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 | |||
601 | static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = { | ||
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 | |||
509 | /* create a pcm device */ | 613 | /* create a pcm device */ |
510 | static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) | 614 | static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) |
511 | { | 615 | { |
@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) | |||
555 | return err; | 659 | return err; |
556 | } | 660 | } |
557 | } | 661 | } |
662 | if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) { | ||
663 | for (i = 0; i < NR_PCM; i++) { | ||
664 | chip->pcm_vol[i].active = 0; | ||
665 | chip->pcm_vol[i].dma = -1; | ||
666 | kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip); | ||
667 | if (!kctl) | ||
668 | return -ENOMEM; | ||
669 | chip->pcm_vol[i].kctl = kctl; | ||
670 | kctl->id.device = 0; | ||
671 | kctl->id.subdevice = i; | ||
672 | err = snd_ctl_add(chip->card, kctl); | ||
673 | if (err < 0) | ||
674 | return err; | ||
675 | } | ||
676 | } | ||
558 | return 0; | 677 | return 0; |
559 | } | 678 | } |