From 532d5381793f3c824f8ff68d7067fab8c76bb811 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Jul 2007 19:02:40 +0200 Subject: [PATCH] [ALSA] hda-codec - Add a generic bind-control helper Added callbacks for a generic bind-control of mixer elements. This can be used for creating a mixer element controlling multiple widgets at the same time. Two macros, HDA_BIND_VOL() and HDA_BIND_SW(), are introduced for creating bind-volume and bind-switch, respectively. It taks the mixer element name and struct hda_bind_ctls pointer, which contains the real control callbacks in ops field and long array for private_value of each bound widget. All widgets have to be the same type (i.e. the same amp capability). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 87 +++++++++++++++++ sound/pci/hda/hda_local.h | 47 +++++++++ sound/pci/hda/patch_analog.c | 177 ++++++++-------------------------- sound/pci/hda/patch_realtek.c | 28 ++---- 4 files changed, 184 insertions(+), 155 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e7843ffeeb2f..36879a93eac4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1005,6 +1005,93 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, return err < 0 ? err : change; } +/* + * generic bound volume/swtich controls + */ +int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + int err; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = *c->values; + err = c->ops->info(kcontrol, uinfo); + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err; +} + +int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + int err; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = *c->values; + err = c->ops->get(kcontrol, ucontrol); + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err; +} + +int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + unsigned long *vals; + int err = 0, change = 0; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + for (vals = c->values; *vals; vals++) { + kcontrol->private_value = *vals; + err = c->ops->put(kcontrol, ucontrol); + if (err < 0) + break; + change |= err; + } + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err < 0 ? err : change; +} + +int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + int err; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = *c->values; + err = c->ops->tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err; +} + +struct hda_ctl_ops snd_hda_bind_vol = { + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = snd_hda_mixer_amp_volume_put, + .tlv = snd_hda_mixer_amp_tlv +}; + +struct hda_ctl_ops snd_hda_bind_sw = { + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = snd_hda_mixer_amp_switch_put, + .tlv = snd_hda_mixer_amp_tlv +}; + /* * SPDIF out controls */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 12428a67eb2a..fafcffe6fc79 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -102,6 +102,53 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* more generic bound controls */ +struct hda_ctl_ops { + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + snd_kcontrol_tlv_rw_t *tlv; +}; + +extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */ +extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */ + +struct hda_bind_ctls { + struct hda_ctl_ops *ops; + long values[]; +}; + +int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv); + +#define HDA_BIND_VOL(xname, bindrec) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\ + .info = snd_hda_mixer_bind_ctls_info,\ + .get = snd_hda_mixer_bind_ctls_get,\ + .put = snd_hda_mixer_bind_ctls_put,\ + .tlv = { .c = snd_hda_mixer_bind_tlv },\ + .private_value = (long) (bindrec) } +#define HDA_BIND_SW(xname, bindrec) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = snd_hda_mixer_bind_ctls_info,\ + .get = snd_hda_mixer_bind_ctls_get,\ + .put = snd_hda_mixer_bind_ctls_put,\ + .private_value = (long) (bindrec) } + +/* + * SPDIF I/O + */ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid); int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 488724f2e304..cc2e944cc59f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -422,94 +422,36 @@ static struct hda_input_mux ad1986a_capture_source = { }, }; -/* - * PCM control - * - * bind volumes/mutes of 3 DACs as a single PCM control for simplicity - */ -#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info +static struct hda_bind_ctls ad1986a_bind_pcm_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), + 0 + }, +}; -static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - - mutex_lock(&ad->amp_mutex); - snd_hda_mixer_amp_volume_get(kcontrol, ucontrol); - mutex_unlock(&ad->amp_mutex); - return 0; -} - -static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - int i, change = 0; - - mutex_lock(&ad->amp_mutex); - for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) { - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT); - change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); - } - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT); - mutex_unlock(&ad->amp_mutex); - return change; -} - -#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info - -static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - - mutex_lock(&ad->amp_mutex); - snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); - mutex_unlock(&ad->amp_mutex); - return 0; -} - -static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - int i, change = 0; - - mutex_lock(&ad->amp_mutex); - for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) { - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT); - change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - } - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT); - mutex_unlock(&ad->amp_mutex); - return change; -} +static struct hda_bind_ctls ad1986a_bind_pcm_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* * mixers */ static struct snd_kcontrol_new ad1986a_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, - .info = ad1986a_pcm_amp_vol_info, - .get = ad1986a_pcm_amp_vol_get, - .put = ad1986a_pcm_amp_vol_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT) - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Switch", - .info = ad1986a_pcm_amp_sw_info, - .get = ad1986a_pcm_amp_sw_get, - .put = ad1986a_pcm_amp_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT) - }, + /* + * bind volumes/mutes of 3 DACs as a single PCM control for simplicity + */ + HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol), + HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw), HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT), @@ -596,41 +538,23 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = { /* laptop-eapd model - 2ch only */ /* master controls both pins 0x1a and 0x1b */ -static int ad1986a_laptop_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; +static struct hda_bind_ctls ad1986a_laptop_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + 0, + }, +}; - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} - -static int ad1986a_laptop_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - 0x80, valp[0] ? 0 : 0x80); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - 0x80, valp[1] ? 0 : 0x80); - snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - 0x80, valp[0] ? 0 : 0x80); - snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - 0x80, valp[1] ? 0 : 0x80); - return change; -} +static struct hda_bind_ctls ad1986a_laptop_master_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + 0, + }, +}; static struct hda_input_mux ad1986a_laptop_eapd_capture_source = { .num_items = 3, @@ -642,23 +566,8 @@ static struct hda_input_mux ad1986a_laptop_eapd_capture_source = { }; static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = ad1986a_laptop_master_vol_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1986a_laptop_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), + HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), @@ -856,7 +765,6 @@ static int patch_ad1986a(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; spec->multiout.max_channels = 6; @@ -1064,7 +972,6 @@ static int patch_ad1983(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; spec->multiout.max_channels = 2; @@ -1466,7 +1373,6 @@ static int patch_ad1981(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; spec->multiout.max_channels = 2; @@ -2672,7 +2578,6 @@ static int patch_ad1988(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; if (is_rev2(codec)) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d839d567f8e4..f27e073d22b1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7140,28 +7140,18 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { { } /* end */ }; -static int alc262_sony_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned long private_save = kcontrol->private_value; - int change; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT); - change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - kcontrol->private_value = private_save; - change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - return change; -} +static struct hda_bind_ctls alc262_sony_bind_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + 0, + }, +}; static struct snd_kcontrol_new alc262_sony_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Front Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc262_sony_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_SW("Front Playback Switch", &alc262_sony_bind_sw), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),