aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/control.h3
-rw-r--r--sound/core/vmaster.c31
-rw-r--r--sound/pci/hda/hda_codec.c97
3 files changed, 89 insertions, 42 deletions
diff --git a/include/sound/control.h b/include/sound/control.h
index bd7246de58e7..a1f1152bc687 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -248,6 +248,9 @@ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kctl,
248 void *private_data); 248 void *private_data);
249void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only); 249void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only);
250#define snd_ctl_sync_vmaster_hook(kctl) snd_ctl_sync_vmaster(kctl, true) 250#define snd_ctl_sync_vmaster_hook(kctl) snd_ctl_sync_vmaster(kctl, true)
251int snd_ctl_apply_vmaster_slaves(struct snd_kcontrol *kctl,
252 int (*func)(struct snd_kcontrol *, void *),
253 void *arg);
251 254
252/* 255/*
253 * Helper functions for jack-detection controls 256 * Helper functions for jack-detection controls
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 6c58e6f73a01..e43af18d4383 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -484,3 +484,34 @@ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
484 master->hook(master->hook_private_data, master->val); 484 master->hook(master->hook_private_data, master->val);
485} 485}
486EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster); 486EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
487
488/**
489 * snd_ctl_apply_vmaster_slaves - Apply function to each vmaster slave
490 * @kctl: vmaster kctl element
491 * @func: function to apply
492 * @arg: optional function argument
493 *
494 * Apply the function @func to each slave kctl of the given vmaster kctl.
495 * Returns 0 if successful, or a negative error code.
496 */
497int snd_ctl_apply_vmaster_slaves(struct snd_kcontrol *kctl,
498 int (*func)(struct snd_kcontrol *, void *),
499 void *arg)
500{
501 struct link_master *master;
502 struct link_slave *slave;
503 int err;
504
505 master = snd_kcontrol_chip(kctl);
506 err = master_init(master);
507 if (err < 0)
508 return err;
509 list_for_each_entry(slave, &master->slaves, list) {
510 err = func(&slave->slave, arg);
511 if (err < 0)
512 return err;
513 }
514
515 return 0;
516}
517EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_slaves);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index b6cf9684c2ec..a0989d231fd0 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1803,36 +1803,6 @@ static int check_slave_present(struct hda_codec *codec,
1803 return 1; 1803 return 1;
1804} 1804}
1805 1805
1806/* guess the value corresponding to 0dB */
1807static int get_kctl_0dB_offset(struct hda_codec *codec,
1808 struct snd_kcontrol *kctl, int *step_to_check)
1809{
1810 int _tlv[4];
1811 const int *tlv = NULL;
1812 int val = -1;
1813
1814 if ((kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) &&
1815 kctl->tlv.c == snd_hda_mixer_amp_tlv) {
1816 get_ctl_amp_tlv(kctl, _tlv);
1817 tlv = _tlv;
1818 } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
1819 tlv = kctl->tlv.p;
1820 if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
1821 int step = tlv[3];
1822 step &= ~TLV_DB_SCALE_MUTE;
1823 if (!step)
1824 return -1;
1825 if (*step_to_check && *step_to_check != step) {
1826 codec_err(codec, "Mismatching dB step for vmaster slave (%d!=%d)\n",
1827 *step_to_check, step);
1828 return -1;
1829 }
1830 *step_to_check = step;
1831 val = -tlv[2] / step;
1832 }
1833 return val;
1834}
1835
1836/* call kctl->put with the given value(s) */ 1806/* call kctl->put with the given value(s) */
1837static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) 1807static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
1838{ 1808{
@@ -1847,19 +1817,58 @@ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
1847 return 0; 1817 return 0;
1848} 1818}
1849 1819
1850/* initialize the slave volume with 0dB */ 1820struct slave_init_arg {
1851static int init_slave_0dB(struct hda_codec *codec, 1821 struct hda_codec *codec;
1852 void *data, struct snd_kcontrol *slave) 1822 int step;
1823};
1824
1825/* initialize the slave volume with 0dB via snd_ctl_apply_vmaster_slaves() */
1826static int init_slave_0dB(struct snd_kcontrol *kctl, void *_arg)
1853{ 1827{
1854 int offset = get_kctl_0dB_offset(codec, slave, data); 1828 struct slave_init_arg *arg = _arg;
1855 if (offset > 0) 1829 int _tlv[4];
1856 put_kctl_with_value(slave, offset); 1830 const int *tlv = NULL;
1831 int step;
1832 int val;
1833
1834 if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1835 if (kctl->tlv.c != snd_hda_mixer_amp_tlv) {
1836 codec_err(arg->codec,
1837 "Unexpected TLV callback for slave %s:%d\n",
1838 kctl->id.name, kctl->id.index);
1839 return 0; /* ignore */
1840 }
1841 get_ctl_amp_tlv(kctl, _tlv);
1842 tlv = _tlv;
1843 } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
1844 tlv = kctl->tlv.p;
1845
1846 if (!tlv || tlv[0] != SNDRV_CTL_TLVT_DB_SCALE)
1847 return 0;
1848
1849 step = tlv[3];
1850 step &= ~TLV_DB_SCALE_MUTE;
1851 if (!step)
1852 return 0;
1853 if (arg->step && arg->step != step) {
1854 codec_err(arg->codec,
1855 "Mismatching dB step for vmaster slave (%d!=%d)\n",
1856 arg->step, step);
1857 return 0;
1858 }
1859
1860 arg->step = step;
1861 val = -tlv[2] / step;
1862 if (val > 0) {
1863 put_kctl_with_value(kctl, val);
1864 return val;
1865 }
1866
1857 return 0; 1867 return 0;
1858} 1868}
1859 1869
1860/* unmute the slave */ 1870/* unmute the slave via snd_ctl_apply_vmaster_slaves() */
1861static int init_slave_unmute(struct hda_codec *codec, 1871static int init_slave_unmute(struct snd_kcontrol *slave, void *_arg)
1862 void *data, struct snd_kcontrol *slave)
1863{ 1872{
1864 return put_kctl_with_value(slave, 1); 1873 return put_kctl_with_value(slave, 1);
1865} 1874}
@@ -1919,9 +1928,13 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
1919 /* init with master mute & zero volume */ 1928 /* init with master mute & zero volume */
1920 put_kctl_with_value(kctl, 0); 1929 put_kctl_with_value(kctl, 0);
1921 if (init_slave_vol) { 1930 if (init_slave_vol) {
1922 int step = 0; 1931 struct slave_init_arg arg = {
1923 map_slaves(codec, slaves, suffix, 1932 .codec = codec,
1924 tlv ? init_slave_0dB : init_slave_unmute, &step); 1933 .step = 0,
1934 };
1935 snd_ctl_apply_vmaster_slaves(kctl,
1936 tlv ? init_slave_0dB : init_slave_unmute,
1937 &arg);
1925 } 1938 }
1926 1939
1927 if (ctl_ret) 1940 if (ctl_ret)