Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb92b7c4 authored by Raymond Yau's avatar Raymond Yau Committed by Takashi Iwai
Browse files

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: default avatarRaymond Yau <superquad.vortex2@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent dcd6c922
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@
#include <sound/mpu401.h>
#include <sound/hwdep.h>
#include <sound/ac97_codec.h>

#include <sound/tlv.h>
#endif

#ifndef CHIP_AU8820
@@ -107,6 +107,14 @@
#define NR_WTPB 0x20		/* WT channels per each bank. */
#define NR_PCM	0x10

struct pcm_vol {
	struct snd_kcontrol *kctl;
	int active;
	int dma;
	int mixin[4];
	int vol[4];
};

/* Structs */
typedef struct {
	//int this_08;          /* Still unknown */
@@ -168,6 +176,7 @@ struct snd_vortex {
	/* Xtalk canceler */
	int xt_mode;		/* 1: speakers, 0:headphones. */
#endif
	struct pcm_vol pcm_vol[NR_PCM];

	int isquad;		/* cache of extended ID codec flag. */

@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt);
/* Connection  stuff. */
static void vortex_connect_default(vortex_t * vortex, int en);
static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
				 int dir, int type);
				 int dir, int type, int subdev);
static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
				  int restype);
#ifndef CHIP_AU8810
+11 −7
Original line number Diff line number Diff line
@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
}

/* Default Connections  */
static int
vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);

static void vortex_connect_default(vortex_t * vortex, int en)
{
@@ -2111,14 +2109,12 @@ static void vortex_connect_default(vortex_t * vortex, int en)
  Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
*/
static int
vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
			int type, int subdev)
{
	stream_t *stream;
	int i, en;
	
	if ((nr_ch == 3)
	    || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
		return -EBUSY;
	struct pcm_vol *p;
	
	if (dma >= 0) {
		en = 0;
@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
							      MIX_DEFIGAIN);
#endif
			}
			if (stream->type == VORTEX_PCM_ADB && en) {
				p = &vortex->pcm_vol[subdev];
				p->dma = dma;
				for (i = 0; i < nr_ch; i++)
					p->mixin[i] = mix[i];
				for (i = 0; i < ch_top; i++)
					p->vol[i] = 0;
			}
		}
#ifndef CHIP_AU8820
		else {
+123 −4
Original line number Diff line number Diff line
@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
	.mask = 0,
};
#endif

static void vortex_notify_pcm_vol_change(struct snd_card *card,
			struct snd_kcontrol *kctl, int activate)
{
	if (activate)
		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
	else
		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
				SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
}

/* open callback */
static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
{
@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
		if (stream != NULL)
			vortex_adb_allocroute(chip, stream->dma,
					      stream->nr_ch, stream->dir,
					      stream->type);
					      stream->type,
					      substream->number);
		/* Alloc routes. */
		dma =
		    vortex_adb_allocroute(chip, -1,
					  params_channels(hw_params),
					  substream->stream, type);
					  substream->stream, type,
					  substream->number);
		if (dma < 0) {
			spin_unlock_irq(&chip->lock);
			return dma;
@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
		vortex_adbdma_setbuffers(chip, dma,
					 params_period_bytes(hw_params),
					 params_periods(hw_params));
		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
			chip->pcm_vol[substream->number].active = 1;
			vortex_notify_pcm_vol_change(chip->card,
				chip->pcm_vol[substream->number].kctl, 1);
		}
	}
#ifndef CHIP_AU8810
	else {
@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
	spin_lock_irq(&chip->lock);
	// Delete audio routes.
	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
		if (stream != NULL)
		if (stream != NULL) {
			if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
				chip->pcm_vol[substream->number].active = 0;
				vortex_notify_pcm_vol_change(chip->card,
					chip->pcm_vol[substream->number].kctl,
					0);
			}
			vortex_adb_allocroute(chip, stream->dma,
					      stream->nr_ch, stream->dir,
					      stream->type);
					      stream->type,
					      substream->number);
		}
	}
#ifndef CHIP_AU8810
	else {
@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
	},
};

/* subdevice PCM Volume control */

static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_info *uinfo)
{
	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
	uinfo->value.integer.min = -128;
	uinfo->value.integer.max = 32;
	return 0;
}

static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int i;
	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
	int subdev = kcontrol->id.subdevice;
	struct pcm_vol *p = &vortex->pcm_vol[subdev];
	int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
	for (i = 0; i < max_chn; i++)
		ucontrol->value.integer.value[i] = p->vol[i];
	return 0;
}

static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	int i;
	int changed = 0;
	int mixin;
	unsigned char vol;
	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
	int subdev = kcontrol->id.subdevice;
	struct pcm_vol *p = &vortex->pcm_vol[subdev];
	int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
	for (i = 0; i < max_chn; i++) {
		if (p->vol[i] != ucontrol->value.integer.value[i]) {
			p->vol[i] = ucontrol->value.integer.value[i];
			if (p->active) {
				switch (vortex->dma_adb[p->dma].nr_ch) {
				case 1:
					mixin = p->mixin[0];
					break;
				case 2:
				default:
					mixin = p->mixin[(i < 2) ? i : (i - 2)];
					break;
				case 4:
					mixin = p->mixin[i];
					break;
				};
				vol = p->vol[i];
				vortex_mix_setinputvolumebyte(vortex,
					vortex->mixplayb[i], mixin, vol);
			}
			changed = 1;
		}
	}
	return changed;
}

static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);

static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = {
	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
	.name = "PCM Playback Volume",
	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
		SNDRV_CTL_ELEM_ACCESS_TLV_READ |
		SNDRV_CTL_ELEM_ACCESS_INACTIVE,
	.info = snd_vortex_pcm_vol_info,
	.get = snd_vortex_pcm_vol_get,
	.put = snd_vortex_pcm_vol_put,
	.tlv = { .p = vortex_pcm_vol_db_scale },
};

/* create a pcm device */
static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
{
@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
				return err;
		}
	}
	if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
		for (i = 0; i < NR_PCM; i++) {
			chip->pcm_vol[i].active = 0;
			chip->pcm_vol[i].dma = -1;
			kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
			if (!kctl)
				return -ENOMEM;
			chip->pcm_vol[i].kctl = kctl;
			kctl->id.device = 0;
			kctl->id.subdevice = i;
			err = snd_ctl_add(chip->card, kctl);
			if (err < 0)
				return err;
		}
	}
	return 0;
}