diff options
| -rw-r--r-- | include/sound/control.h | 3 | ||||
| -rw-r--r-- | sound/core/vmaster.c | 31 | ||||
| -rw-r--r-- | sound/pci/hda/hda_codec.c | 97 |
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); |
| 249 | void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only); | 249 | void 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) |
| 251 | int 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 | } |
| 486 | EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster); | 486 | EXPORT_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 | */ | ||
| 497 | int 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 | } | ||
| 517 | EXPORT_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 */ | ||
| 1807 | static 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) */ |
| 1837 | static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) | 1807 | static 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 */ | 1820 | struct slave_init_arg { |
| 1851 | static 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() */ | ||
| 1826 | static 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() */ |
| 1861 | static int init_slave_unmute(struct hda_codec *codec, | 1871 | static 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) |
