From a23544a6cc448e3a90630814ddb79690eb9ce444 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 Jan 2014 22:52:28 +0000 Subject: [PATCH 01/42] ASoC: omap: Make RX51 depend on GPIOLIB due to jack usage Since the GPIO jacks are only supported if gpiolib is built and fail to compile otherwise add a build depedency. This is unlikely to have any practical impact outside of coverage testing. Reported-by: Russell King Signed-off-by: Mark Brown Acked-by: Jarkko Nikula --- sound/soc/omap/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 4a07f7179690..22ad9c5654b5 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -30,6 +30,7 @@ config SND_OMAP_SOC_RX51 select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 + depends on GPIOLIB help Say Y if you want to add support for SoC audio on Nokia RX-51 hardware. This is also known as Nokia N900 product. From 34354792432b6e0a3b156819a1a19716c50d3473 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jan 2014 16:27:51 +0000 Subject: [PATCH 02/42] ASoC: wm5110: Extend SYSCLK patch file for rev D Latest evaluation of the the device has given some patch file additions for improved performance. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/wm5110.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 0ab2dc296474..c4c9ed7acd78 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -43,6 +43,54 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = { { 0x3133, 0x1201 }, { 0x3183, 0x1501 }, { 0x31D3, 0x1401 }, + { 0x0049, 0x01ea }, + { 0x004a, 0x01f2 }, + { 0x0057, 0x01e7 }, + { 0x0058, 0x01fb }, + { 0x33ce, 0xc4f5 }, + { 0x33cf, 0x1361 }, + { 0x33d0, 0x0402 }, + { 0x33d1, 0x4700 }, + { 0x33d2, 0x026d }, + { 0x33d3, 0xff00 }, + { 0x33d4, 0x026d }, + { 0x33d5, 0x0101 }, + { 0x33d6, 0xc4f5 }, + { 0x33d7, 0x0361 }, + { 0x33d8, 0x0402 }, + { 0x33d9, 0x6701 }, + { 0x33da, 0xc4f5 }, + { 0x33db, 0x136f }, + { 0x33dc, 0xc4f5 }, + { 0x33dd, 0x134f }, + { 0x33de, 0xc4f5 }, + { 0x33df, 0x131f }, + { 0x33e0, 0x026d }, + { 0x33e1, 0x4f01 }, + { 0x33e2, 0x026d }, + { 0x33e3, 0xf100 }, + { 0x33e4, 0x026d }, + { 0x33e5, 0x0001 }, + { 0x33e6, 0xc4f5 }, + { 0x33e7, 0x0361 }, + { 0x33e8, 0x0402 }, + { 0x33e9, 0x6601 }, + { 0x33ea, 0xc4f5 }, + { 0x33eb, 0x136f }, + { 0x33ec, 0xc4f5 }, + { 0x33ed, 0x134f }, + { 0x33ee, 0xc4f5 }, + { 0x33ef, 0x131f }, + { 0x33f0, 0x026d }, + { 0x33f1, 0x4e01 }, + { 0x33f2, 0x026d }, + { 0x33f3, 0xf000 }, + { 0x33f6, 0xc4f5 }, + { 0x33f7, 0x1361 }, + { 0x33f8, 0x0402 }, + { 0x33f9, 0x4600 }, + { 0x33fa, 0x026d }, + { 0x33fb, 0xfe00 }, }; static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, From ae1f8ce1925645b047394a59d4108e27fad9a9d8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 20 Jan 2014 17:35:40 -0200 Subject: [PATCH 03/42] ASoC: fsl_ssi: We do support master mode now Since commit aafa85e71a (ASoC: fsl_ssi: Add DAI master mode support for SSI on i.MX series) master mode is supported, so update the comments and code to reflect that. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index f9090b167ad7..c5205690791e 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1262,18 +1262,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) return -EINVAL; hw_type = (enum fsl_ssi_type) of_id->data; - /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop) { dev_err(&pdev->dev, "fsl,mode property is necessary\n"); return -EINVAL; } - if (!strcmp(sprop, "ac97-slave")) { + if (!strcmp(sprop, "ac97-slave")) ac97 = true; - } else if (strcmp(sprop, "i2s-slave")) { - dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); - return -ENODEV; - } /* The DAI name is the last part of the full name of the node. */ p = strrchr(np->full_name, '/') + 1; From efe2ab9b0274b60327ca148e101177ba9708b9ba Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 20 Jan 2014 17:35:39 -0200 Subject: [PATCH 04/42] ASoC: fsl_ssi: Do not print 'baud clock' error message all the time Currently everytime we get the following message on boot: fsl-ssi-dai 202c000.ssi: could not get baud clock: -2 This is not really useful information to get on every boot, so make it a debug message instead. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c5205690791e..1c791ddbf006 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1386,7 +1386,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) */ ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); if (IS_ERR(ssi_private->baudclk)) - dev_warn(&pdev->dev, "could not get baud clock: %ld\n", + dev_dbg(&pdev->dev, "could not get baud clock: %ld\n", PTR_ERR(ssi_private->baudclk)); else clk_prepare_enable(ssi_private->baudclk); From b75b1518a5e76f76324b371b9b0013b8e79e88ea Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Jan 2014 07:44:27 +0100 Subject: [PATCH 05/42] ALSA: cs46xx: Fix memory leak at destructor The release of module object itself was forgotten. Spotted by COVERIY CID 1162828. Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index f18e5878f58b..062398ec5335 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -369,6 +369,7 @@ static void free_module_desc(struct dsp_module_desc *module) kfree(module->segments[i].data); kfree(module->segments); } + kfree(module); } /* firmware binary format: From 2387083157b858f0300f40e629b0fea30f4132c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 29 Nov 2013 14:13:12 +0100 Subject: [PATCH 06/42] ALSA: hda - Apply +5dB output amp on ASUS Zenbook UX31A ASUS Zenbook UX31A has yet another problem -- softer output level than others. According to the measurement, the peak output difference between 31A and 31E is 5dB. As ALC269VB has a COEF for the class-D pre-amp, let's apply it for +5dB. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f9b22fb6dd0b..c435ae0a19a6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3833,6 +3833,7 @@ enum { ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, ALC269VB_FIXUP_ASUS_ZENBOOK, + ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, ALC269VB_FIXUP_ORDISSIMO_EVE2, ALC283_FIXUP_CHROME_BOOK, @@ -4126,6 +4127,17 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269VB_FIXUP_DMIC, }, + [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* class-D output amp +5dB */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 }, + {} + }, + .chained = true, + .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK, + }, [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, @@ -4282,7 +4294,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), - SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK), + SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), From abffae647e5db10133dc87ce7bb1fd30d7c0f737 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 22 Jan 2014 17:30:38 +0530 Subject: [PATCH 07/42] ASoC: samsung: Fix build regressions due to gpio re-org MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent changes through commits c67d0f29262b ("ARM: s3c24xx: get rid of custom "), b0161caa72b6 ("ARM: S3C[24|64]xx: move includes back under scope"), 364374121b78 ("ARM: s3c24xx: explicit dependency on ") and 41c3548e6da6 ("ARM: s3c64xx: get rid of custom ") caused build regressions due to broken dependencies. Fix the following errors by including the necessary header files explicitly: sound/soc/samsung/h1940_uda1380.c:56:3: error: implicit declaration of function ‘S3C2410_GPG’ sound/soc/samsung/h1940_uda1380.c:149:18: error: ‘S3C_GPIO_END’ undeclared (first use in this function) sound/soc/samsung/h1940_uda1380.c:234:21: error: ‘S3C_GPIO_END’ undeclared (first use in this function) sound/soc/samsung/h1940_uda1380.c:270:12: error: ‘S3C_GPIO_END’ undeclared (first use in this function) sound/soc/samsung/neo1973_wm8753.c:239:2: error: implicit declaration of function ‘S3C2410_GPJ’ sound/soc/samsung/rx1950_uda1380.c:67:3: error: implicit declaration of function ‘S3C2410_GPG’ sound/soc/samsung/s3c2412-i2s.c:86:2: error: implicit declaration of function ‘s3c_gpio_cfgall_range’ sound/soc/samsung/s3c2412-i2s.c:86:2: error: implicit declaration of function ‘S3C2410_GPE’ sound/soc/samsung/s3c2412-i2s.c:86:2: error: implicit declaration of function ‘S3C_GPIO_SFN’ sound/soc/samsung/s3c2412-i2s.c:87:10: error: ‘S3C_GPIO_PULL_NONE’ undeclared sound/soc/samsung/s3c24xx-i2s.c:394:2: error: implicit declaration of function ‘s3c_gpio_cfgall_range’ sound/soc/samsung/s3c24xx-i2s.c:394:2: error: implicit declaration of function ‘S3C2410_GPE’ sound/soc/samsung/s3c24xx-i2s.c:394:2: error: implicit declaration of function ‘S3C_GPIO_SFN’ sound/soc/samsung/s3c24xx-i2s.c:395:10: error: ‘S3C_GPIO_PULL_NONE’ undeclared sound/soc/samsung/smartq_wm8987.c:112:3: error: implicit declaration of function ‘S3C64XX_GPL’ Signed-off-by: Sachin Kamat Acked-by: Linus Walleij Signed-off-by: Mark Brown --- sound/soc/samsung/h1940_uda1380.c | 1 + sound/soc/samsung/neo1973_wm8753.c | 1 + sound/soc/samsung/rx1950_uda1380.c | 1 + sound/soc/samsung/s3c2412-i2s.c | 2 ++ sound/soc/samsung/s3c24xx-i2s.c | 2 ++ sound/soc/samsung/smartq_wm8987.c | 1 + 6 files changed, 8 insertions(+) diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index fa91376e323d..fbced589d077 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -23,6 +23,7 @@ #include "regs-iis.h" #include +#include #include "s3c24xx-i2s.h" static unsigned int rates[] = { diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 807db417d234..98a04c11202d 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -20,6 +20,7 @@ #include +#include #include #include "regs-iis.h" diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 704460a37005..06ebdc061770 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -24,6 +24,7 @@ #include #include +#include #include "regs-iis.h" #include diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index ea885cb9f76c..d0794458963a 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -26,6 +26,8 @@ #include #include +#include +#include #include "dma.h" #include "regs-i2s-v2.h" diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 9c8ebd872fac..f31e916dd8c4 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -24,6 +24,8 @@ #include #include +#include +#include #include "regs-iis.h" #include "dma.h" diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 58ae3237ef69..c3b2adafb7b5 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -19,6 +19,7 @@ #include #include +#include #include #include "i2s.h" From 9eeb95727c1137217d61141096c308dc04c234d1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 22 Jan 2014 17:30:39 +0530 Subject: [PATCH 08/42] ASoC: Samsung: s3c-i2s-v2: Fix build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following error introduced by commit eca3b01d0885 ("ASoC: switch over to use snd_soc_register_component() on s3c i2s"): sound/soc/samsung/s3c-i2s-v2.c:732:32: error: ‘drv’ undeclared (first use in this function) Signed-off-by: Sachin Kamat Cc: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/samsung/s3c-i2s-v2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index fefc56100349..79e7efb9283c 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -729,7 +729,7 @@ int s3c_i2sv2_register_component(struct device *dev, int id, struct snd_soc_component_driver *cmp_drv, struct snd_soc_dai_driver *dai_drv) { - struct snd_soc_dai_ops *ops = drv->ops; + struct snd_soc_dai_ops *ops = dai_drv->ops; ops->trigger = s3c2412_i2s_trigger; if (!ops->hw_params) @@ -742,8 +742,8 @@ int s3c_i2sv2_register_component(struct device *dev, int id, if (!ops->delay) ops->delay = s3c2412_i2s_delay; - drv->suspend = s3c2412_i2s_suspend; - drv->resume = s3c2412_i2s_resume; + dai_drv->suspend = s3c2412_i2s_suspend; + dai_drv->resume = s3c2412_i2s_resume; return snd_soc_register_component(dev, cmp_drv, dai_drv, 1); } From f83183ca3c25275706427a9f88f83208dec647d1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 22 Jan 2014 17:30:43 +0530 Subject: [PATCH 09/42] ASoC: samsung: smdk_wm8994: Fix build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following build error and warning when OF is not defined: sound/soc/samsung/smdk_wm8994.c:191:23: error: ‘samsung_wm8994_of_match’ undeclared (first use in this function) sound/soc/samsung/smdk_wm8994.c:47:32: warning: ‘smdk_board_data’ defined but not used [-Wunused-variable] of_match_ptr() is used so that samsung_wm8994_of_match gets dropped (as unused) by the compiler when OF is not defined. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/smdk_wm8994.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index b072bd107b31..d38ae98e2f32 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -152,13 +152,11 @@ static struct snd_soc_card smdk = { .num_links = ARRAY_SIZE(smdk_dai), }; -#ifdef CONFIG_OF static const struct of_device_id samsung_wm8994_of_match[] = { { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data }, {}, }; MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); -#endif /* CONFIG_OF */ static int smdk_audio_probe(struct platform_device *pdev) { @@ -188,7 +186,7 @@ static int smdk_audio_probe(struct platform_device *pdev) smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node; } - id = of_match_device(samsung_wm8994_of_match, &pdev->dev); + id = of_match_device(of_match_ptr(samsung_wm8994_of_match), &pdev->dev); if (id) *board = *((struct smdk_wm8994_data *)id->data); From 4c3773eda49c872a3034382f8ec3080002e715bf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 23 Jan 2014 11:21:28 +0300 Subject: [PATCH 10/42] ALSA: bits vs bytes bug in snd_card_create() The test here is intended intended to prevent shift wrapping bugs when we do "1U << idx2". We should consider the number of bits in a u32 instead of the number of bytes. [fix another chunk similarly by tiwai] Fixes: 7bb2491b35a2 ('ALSA: Add kconfig to specify the max card numbers') Signed-off-by: Dan Carpenter Cc: Signed-off-by: Takashi Iwai --- sound/core/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 1351f22f651c..e3c93cd77ee7 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -170,7 +170,7 @@ int snd_card_create(int idx, const char *xid, if (idx < 0) { for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) { /* idx == -1 == 0xffff means: take any free slot */ - if (idx2 < sizeof(int) && !(idx & (1U << idx2))) + if (idx2 < 32 && !(idx & (1U << idx2))) continue; if (!test_bit(idx2, snd_cards_lock)) { if (module_slot_match(module, idx2)) { @@ -183,7 +183,7 @@ int snd_card_create(int idx, const char *xid, if (idx < 0) { for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) { /* idx == -1 == 0xffff means: take any free slot */ - if (idx2 < sizeof(int) && !(idx & (1U << idx2))) + if (idx2 < 32 && !(idx & (1U << idx2))) continue; if (!test_bit(idx2, snd_cards_lock)) { if (!slots[idx2] || !*slots[idx2]) { From deb6596f163b7340ff8f5a7c23e25317e165c669 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 23 Jan 2014 11:02:15 +0100 Subject: [PATCH 11/42] ALSA: Refactor slot assignment code There are two loops that are almost identical but only with different checks. Refactor them with a simple helper, and give a bit more comments what's doing there. Signed-off-by: Takashi Iwai --- sound/core/init.c | 57 +++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index e3c93cd77ee7..0d42fcda0de2 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -131,6 +131,31 @@ static inline int init_info_for_card(struct snd_card *card) #define init_info_for_card(card) #endif +static int check_empty_slot(struct module *module, int slot) +{ + return !slots[slot] || !*slots[slot]; +} + +/* return an empty slot number (>= 0) found in the given bitmask @mask. + * @mask == -1 == 0xffffffff means: take any free slot up to 32 + * when no slot is available, return the original @mask as is. + */ +static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int), + struct module *module) +{ + int slot; + + for (slot = 0; slot < SNDRV_CARDS; slot++) { + if (slot < 32 && !(mask & (1U << slot))) + continue; + if (!test_bit(slot, snd_cards_lock)) { + if (check(module, slot)) + return slot; /* found */ + } + } + return mask; /* unchanged */ +} + /** * snd_card_create - create and initialize a soundcard structure * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] @@ -152,7 +177,7 @@ int snd_card_create(int idx, const char *xid, struct snd_card **card_ret) { struct snd_card *card; - int err, idx2; + int err; if (snd_BUG_ON(!card_ret)) return -EINVAL; @@ -167,32 +192,10 @@ int snd_card_create(int idx, const char *xid, strlcpy(card->id, xid, sizeof(card->id)); err = 0; mutex_lock(&snd_card_mutex); - if (idx < 0) { - for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) { - /* idx == -1 == 0xffff means: take any free slot */ - if (idx2 < 32 && !(idx & (1U << idx2))) - continue; - if (!test_bit(idx2, snd_cards_lock)) { - if (module_slot_match(module, idx2)) { - idx = idx2; - break; - } - } - } - } - if (idx < 0) { - for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) { - /* idx == -1 == 0xffff means: take any free slot */ - if (idx2 < 32 && !(idx & (1U << idx2))) - continue; - if (!test_bit(idx2, snd_cards_lock)) { - if (!slots[idx2] || !*slots[idx2]) { - idx = idx2; - break; - } - } - } - } + if (idx < 0) /* first check the matching module-name slot */ + idx = get_slot_from_bitmask(idx, module_slot_match, module); + if (idx < 0) /* if not matched, assign an empty slot */ + idx = get_slot_from_bitmask(idx, check_empty_slot, module); if (idx < 0) err = -ENODEV; else if (idx < snd_ecards_limit) { From 0cf0f174400d6953df8c09ed5ed92b37ec7a675b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 23 Jan 2014 16:23:26 +0530 Subject: [PATCH 12/42] ASoC: wm5100: Export wm5100_detect Export the symbol so that it is accessible to modules. Fixes the following error: ERROR: "wm5100_detect" [sound/soc/samsung/snd-soc-lowland.ko] undefined! Signed-off-by: Sachin Kamat Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5100.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 4cf91deabc02..4e3e31aaf509 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -2141,6 +2142,7 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) return 0; } +EXPORT_SYMBOL_GPL(wm5100_detect); static irqreturn_t wm5100_irq(int irq, void *data) { From 4a9eee01b80b2e24d0d177df08334c0e4e4c3375 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 23 Jan 2014 16:22:06 +0530 Subject: [PATCH 13/42] ASoC: samsung: Fix Kconfig dependency Select S3C24XX_DMA instead of S3C2410_DMA to avoid following dependency issues and build errors: warning: (CPU_S3C2410 && CPU_S3C2442 && SND_SOC_SAMSUNG && SND_S3C24XX_I2S && SND_S3C2412_SOC_I2S && SND_SOC_SAMSUNG_SMDK2443_WM9710 && SND_SOC_SAMSUNG_LN2440SBC_ALC650) selects S3C2410_DMA which has unmet direct dependencies (ARCH_S3C24XX && S3C24XX_DMA && (CPU_S3C2410 || CPU_S3C2442)) warning: (CPU_S3C2410 && CPU_S3C2442 && SND_SOC_SAMSUNG && SND_S3C24XX_I2S && SND_S3C2412_SOC_I2S && SND_SOC_SAMSUNG_SMDK2443_WM9710 && SND_SOC_SAMSUNG_LN2440SBC_ALC650) selects S3C2410_DMA which has unmet direct dependencies (ARCH_S3C24XX && S3C24XX_DMA && (CPU_S3C2410 || CPU_S3C2442)) arch/arm/mach-s3c24xx/built-in.o: In function `s3c2410_dma_add': arch/arm/mach-s3c24xx/dma-s3c2410.c:134: undefined reference to `s3c2410_dma_init' arch/arm/mach-s3c24xx/dma-s3c2410.c:135: undefined reference to `s3c24xx_dma_order_set' arch/arm/mach-s3c24xx/dma-s3c2410.c:136: undefined reference to `s3c24xx_dma_init_map' arch/arm/plat-samsung/include/plat/dma-ops.h:60: undefined reference to `s3c_dma_get_ops' sound/soc/samsung/s3c24xx-i2s.c:293: undefined reference to `s3c2410_dma_ctrl' arch/arm/plat-samsung/include/plat/dma-ops.h:60: undefined reference to `s3c_dma_get_ops' arch/arm/plat-samsung/include/plat/dma-ops.h:60: undefined reference to `s3c_dma_get_ops' sound/built-in.o: In function `s3c2412_i2s_trigger': sound/soc/samsung/s3c-i2s-v2.c:432: undefined reference to `s3c2410_dma_ctrl' Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 27930fc432dc..e8fbf4e5732f 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -19,7 +19,7 @@ config SND_S3C_DMA_LEGACY config SND_S3C24XX_I2S tristate - select S3C2410_DMA + select S3C24XX_DMA config SND_S3C_I2SV2_SOC tristate From bb651b3dce07e981e20384efa9cc89b93b974b49 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 10 Jan 2014 13:17:27 +0800 Subject: [PATCH 14/42] ASoC: simple-card: fix simple card widgets routing property name usage Fix the usage of simple card widgets routing property, and make it the same with simple card routing property name. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/simple-card.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index e9e20ec67d62..19c84df5fffa 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -43,7 +43,7 @@ Example: sound { compatible = "simple-audio-card"; simple-audio-card,format = "left_j"; - simple-audio-routing = + simple-audio-card,routing = "MIC_IN", "Mic Jack", "Headphone Jack", "HP_OUT", "Ext Spk", "LINE_OUT"; From 9b35a7b65f15ac8769619146ab12933ffa5dcaf8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 24 Jan 2014 14:09:25 +0530 Subject: [PATCH 15/42] ASoC: Samsung: Fix build error due to missing dependency Depend on MFD_ARIZONA to avoid the following build errors: sound/soc/codecs/arizona.c:218: undefined reference to `arizona_request_irq' sound/soc/codecs/arizona.c:226: undefined reference to `arizona_request_irq' sound/soc/codecs/arizona.c:1719: undefined reference to `arizona_request_irq' Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index e8fbf4e5732f..454f41cfc828 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -210,7 +210,7 @@ config SND_SOC_TOBERMORY config SND_SOC_BELLS tristate "Audio support for Wolfson Bells" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 + depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA select SND_SAMSUNG_I2S select SND_SOC_WM5102 select SND_SOC_WM5110 From 57e33781ce5aaab86b4db7799f3505988b5226e2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 24 Jan 2014 16:23:22 +0530 Subject: [PATCH 16/42] ASoC: samsung: Add NULL check in i2s.c 'res' could be NULL from one of the operations above (line 1243). Thus check 'res' for NULL before releasing the region to avoid null pointer dereference. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 92f64363427d..a9da24f34834 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1268,7 +1268,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) return 0; err: - release_mem_region(regs_base, resource_size(res)); + if (res) + release_mem_region(regs_base, resource_size(res)); return ret; } From 4c021cc5a49eeb423c2708374a5745b1acdc4f8b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 24 Jan 2014 16:23:23 +0530 Subject: [PATCH 17/42] ASoC: samsung: Remove dma.h inclusion mach/dma.h is not referenced by this file. Remove it. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index a9da24f34834..0a9b44c940ce 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -22,8 +22,6 @@ #include #include -#include - #include #include "dma.h" From e7729a415315fcd9516912050d85d5aaebcededc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Fri, 24 Jan 2014 14:56:14 -0500 Subject: [PATCH 18/42] ALSA: hda - Fix silent output on MacBook Air 1,1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to other Apple products, MBA 1,1 needs a specific quirk. Pin 0x18 must be set to VREF_50 to have sound output. This was no longer done since commit 1a97b7f, resulting in a mute built-in speaker. This patch corrects the regression by creating a fixup for the MBA 1,1. Fixes: 1a97b7f22774 ("ALSA: hda/realtek - Remove the last static quirks for ALC882") Cc: [v3.4+] Tested-by: Adrien Vergé Signed-off-by: Adrien Vergé Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c435ae0a19a6..d723c1fdd5f3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1819,6 +1819,7 @@ enum { ALC889_FIXUP_DAC_ROUTE, ALC889_FIXUP_MBP_VREF, ALC889_FIXUP_IMAC91_VREF, + ALC889_FIXUP_MBA11_VREF, ALC889_FIXUP_MBA21_VREF, ALC882_FIXUP_INV_DMIC, ALC882_FIXUP_NO_PRIMARY_HP, @@ -1949,6 +1950,16 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); } +/* Set VREF on speaker pins on mba11 */ +static void alc889_fixup_mba11_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static hda_nid_t nids[1] = { 0x18 }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + /* Set VREF on speaker pins on mba21 */ static void alc889_fixup_mba21_vref(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -2167,6 +2178,12 @@ static const struct hda_fixup alc882_fixups[] = { .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, + [ALC889_FIXUP_MBA11_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba11_vref, + .chained = true, + .chain_id = ALC889_FIXUP_MBP_VREF, + }, [ALC889_FIXUP_MBA21_VREF] = { .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_mba21_vref, @@ -2242,7 +2259,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF), SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF), SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), From b44aa40f875b06b1986a18251d601ee1832c4476 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 27 Jan 2014 13:03:05 +0100 Subject: [PATCH 19/42] ASoC: tlv320aic32x4: Fix mono playback Playback of a mono stream should output the same stream on both channels. At the moment only the left analog signal is valid, the right one is just noise. This patch maps the left digital channel onto both DACs when receiving a mono stream. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 18 +++++++++++------- sound/soc/codecs/tlv320aic32x4.h | 1 + 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 385dec16eb8a..ee9ad05f9137 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -450,6 +450,17 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, } snd_soc_write(codec, AIC32X4_IFACE1, data); + if (params_channels(params) == 1) { + data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; + } else { + if (aic32x4->swapdacs) + data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN; + else + data = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; + } + snd_soc_update_bits(codec, AIC32X4_DACSETUP, AIC32X4_DAC_CHAN_MASK, + data); + return 0; } @@ -606,13 +617,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec) } snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); - /* Do DACs need to be swapped? */ - if (aic32x4->swapdacs) { - snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); - } else { - snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); - } - /* Mic PGA routing */ if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 35774223fd91..83795af8c75f 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -138,6 +138,7 @@ #define AIC32X4_LDAC2RCHN (0x02 << 4) #define AIC32X4_LDAC2LCHN (0x01 << 4) #define AIC32X4_RDAC2RCHN (0x01 << 2) +#define AIC32X4_DAC_CHAN_MASK 0x3c #define AIC32X4_SSTEP2WCLK 0x01 #define AIC32X4_MUTEON 0x0C From 609e6025b8ed347c5eba54eb12357193e46dd34f Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 27 Jan 2014 13:03:06 +0100 Subject: [PATCH 20/42] ASoC: tlv320aic32x4: Fix MICPGA input configuration Currently the Negative Terminal Input Routing Configuration is only set when there is a special routing configuration. If we don't use one of the inputs IN1 or IN2 as negative terminal input, the PGA and recording does not work. This patch adds a route from CM1L/CM1R to the PGA as negative input by default. With this configuration the PGA can amplify all input signals and line-in/mic works again. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 10 ++++++---- sound/soc/codecs/tlv320aic32x4.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index ee9ad05f9137..688151ba309a 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -618,12 +618,14 @@ static int aic32x4_probe(struct snd_soc_codec *codec) snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); /* Mic PGA routing */ - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); - } - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + else + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_CM1L_10K); + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); - } + else + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_CM1R_10K); aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 83795af8c75f..995f033a855d 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -120,7 +120,9 @@ #define AIC32X4_MICBIAS_2075V 0x60 #define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_LMICPGANIN_CM1L_10K 0x40 #define AIC32X4_RMICPGANIN_IN1L_10K 0x10 +#define AIC32X4_RMICPGANIN_CM1R_10K 0x40 #define AIC32X4_LMICPGAVOL_NOGAIN 0x80 #define AIC32X4_RMICPGAVOL_NOGAIN 0x80 From cd262518a3ae4465e8e51c29641d98c4ed0651a1 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 29 Jan 2014 10:37:10 +0100 Subject: [PATCH 21/42] ALSA: hda - Add parameter for dumping processing coefficients Processing coefficients are often a vital part of the codec's configuration, so dumping them can be important. However, because they are undocumented and secret, we do not want to enable this for all codecs by default. Therefore instead add this as a debugging parameter. I have prepared for codecs that want to enable this by default by the extra dump_coef bitfield, but unsure if we want to do that as long as the (unlikely, but still) race remains. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_proc.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2b5d19e48a27..ab2a444ba501 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -361,6 +361,7 @@ struct hda_codec { unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */ + unsigned int dump_coef:1; /* dump processing coefs in codec proc file */ #ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index a8cb22eec89e..ce5a6da83419 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -24,9 +24,14 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" +static int dump_coef = -1; +module_param(dump_coef, int, 0644); +MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)"); + static char *bits_names(unsigned int bits, char *names[], int size) { int i, n; @@ -488,14 +493,39 @@ static void print_unsol_cap(struct snd_info_buffer *buffer, (unsol & AC_UNSOL_ENABLED) ? 1 : 0); } +static inline bool can_dump_coef(struct hda_codec *codec) +{ + switch (dump_coef) { + case 0: return false; + case 1: return true; + default: return codec->dump_coef; + } +} + static void print_proc_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { + unsigned int i, ncoeff, oldindex; unsigned int proc_caps = snd_hda_param_read(codec, nid, AC_PAR_PROC_CAP); + ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT; snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", - proc_caps & AC_PCAP_BENIGN, - (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT); + proc_caps & AC_PCAP_BENIGN, ncoeff); + + if (!can_dump_coef(codec)) + return; + + /* Note: This is racy - another process could run in parallel and change + the coef index too. */ + oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0); + for (i = 0; i < ncoeff; i++) { + unsigned int val; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i); + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, + 0); + snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val); + } + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex); } static void print_conn_list(struct snd_info_buffer *buffer, From 10dd44dc88419ba5278435669ac9e35f9f410c7e Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:02 +0400 Subject: [PATCH 22/42] ALSA: oxygen: add the separate SPI waiting function The oxygen_wait_spi() function now performs waiting when the SPI bus completes a transaction. Introduce the timeout error checking and increase timeout to 200 from 40. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/oxygen_io.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 521eae458348..4b88c7a332e4 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -194,6 +194,24 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, } EXPORT_SYMBOL(oxygen_write_ac97_masked); +static int oxygen_wait_spi(struct oxygen *chip) +{ + unsigned int count; + + /* + * Higher timeout to be sure: 200 us; + * actual transaction should not need more than 40 us. + */ + for (count = 50; count > 0; count--) { + udelay(4); + if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & + OXYGEN_SPI_BUSY) == 0) + return 0; + } + snd_printk(KERN_ERR "oxygen: SPI wait timeout\n"); + return -EIO; +} + void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) { unsigned int count; From 303cff30d36e6a450c3ced1e3bed034038d87a86 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:03 +0400 Subject: [PATCH 23/42] ALSA: oxygen: modify the SPI writing function Modify the oxygen_write_spi() function to use the newly introduced oxygen_wait_spi() function. Change return value from void to int, so it can return error codes. Older drivers just ignore that return value, new drivers can check this value. We need to wait AFTER initiating the SPI transaction, otherwise read operation will not work. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/oxygen.h | 2 +- sound/pci/oxygen/oxygen_io.c | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 09a24b24958b..c10ab077afd8 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -198,7 +198,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, unsigned int index, u16 data, u16 mask); -void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); +int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data); void oxygen_reset_uart(struct oxygen *chip); diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 4b88c7a332e4..3274907189fe 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -212,23 +212,18 @@ static int oxygen_wait_spi(struct oxygen *chip) return -EIO; } -void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) { - unsigned int count; - - /* should not need more than 30.72 us (24 * 1.28 us) */ - count = 10; - while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) - && count > 0) { - udelay(4); - --count; - } - + /* + * We need to wait AFTER initiating the SPI transaction, + * otherwise read operations will not work. + */ oxygen_write8(chip, OXYGEN_SPI_DATA1, data); oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); if (control & OXYGEN_SPI_DATA_LENGTH_3) oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); + return oxygen_wait_spi(chip); } EXPORT_SYMBOL(oxygen_write_spi); From 079e0cb776cf52b09fae89551b8a7eaff5639166 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:04 +0400 Subject: [PATCH 24/42] ALSA: oxygen: add mute mask for the OXYGEN_PLAY_ROUTING register The Xonar DG/DGX driver needs this mask to mute unnecessary channels. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/oxygen_regs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h index 63dc7a0ab555..8c191badaae8 100644 --- a/sound/pci/oxygen/oxygen_regs.h +++ b/sound/pci/oxygen/oxygen_regs.h @@ -318,6 +318,7 @@ #define OXYGEN_PLAY_MUTE23 0x0002 #define OXYGEN_PLAY_MUTE45 0x0004 #define OXYGEN_PLAY_MUTE67 0x0008 +#define OXYGEN_PLAY_MUTE_MASK 0x000f #define OXYGEN_PLAY_MULTICH_MASK 0x0010 #define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000 #define OXYGEN_PLAY_MULTICH_AC97 0x0010 From e9c2f10b432d96b1ba28bdc22bd984e7eacd2acf Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:05 +0400 Subject: [PATCH 25/42] ALSA: oxygen: export oxygen_update_dac_routing symbol When the user switches the output from stereo to multichannel or vice versa, the driver needs to update the channel routing. Instead of creating additional subroutines, I better export existing oxygen_update_dac_routing symbol from the oxygen mixer and call this function. It calls model.adjust_dac_routing() and my function does the work. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/oxygen_mixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index c0dbb52d45be..5988e044c519 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -190,6 +190,7 @@ void oxygen_update_dac_routing(struct oxygen *chip) if (chip->model.update_center_lfe_mix) chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2); } +EXPORT_SYMBOL(oxygen_update_dac_routing); static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { From c4d4390c581603ec449c648df3be6b50281db8a3 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:06 +0400 Subject: [PATCH 26/42] ALSA: oxygen: change description of the xonar_dg.c file Add some additional information in comments and my copyright. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 77acd790ea47..7f5259e70a71 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -2,7 +2,7 @@ * card driver for the Xonar DG/DGX * * Copyright (c) Clemens Ladisch - * + * Copyright (c) Roman Volkov * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2. @@ -20,27 +20,35 @@ * Xonar DG/DGX * ------------ * + * CS4245 and CS4361 both will mute all outputs if any clock ratio + * is invalid. + * * CMI8788: * * SPI 0 -> CS4245 * + * Playback: * I²S 1 -> CS4245 * I²S 2 -> CS4361 (center/LFE) * I²S 3 -> CS4361 (surround) * I²S 4 -> CS4361 (front) + * Capture: + * I²S ADC 1 <- CS4245 * * GPIO 3 <- ? * GPIO 4 <- headphone detect - * GPIO 5 -> route input jack to line-in (0) or mic-in (1) - * GPIO 6 -> route input jack to line-in (0) or mic-in (1) - * GPIO 7 -> enable rear headphone amp + * GPIO 5 -> enable ADC analog circuit for the left channel + * GPIO 6 -> enable ADC analog circuit for the right channel + * GPIO 7 -> switch green rear output jack between CS4245 and and the first + * channel of CS4361 (mechanical relay) * GPIO 8 -> enable output to speakers * * CS4245: * + * input 0 <- mic * input 1 <- aux * input 2 <- front mic - * input 4 <- line/mic + * input 4 <- line * DAC out -> headphones * aux out -> front panel headphones */ From ddd624c332698eb3ee5293bca6b5b3a97d05c0b6 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:07 +0400 Subject: [PATCH 27/42] ALSA: oxygen: additional definitions for the Xonar DG/DGX card Add additional constants to the xonar_dg.h file: capture and playback sources. Move GPIO_* constants and the dg struct to the header file from the xonar_dg.c file. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 14 -------------- sound/pci/oxygen/xonar_dg.h | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 7f5259e70a71..c175720f1c7a 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -64,20 +64,6 @@ #include "xonar_dg.h" #include "cs4245.h" -#define GPIO_MAGIC 0x0008 -#define GPIO_HP_DETECT 0x0010 -#define GPIO_INPUT_ROUTE 0x0060 -#define GPIO_HP_REAR 0x0080 -#define GPIO_OUTPUT_ENABLE 0x0100 - -struct dg { - unsigned int output_sel; - s8 input_vol[4][2]; - unsigned int input_sel; - u8 hp_vol_att; - u8 cs4245_regs[0x11]; -}; - static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 5688d78609a9..081269224850 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -3,6 +3,29 @@ #include "oxygen.h" +#define GPIO_MAGIC 0x0008 +#define GPIO_HP_DETECT 0x0010 +#define GPIO_INPUT_ROUTE 0x0060 +#define GPIO_HP_REAR 0x0080 +#define GPIO_OUTPUT_ENABLE 0x0100 + +#define CAPTURE_SRC_MIC 0 +#define CAPTURE_SRC_FP_MIC 1 +#define CAPTURE_SRC_LINE 2 +#define CAPTURE_SRC_AUX 3 + +#define PLAYBACK_DST_HP 0 +#define PLAYBACK_DST_HP_FP 1 +#define PLAYBACK_DST_MULTICH 2 + +struct dg { + unsigned int output_sel; + s8 input_vol[4][2]; + unsigned int input_sel; + u8 hp_vol_att; + u8 cs4245_regs[0x11]; +}; + extern struct oxygen_model model_xonar_dg; #endif From bed61935cc5b70f84480dfd465c0e15a060c1f2c Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:08 +0400 Subject: [PATCH 28/42] ALSA: oxygen: Xonar DG(X): add new CS4245 SPI functions Add the new SPI write and read functions. The SPI read function is used for creating initial registers dump and may be used for debugging purposes. SPI operations are cached, so there is a new function to manage the cache (shadow). I have to remove the shift from the CS4245_SPI_* constants, since when we are performing the reading, we need to shift by 8 instead of 16. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/cs4245.h | 7 +++-- sound/pci/oxygen/xonar_dg.c | 63 +++++++++++++++++++++++++++++++++++-- sound/pci/oxygen/xonar_dg.h | 7 +++++ 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/sound/pci/oxygen/cs4245.h b/sound/pci/oxygen/cs4245.h index 5e0197e07dd1..99098657695a 100644 --- a/sound/pci/oxygen/cs4245.h +++ b/sound/pci/oxygen/cs4245.h @@ -102,6 +102,9 @@ #define CS4245_ADC_OVFL 0x02 #define CS4245_ADC_UNDRFL 0x01 +#define CS4245_SPI_ADDRESS_S (0x9e << 16) +#define CS4245_SPI_WRITE_S (0 << 16) -#define CS4245_SPI_ADDRESS (0x9e << 16) -#define CS4245_SPI_WRITE (0 << 16) +#define CS4245_SPI_ADDRESS 0x9e +#define CS4245_SPI_WRITE 0 +#define CS4245_SPI_READ 1 diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index c175720f1c7a..2518c611d5c5 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -64,6 +64,65 @@ #include "xonar_dg.h" #include "cs4245.h" +int cs4245_write_spi(struct oxygen *chip, u8 reg) +{ + struct dg *data = chip->model_data; + unsigned int packet; + + packet = reg << 8; + packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16; + packet |= data->cs4245_shadow[reg]; + + return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_3 | + OXYGEN_SPI_CLOCK_1280 | + (0 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + packet); +} + +int cs4245_read_spi(struct oxygen *chip, u8 addr) +{ + struct dg *data = chip->model_data; + int ret; + + ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI | + OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), + ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr); + if (ret < 0) + return ret; + + ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI | + OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), + (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8); + if (ret < 0) + return ret; + + data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1); + + return 0; +} + +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) +{ + struct dg *data = chip->model_data; + unsigned char addr; + int ret; + + for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) { + ret = (op == CS4245_SAVE_TO_SHADOW ? + cs4245_read_spi(chip, addr) : + cs4245_write_spi(chip, addr)); + if (ret < 0) + return ret; + } + return 0; +} + static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; @@ -73,8 +132,8 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, - CS4245_SPI_ADDRESS | - CS4245_SPI_WRITE | + CS4245_SPI_ADDRESS_S | + CS4245_SPI_WRITE_S | (reg << 8) | value); data->cs4245_regs[reg] = value; } diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 081269224850..f2fa846d7246 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -18,7 +18,14 @@ #define PLAYBACK_DST_HP_FP 1 #define PLAYBACK_DST_MULTICH 2 +enum cs4245_shadow_operation { + CS4245_SAVE_TO_SHADOW, + CS4245_LOAD_FROM_SHADOW +}; + struct dg { + /* shadow copy of the CS4245 register space */ + unsigned char cs4245_shadow[17]; unsigned int output_sel; s8 input_vol[4][2]; unsigned int input_sel; From 3c1611ddd1b67628bd946111edae8f5366a14edf Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:09 +0400 Subject: [PATCH 29/42] ALSA: oxygen: Xonar DG(X): modify initialization functions Change CS4245 initialization: different sequence and GPIO values, according to datasheets and reverse-engineering information. Change cleanup/resume/suspend functions, since they use initialization. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 110 +++++++++++++++--------------------- sound/pci/oxygen/xonar_dg.h | 1 - 2 files changed, 44 insertions(+), 67 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 2518c611d5c5..4177fb131086 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -135,68 +135,47 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) CS4245_SPI_ADDRESS_S | CS4245_SPI_WRITE_S | (reg << 8) | value); - data->cs4245_regs[reg] = value; + data->cs4245_shadow[reg] = value; } static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; - if (value != data->cs4245_regs[reg]) + if (value != data->cs4245_shadow[reg]) cs4245_write(chip, reg, value); } -static void cs4245_registers_init(struct oxygen *chip) -{ - struct dg *data = chip->model_data; - - cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN); - cs4245_write(chip, CS4245_DAC_CTRL_1, - data->cs4245_regs[CS4245_DAC_CTRL_1]); - cs4245_write(chip, CS4245_ADC_CTRL, - data->cs4245_regs[CS4245_ADC_CTRL]); - cs4245_write(chip, CS4245_SIGNAL_SEL, - data->cs4245_regs[CS4245_SIGNAL_SEL]); - cs4245_write(chip, CS4245_PGA_B_CTRL, - data->cs4245_regs[CS4245_PGA_B_CTRL]); - cs4245_write(chip, CS4245_PGA_A_CTRL, - data->cs4245_regs[CS4245_PGA_A_CTRL]); - cs4245_write(chip, CS4245_ANALOG_IN, - data->cs4245_regs[CS4245_ANALOG_IN]); - cs4245_write(chip, CS4245_DAC_A_CTRL, - data->cs4245_regs[CS4245_DAC_A_CTRL]); - cs4245_write(chip, CS4245_DAC_B_CTRL, - data->cs4245_regs[CS4245_DAC_B_CTRL]); - cs4245_write(chip, CS4245_DAC_CTRL_2, - CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC); - cs4245_write(chip, CS4245_INT_MASK, 0); - cs4245_write(chip, CS4245_POWER_CTRL, 0); -} - static void cs4245_init(struct oxygen *chip) { struct dg *data = chip->model_data; - data->cs4245_regs[CS4245_DAC_CTRL_1] = - CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; - data->cs4245_regs[CS4245_ADC_CTRL] = - CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; - data->cs4245_regs[CS4245_SIGNAL_SEL] = - CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH; - data->cs4245_regs[CS4245_PGA_B_CTRL] = 0; - data->cs4245_regs[CS4245_PGA_A_CTRL] = 0; - data->cs4245_regs[CS4245_ANALOG_IN] = - CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4; - data->cs4245_regs[CS4245_DAC_A_CTRL] = 0; - data->cs4245_regs[CS4245_DAC_B_CTRL] = 0; - cs4245_registers_init(chip); - snd_component_add(chip->card, "CS4245"); -} + /* save the initial state: codec version, registers */ + cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW); -static void dg_output_enable(struct oxygen *chip) -{ - msleep(2500); - oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + /* + * Power up the CODEC internals, enable soft ramp & zero cross, work in + * async. mode, enable aux output from DAC. Invert DAC output as in the + * Windows driver. + */ + data->cs4245_shadow[CS4245_POWER_CTRL] = 0; + data->cs4245_shadow[CS4245_SIGNAL_SEL] = + CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH; + data->cs4245_shadow[CS4245_DAC_CTRL_1] = + CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; + data->cs4245_shadow[CS4245_DAC_CTRL_2] = + CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC; + data->cs4245_shadow[CS4245_ADC_CTRL] = + CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; + data->cs4245_shadow[CS4245_ANALOG_IN] = + CS4245_PGA_SOFT | CS4245_PGA_ZERO; + data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0; + data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0; + data->cs4245_shadow[CS4245_DAC_A_CTRL] = 4; + data->cs4245_shadow[CS4245_DAC_B_CTRL] = 4; + + cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); + snd_component_add(chip->card, "CS4245"); } static void dg_init(struct oxygen *chip) @@ -208,14 +187,12 @@ static void dg_init(struct oxygen *chip) data->hp_vol_att = 2 * 16; cs4245_init(chip); - - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_MAGIC | GPIO_HP_DETECT); - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, - GPIO_INPUT_ROUTE | GPIO_HP_REAR); - dg_output_enable(chip); + oxygen_write16(chip, OXYGEN_GPIO_CONTROL, + GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE); + oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE); + msleep(2500); /* anti-pop delay */ + oxygen_write16(chip, OXYGEN_GPIO_DATA, + GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); } static void dg_cleanup(struct oxygen *chip) @@ -230,8 +207,9 @@ static void dg_suspend(struct oxygen *chip) static void dg_resume(struct oxygen *chip) { - cs4245_registers_init(chip); - dg_output_enable(chip); + cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); + msleep(2500); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); } static void set_cs4245_dac_params(struct oxygen *chip, @@ -240,7 +218,7 @@ static void set_cs4245_dac_params(struct oxygen *chip, struct dg *data = chip->model_data; u8 value; - value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; + value = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; if (params_rate(params) <= 50000) value |= CS4245_DAC_FM_SINGLE; else if (params_rate(params) <= 100000) @@ -256,7 +234,7 @@ static void set_cs4245_adc_params(struct oxygen *chip, struct dg *data = chip->model_data; u8 value; - value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; + value = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; if (params_rate(params) <= 50000) value |= CS4245_ADC_FM_SINGLE; else if (params_rate(params) <= 100000) @@ -333,7 +311,7 @@ static int output_switch_put(struct snd_kcontrol *ctl, if (changed) { data->output_sel = value->value.enumerated.item[0]; - reg = data->cs4245_regs[CS4245_SIGNAL_SEL] & + reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & ~CS4245_A_OUT_SEL_MASK; reg |= data->output_sel == 2 ? CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; @@ -504,7 +482,7 @@ static int input_sel_put(struct snd_kcontrol *ctl, data->input_sel = value->value.enumerated.item[0]; cs4245_write(chip, CS4245_ANALOG_IN, - (data->cs4245_regs[CS4245_ANALOG_IN] & + (data->cs4245_shadow[CS4245_ANALOG_IN] & ~CS4245_SEL_MASK) | sel_values[data->input_sel]); @@ -534,7 +512,7 @@ static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) struct dg *data = chip->model_data; value->value.enumerated.item[0] = - !!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); + !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); return 0; } @@ -546,10 +524,10 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) int changed; mutex_lock(&chip->mutex); - reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; + reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; if (value->value.enumerated.item[0]) reg |= CS4245_HPF_FREEZE; - changed = reg != data->cs4245_regs[CS4245_ADC_CTRL]; + changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; if (changed) cs4245_write(chip, CS4245_ADC_CTRL, reg); mutex_unlock(&chip->mutex); @@ -629,7 +607,7 @@ static void dump_cs4245_registers(struct oxygen *chip, snd_iprintf(buffer, "\nCS4245:"); for (i = 1; i <= 0x10; ++i) - snd_iprintf(buffer, " %02x", data->cs4245_regs[i]); + snd_iprintf(buffer, " %02x", data->cs4245_shadow[i]); snd_iprintf(buffer, "\n"); } diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index f2fa846d7246..944685edfce4 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -30,7 +30,6 @@ struct dg { s8 input_vol[4][2]; unsigned int input_sel; u8 hp_vol_att; - u8 cs4245_regs[0x11]; }; extern struct oxygen_model model_xonar_dg; From fddc106bc35ac2663f42c99bdf404c155a34b9a7 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:10 +0400 Subject: [PATCH 30/42] ALSA: oxygen: Xonar DG(X): modify DAC/ADC parameters function When selecting the audio sample rate for CS4245, the MCLK divider should also be changed. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 54 ++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 4177fb131086..81c004c78766 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -216,32 +216,50 @@ static void set_cs4245_dac_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; - u8 value; + unsigned char dac_ctrl; + unsigned char mclk_freq; - value = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; - if (params_rate(params) <= 50000) - value |= CS4245_DAC_FM_SINGLE; - else if (params_rate(params) <= 100000) - value |= CS4245_DAC_FM_DOUBLE; - else - value |= CS4245_DAC_FM_QUAD; - cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value); + dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; + mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK; + if (params_rate(params) <= 50000) { + dac_ctrl |= CS4245_DAC_FM_SINGLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; + } else if (params_rate(params) <= 100000) { + dac_ctrl |= CS4245_DAC_FM_DOUBLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; + } else { + dac_ctrl |= CS4245_DAC_FM_QUAD; + mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT; + } + data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl; + data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; + cs4245_write_spi(chip, CS4245_DAC_CTRL_1); + cs4245_write_spi(chip, CS4245_MCLK_FREQ); } static void set_cs4245_adc_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; - u8 value; + unsigned char adc_ctrl; + unsigned char mclk_freq; - value = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; - if (params_rate(params) <= 50000) - value |= CS4245_ADC_FM_SINGLE; - else if (params_rate(params) <= 100000) - value |= CS4245_ADC_FM_DOUBLE; - else - value |= CS4245_ADC_FM_QUAD; - cs4245_write_cached(chip, CS4245_ADC_CTRL, value); + adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; + mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK; + if (params_rate(params) <= 50000) { + adc_ctrl |= CS4245_ADC_FM_SINGLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; + } else if (params_rate(params) <= 100000) { + adc_ctrl |= CS4245_ADC_FM_DOUBLE; + mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; + } else { + adc_ctrl |= CS4245_ADC_FM_QUAD; + mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT; + } + data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl; + data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; + cs4245_write_spi(chip, CS4245_ADC_CTRL); + cs4245_write_spi(chip, CS4245_MCLK_FREQ); } static inline unsigned int shift_bits(unsigned int value, From 1f91ecc14deea9461aca93273d78871ec4d98fcd Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:11 +0400 Subject: [PATCH 31/42] ALSA: oxygen: modify adjust_dg_dac_routing function When selecting the audio output destinations (headphones, FP headphones, multichannel output), the channel routing should be changed depending on what destination selected. Also unnecessary I2S channels are digitally muted. This function called when the user selects the destination in the ALSA mixer. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 44 +++++++++++++++++-------------------- sound/pci/oxygen/xonar_dg.h | 2 ++ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 81c004c78766..329da5434556 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -262,33 +262,29 @@ static void set_cs4245_adc_params(struct oxygen *chip, cs4245_write_spi(chip, CS4245_MCLK_FREQ); } -static inline unsigned int shift_bits(unsigned int value, - unsigned int shift_from, - unsigned int shift_to, - unsigned int mask) -{ - if (shift_from < shift_to) - return (value << (shift_to - shift_from)) & mask; - else - return (value >> (shift_from - shift_to)) & mask; -} - static unsigned int adjust_dg_dac_routing(struct oxygen *chip, unsigned int play_routing) { - return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | - shift_bits(play_routing, - OXYGEN_PLAY_DAC2_SOURCE_SHIFT, - OXYGEN_PLAY_DAC1_SOURCE_SHIFT, - OXYGEN_PLAY_DAC1_SOURCE_MASK) | - shift_bits(play_routing, - OXYGEN_PLAY_DAC1_SOURCE_SHIFT, - OXYGEN_PLAY_DAC2_SOURCE_SHIFT, - OXYGEN_PLAY_DAC2_SOURCE_MASK) | - shift_bits(play_routing, - OXYGEN_PLAY_DAC0_SOURCE_SHIFT, - OXYGEN_PLAY_DAC3_SOURCE_SHIFT, - OXYGEN_PLAY_DAC3_SOURCE_MASK); + struct dg *data = chip->model_data; + unsigned int routing = 0; + + switch (data->pcm_output) { + case PLAYBACK_DST_HP: + case PLAYBACK_DST_HP_FP: + oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, + OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 | + OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK); + break; + case PLAYBACK_DST_MULTICH: + routing = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, + OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK); + break; + } + return routing; } static int output_switch_info(struct snd_kcontrol *ctl, diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 944685edfce4..a9fba40bc013 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -26,6 +26,8 @@ enum cs4245_shadow_operation { struct dg { /* shadow copy of the CS4245 register space */ unsigned char cs4245_shadow[17]; + /* output select: headphone/speakers */ + unsigned char pcm_output; unsigned int output_sel; s8 input_vol[4][2]; unsigned int input_sel; From 06f70d0da029a0ae9dbb7e383f853db483575f87 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:12 +0400 Subject: [PATCH 32/42] ALSA: oxygen: modify CS4245 register dumping function Change the function to read the data from the new shadow buffer. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 329da5434556..4a4266519934 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -617,11 +617,12 @@ static void dump_cs4245_registers(struct oxygen *chip, struct snd_info_buffer *buffer) { struct dg *data = chip->model_data; - unsigned int i; + unsigned int addr; snd_iprintf(buffer, "\nCS4245:"); - for (i = 1; i <= 0x10; ++i) - snd_iprintf(buffer, " %02x", data->cs4245_shadow[i]); + cs4245_read_spi(chip, CS4245_INT_STATUS); + for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) + snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); snd_iprintf(buffer, "\n"); } From 041f26b6257d15449e1634ba8dea426dd06514c7 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:13 +0400 Subject: [PATCH 33/42] ALSA: oxygen: Xonar DG(X): move the mixer code into another file Moving the mixer code away makes things easier. The mixer will control the driver, so the functions of the driver need to be non-static. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/Makefile | 2 +- sound/pci/oxygen/xonar_dg.c | 373 +---------------------------- sound/pci/oxygen/xonar_dg.h | 19 ++ sound/pci/oxygen/xonar_dg_mixer.c | 381 ++++++++++++++++++++++++++++++ 4 files changed, 411 insertions(+), 364 deletions(-) create mode 100644 sound/pci/oxygen/xonar_dg_mixer.c diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 0f8726551fde..8f4c409f7e45 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,5 +1,5 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o -snd-oxygen-objs := oxygen.o xonar_dg.o +snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o snd-virtuoso-objs := virtuoso.o xonar_lib.o \ xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 4a4266519934..d12d83554abb 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -123,7 +123,7 @@ int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) return 0; } -static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) +void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; @@ -138,7 +138,7 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) data->cs4245_shadow[reg] = value; } -static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) +void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) { struct dg *data = chip->model_data; @@ -178,7 +178,7 @@ static void cs4245_init(struct oxygen *chip) snd_component_add(chip->card, "CS4245"); } -static void dg_init(struct oxygen *chip) +void dg_init(struct oxygen *chip) { struct dg *data = chip->model_data; @@ -195,24 +195,24 @@ static void dg_init(struct oxygen *chip) GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); } -static void dg_cleanup(struct oxygen *chip) +void dg_cleanup(struct oxygen *chip) { oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); } -static void dg_suspend(struct oxygen *chip) +void dg_suspend(struct oxygen *chip) { dg_cleanup(chip); } -static void dg_resume(struct oxygen *chip) +void dg_resume(struct oxygen *chip) { cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); msleep(2500); oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); } -static void set_cs4245_dac_params(struct oxygen *chip, +void set_cs4245_dac_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; @@ -237,7 +237,7 @@ static void set_cs4245_dac_params(struct oxygen *chip, cs4245_write_spi(chip, CS4245_MCLK_FREQ); } -static void set_cs4245_adc_params(struct oxygen *chip, +void set_cs4245_adc_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct dg *data = chip->model_data; @@ -262,7 +262,7 @@ static void set_cs4245_adc_params(struct oxygen *chip, cs4245_write_spi(chip, CS4245_MCLK_FREQ); } -static unsigned int adjust_dg_dac_routing(struct oxygen *chip, +unsigned int adjust_dg_dac_routing(struct oxygen *chip, unsigned int play_routing) { struct dg *data = chip->model_data; @@ -287,333 +287,7 @@ static unsigned int adjust_dg_dac_routing(struct oxygen *chip, return routing; } -static int output_switch_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "Speakers", "Headphones", "FP Headphones" - }; - - return snd_ctl_enum_info(info, 1, 3, names); -} - -static int output_switch_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - mutex_lock(&chip->mutex); - value->value.enumerated.item[0] = data->output_sel; - mutex_unlock(&chip->mutex); - return 0; -} - -static int output_switch_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - u8 reg; - int changed; - - if (value->value.enumerated.item[0] > 2) - return -EINVAL; - - mutex_lock(&chip->mutex); - changed = value->value.enumerated.item[0] != data->output_sel; - if (changed) { - data->output_sel = value->value.enumerated.item[0]; - - reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & - ~CS4245_A_OUT_SEL_MASK; - reg |= data->output_sel == 2 ? - CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; - cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); - - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, - data->output_sel ? data->hp_vol_att : 0); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, - data->output_sel ? data->hp_vol_att : 0); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->output_sel == 1 ? GPIO_HP_REAR : 0, - GPIO_HP_REAR); - } - mutex_unlock(&chip->mutex); - return changed; -} - -static int hp_volume_offset_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "< 64 ohms", "64-150 ohms", "150-300 ohms" - }; - - return snd_ctl_enum_info(info, 1, 3, names); -} - -static int hp_volume_offset_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - mutex_lock(&chip->mutex); - if (data->hp_vol_att > 2 * 7) - value->value.enumerated.item[0] = 0; - else if (data->hp_vol_att > 0) - value->value.enumerated.item[0] = 1; - else - value->value.enumerated.item[0] = 2; - mutex_unlock(&chip->mutex); - return 0; -} - -static int hp_volume_offset_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - s8 att; - int changed; - - if (value->value.enumerated.item[0] > 2) - return -EINVAL; - att = atts[value->value.enumerated.item[0]]; - mutex_lock(&chip->mutex); - changed = att != data->hp_vol_att; - if (changed) { - data->hp_vol_att = att; - if (data->output_sel) { - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); - } - } - mutex_unlock(&chip->mutex); - return changed; -} - -static int input_vol_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 2; - info->value.integer.min = 2 * -12; - info->value.integer.max = 2 * 12; - return 0; -} - -static int input_vol_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - unsigned int idx = ctl->private_value; - - mutex_lock(&chip->mutex); - value->value.integer.value[0] = data->input_vol[idx][0]; - value->value.integer.value[1] = data->input_vol[idx][1]; - mutex_unlock(&chip->mutex); - return 0; -} - -static int input_vol_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - unsigned int idx = ctl->private_value; - int changed = 0; - - if (value->value.integer.value[0] < 2 * -12 || - value->value.integer.value[0] > 2 * 12 || - value->value.integer.value[1] < 2 * -12 || - value->value.integer.value[1] > 2 * 12) - return -EINVAL; - mutex_lock(&chip->mutex); - changed = data->input_vol[idx][0] != value->value.integer.value[0] || - data->input_vol[idx][1] != value->value.integer.value[1]; - if (changed) { - data->input_vol[idx][0] = value->value.integer.value[0]; - data->input_vol[idx][1] = value->value.integer.value[1]; - if (idx == data->input_sel) { - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[idx][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[idx][1]); - } - } - mutex_unlock(&chip->mutex); - return changed; -} - -static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); - -static int input_sel_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[4] = { - "Mic", "Aux", "Front Mic", "Line" - }; - - return snd_ctl_enum_info(info, 1, 4, names); -} - -static int input_sel_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - mutex_lock(&chip->mutex); - value->value.enumerated.item[0] = data->input_sel; - mutex_unlock(&chip->mutex); - return 0; -} - -static int input_sel_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - static const u8 sel_values[4] = { - CS4245_SEL_MIC, - CS4245_SEL_INPUT_1, - CS4245_SEL_INPUT_2, - CS4245_SEL_INPUT_4 - }; - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - int changed; - - if (value->value.enumerated.item[0] > 3) - return -EINVAL; - - mutex_lock(&chip->mutex); - changed = value->value.enumerated.item[0] != data->input_sel; - if (changed) { - data->input_sel = value->value.enumerated.item[0]; - - cs4245_write(chip, CS4245_ANALOG_IN, - (data->cs4245_shadow[CS4245_ANALOG_IN] & - ~CS4245_SEL_MASK) | - sel_values[data->input_sel]); - - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[data->input_sel][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[data->input_sel][1]); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->input_sel ? 0 : GPIO_INPUT_ROUTE, - GPIO_INPUT_ROUTE); - } - mutex_unlock(&chip->mutex); - return changed; -} - -static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) -{ - static const char *const names[2] = { "Active", "Frozen" }; - - return snd_ctl_enum_info(info, 1, 2, names); -} - -static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - - value->value.enumerated.item[0] = - !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); - return 0; -} - -static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - struct dg *data = chip->model_data; - u8 reg; - int changed; - - mutex_lock(&chip->mutex); - reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; - if (value->value.enumerated.item[0]) - reg |= CS4245_HPF_FREEZE; - changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; - if (changed) - cs4245_write(chip, CS4245_ADC_CTRL, reg); - mutex_unlock(&chip->mutex); - return changed; -} - -#define INPUT_VOLUME(xname, index) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .info = input_vol_info, \ - .get = input_vol_get, \ - .put = input_vol_put, \ - .tlv = { .p = cs4245_pga_db_scale }, \ - .private_value = index, \ -} -static const struct snd_kcontrol_new dg_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Output Playback Enum", - .info = output_switch_info, - .get = output_switch_get, - .put = output_switch_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphones Impedance Playback Enum", - .info = hp_volume_offset_info, - .get = hp_volume_offset_get, - .put = hp_volume_offset_put, - }, - INPUT_VOLUME("Mic Capture Volume", 0), - INPUT_VOLUME("Aux Capture Volume", 1), - INPUT_VOLUME("Front Mic Capture Volume", 2), - INPUT_VOLUME("Line Capture Volume", 3), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = input_sel_info, - .get = input_sel_get, - .put = input_sel_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "ADC High-pass Filter Capture Enum", - .info = hpf_info, - .get = hpf_get, - .put = hpf_put, - }, -}; - -static int dg_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "Master Playback ", 16)) - return 1; - return 0; -} - -static int dg_mixer_init(struct oxygen *chip) -{ - unsigned int i; - int err; - - for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { - err = snd_ctl_add(chip->card, - snd_ctl_new1(&dg_controls[i], chip)); - if (err < 0) - return err; - } - return 0; -} - -static void dump_cs4245_registers(struct oxygen *chip, +void dump_cs4245_registers(struct oxygen *chip, struct snd_info_buffer *buffer) { struct dg *data = chip->model_data; @@ -625,30 +299,3 @@ static void dump_cs4245_registers(struct oxygen *chip, snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); snd_iprintf(buffer, "\n"); } - -struct oxygen_model model_xonar_dg = { - .longname = "C-Media Oxygen HD Audio", - .chip = "CMI8786", - .init = dg_init, - .control_filter = dg_control_filter, - .mixer_init = dg_mixer_init, - .cleanup = dg_cleanup, - .suspend = dg_suspend, - .resume = dg_resume, - .set_dac_params = set_cs4245_dac_params, - .set_adc_params = set_cs4245_adc_params, - .adjust_dac_routing = adjust_dg_dac_routing, - .dump_registers = dump_cs4245_registers, - .model_data_size = sizeof(struct dg), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | - CAPTURE_1_FROM_SPDIF, - .dac_channels_pcm = 6, - .dac_channels_mixer = 0, - .function_flags = OXYGEN_FUNCTION_SPI, - .dac_mclks = OXYGEN_MCLKS(256, 128, 128), - .adc_mclks = OXYGEN_MCLKS(256, 128, 128), - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index a9fba40bc013..0941ca2e0ca2 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -34,6 +34,25 @@ struct dg { u8 hp_vol_att; }; +/* Xonar DG control routines */ +int cs4245_write_spi(struct oxygen *chip, u8 reg); +int cs4245_read_spi(struct oxygen *chip, u8 reg); +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op); +void dg_init(struct oxygen *chip); +void set_cs4245_dac_params(struct oxygen *chip, + struct snd_pcm_hw_params *params); +void set_cs4245_adc_params(struct oxygen *chip, + struct snd_pcm_hw_params *params); +unsigned int adjust_dg_dac_routing(struct oxygen *chip, + unsigned int play_routing); +void dump_cs4245_registers(struct oxygen *chip, + struct snd_info_buffer *buffer); +void dg_suspend(struct oxygen *chip); +void dg_resume(struct oxygen *chip); +void dg_cleanup(struct oxygen *chip); +void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value); +void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value); + extern struct oxygen_model model_xonar_dg; #endif diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c new file mode 100644 index 000000000000..a2cd0d31ac77 --- /dev/null +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -0,0 +1,381 @@ +/* + * Mixer controls for the Xonar DG/DGX + * + * Copyright (c) Clemens Ladisch + * Copyright (c) Roman Volkov + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "oxygen.h" +#include "xonar_dg.h" +#include "cs4245.h" + +static int output_switch_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Speakers", "Headphones", "FP Headphones" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int output_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + value->value.enumerated.item[0] = data->output_sel; + mutex_unlock(&chip->mutex); + return 0; +} + +static int output_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + u8 reg; + int changed; + + if (value->value.enumerated.item[0] > 2) + return -EINVAL; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != data->output_sel; + if (changed) { + data->output_sel = value->value.enumerated.item[0]; + + reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & + ~CS4245_A_OUT_SEL_MASK; + reg |= data->output_sel == 2 ? + CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; + cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); + + cs4245_write_cached(chip, CS4245_DAC_A_CTRL, + data->output_sel ? data->hp_vol_att : 0); + cs4245_write_cached(chip, CS4245_DAC_B_CTRL, + data->output_sel ? data->hp_vol_att : 0); + + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + data->output_sel == 1 ? GPIO_HP_REAR : 0, + GPIO_HP_REAR); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int hp_volume_offset_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "< 64 ohms", "64-150 ohms", "150-300 ohms" + }; + + return snd_ctl_enum_info(info, 1, 3, names); +} + +static int hp_volume_offset_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + if (data->hp_vol_att > 2 * 7) + value->value.enumerated.item[0] = 0; + else if (data->hp_vol_att > 0) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + mutex_unlock(&chip->mutex); + return 0; +} + +static int hp_volume_offset_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + s8 att; + int changed; + + if (value->value.enumerated.item[0] > 2) + return -EINVAL; + att = atts[value->value.enumerated.item[0]]; + mutex_lock(&chip->mutex); + changed = att != data->hp_vol_att; + if (changed) { + data->hp_vol_att = att; + if (data->output_sel) { + cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); + cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); + } + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int input_vol_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 2 * -12; + info->value.integer.max = 2 * 12; + return 0; +} + +static int input_vol_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + unsigned int idx = ctl->private_value; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = data->input_vol[idx][0]; + value->value.integer.value[1] = data->input_vol[idx][1]; + mutex_unlock(&chip->mutex); + return 0; +} + +static int input_vol_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + unsigned int idx = ctl->private_value; + int changed = 0; + + if (value->value.integer.value[0] < 2 * -12 || + value->value.integer.value[0] > 2 * 12 || + value->value.integer.value[1] < 2 * -12 || + value->value.integer.value[1] > 2 * 12) + return -EINVAL; + mutex_lock(&chip->mutex); + changed = data->input_vol[idx][0] != value->value.integer.value[0] || + data->input_vol[idx][1] != value->value.integer.value[1]; + if (changed) { + data->input_vol[idx][0] = value->value.integer.value[0]; + data->input_vol[idx][1] = value->value.integer.value[1]; + if (idx == data->input_sel) { + cs4245_write_cached(chip, CS4245_PGA_A_CTRL, + data->input_vol[idx][0]); + cs4245_write_cached(chip, CS4245_PGA_B_CTRL, + data->input_vol[idx][1]); + } + } + mutex_unlock(&chip->mutex); + return changed; +} + +static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); + +static int input_sel_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[4] = { + "Mic", "Aux", "Front Mic", "Line" + }; + + return snd_ctl_enum_info(info, 1, 4, names); +} + +static int input_sel_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + value->value.enumerated.item[0] = data->input_sel; + mutex_unlock(&chip->mutex); + return 0; +} + +static int input_sel_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + static const u8 sel_values[4] = { + CS4245_SEL_MIC, + CS4245_SEL_INPUT_1, + CS4245_SEL_INPUT_2, + CS4245_SEL_INPUT_4 + }; + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + int changed; + + if (value->value.enumerated.item[0] > 3) + return -EINVAL; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != data->input_sel; + if (changed) { + data->input_sel = value->value.enumerated.item[0]; + + cs4245_write(chip, CS4245_ANALOG_IN, + (data->cs4245_shadow[CS4245_ANALOG_IN] & + ~CS4245_SEL_MASK) | + sel_values[data->input_sel]); + + cs4245_write_cached(chip, CS4245_PGA_A_CTRL, + data->input_vol[data->input_sel][0]); + cs4245_write_cached(chip, CS4245_PGA_B_CTRL, + data->input_vol[data->input_sel][1]); + + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + data->input_sel ? 0 : GPIO_INPUT_ROUTE, + GPIO_INPUT_ROUTE); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { "Active", "Frozen" }; + + return snd_ctl_enum_info(info, 1, 2, names); +} + +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + value->value.enumerated.item[0] = + !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); + return 0; +} + +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + u8 reg; + int changed; + + mutex_lock(&chip->mutex); + reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; + if (value->value.enumerated.item[0]) + reg |= CS4245_HPF_FREEZE; + changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; + if (changed) + cs4245_write(chip, CS4245_ADC_CTRL, reg); + mutex_unlock(&chip->mutex); + return changed; +} + +#define INPUT_VOLUME(xname, index) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = input_vol_info, \ + .get = input_vol_get, \ + .put = input_vol_put, \ + .tlv = { .p = cs4245_pga_db_scale }, \ + .private_value = index, \ +} +static const struct snd_kcontrol_new dg_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output Playback Enum", + .info = output_switch_info, + .get = output_switch_get, + .put = output_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphones Impedance Playback Enum", + .info = hp_volume_offset_info, + .get = hp_volume_offset_get, + .put = hp_volume_offset_put, + }, + INPUT_VOLUME("Mic Capture Volume", 0), + INPUT_VOLUME("Aux Capture Volume", 1), + INPUT_VOLUME("Front Mic Capture Volume", 2), + INPUT_VOLUME("Line Capture Volume", 3), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = input_sel_info, + .get = input_sel_get, + .put = input_sel_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC High-pass Filter Capture Enum", + .info = hpf_info, + .get = hpf_get, + .put = hpf_put, + }, +}; + +static int dg_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "Master Playback ", 16)) + return 1; + return 0; +} + +static int dg_mixer_init(struct oxygen *chip) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&dg_controls[i], chip)); + if (err < 0) + return err; + } + return 0; +} + +struct oxygen_model model_xonar_dg = { + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8786", + .init = dg_init, + .control_filter = dg_control_filter, + .mixer_init = dg_mixer_init, + .cleanup = dg_cleanup, + .suspend = dg_suspend, + .resume = dg_resume, + .set_dac_params = set_cs4245_dac_params, + .set_adc_params = set_cs4245_adc_params, + .adjust_dac_routing = adjust_dg_dac_routing, + .dump_registers = dump_cs4245_registers, + .model_data_size = sizeof(struct dg), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels_pcm = 6, + .dac_channels_mixer = 0, + .function_flags = OXYGEN_FUNCTION_SPI, + .dac_mclks = OXYGEN_MCLKS(256, 128, 128), + .adc_mclks = OXYGEN_MCLKS(256, 128, 128), + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; From 3dd77654fb1d7f68b9739f3039bad8dbbc0739f8 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:14 +0400 Subject: [PATCH 34/42] ALSA: oxygen: Xonar DG(X): capture from I2S channel 1, not 2 Actually CS4245 connected to the I2S channel 1 for capture, not channel 2. Otherwise capturing and playback does not work for CS4245. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg_mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index a2cd0d31ac77..7e9fc4af81bd 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -369,7 +369,7 @@ struct oxygen_model model_xonar_dg = { .model_data_size = sizeof(struct dg), .device_config = PLAYBACK_0_TO_I2S | PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | + CAPTURE_0_FROM_I2S_1 | CAPTURE_1_FROM_SPDIF, .dac_channels_pcm = 6, .dac_channels_mixer = 0, From 2809cb84d1672b639a4a41a0fa077fb554699072 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:15 +0400 Subject: [PATCH 35/42] ALSA: oxygen: Xonar DG(X): modify playback output select Change the order of elements in the output select control. This will reduce the number of relay switches. Change 'put' function to call the oxygen_update_dac_routing() function. Otherwise multichannel playback does not work. Also there is a new function to apply settings, this prevents from duplicating the code. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 2 +- sound/pci/oxygen/xonar_dg.h | 3 +- sound/pci/oxygen/xonar_dg_mixer.c | 75 ++++++++++++++++++------------- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index d12d83554abb..6cec934e6628 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -268,7 +268,7 @@ unsigned int adjust_dg_dac_routing(struct oxygen *chip, struct dg *data = chip->model_data; unsigned int routing = 0; - switch (data->pcm_output) { + switch (data->output_sel) { case PLAYBACK_DST_HP: case PLAYBACK_DST_HP_FP: oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 0941ca2e0ca2..a5cb75121f40 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -27,8 +27,7 @@ struct dg { /* shadow copy of the CS4245 register space */ unsigned char cs4245_shadow[17]; /* output select: headphone/speakers */ - unsigned char pcm_output; - unsigned int output_sel; + unsigned char output_sel; s8 input_vol[4][2]; unsigned int input_sel; u8 hp_vol_att; diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 7e9fc4af81bd..6dfe63554742 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -27,17 +27,46 @@ #include "xonar_dg.h" #include "cs4245.h" -static int output_switch_info(struct snd_kcontrol *ctl, +/* analog output select */ + +static int output_select_apply(struct oxygen *chip) +{ + struct dg *data = chip->model_data; + + data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK; + if (data->output_sel == PLAYBACK_DST_HP) { + /* mute FP (aux output) amplifier, switch rear jack to CS4245 */ + oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); + } else if (data->output_sel == PLAYBACK_DST_HP_FP) { + /* + * Unmute FP amplifier, switch rear jack to CS4361; + * I2S channels 2,3,4 should be inactive. + */ + oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); + data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC; + } else { + /* + * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp., + * and change playback routing. + */ + oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); + } + return cs4245_write_spi(chip, CS4245_SIGNAL_SEL); +} + +static int output_select_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[3] = { - "Speakers", "Headphones", "FP Headphones" + "Stereo Headphones", + "Stereo Headphones FP", + "Multichannel", }; return snd_ctl_enum_info(info, 1, 3, names); } -static int output_switch_get(struct snd_kcontrol *ctl, +static int output_select_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; @@ -49,38 +78,24 @@ static int output_switch_get(struct snd_kcontrol *ctl, return 0; } -static int output_switch_put(struct snd_kcontrol *ctl, +static int output_select_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; - u8 reg; - int changed; - - if (value->value.enumerated.item[0] > 2) - return -EINVAL; + unsigned int new = value->value.enumerated.item[0]; + int changed = 0; + int ret; mutex_lock(&chip->mutex); - changed = value->value.enumerated.item[0] != data->output_sel; - if (changed) { - data->output_sel = value->value.enumerated.item[0]; - - reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & - ~CS4245_A_OUT_SEL_MASK; - reg |= data->output_sel == 2 ? - CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; - cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); - - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, - data->output_sel ? data->hp_vol_att : 0); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, - data->output_sel ? data->hp_vol_att : 0); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->output_sel == 1 ? GPIO_HP_REAR : 0, - GPIO_HP_REAR); + if (data->output_sel != new) { + data->output_sel = new; + ret = output_select_apply(chip); + changed = ret >= 0 ? 1 : ret; + oxygen_update_dac_routing(chip); } mutex_unlock(&chip->mutex); + return changed; } @@ -301,9 +316,9 @@ static const struct snd_kcontrol_new dg_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Output Playback Enum", - .info = output_switch_info, - .get = output_switch_get, - .put = output_switch_put, + .info = output_select_info, + .get = output_select_get, + .put = output_select_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, From c754639a29e8d00933ccd2d7ec41505d0074de8b Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:16 +0400 Subject: [PATCH 36/42] ALSA: oxygen: Xonar DG(X): use headphone volume control I tried both variants: volume control and impedance selector. In the first case one minus is that we can't change the volume of multichannel output without additional software volume control. However, I am using this variant for the last three months and this seems good. All multichannel speaker systems have internal amplifier with the volume control included, but not all headphones have this regulator. In the second case, my software volume control does not save the value after reboot. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg_mixer.c | 117 +++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 33 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 6dfe63554742..0c310e7c3a04 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -99,54 +99,93 @@ static int output_select_put(struct snd_kcontrol *ctl, return changed; } -static int hp_volume_offset_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "< 64 ohms", "64-150 ohms", "150-300 ohms" - }; +/* CS4245 Headphone Channels A&B Volume Control */ - return snd_ctl_enum_info(info, 1, 3, names); +static int hp_stereo_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 255; + return 0; } -static int hp_volume_offset_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) +static int hp_stereo_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) { struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; + unsigned int tmp; mutex_lock(&chip->mutex); - if (data->hp_vol_att > 2 * 7) - value->value.enumerated.item[0] = 0; - else if (data->hp_vol_att > 0) - value->value.enumerated.item[0] = 1; - else - value->value.enumerated.item[0] = 2; + tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255; + val->value.integer.value[0] = tmp; + tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255; + val->value.integer.value[1] = tmp; mutex_unlock(&chip->mutex); return 0; } -static int hp_volume_offset_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) +static int hp_stereo_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) { - static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; - s8 att; + int ret; + int changed = 0; + long new1 = val->value.integer.value[0]; + long new2 = val->value.integer.value[1]; + + if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0)) + return -EINVAL; + + mutex_lock(&chip->mutex); + if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || + (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { + data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; + data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; + ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); + if (ret >= 0) + ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL); + changed = ret >= 0 ? 1 : ret; + } + mutex_unlock(&chip->mutex); + + return changed; +} + +/* Headphone Mute */ + +static int hp_mute_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + + mutex_lock(&chip->mutex); + val->value.integer.value[0] = + !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC); + mutex_unlock(&chip->mutex); + return 0; +} + +static int hp_mute_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *val) +{ + struct oxygen *chip = ctl->private_data; + struct dg *data = chip->model_data; + int ret; int changed; - if (value->value.enumerated.item[0] > 2) + if (val->value.integer.value[0] > 1) return -EINVAL; - att = atts[value->value.enumerated.item[0]]; mutex_lock(&chip->mutex); - changed = att != data->hp_vol_att; - if (changed) { - data->hp_vol_att = att; - if (data->output_sel) { - cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); - cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); - } - } + data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; + data->cs4245_shadow[CS4245_DAC_CTRL_1] |= + (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; + ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); + changed = ret >= 0 ? 1 : ret; mutex_unlock(&chip->mutex); return changed; } @@ -312,6 +351,7 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) .tlv = { .p = cs4245_pga_db_scale }, \ .private_value = index, \ } +static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); static const struct snd_kcontrol_new dg_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -322,10 +362,21 @@ static const struct snd_kcontrol_new dg_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphones Impedance Playback Enum", - .info = hp_volume_offset_info, - .get = hp_volume_offset_get, - .put = hp_volume_offset_put, + .name = "Headphone Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = hp_stereo_volume_info, + .get = hp_stereo_volume_get, + .put = hp_stereo_volume_put, + .tlv = { .p = hp_db_scale, }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_mono_info, + .get = hp_mute_get, + .put = hp_mute_put, }, INPUT_VOLUME("Mic Capture Volume", 0), INPUT_VOLUME("Aux Capture Volume", 1), From cf218b2ef345a26f4ae242907150035950f90cb8 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:17 +0400 Subject: [PATCH 37/42] ALSA: oxygen: Xonar DG(X): modify capture volume functions Modify the input_vol_* functions to use the new SPI routines, There is a new applying function that will be called when the capture source changed. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.h | 3 ++- sound/pci/oxygen/xonar_dg_mixer.c | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index a5cb75121f40..a7e511026f48 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -28,7 +28,8 @@ struct dg { unsigned char cs4245_shadow[17]; /* output select: headphone/speakers */ unsigned char output_sel; - s8 input_vol[4][2]; + /* volumes for all capture sources */ + char input_vol[4][2]; unsigned int input_sel; u8 hp_vol_att; }; diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 0c310e7c3a04..2417a1efd719 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -190,6 +190,21 @@ static int hp_mute_put(struct snd_kcontrol *ctl, return changed; } +/* capture volume for all sources */ + +static int input_volume_apply(struct oxygen *chip, char left, char right) +{ + struct dg *data = chip->model_data; + int ret; + + data->cs4245_shadow[CS4245_PGA_A_CTRL] = left; + data->cs4245_shadow[CS4245_PGA_B_CTRL] = right; + ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL); + if (ret < 0) + return ret; + return cs4245_write_spi(chip, CS4245_PGA_B_CTRL); +} + static int input_vol_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -221,6 +236,7 @@ static int input_vol_put(struct snd_kcontrol *ctl, struct dg *data = chip->model_data; unsigned int idx = ctl->private_value; int changed = 0; + int ret = 0; if (value->value.integer.value[0] < 2 * -12 || value->value.integer.value[0] > 2 * 12 || @@ -234,18 +250,16 @@ static int input_vol_put(struct snd_kcontrol *ctl, data->input_vol[idx][0] = value->value.integer.value[0]; data->input_vol[idx][1] = value->value.integer.value[1]; if (idx == data->input_sel) { - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[idx][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[idx][1]); + ret = input_volume_apply(chip, + data->input_vol[idx][0], + data->input_vol[idx][1]); } + changed = ret >= 0 ? 1 : ret; } mutex_unlock(&chip->mutex); return changed; } -static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); - static int input_sel_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -345,13 +359,16 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) #define INPUT_VOLUME(xname, index) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .info = input_vol_info, \ .get = input_vol_get, \ .put = input_vol_put, \ - .tlv = { .p = cs4245_pga_db_scale }, \ + .tlv = { .p = pga_db_scale }, \ .private_value = index, \ } static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); +static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200); static const struct snd_kcontrol_new dg_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, From 70e0d82d5e5d65e96f56ced3de97221fdf62cf38 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:18 +0400 Subject: [PATCH 38/42] ALSA: oxygen: Xonar DG(X): modify input select functions First of all, we should not touch the GPIOs. They are not for selecting the capture source, but they seems just enable the whole audio input curcuit. The 'put' function calls the 'apply' functions to change register values. Change the order of capture sources. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.h | 3 +- sound/pci/oxygen/xonar_dg_mixer.c | 52 +++++++++++++++++-------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index a7e511026f48..d900323f73d0 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -30,7 +30,8 @@ struct dg { unsigned char output_sel; /* volumes for all capture sources */ char input_vol[4][2]; - unsigned int input_sel; + /* input select: mic/fp mic/line/aux */ + unsigned char input_sel; u8 hp_vol_att; }; diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 2417a1efd719..41ee39359bd6 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -260,11 +260,27 @@ static int input_vol_put(struct snd_kcontrol *ctl, return changed; } +/* Capture Source */ + +static int input_source_apply(struct oxygen *chip) +{ + struct dg *data = chip->model_data; + + data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK; + if (data->input_sel == CAPTURE_SRC_FP_MIC) + data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2; + else if (data->input_sel == CAPTURE_SRC_LINE) + data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4; + else if (data->input_sel != CAPTURE_SRC_MIC) + data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1; + return cs4245_write_spi(chip, CS4245_ANALOG_IN); +} + static int input_sel_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[4] = { - "Mic", "Aux", "Front Mic", "Line" + "Mic", "Front Mic", "Line", "Aux" }; return snd_ctl_enum_info(info, 1, 4, names); @@ -285,15 +301,10 @@ static int input_sel_get(struct snd_kcontrol *ctl, static int input_sel_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { - static const u8 sel_values[4] = { - CS4245_SEL_MIC, - CS4245_SEL_INPUT_1, - CS4245_SEL_INPUT_2, - CS4245_SEL_INPUT_4 - }; struct oxygen *chip = ctl->private_data; struct dg *data = chip->model_data; int changed; + int ret; if (value->value.enumerated.item[0] > 3) return -EINVAL; @@ -303,19 +314,12 @@ static int input_sel_put(struct snd_kcontrol *ctl, if (changed) { data->input_sel = value->value.enumerated.item[0]; - cs4245_write(chip, CS4245_ANALOG_IN, - (data->cs4245_shadow[CS4245_ANALOG_IN] & - ~CS4245_SEL_MASK) | - sel_values[data->input_sel]); - - cs4245_write_cached(chip, CS4245_PGA_A_CTRL, - data->input_vol[data->input_sel][0]); - cs4245_write_cached(chip, CS4245_PGA_B_CTRL, - data->input_vol[data->input_sel][1]); - - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - data->input_sel ? 0 : GPIO_INPUT_ROUTE, - GPIO_INPUT_ROUTE); + ret = input_source_apply(chip); + if (ret >= 0) + ret = input_volume_apply(chip, + data->input_vol[data->input_sel][0], + data->input_vol[data->input_sel][1]); + changed = ret >= 0 ? 1 : ret; } mutex_unlock(&chip->mutex); return changed; @@ -395,10 +399,10 @@ static const struct snd_kcontrol_new dg_controls[] = { .get = hp_mute_get, .put = hp_mute_put, }, - INPUT_VOLUME("Mic Capture Volume", 0), - INPUT_VOLUME("Aux Capture Volume", 1), - INPUT_VOLUME("Front Mic Capture Volume", 2), - INPUT_VOLUME("Line Capture Volume", 3), + INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC), + INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC), + INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE), + INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", From fc114e9fbaf555e2d7fbfe144dac716142e22331 Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:19 +0400 Subject: [PATCH 39/42] ALSA: oxygen: Xonar DG(X): modify high-pass filter control Change the 'put' function of the high-pass filter control to use the new SPI functions. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg_mixer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index 41ee39359bd6..dfdfc991f8e6 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -325,6 +325,8 @@ static int input_sel_put(struct snd_kcontrol *ctl, return changed; } +/* ADC high-pass filter */ + static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[2] = { "Active", "Frozen" }; @@ -354,8 +356,10 @@ static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) if (value->value.enumerated.item[0]) reg |= CS4245_HPF_FREEZE; changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; - if (changed) - cs4245_write(chip, CS4245_ADC_CTRL, reg); + if (changed) { + data->cs4245_shadow[CS4245_ADC_CTRL] = reg; + cs4245_write_spi(chip, CS4245_ADC_CTRL); + } mutex_unlock(&chip->mutex); return changed; } From 3f49a66f6ceff1c87b49858644771c17763902ab Mon Sep 17 00:00:00 2001 From: Roman Volkov Date: Fri, 24 Jan 2014 16:18:20 +0400 Subject: [PATCH 40/42] ALSA: oxygen: Xonar DG(X): cleanup and minor changes Remove old SPI control functions, change anti-pop init sequence, remove some garbage from structures. The 'Apply' functions must be called at the mixer initialization, otherwise mixer settings sometimes will not be applied at startup. Signed-off-by: Roman Volkov Signed-off-by: Clemens Ladisch --- sound/pci/oxygen/xonar_dg.c | 36 ++++++------------------------- sound/pci/oxygen/xonar_dg.h | 3 --- sound/pci/oxygen/xonar_dg_mixer.c | 5 +++++ 3 files changed, 11 insertions(+), 33 deletions(-) diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 6cec934e6628..ed6f199f8a38 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -123,29 +123,6 @@ int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) return 0; } -void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) -{ - struct dg *data = chip->model_data; - - oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | - OXYGEN_SPI_DATA_LENGTH_3 | - OXYGEN_SPI_CLOCK_1280 | - (0 << OXYGEN_SPI_CODEC_SHIFT) | - OXYGEN_SPI_CEN_LATCH_CLOCK_HI, - CS4245_SPI_ADDRESS_S | - CS4245_SPI_WRITE_S | - (reg << 8) | value); - data->cs4245_shadow[reg] = value; -} - -void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) -{ - struct dg *data = chip->model_data; - - if (value != data->cs4245_shadow[reg]) - cs4245_write(chip, reg, value); -} - static void cs4245_init(struct oxygen *chip) { struct dg *data = chip->model_data; @@ -171,8 +148,8 @@ static void cs4245_init(struct oxygen *chip) CS4245_PGA_SOFT | CS4245_PGA_ZERO; data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0; data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0; - data->cs4245_shadow[CS4245_DAC_A_CTRL] = 4; - data->cs4245_shadow[CS4245_DAC_B_CTRL] = 4; + data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8; + data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8; cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); snd_component_add(chip->card, "CS4245"); @@ -182,15 +159,14 @@ void dg_init(struct oxygen *chip) { struct dg *data = chip->model_data; - data->output_sel = 0; - data->input_sel = 3; - data->hp_vol_att = 2 * 16; + data->output_sel = PLAYBACK_DST_HP_FP; + data->input_sel = CAPTURE_SRC_MIC; cs4245_init(chip); oxygen_write16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE); - oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE); - msleep(2500); /* anti-pop delay */ + /* anti-pop delay, wait some time before enabling the output */ + msleep(2500); oxygen_write16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); } diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index d900323f73d0..d461df357aa1 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -32,7 +32,6 @@ struct dg { char input_vol[4][2]; /* input select: mic/fp mic/line/aux */ unsigned char input_sel; - u8 hp_vol_att; }; /* Xonar DG control routines */ @@ -51,8 +50,6 @@ void dump_cs4245_registers(struct oxygen *chip, void dg_suspend(struct oxygen *chip); void dg_resume(struct oxygen *chip); void dg_cleanup(struct oxygen *chip); -void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value); -void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value); extern struct oxygen_model model_xonar_dg; diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index dfdfc991f8e6..b885dac28a09 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -435,12 +435,17 @@ static int dg_mixer_init(struct oxygen *chip) unsigned int i; int err; + output_select_apply(chip); + input_source_apply(chip); + oxygen_update_dac_routing(chip); + for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { err = snd_ctl_add(chip->card, snd_ctl_new1(&dg_controls[i], chip)); if (err < 0) return err; } + return 0; } From 5e87d5807110afd6bd659e1b98929fab43f3732f Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Thu, 30 Jan 2014 10:55:51 +0800 Subject: [PATCH 41/42] ALSA: hda - add headset mic detect quirks for another Dell laptop When we plug a 3-ring headset on the Dell machine (Vendor ID: 0x10ec0255, Subsystem ID: 0x1028064d), the headset mic can't be detected, after apply this patch, the headset mic can work well. BugLink: https://bugs.launchpad.net/bugs/1260303 Cc: David Henningsson Tested-by: Doro Wu Cc: stable@vger.kernel.org Signed-off-by: Hui Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d723c1fdd5f3..56a8f1876603 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4294,6 +4294,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x063e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0640, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0651, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0652, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0653, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), From 75fae117a5dbde5ab984fa5c60705758cfbc6433 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 30 Jan 2014 11:52:16 -0700 Subject: [PATCH 42/42] ALSA: hda/hdmi - allow PIN_OUT to be dynamically enabled Commit 384a48d71520 "ALSA: hda: HDMI: Support codecs with fewer cvts than pins" dynamically enabled each pin widget's PIN_OUT only when the pin was actively in use. This was required on certain NVIDIA CODECs for correct operation. Specifically, if multiple pin widgets each had their mux input select the same audio converter widget and each pin widget had PIN_OUT enabled, then only one of the pin widgets would actually receive the audio, and often not the one the user wanted! However, this apparently broke some Intel systems, and commit 6169b673618b "ALSA: hda - Always turn on pins for HDMI/DP" reverted the dynamic setting of PIN_OUT. This in turn broke the afore-mentioned NVIDIA CODECs. This change supports either dynamic or static handling of PIN_OUT, selected by a flag set up during CODEC initialization. This flag is enabled for all recent NVIDIA GPUs. Reported-by: Uosis Cc: # v3.13 Signed-off-by: Stephen Warren Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 40 ++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 64f0a5e73a25..5ef95034d041 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -132,6 +132,9 @@ struct hdmi_spec { struct hdmi_eld temp_eld; struct hdmi_ops ops; + + bool dyn_pin_out; + /* * Non-generic VIA/NVIDIA specific */ @@ -500,15 +503,25 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) { + struct hdmi_spec *spec = codec->spec; + int pin_out; + /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - /* Enable pin out: some machines with GM965 gets broken output when - * the pin is disabled or changed while using with HDMI - */ + + if (spec->dyn_pin_out) + /* Disable pin out until stream is active */ + pin_out = 0; + else + /* Enable pin out: some machines with GM965 gets broken output + * when the pin is disabled or changed while using with HDMI + */ + pin_out = PIN_OUT; + snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); } static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) @@ -1735,6 +1748,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hda_nid_t pin_nid = per_pin->pin_nid; bool non_pcm; + int pinctl; non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); mutex_lock(&per_pin->lock); @@ -1744,6 +1758,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); mutex_unlock(&per_pin->lock); + if (spec->dyn_pin_out) { + pinctl = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl | PIN_OUT); + } + return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } @@ -1763,6 +1785,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, int cvt_idx, pin_idx; struct hdmi_spec_per_cvt *per_cvt; struct hdmi_spec_per_pin *per_pin; + int pinctl; if (hinfo->nid) { cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid); @@ -1779,6 +1802,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, return -EINVAL; per_pin = get_pin(spec, pin_idx); + if (spec->dyn_pin_out) { + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl & ~PIN_OUT); + } + snd_hda_spdif_ctls_unassign(codec, pin_idx); mutex_lock(&per_pin->lock); @@ -2840,6 +2871,7 @@ static int patch_nvhdmi(struct hda_codec *codec) return err; spec = codec->spec; + spec->dyn_pin_out = true; spec->ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type;