Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Raymond Yau and committed by
Takashi Iwai
bb92b7c4 dcd6c922

+145 -13
+11 -2
sound/pci/au88x0/au88x0.h
··· 26 26 #include <sound/mpu401.h> 27 27 #include <sound/hwdep.h> 28 28 #include <sound/ac97_codec.h> 29 - 29 + #include <sound/tlv.h> 30 30 #endif 31 31 32 32 #ifndef CHIP_AU8820 ··· 107 107 #define NR_WTPB 0x20 /* WT channels per each bank. */ 108 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 118 /* Structs */ 111 119 typedef struct { 112 120 //int this_08; /* Still unknown */ ··· 176 168 /* Xtalk canceler */ 177 169 int xt_mode; /* 1: speakers, 0:headphones. */ 178 170 #endif 171 + struct pcm_vol pcm_vol[NR_PCM]; 179 172 180 173 int isquad; /* cache of extended ID codec flag. */ 181 174 ··· 248 239 /* Connection stuff. */ 249 240 static void vortex_connect_default(vortex_t * vortex, int en); 250 241 static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, 251 - int dir, int type); 242 + int dir, int type, int subdev); 252 243 static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, 253 244 int restype); 254 245 #ifndef CHIP_AU8810
+11 -7
sound/pci/au88x0/au88x0_core.c
··· 2050 2050 } 2051 2051 2052 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 2054 static void vortex_connect_default(vortex_t * vortex, int en) 2057 2055 { ··· 2109 2111 Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0. 2110 2112 */ 2111 2113 static int 2112 - vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) 2114 + vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, 2115 + int type, int subdev) 2113 2116 { 2114 2117 stream_t *stream; 2115 2118 int i, en; 2119 + struct pcm_vol *p; 2116 2120 2117 - if ((nr_ch == 3) 2118 - || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2))) 2119 - return -EBUSY; 2120 - 2121 2121 if (dma >= 0) { 2122 2122 en = 0; 2123 2123 vortex_adb_checkinout(vortex, ··· 2245 2249 mix[i % nr_ch], 2246 2250 MIX_DEFIGAIN); 2247 2251 #endif 2252 + } 2253 + if (stream->type == VORTEX_PCM_ADB && en) { 2254 + p = &vortex->pcm_vol[subdev]; 2255 + p->dma = dma; 2256 + for (i = 0; i < nr_ch; i++) 2257 + p->mixin[i] = mix[i]; 2258 + for (i = 0; i < ch_top; i++) 2259 + p->vol[i] = 0; 2248 2260 } 2249 2261 } 2250 2262 #ifndef CHIP_AU8820
+123 -4
sound/pci/au88x0/au88x0_pcm.c
··· 122 122 .mask = 0, 123 123 }; 124 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 137 /* open callback */ 126 138 static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) 127 139 { ··· 242 230 if (stream != NULL) 243 231 vortex_adb_allocroute(chip, stream->dma, 244 232 stream->nr_ch, stream->dir, 245 - stream->type); 233 + stream->type, 234 + substream->number); 246 235 /* Alloc routes. */ 247 236 dma = 248 237 vortex_adb_allocroute(chip, -1, 249 238 params_channels(hw_params), 250 - substream->stream, type); 239 + substream->stream, type, 240 + substream->number); 251 241 if (dma < 0) { 252 242 spin_unlock_irq(&chip->lock); 253 243 return dma; ··· 260 246 vortex_adbdma_setbuffers(chip, dma, 261 247 params_period_bytes(hw_params), 262 248 params_periods(hw_params)); 249 + if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { 250 + chip->pcm_vol[substream->number].active = 1; 251 + vortex_notify_pcm_vol_change(chip->card, 252 + chip->pcm_vol[substream->number].kctl, 1); 253 + } 263 254 } 264 255 #ifndef CHIP_AU8810 265 256 else { ··· 294 275 spin_lock_irq(&chip->lock); 295 276 // Delete audio routes. 296 277 if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { 297 - if (stream != NULL) 278 + if (stream != NULL) { 279 + if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { 280 + chip->pcm_vol[substream->number].active = 0; 281 + vortex_notify_pcm_vol_change(chip->card, 282 + chip->pcm_vol[substream->number].kctl, 283 + 0); 284 + } 298 285 vortex_adb_allocroute(chip, stream->dma, 299 286 stream->nr_ch, stream->dir, 300 - stream->type); 287 + stream->type, 288 + substream->number); 289 + } 301 290 } 302 291 #ifndef CHIP_AU8810 303 292 else { ··· 533 506 }, 534 507 }; 535 508 509 + /* subdevice PCM Volume control */ 510 + 511 + static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol, 512 + struct snd_ctl_elem_info *uinfo) 513 + { 514 + vortex_t *vortex = snd_kcontrol_chip(kcontrol); 515 + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 516 + uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2); 517 + uinfo->value.integer.min = -128; 518 + uinfo->value.integer.max = 32; 519 + return 0; 520 + } 521 + 522 + static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol, 523 + struct snd_ctl_elem_value *ucontrol) 524 + { 525 + int i; 526 + vortex_t *vortex = snd_kcontrol_chip(kcontrol); 527 + int subdev = kcontrol->id.subdevice; 528 + struct pcm_vol *p = &vortex->pcm_vol[subdev]; 529 + int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2); 530 + for (i = 0; i < max_chn; i++) 531 + ucontrol->value.integer.value[i] = p->vol[i]; 532 + return 0; 533 + } 534 + 535 + static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol, 536 + struct snd_ctl_elem_value *ucontrol) 537 + { 538 + int i; 539 + int changed = 0; 540 + int mixin; 541 + unsigned char vol; 542 + vortex_t *vortex = snd_kcontrol_chip(kcontrol); 543 + int subdev = kcontrol->id.subdevice; 544 + struct pcm_vol *p = &vortex->pcm_vol[subdev]; 545 + int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2); 546 + for (i = 0; i < max_chn; i++) { 547 + if (p->vol[i] != ucontrol->value.integer.value[i]) { 548 + p->vol[i] = ucontrol->value.integer.value[i]; 549 + if (p->active) { 550 + switch (vortex->dma_adb[p->dma].nr_ch) { 551 + case 1: 552 + mixin = p->mixin[0]; 553 + break; 554 + case 2: 555 + default: 556 + mixin = p->mixin[(i < 2) ? i : (i - 2)]; 557 + break; 558 + case 4: 559 + mixin = p->mixin[i]; 560 + break; 561 + }; 562 + vol = p->vol[i]; 563 + vortex_mix_setinputvolumebyte(vortex, 564 + vortex->mixplayb[i], mixin, vol); 565 + } 566 + changed = 1; 567 + } 568 + } 569 + return changed; 570 + } 571 + 572 + static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400); 573 + 574 + static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = { 575 + .iface = SNDRV_CTL_ELEM_IFACE_PCM, 576 + .name = "PCM Playback Volume", 577 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 578 + SNDRV_CTL_ELEM_ACCESS_TLV_READ | 579 + SNDRV_CTL_ELEM_ACCESS_INACTIVE, 580 + .info = snd_vortex_pcm_vol_info, 581 + .get = snd_vortex_pcm_vol_get, 582 + .put = snd_vortex_pcm_vol_put, 583 + .tlv = { .p = vortex_pcm_vol_db_scale }, 584 + }; 585 + 536 586 /* create a pcm device */ 537 587 static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) 538 588 { ··· 656 552 if (!kctl) 657 553 return -ENOMEM; 658 554 if ((err = snd_ctl_add(chip->card, kctl)) < 0) 555 + return err; 556 + } 557 + } 558 + if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) { 559 + for (i = 0; i < NR_PCM; i++) { 560 + chip->pcm_vol[i].active = 0; 561 + chip->pcm_vol[i].dma = -1; 562 + kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip); 563 + if (!kctl) 564 + return -ENOMEM; 565 + chip->pcm_vol[i].kctl = kctl; 566 + kctl->id.device = 0; 567 + kctl->id.subdevice = i; 568 + err = snd_ctl_add(chip->card, kctl); 569 + if (err < 0) 659 570 return err; 660 571 } 661 572 }