From 887e83e32d99e43798e9ac9a274ad4debebf03f7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:18:50 +0800 Subject: ASoC: mediatek: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Acked-by: Koro Chen Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173-max98090.c | 11 +---------- sound/soc/mediatek/mt8173-rt5650-rt5676.c | 11 +---------- 2 files changed, 2 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c index 4d44b5803e55..72d66cbed88d 100644 --- a/sound/soc/mediatek/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -169,21 +169,13 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) } card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); return ret; } -static int mt8173_max98090_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static const struct of_device_id mt8173_max98090_dt_match[] = { { .compatible = "mediatek,mt8173-max98090", }, { } @@ -200,7 +192,6 @@ static struct platform_driver mt8173_max98090_driver = { #endif }, .probe = mt8173_max98090_dev_probe, - .remove = mt8173_max98090_dev_remove, }; module_platform_driver(mt8173_max98090_driver); diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 094055323059..bff96e4e8b5d 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -234,21 +234,13 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); return ret; } -static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { { .compatible = "mediatek,mt8173-rt5650-rt5676", }, { } @@ -265,7 +257,6 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = { #endif }, .probe = mt8173_rt5650_rt5676_dev_probe, - .remove = mt8173_rt5650_rt5676_dev_remove, }; module_platform_driver(mt8173_rt5650_rt5676_driver); -- cgit v1.2.3-59-g8ed1b From 29d28c5060023e661965c51c7e8f53bc9eb44fc4 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 11 Sep 2015 19:10:38 +0530 Subject: ALSA: hdsp: fix memory leak If the size of the firmware is less than expected size then we are exiting with the error code but we missed releasing the firmware. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdsp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 9bba275b4c9b..2875b4f6d8c9 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5112,6 +5112,7 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp) dev_err(hdsp->card->dev, "too short firmware size %d (expected %d)\n", (int)fw->size, HDSP_FIRMWARE_SIZE); + release_firmware(fw); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 0052b7dcf9d9ec6be4fc3fe815a2ceda623bb9d1 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 13 Sep 2015 19:00:05 +0900 Subject: ALSA: pcm: remove structure member of 'struct snd_pcm_hwptr_log *' type because this structure had been removed This structure was added by 4d96eb255c53 ('ALSA: pcm_lib - add possibility to log last 10 DMA ring buffer positions') to store PCM pointers information of latest 10 pointer movements (=XRUN_LOG_CNT). When CONFIG_SND_PCM_XRUN_DEBUG is configured, 'struct snd_pcm_runtime' has 'hwptr_log' member with a pointer to the structure. When calling xrun_log() in pcm_lib.c, the structure was allocated to the pointer. When calling snd_pcm_detach_substream() in pcm.c, the allocated pointer is released. In f5914908a5b7 ('ALSA: pcm: Replace PCM hwptr tracking with tracepoints'), the pointer logging is replaced with using Linux Kernel Tracepoints. The structure was also removed, while it's just declared. The member and kfree still remains. This commit removes the member and related codes. I think this was overlooked because it brings no errors/warnings to C compilers. Fixes: f5914908a5b7 ('ALSA: pcm: Replace PCM hwptr tracking with tracepoints') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 6 ------ sound/core/pcm.c | 3 --- 2 files changed, 9 deletions(-) (limited to 'sound') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 691e7ee0a510..a4fcc9456194 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -285,8 +285,6 @@ struct snd_pcm_hw_constraint_ranges { unsigned int mask; }; -struct snd_pcm_hwptr_log; - /* * userspace-provided audio timestamp config to kernel, * structure is for internal use only and filled with dedicated unpack routine @@ -428,10 +426,6 @@ struct snd_pcm_runtime { /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; #endif - -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - struct snd_pcm_hwptr_log *hwptr_log; -#endif }; struct snd_pcm_group { /* keep linked substreams */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 02bd96954dc4..308c9ecf73db 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); -#ifdef CONFIG_SND_PCM_XRUN_DEBUG - kfree(runtime->hwptr_log); -#endif kfree(runtime); substream->runtime = NULL; put_pid(substream->pid); -- cgit v1.2.3-59-g8ed1b From 2342cafe2269fe7cccda0aea88e30e8f4bcc075f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:16:01 +0800 Subject: ASoC: au1x: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/au1x/db1000.c | 10 +--------- sound/soc/au1x/db1200.c | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c index 452f404abfd2..e97c32798e98 100644 --- a/sound/soc/au1x/db1000.c +++ b/sound/soc/au1x/db1000.c @@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev) { struct snd_soc_card *card = &db1000_ac97; card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -static int db1000_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } static struct platform_driver db1000_audio_driver = { @@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = { .pm = &snd_soc_pm_ops, }, .probe = db1000_audio_probe, - .remove = db1000_audio_remove, }; module_platform_driver(db1000_audio_driver); diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 58c3164802b8..638ca0ba7e6e 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -174,14 +174,7 @@ static int db1200_audio_probe(struct platform_device *pdev) card = db1200_cards[pid->driver_data]; card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -static int db1200_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } static struct platform_driver db1200_audio_driver = { @@ -191,7 +184,6 @@ static struct platform_driver db1200_audio_driver = { }, .id_table = db1200_pids, .probe = db1200_audio_probe, - .remove = db1200_audio_remove, }; module_platform_driver(db1200_audio_driver); -- cgit v1.2.3-59-g8ed1b From 76b0d1f5e748630e01763d7fd6bec8436af7ec51 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:17:44 +0800 Subject: ASoC: blackfin: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-ad1836.c | 11 +---------- sound/soc/blackfin/bfin-eval-adau1373.c | 12 +----------- sound/soc/blackfin/bfin-eval-adau1701.c | 12 +----------- sound/soc/blackfin/bfin-eval-adav80x.c | 12 +----------- 4 files changed, 4 insertions(+), 43 deletions(-) (limited to 'sound') diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index 5bf1501e5e3c..864df2616e10 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev) card->dev = &pdev->dev; platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "Failed to register card\n"); return ret; } -static int bf5xx_ad1836_driver_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver bf5xx_ad1836_driver = { .driver = { .name = "bfin-snd-ad1836", .pm = &snd_soc_pm_ops, }, .probe = bf5xx_ad1836_driver_probe, - .remove = bf5xx_ad1836_driver_remove, }; module_platform_driver(bf5xx_ad1836_driver); diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c index 523baf5820d7..72ac78988426 100644 --- a/sound/soc/blackfin/bfin-eval-adau1373.c +++ b/sound/soc/blackfin/bfin-eval-adau1373.c @@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev) card->dev = &pdev->dev; - return snd_soc_register_card(&bfin_eval_adau1373); -} - -static int bfin_eval_adau1373_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373); } static struct platform_driver bfin_eval_adau1373_driver = { @@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = { .pm = &snd_soc_pm_ops, }, .probe = bfin_eval_adau1373_probe, - .remove = bfin_eval_adau1373_remove, }; module_platform_driver(bfin_eval_adau1373_driver); diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c index f9e926dfd4ef..5c67f72cf9a9 100644 --- a/sound/soc/blackfin/bfin-eval-adau1701.c +++ b/sound/soc/blackfin/bfin-eval-adau1701.c @@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev) card->dev = &pdev->dev; - return snd_soc_register_card(&bfin_eval_adau1701); -} - -static int bfin_eval_adau1701_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701); } static struct platform_driver bfin_eval_adau1701_driver = { @@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = { .pm = &snd_soc_pm_ops, }, .probe = bfin_eval_adau1701_probe, - .remove = bfin_eval_adau1701_remove, }; module_platform_driver(bfin_eval_adau1701_driver); diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c index 27eee66afdb2..1037477d10b2 100644 --- a/sound/soc/blackfin/bfin-eval-adav80x.c +++ b/sound/soc/blackfin/bfin-eval-adav80x.c @@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev) card->dev = &pdev->dev; - return snd_soc_register_card(&bfin_eval_adav80x); -} - -static int bfin_eval_adav80x_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x); } static const struct platform_device_id bfin_eval_adav80x_ids[] = { @@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = { .pm = &snd_soc_pm_ops, }, .probe = bfin_eval_adav80x_probe, - .remove = bfin_eval_adav80x_remove, .id_table = bfin_eval_adav80x_ids, }; -- cgit v1.2.3-59-g8ed1b From 5b73de19fd65f11a8c1b33bb7aeb0275898f8786 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Aug 2015 17:20:01 +0800 Subject: ASoC: mxs-sgtl5000: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 6e6fce6a14ba..2b23ffbac6b1 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) card->dev = &pdev->dev; platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) static int mxs_sgtl5000_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - mxs_saif_put_mclk(0); - snd_soc_unregister_card(card); - return 0; } -- cgit v1.2.3-59-g8ed1b From 2fd7076a43710666b147e206866ecd946b0b33e5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 10:32:59 +0800 Subject: ASoC: pxa: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/pxa/brownstone.c | 9 +-------- sound/soc/pxa/corgi.c | 11 +---------- sound/soc/pxa/e740_wm9705.c | 5 +---- sound/soc/pxa/e750_wm9705.c | 5 +---- sound/soc/pxa/e800_wm9712.c | 5 +---- sound/soc/pxa/hx4700.c | 4 +--- sound/soc/pxa/imote2.c | 11 +---------- sound/soc/pxa/mioa701_wm9713.c | 11 +---------- sound/soc/pxa/palm27x.c | 9 +-------- sound/soc/pxa/poodle.c | 11 +---------- sound/soc/pxa/spitz.c | 5 +---- sound/soc/pxa/tosa.c | 5 +---- sound/soc/pxa/ttc-dkb.c | 12 +----------- 13 files changed, 13 insertions(+), 90 deletions(-) (limited to 'sound') diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 2b26318bc200..6147e86e9b0f 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -116,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev) int ret; brownstone.dev = &pdev->dev; - ret = snd_soc_register_card(&brownstone); + ret = devm_snd_soc_register_card(&pdev->dev, &brownstone); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int brownstone_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&brownstone); - return 0; -} - static struct platform_driver mmp_driver = { .driver = { .name = "brownstone-audio", .pm = &snd_soc_pm_ops, }, .probe = brownstone_probe, - .remove = brownstone_remove, }; module_platform_driver(mmp_driver); diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 3580d10c9f28..c97dc13d3608 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int corgi_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver corgi_driver = { .driver = { .name = "corgi-audio", .pm = &snd_soc_pm_ops, }, .probe = corgi_probe, - .remove = corgi_remove, }; module_platform_driver(corgi_driver); diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index d72e124a3676..1de876529aa1 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev) static int e740_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios)); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 48f2d7c2e68c..b7eb7cd5df7d 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev) static int e750_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios)); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 45d4bd46fff6..41bf71466a7b 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev) static int e800_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios)); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 9f8be7cd567e..ecbf2873b7ff 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev) return ret; snd_soc_card_hx4700.dev = &pdev->dev; - ret = snd_soc_register_card(&snd_soc_card_hx4700); + ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700); if (ret) gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios)); @@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev) static int hx4700_audio_remove(struct platform_device *pdev) { - snd_soc_unregister_card(&snd_soc_card_hx4700); - gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0); gpio_set_value(GPIO107_HX4700_SPK_nSD, 0); diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c index 29fabbfd21f1..9d0e40771ef5 100644 --- a/sound/soc/pxa/imote2.c +++ b/sound/soc/pxa/imote2.c @@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int imote2_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver imote2_driver = { .driver = { .name = "imote2-audio", .pm = &snd_soc_pm_ops, }, .probe = imote2_probe, - .remove = imote2_remove, }; module_platform_driver(imote2_driver); diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index a9615a574546..29bc60e85e92 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev) return -ENODEV; mioa701.dev = &pdev->dev; - rc = snd_soc_register_card(&mioa701); + rc = devm_snd_soc_register_card(&pdev->dev, &mioa701); if (!rc) dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will" "lead to overheating and possible destruction of your device." @@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev) return rc; } -static int mioa701_wm9713_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver mioa701_wm9713_driver = { .probe = mioa701_wm9713_probe, - .remove = mioa701_wm9713_remove, .driver = { .name = "mioa701-wm9713", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index c20bbc042425..4e74d9573f03 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev) palm27x_asoc.dev = &pdev->dev; - ret = snd_soc_register_card(&palm27x_asoc); + ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int palm27x_asoc_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&palm27x_asoc); - return 0; -} - static struct platform_driver palm27x_wm9712_driver = { .probe = palm27x_asoc_probe, - .remove = palm27x_asoc_remove, .driver = { .name = "palm27x-asoc", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 80b457ac522a..84d0e2e50808 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -267,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } -static int poodle_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; -} - static struct platform_driver poodle_driver = { .driver = { .name = "poodle-audio", .pm = &snd_soc_pm_ops, }, .probe = poodle_probe, - .remove = poodle_remove, }; module_platform_driver(poodle_driver); diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 461123ad5ff2..b00222620fd0 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -322,9 +322,6 @@ err1: static int spitz_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); gpio_free(spitz_mic_gpio); return 0; } diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f59f566551ef..49518dd642aa 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -233,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -244,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev) static int tosa_remove(struct platform_device *pdev) { - struct snd_soc_card *card = platform_get_drvdata(pdev); - gpio_free(TOSA_GPIO_L_MUTE); - snd_soc_unregister_card(card); return 0; } diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index 1753c7d9e760..65c20f779177 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev) card->dev = &pdev->dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); @@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev) return ret; } -static int ttc_dkb_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static struct platform_driver ttc_dkb_driver = { .driver = { .name = "ttc-dkb-audio", .pm = &snd_soc_pm_ops, }, .probe = ttc_dkb_probe, - .remove = ttc_dkb_remove, }; module_platform_driver(ttc_dkb_driver); -- cgit v1.2.3-59-g8ed1b From 1ff68f55901ffd25b40f23bc6296657beca8377f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 21:11:10 +0800 Subject: ASoC: sh: siu_dai: Convert to use resource managed APIs Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/sh/siu_dai.c | 85 ++++++++++++++------------------------------------ 1 file changed, 24 insertions(+), 61 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index abb0d956231c..76b2ab8c2b4a 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev) struct siu_info *info; int ret; - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; siu_i2s_data = info; @@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev) ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); if (ret) - goto ereqfw; + return ret; /* * Loaded firmware is "const" - read only, but we have to modify it in @@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev) release_firmware(fw_entry); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto egetres; - } + if (!res) + return -ENODEV; - region = request_mem_region(res->start, resource_size(res), - pdev->name); + region = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name); if (!region) { dev_err(&pdev->dev, "SIU region already claimed\n"); - ret = -EBUSY; - goto ereqmemreg; + return -EBUSY; } - ret = -ENOMEM; - info->pram = ioremap(res->start, PRAM_SIZE); + info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE); if (!info->pram) - goto emappram; - info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE); + return -ENOMEM; + info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET, + XRAM_SIZE); if (!info->xram) - goto emapxram; - info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE); + return -ENOMEM; + info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET, + YRAM_SIZE); if (!info->yram) - goto emapyram; - info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) - - REG_OFFSET); + return -ENOMEM; + info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET, + resource_size(res) - REG_OFFSET); if (!info->reg) - goto emapreg; + return -ENOMEM; dev_set_drvdata(&pdev->dev, info); /* register using ARRAY version so we can keep dai name */ - ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component, - &siu_i2s_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component, + &siu_i2s_dai, 1); if (ret < 0) - goto edaiinit; + return ret; - ret = snd_soc_register_platform(&pdev->dev, &siu_platform); + ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform); if (ret < 0) - goto esocregp; + return ret; pm_runtime_enable(&pdev->dev); - return ret; - -esocregp: - snd_soc_unregister_component(&pdev->dev); -edaiinit: - iounmap(info->reg); -emapreg: - iounmap(info->yram); -emapyram: - iounmap(info->xram); -emapxram: - iounmap(info->pram); -emappram: - release_mem_region(res->start, resource_size(res)); -ereqmemreg: -egetres: -ereqfw: - kfree(info); - - return ret; + return 0; } static int siu_remove(struct platform_device *pdev) { - struct siu_info *info = dev_get_drvdata(&pdev->dev); - struct resource *res; - pm_runtime_disable(&pdev->dev); - - snd_soc_unregister_platform(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - - iounmap(info->reg); - iounmap(info->yram); - iounmap(info->xram); - iounmap(info->pram); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); - kfree(info); - return 0; } -- cgit v1.2.3-59-g8ed1b From 9ed747641b0fe122a827494b1d490bc2e2e45347 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 2 Sep 2015 12:01:27 +0800 Subject: ASoC: intel: broadwell: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/intel/boards/broadwell.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 8bafaf6ceab1..3f8a1e10bed0 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev) { broadwell_rt286.dev = &pdev->dev; - return snd_soc_register_card(&broadwell_rt286); -} - -static int broadwell_audio_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&broadwell_rt286); - return 0; + return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286); } static struct platform_driver broadwell_audio = { .probe = broadwell_audio_probe, - .remove = broadwell_audio_remove, .driver = { .name = "broadwell-audio", }, -- cgit v1.2.3-59-g8ed1b From aab7826f592b4af9a1407cf5b717978dc16cc19a Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:55:08 +0200 Subject: ASoC: atmel_wm8904: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/atmel/atmel_wm8904.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index aa354e1c6ff7..1933bcd46cca 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = { { .compatible = "atmel,asoc-wm8904", }, { } }; +MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids); #endif static struct platform_driver atmel_asoc_wm8904_driver = { -- cgit v1.2.3-59-g8ed1b From 5226f2340c67225d27f61330205f16314881cc5c Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:57:47 +0200 Subject: ASoC: fsl-asoc-card: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5aeb6ed4827e..97bb4c5544cd 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -592,6 +592,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-wm8960", }, {} }; +MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); static struct platform_driver fsl_asoc_card_driver = { .probe = fsl_asoc_card_probe, -- cgit v1.2.3-59-g8ed1b From c759241fe2f16e6be43675abaa715f0da9d7a254 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:58:23 +0200 Subject: ASoC: fsl_sai: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a18fd92c4a85..9366b5a42e1d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -801,6 +801,7 @@ static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,imx6sx-sai", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, fsl_sai_ids); static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, -- cgit v1.2.3-59-g8ed1b From 27fcf913f6462662e9ec1b74ce04961126852db9 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:58:57 +0200 Subject: ASoC: jz4740: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/jz4740/jz4740-i2s.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index b05fb1c1a848..794a3499e567 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = { { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, jz4740_of_matches); #endif static int jz4740_i2s_dev_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 30953a804714fcfe49bec594239306bf5b476464 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 12:59:32 +0200 Subject: ASoC: kirkwood: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/kirkwood/armada-370-db.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c index de7563bdc5c2..e0304d544f26 100644 --- a/sound/soc/kirkwood/armada-370-db.c +++ b/sound/soc/kirkwood/armada-370-db.c @@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = { { .compatible = "marvell,a370db-audio" }, { }, }; +MODULE_DEVICE_TABLE(of, a370db_dt_ids); static struct platform_driver a370db_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From baafd373e9287f20ca0c9a6bb38eb6785a146ac2 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 13:00:03 +0200 Subject: ASoC: pxa: Fix module autoload for OF platform drivers These platform drivers have a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/pxa/pxa-ssp.c | 1 + sound/soc/pxa/pxa2xx-pcm.c | 1 + 2 files changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 3da485ec1de7..da03fad1b9cd 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -809,6 +809,7 @@ static const struct of_device_id pxa_ssp_of_ids[] = { { .compatible = "mrvl,pxa-ssp-dai" }, {} }; +MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); #endif static int asoc_ssp_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 831ee37d2e3e..29a3fdbb7b59 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -132,6 +132,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = { { .compatible = "mrvl,pxa-pcm-audio" }, { } }; +MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match); #endif static struct platform_driver pxa_pcm_driver = { -- cgit v1.2.3-59-g8ed1b From cfa7a38c754a56fd80762d1251f4c344eb535230 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 13:00:40 +0200 Subject: ASoC: mop500: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/ux500/mop500.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 4e0c0e502ade..ba9fc099cf67 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = { { .compatible = "stericsson,snd-soc-mop500", }, {}, }; +MODULE_DEVICE_TABLE(of, snd_soc_mop500_match); static struct platform_driver snd_soc_mop500_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From c81740e00556312af32fb3cd5ec12174935e5f70 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 3 Sep 2015 13:01:15 +0200 Subject: ASoC: ux500: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_dai.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index f5df08ded770..6ba8ae9ecc7a 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -843,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = { { .compatible = "stericsson,ux500-msp-i2s", }, {}, }; +MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match); static struct platform_driver msp_i2s_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From 2719a752b6e96b22ac6a0d5b9397c21c93abcd0f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:39:59 +0000 Subject: ASoC: ak4642: use *dev on ak4642_i2c_probe() Let's replace &i2c->dev to dev Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4642.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 4a90143d0e90..9af06d30386b 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -584,7 +584,8 @@ static const struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct device_node *np = i2c->dev.of_node; + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; struct ak4642_priv *priv; @@ -592,7 +593,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, if (np) { const struct of_device_id *of_id; - of_id = of_match_device(ak4642_of_match, &i2c->dev); + of_id = of_match_device(ak4642_of_match, dev); if (of_id) drvdata = of_id->data; } else { @@ -600,11 +601,11 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, } if (!drvdata) { - dev_err(&i2c->dev, "Unknown device type\n"); + dev_err(dev, "Unknown device type\n"); return -EINVAL; } - priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -616,7 +617,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, if (IS_ERR(regmap)) return PTR_ERR(regmap); - return snd_soc_register_codec(&i2c->dev, + return snd_soc_register_codec(dev, &soc_codec_dev_ak4642, &ak4642_dai, 1); } -- cgit v1.2.3-59-g8ed1b From 171a0138ab75fcbe1228c4af0442221efccfb197 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:40:19 +0000 Subject: ASoC: ak4642: enable to use MCKO as fixed rate output pin on DT ak4642 chip can output clock via MCKO pin as audio reference clock. This patch supports it on DT Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4642.txt | 22 +++- sound/soc/codecs/ak4642.c | 141 ++++++++++++++------- 2 files changed, 114 insertions(+), 49 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt index 623d4e70ae11..340784db6808 100644 --- a/Documentation/devicetree/bindings/sound/ak4642.txt +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -7,7 +7,14 @@ Required properties: - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648" - reg : The chip select number on the I2C bus -Example: +Optional properties: + + - #clock-cells : common clock binding; shall be set to 0 + - clocks : common clock binding; MCKI clock + - clock-frequency : common clock binding; frequency of MCKO + - clock-output-names : common clock binding; MCKO clock name + +Example 1: &i2c { ak4648: ak4648@0x12 { @@ -15,3 +22,16 @@ Example: reg = <0x12>; }; }; + +Example 2: + +&i2c { + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + reg = <0x12>; + #clock-cells = <0>; + clocks = <&audio_clock>; + clock-frequency = <12288000>; + clock-output-names = "ak4643_mcko"; + }; +}; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 9af06d30386b..b5c4981c9f4c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -23,6 +23,8 @@ * AK4648 is tested. */ +#include +#include #include #include #include @@ -128,11 +130,8 @@ #define I2S (3 << 0) /* MD_CTL2 */ -#define FS0 (1 << 0) -#define FS1 (1 << 1) -#define FS2 (1 << 2) -#define FS3 (1 << 5) -#define FS_MASK (FS0 | FS1 | FS2 | FS3) +#define FS(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) +#define PS(val) ((val & 0x3) << 6) /* MD_CTL3 */ #define BST1 (1 << 3) @@ -147,6 +146,7 @@ struct ak4642_drvdata { struct ak4642_priv { const struct ak4642_drvdata *drvdata; + struct clk *mcko; }; /* @@ -430,56 +430,55 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int ak4642_set_mcko(struct snd_soc_codec *codec, + u32 frequency) +{ + u32 fs_list[] = { + [0] = 8000, + [1] = 12000, + [2] = 16000, + [3] = 24000, + [4] = 7350, + [5] = 11025, + [6] = 14700, + [7] = 22050, + [10] = 32000, + [11] = 48000, + [14] = 29400, + [15] = 44100, + }; + u32 ps_list[] = { + [0] = 256, + [1] = 128, + [2] = 64, + [3] = 32 + }; + int ps, fs; + + for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) { + for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) { + if (frequency == ps_list[ps] * fs_list[fs]) { + snd_soc_write(codec, MD_CTL2, PS(ps) | FS(fs)); + return 0; + } + } + } + + return 0; +} + static int ak4642_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - u8 rate; + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + u32 rate = clk_get_rate(priv->mcko); - switch (params_rate(params)) { - case 7350: - rate = FS2; - break; - case 8000: - rate = 0; - break; - case 11025: - rate = FS2 | FS0; - break; - case 12000: - rate = FS0; - break; - case 14700: - rate = FS2 | FS1; - break; - case 16000: - rate = FS1; - break; - case 22050: - rate = FS2 | FS1 | FS0; - break; - case 24000: - rate = FS1 | FS0; - break; - case 29400: - rate = FS3 | FS2 | FS1; - break; - case 32000: - rate = FS3 | FS1; - break; - case 44100: - rate = FS3 | FS2 | FS1 | FS0; - break; - case 48000: - rate = FS3 | FS1 | FS0; - break; - default: - return -EINVAL; - } - snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate); + if (!rate) + rate = params_rate(params) * 256; - return 0; + return ak4642_set_mcko(codec, rate); } static int ak4642_set_bias_level(struct snd_soc_codec *codec, @@ -532,7 +531,18 @@ static int ak4642_resume(struct snd_soc_codec *codec) return 0; } +static int ak4642_probe(struct snd_soc_codec *codec) +{ + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (priv->mcko) + ak4642_set_mcko(codec, clk_get_rate(priv->mcko)); + + return 0; +} + static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { + .probe = ak4642_probe, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, .controls = ak4642_snd_controls, @@ -580,6 +590,35 @@ static const struct ak4642_drvdata ak4648_drvdata = { .extended_frequencies = 1, }; +#ifdef CONFIG_COMMON_CLK +static struct clk *ak4642_of_parse_mcko(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct clk *clk; + const char *clk_name = np->name; + const char *parent_clk_name = NULL; + u32 rate; + + if (of_property_read_u32(np, "clock-frequency", &rate)) + return NULL; + + if (of_property_read_bool(np, "clocks")) + parent_clk_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, + (parent_clk_name) ? 0 : CLK_IS_ROOT, + rate); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return clk; +} +#else +#define ak4642_of_parse_mcko(d) 0 +#endif + static const struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -589,10 +628,15 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; struct ak4642_priv *priv; + struct clk *mcko = NULL; if (np) { const struct of_device_id *of_id; + mcko = ak4642_of_parse_mcko(dev); + if (IS_ERR(mcko)) + mcko = NULL; + of_id = of_match_device(ak4642_of_match, dev); if (of_id) drvdata = of_id->data; @@ -610,6 +654,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, return -ENOMEM; priv->drvdata = drvdata; + priv->mcko = mcko; i2c_set_clientdata(i2c, priv); -- cgit v1.2.3-59-g8ed1b From c25f20e1de6e22960f69cc93220e71ba8b5ee8b6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:48:52 +0000 Subject: ASoC: rsnd: tidyup rsnd_dma_to_xxx() position rsnd_dma_to_xxx() macro should exist in same place Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsnd.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 7a0e52b4640a..6ae8068fae3d 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -214,6 +214,7 @@ struct rsnd_dma { }; #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma); void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma); @@ -225,8 +226,6 @@ int rsnd_dma_probe(struct platform_device *pdev, struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); -#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) - /* * R-Car sound mod */ -- cgit v1.2.3-59-g8ed1b From b9bfe9d5f7755e225989a253e427cc620c001662 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:49:12 +0000 Subject: ASoC: rsnd: rename rsnd_src_pcm_new() to rsnd_src_pcm_new_gen2() rsnd_src_pcm_new() is used only from Gen2. make it clear in function name, and remove unneeded Gen1 check. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 89a18e102feb..06555a3e8b79 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_IFSVR, fsrate); } -static int rsnd_src_pcm_new(struct rsnd_mod *mod, +static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -931,12 +930,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, * enable SRC sync convert if possible */ - /* - * Gen1 is not supported - */ - if (rsnd_is_gen1(priv)) - return 0; - /* * SRC sync convert needs clock master */ @@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = { .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, .hw_params = rsnd_src_hw_params, - .pcm_new = rsnd_src_pcm_new, + .pcm_new = rsnd_src_pcm_new_gen2, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) -- cgit v1.2.3-59-g8ed1b From 7486d80f7d853f50088124824bf62d9a4d14de75 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 06:49:29 +0000 Subject: ASoC: rsnd: remove unneeded sh_clk header sh_clk header is not needed, and it will create confusion. Let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 1 - sound/soc/sh/rcar/adg.c | 1 - 2 files changed, 2 deletions(-) (limited to 'sound') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index bb7b2ebfee7b..d8e33d38da43 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -12,7 +12,6 @@ #ifndef RCAR_SND_H #define RCAR_SND_H -#include #define RSND_GEN1_SRU 0 #define RSND_GEN1_ADG 1 diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index fefc881dbac2..b512be82306e 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -7,7 +7,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#include #include "rsnd.h" #define CLKA 0 -- cgit v1.2.3-59-g8ed1b From ac37a45b0b6c8400719bb837f1c321079b72db53 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:01:58 +0000 Subject: ASoC: rsnd: Add Gen3 initial support Renesas sound Gen3 is updated version of Gen2. We need to update driver for it, but basically it should works as Gen2 compatible. This is initial support for Gen3. Gen3 specific feature will be incrementally added in the future Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/renesas,rsnd.txt | 2 ++ sound/soc/sh/rcar/core.c | 1 + 2 files changed, 3 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 776cf6aa8db9..bf6fd1af0a11 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -4,10 +4,12 @@ Required properties: - compatible : "renesas,rcar_sound-", fallbacks "renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen2" if generation2 + "renesas,rcar_sound-gen3" if generation3 Examples with soctypes are: - "renesas,rcar_sound-r8a7778" (R-Car M1A) - "renesas,rcar_sound-r8a7790" (R-Car H2) - "renesas,rcar_sound-r8a7791" (R-Car M2-W) + - "renesas,rcar_sound-r8a7795" (R-Car H3) - reg : Should contain the register physical address. required register is SRU/ADG/SSI if generation1 diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f3feed5ce9b6..870f94415abc 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = { static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, + { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */ {}, }; MODULE_DEVICE_TABLE(of, rsnd_of_match); -- cgit v1.2.3-59-g8ed1b From b76e218ae5e5e8d8025b75066e82ad0674e2e845 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:02:21 +0000 Subject: ASoC: rsnd: add rsnd_mod_get() macro and use it Renesas sound driver has SSI/SRC/DVC/CTU/MIX, and these are controlled as modules. And these module are member of each modules's private data. It used own method to get module pointer, but Let's use common method Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 6 ++--- sound/soc/sh/rcar/dvc.c | 6 ++--- sound/soc/sh/rcar/mix.c | 6 ++--- sound/soc/sh/rcar/rsnd.h | 1 + sound/soc/sh/rcar/src.c | 6 ++--- sound/soc/sh/rcar/ssi.c | 58 ++++++++++++++++++++++++++++-------------------- 6 files changed, 47 insertions(+), 36 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 05498bba5874..a3e7c716e1f7 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -66,7 +66,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) id = 0; - return &((struct rsnd_ctu *)(priv->ctu) + id)->mod; + return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id); } static void rsnd_of_parse_ctu(struct platform_device *pdev, @@ -150,7 +150,7 @@ int rsnd_ctu_probe(struct platform_device *pdev, ctu->info = &info->ctu_info[i]; - ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, clk, RSND_MOD_CTU, i); if (ret) return ret; @@ -166,6 +166,6 @@ void rsnd_ctu_remove(struct platform_device *pdev, int i; for_each_rsnd_ctu(ctu, priv, i) { - rsnd_mod_quit(&ctu->mod); + rsnd_mod_quit(rsnd_mod_get(ctu)); } } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 57796387d482..8d8eee6350c9 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -282,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) id = 0; - return &((struct rsnd_dvc *)(priv->dvc) + id)->mod; + return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id); } static void rsnd_of_parse_dvc(struct platform_device *pdev, @@ -361,7 +361,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); if (ret) return ret; @@ -377,6 +377,6 @@ void rsnd_dvc_remove(struct platform_device *pdev, int i; for_each_rsnd_dvc(dvc, priv, i) { - rsnd_mod_quit(&dvc->mod); + rsnd_mod_quit(rsnd_mod_get(dvc)); } } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 0d5c102db6f5..8544403ffb26 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -99,7 +99,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) id = 0; - return &((struct rsnd_mix *)(priv->mix) + id)->mod; + return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id); } static void rsnd_of_parse_mix(struct platform_device *pdev, @@ -179,7 +179,7 @@ int rsnd_mix_probe(struct platform_device *pdev, mix->info = &info->mix_info[i]; - ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops, + ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, clk, RSND_MOD_MIX, i); if (ret) return ret; @@ -195,6 +195,6 @@ void rsnd_mix_remove(struct platform_device *pdev, int i; for_each_rsnd_mix(mix, priv, i) { - rsnd_mod_quit(&mix->mod); + rsnd_mod_quit(rsnd_mod_get(mix)); } } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 6ae8068fae3d..f35d9cc61686 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -331,6 +331,7 @@ struct rsnd_mod { #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) #define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) +#define rsnd_mod_get(ip) (&(ip)->mod) int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 06555a3e8b79..ca7a20f03c9b 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -976,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) id = 0; - return &((struct rsnd_src *)(priv->src) + id)->mod; + return rsnd_mod_get((struct rsnd_src *)(priv->src) + id); } static void rsnd_of_parse_src(struct platform_device *pdev, @@ -1071,7 +1071,7 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i); if (ret) return ret; } @@ -1086,6 +1086,6 @@ void rsnd_src_remove(struct platform_device *pdev, int i; for_each_rsnd_src(src, priv, i) { - rsnd_mod_quit(&src->mod); + rsnd_mod_quit(rsnd_mod_get(src)); } } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d45b9a7e324e..91712e88405d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -128,6 +128,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, struct rsnd_priv *priv = rsnd_io_to_priv(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_mod_get(ssi); int i, j, ret; int adg_clk_div_table[] = { 1, 6, /* see adg.c */ @@ -152,14 +153,14 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, main_rate = rate / adg_clk_div_table[i] * 32 * 2 * ssi_clk_mul_table[j]; - ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(&ssi->mod), - rsnd_mod_id(&ssi->mod), rate); + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); return 0; } @@ -172,8 +173,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) { + struct rsnd_mod *mod = rsnd_mod_get(ssi); + ssi->cr_clk = 0; - rsnd_adg_ssi_clk_stop(&ssi->mod); + rsnd_adg_ssi_clk_stop(mod); } static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, @@ -182,11 +185,12 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_mod_get(ssi); u32 cr_mode; u32 cr; if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(&ssi->mod); + rsnd_mod_hw_start(mod); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -198,7 +202,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } } - if (rsnd_ssi_is_dma_mode(&ssi->mod)) { + if (rsnd_ssi_is_dma_mode(mod)) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { @@ -210,24 +214,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, cr_mode | EN; - rsnd_mod_write(&ssi->mod, SSICR, cr); + rsnd_mod_write(mod, SSICR, cr); /* enable WS continue */ if (rsnd_rdai_is_clk_master(rdai)) - rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + rsnd_mod_write(mod, SSIWSR, CONT); /* clear error status */ - rsnd_mod_write(&ssi->mod, SSISR, 0); + rsnd_mod_write(mod, SSISR, 0); ssi->usrcnt++; dev_dbg(dev, "%s[%d] hw started\n", - rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); + rsnd_mod_name(mod), rsnd_mod_id(mod)); } static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) { - struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; @@ -247,15 +252,15 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) cr = ssi->cr_own | ssi->cr_clk; - rsnd_mod_write(&ssi->mod, SSICR, cr | EN); - rsnd_ssi_status_check(&ssi->mod, DIRQ); + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); /* * disable SSI, * and, wait idle state */ - rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ - rsnd_ssi_status_check(&ssi->mod, IIRQ); + rsnd_mod_write(mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(mod, IIRQ); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -266,13 +271,13 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) rsnd_ssi_master_clk_stop(ssi); } - rsnd_mod_hw_stop(&ssi->mod); + rsnd_mod_hw_stop(mod); ssi->chan = 0; } dev_dbg(dev, "%s[%d] hw stopped\n", - rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); + rsnd_mod_name(mod), rsnd_mod_id(mod)); } /* @@ -371,7 +376,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, /* It will be removed on rsnd_ssi_hw_stop */ ssi->chan = chan; if (ssi_parent) - return rsnd_ssi_hw_params(&ssi_parent->mod, io, + return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io, substream, params); return 0; @@ -379,12 +384,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) { + struct rsnd_mod *mod = rsnd_mod_get(ssi); + /* under/over flow error */ if (status & (UIRQ | OIRQ)) { ssi->err++; /* clear error status */ - rsnd_mod_write(&ssi->mod, SSISR, 0); + rsnd_mod_write(mod, SSISR, 0); } } @@ -656,7 +663,7 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) id = 0; - return &((struct rsnd_ssi *)(priv->ssi) + id)->mod; + return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); } int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) @@ -668,10 +675,12 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { - if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) + struct rsnd_mod *mod = rsnd_mod_get(ssi); + + if (!rsnd_ssi_is_pin_sharing(mod)) return; - switch (rsnd_mod_id(&ssi->mod)) { + switch (rsnd_mod_id(mod)) { case 1: case 2: ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); @@ -794,7 +803,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, + RSND_MOD_SSI, i); if (ret) return ret; @@ -811,6 +821,6 @@ void rsnd_ssi_remove(struct platform_device *pdev, int i; for_each_rsnd_ssi(ssi, priv, i) { - rsnd_mod_quit(&ssi->mod); + rsnd_mod_quit(rsnd_mod_get(ssi)); } } -- cgit v1.2.3-59-g8ed1b From 1665a9e5e224a44798ea6ae16b507d48752eb4a1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:02:39 +0000 Subject: ASoC: rsnd: ADG uses mod base common method Renesas sound has ADG IP, but it is special device. (It is clock generater, and it doesn't need MSTP) Thus, ADG didn't use mod base common method on rsnd driver. But it can be confusable point. Let's use common method Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 67 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index b512be82306e..9ff1736e6955 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -15,8 +15,13 @@ #define CLKI 3 #define CLKMAX 4 +static struct rsnd_mod_ops adg_ops = { + .name = "adg", +}; + struct rsnd_adg { struct clk *clk[CLKMAX]; + struct rsnd_mod mod; int rbga_rate_for_441khz_div_6; /* RBGA */ int rbgb_rate_for_48khz_div_6; /* RBGB */ @@ -59,6 +64,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int id = rsnd_mod_id(mod); int shift = (id % 2) ? 16 : 0; u32 mask, val; @@ -68,7 +76,7 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, val = val << shift; mask = 0xffff << shift; - rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val); + rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); return 0; } @@ -77,6 +85,9 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, u32 timsel) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int is_play = rsnd_io_is_play(io); int id = rsnd_mod_id(mod); int shift = (id % 2) ? 16 : 0; @@ -94,24 +105,24 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, switch (id / 2) { case 0: - rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out); break; case 1: - rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out); break; case 2: - rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out); break; case 3: - rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out); break; case 4: - rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in); - rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out); break; } @@ -125,6 +136,7 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); int idx, sel, div, step, ret; u32 val, en; @@ -180,7 +192,7 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return ret; } - rsnd_mod_bset(mod, DIV_EN, en, en); + rsnd_mod_bset(adg_mod, DIV_EN, en, en); dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); @@ -201,6 +213,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, unsigned int dst_rate) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); int idx, sel, div, shift; u32 mask, val; @@ -237,13 +250,13 @@ find_rate: switch (id / 4) { case 0: - rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val); break; case 1: - rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val); break; case 2: - rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val); break; } @@ -258,6 +271,9 @@ find_rate: static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int id = rsnd_mod_id(mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; @@ -273,13 +289,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) switch (id / 4) { case 0: - rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val); break; case 1: - rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val); break; case 2: - rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val); + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); break; } } @@ -299,6 +315,7 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; int i; @@ -342,9 +359,9 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: /* see rsnd_adg_ssi_clk_init() */ - rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr); - rsnd_mod_write(mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(mod, BRRB, 0x00000002); /* 1/6 */ + rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, adg->ckr); + rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ + rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ /* * This "mod" = "ssi" here. @@ -421,6 +438,14 @@ int rsnd_adg_probe(struct platform_device *pdev, return -ENOMEM; } + /* + * ADG is special module. + * Use ADG mod without rsnd_mod_init() to make debug easy + * for rsnd_write/rsnd_read + */ + adg->mod.ops = &adg_ops; + adg->mod.priv = priv; + adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); -- cgit v1.2.3-59-g8ed1b From f1df12290722f998f5eb173ae30434d313aea54c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:03:08 +0000 Subject: ASoC: rsnd: add common mod confirm method Renesas sound has SSI/SRC/DVC/MIX/ADG modules, and these have original register mapping. Thus this driver is using regmap field, and each module is using it based on each module ID. Sometimes, each module needs other module to controlling. but current each function is using just "mod" as parameter name. This is confusable. For example, if SSI0 and SRC2 are connected, and if SRC module function has bug of module access, and if it needs to control connected SSI, SRC function will access to SSI2 (It should access to SSI0, but it uses SRC's ID 2). This is easy to happen in current driver style. To avoid this kind of confusable trouble, this patch adds module confirm macro for debug purpose. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 30 +++++++++++++++++++----------- sound/soc/sh/rcar/core.c | 11 +++++++++++ sound/soc/sh/rcar/rsnd.h | 11 +++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 9ff1736e6955..48bb38d38499 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -81,19 +81,21 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, +static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, u32 timsel) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); int is_play = rsnd_io_is_play(io); - int id = rsnd_mod_id(mod); + int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; u32 mask, ws; u32 in, out; + rsnd_mod_confirm_src(src_mod); + ws = rsnd_adg_ssi_ws_timing_gen2(io); in = (is_play) ? timsel : ws; @@ -129,12 +131,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod, return 0; } -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, unsigned int src_rate, unsigned int dst_rate) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); @@ -149,6 +151,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ }; + rsnd_mod_confirm_src(src_mod); + min = ~0; val = 0; en = 0; @@ -186,7 +190,7 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return -EIO; } - ret = rsnd_adg_set_src_timsel_gen2(mod, io, val); + ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); if (ret < 0) { dev_err(dev, "timsel error\n"); return ret; @@ -199,12 +203,14 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io) { u32 val = rsnd_adg_ssi_ws_timing_gen2(io); - return rsnd_adg_set_src_timsel_gen2(mod, io, val); + rsnd_mod_confirm_src(src_mod); + + return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); } int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, @@ -269,15 +275,17 @@ find_rate: return 0; } -static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) +static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int id = rsnd_mod_id(mod); + int id = rsnd_mod_id(ssi_mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; + rsnd_mod_confirm_ssi(ssi_mod); + val = val << shift; /* diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 870f94415abc..eec294da81e3 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -127,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match); #define rsnd_info_id(priv, io, name) \ ((io)->info->name - priv->info->name##_info) +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) +{ + if (mod->type != type) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "%s[%d] is not your expected module\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } +} + /* * rsnd_mod functions */ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index f35d9cc61686..e4068d78616c 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -627,4 +627,15 @@ void rsnd_dvc_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); +#ifdef DEBUG +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); +#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI) +#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC) +#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC) +#else +#define rsnd_mod_confirm_ssi(mssi) +#define rsnd_mod_confirm_src(msrc) +#define rsnd_mod_confirm_dvc(mdvc) +#endif + #endif -- cgit v1.2.3-59-g8ed1b From 5c6901d98b35c3192336e419344f0f22f86b8845 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:03:25 +0000 Subject: ASoC: rsnd: ADG initial setup on rsnd_adg_ssi_clk_init() ADG is special IP since it doesn't have MSTP. And now, ADG has common mod base register access. We can now setup ADG initial setting when probe timing. It is needed if sound card is based on AUIDO_CLK which is used as Master clock. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 48bb38d38499..276703ba3e1f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -25,7 +25,6 @@ struct rsnd_adg { int rbga_rate_for_441khz_div_6; /* RBGA */ int rbgb_rate_for_48khz_div_6; /* RBGB */ - u32 ckr; }; #define for_each_rsnd_clk(pos, adg, i) \ @@ -323,7 +322,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; int i; @@ -366,11 +364,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: - /* see rsnd_adg_ssi_clk_init() */ - rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, adg->ckr); - rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ - /* * This "mod" = "ssi" here. * we can get "ssi id" from mod @@ -386,6 +379,7 @@ found_clock: static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); unsigned long rate; u32 ckr; int i; @@ -405,6 +399,7 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) * and, BRGB outputs 48.0kHz base parent clock 1/32 here. * see * rsnd_adg_ssi_clk_try_start() + * rsnd_ssi_master_clk_start() */ ckr = 0; adg->rbga_rate_for_441khz_div_6 = 0; @@ -428,7 +423,9 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) } } - adg->ckr = ckr; + rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr); + rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ + rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ } int rsnd_adg_probe(struct platform_device *pdev, -- cgit v1.2.3-59-g8ed1b From eae6fff4f15a9d1969412bb5aa5a3585f00821fb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:03:48 +0000 Subject: ASoC: rsnd: tidyup ADG clock calculate method Current ADG clock calculation needs ADG and SSI settings. Thus, SSI side clock request function depends on ADG settings. After reconsideration, we can close this method inside ADG. This function uses new method. And it becomes preparation for AUDIO_CLKOUT support. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 77 ++++++++++++++++++++++++++++++++++--------------- sound/soc/sh/rcar/ssi.c | 46 +++++++++++++---------------- 2 files changed, 74 insertions(+), 49 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 276703ba3e1f..a0b9aaa1af87 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -15,6 +15,8 @@ #define CLKI 3 #define CLKMAX 4 +#define BRRx_MASK(x) (0x3FF & x) + static struct rsnd_mod_ops adg_ops = { .name = "adg", }; @@ -23,8 +25,8 @@ struct rsnd_adg { struct clk *clk[CLKMAX]; struct rsnd_mod mod; - int rbga_rate_for_441khz_div_6; /* RBGA */ - int rbgb_rate_for_48khz_div_6; /* RBGB */ + int rbga_rate_for_441khz; /* RBGA */ + int rbgb_rate_for_48khz; /* RBGB */ }; #define for_each_rsnd_clk(pos, adg, i) \ @@ -34,6 +36,21 @@ struct rsnd_adg { i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) +static u32 rsnd_adg_calculate_rbgx(unsigned long div) +{ + int i, ratio; + + if (!div) + return 0; + + for (i = 3; i >= 0; i--) { + ratio = 2 << (i * 2); + if (0 == (div % ratio)) + return (u32)((i << 8) | ((div / ratio) - 1)); + } + + return ~0; +} static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { @@ -146,8 +163,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ - adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */ - adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ + adg->rbga_rate_for_441khz, /* 0011: RBGA */ + adg->rbgb_rate_for_48khz, /* 0100: RBGB */ }; rsnd_mod_confirm_src(src_mod); @@ -228,8 +245,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */ clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */ 0, /* 011: MLBCLK (not used) */ - adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */ - adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */ + adg->rbga_rate_for_441khz, /* 100: RBGA */ + adg->rbgb_rate_for_48khz, /* 101: RBGB */ }; /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */ @@ -348,14 +365,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) } /* - * find 1/6 clock from BRGA/BRGB + * find divided clock from BRGA/BRGB */ - if (rate == adg->rbga_rate_for_441khz_div_6) { + if (rate == adg->rbga_rate_for_441khz) { data = 0x10; goto found_clock; } - if (rate == adg->rbgb_rate_for_48khz_div_6) { + if (rate == adg->rbgb_rate_for_48khz) { data = 0x20; goto found_clock; } @@ -380,8 +397,9 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - unsigned long rate; - u32 ckr; + struct device *dev = rsnd_priv_to_dev(priv); + unsigned long rate, div; + u32 ckr, rbgx, rbga, rbgb; int i; int brg_table[] = { [CLKA] = 0x0, @@ -395,15 +413,15 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) * have 44.1kHz or 48kHz base clocks for now. * * SSI itself can divide parent clock by 1/1 - 1/16 - * So, BRGA outputs 44.1kHz base parent clock 1/32, - * and, BRGB outputs 48.0kHz base parent clock 1/32 here. * see * rsnd_adg_ssi_clk_try_start() * rsnd_ssi_master_clk_start() */ ckr = 0; - adg->rbga_rate_for_441khz_div_6 = 0; - adg->rbgb_rate_for_48khz_div_6 = 0; + rbga = 2; /* default 1/6 */ + rbgb = 2; /* default 1/6 */ + adg->rbga_rate_for_441khz = 0; + adg->rbgb_rate_for_48khz = 0; for_each_rsnd_clk(clk, adg, i) { rate = clk_get_rate(clk); @@ -411,21 +429,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) continue; /* RBGA */ - if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) { - adg->rbga_rate_for_441khz_div_6 = rate / 6; - ckr |= brg_table[i] << 20; + if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { + div = 6; + rbgx = rsnd_adg_calculate_rbgx(div); + if (BRRx_MASK(rbgx) == rbgx) { + rbga = rbgx; + adg->rbga_rate_for_441khz = rate / div; + ckr |= brg_table[i] << 20; + } } /* RBGB */ - if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) { - adg->rbgb_rate_for_48khz_div_6 = rate / 6; - ckr |= brg_table[i] << 16; + if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { + div = 6; + rbgx = rsnd_adg_calculate_rbgx(div); + if (BRRx_MASK(rbgx) == rbgx) { + rbgb = rbgx; + adg->rbgb_rate_for_48khz = rate / div; + ckr |= brg_table[i] << 16; + } } } rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr); - rsnd_mod_write(adg_mod, BRRA, 0x00000002); /* 1/6 */ - rsnd_mod_write(adg_mod, BRRB, 0x00000002); /* 1/6 */ + rsnd_mod_write(adg_mod, BRRA, rbga); + rsnd_mod_write(adg_mod, BRRB, rbgb); + + dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", + ckr, rbga, rbgb); } int rsnd_adg_probe(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 91712e88405d..5e05f9422073 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -129,10 +129,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod *mod = rsnd_mod_get(ssi); - int i, j, ret; - int adg_clk_div_table[] = { - 1, 6, /* see adg.c */ - }; + int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, }; @@ -142,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * Find best clock, and try to start ADG */ - for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { - for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { - - /* - * this driver is assuming that - * system word is 64fs (= 2 x 32bit) - * see rsnd_ssi_init() - */ - main_rate = rate / adg_clk_div_table[i] - * 32 * 2 * ssi_clk_mul_table[j]; - - ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); - if (0 == ret) { - ssi->cr_clk = FORCE | SWL_32 | - SCKD | SWSD | CKDV(j); - - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod), rate); - - return 0; - } + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_init() + */ + main_rate = rate * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); + if (0 == ret) { + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "%s[%d] outputs %u Hz\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); + + return 0; } } -- cgit v1.2.3-59-g8ed1b From 3af6c3ac91eb937fc611f9c745f89e7c53d55282 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:04:06 +0000 Subject: ASoC: rsnd: tidyup ADG debug message for clock selection It didn't have "\n", and indicated different data Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index a0b9aaa1af87..606d416c51b2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -387,8 +387,9 @@ found_clock: */ rsnd_adg_set_ssi_clk(mod, data); - dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", - rsnd_mod_id(mod), i, rate); + dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + data, rate); return 0; } -- cgit v1.2.3-59-g8ed1b From 248e88c2fb5a09eb87b21b00fd4167cc99c05759 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:04:24 +0000 Subject: ASoC: rsnd: adg: ignore undefined clock error undefined clock is not error. Accept such case. And this is prepare for clock out support in the same time. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 606d416c51b2..d4fb11a3ce64 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -394,6 +394,28 @@ found_clock: return 0; } +static void rsnd_adg_get_clkin(struct rsnd_priv *priv, + struct rsnd_adg *adg) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + static const char * const clk_name[] = { + [CLKA] = "clk_a", + [CLKB] = "clk_b", + [CLKC] = "clk_c", + [CLKI] = "clk_i", + }; + int i; + + for (i = 0; i < CLKMAX; i++) { + clk = devm_clk_get(dev, clk_name[i]); + adg->clk[i] = IS_ERR(clk) ? NULL : clk; + } + + for_each_rsnd_clk(clk, adg, i) + dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); +} + static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) { struct clk *clk; @@ -466,8 +488,6 @@ int rsnd_adg_probe(struct platform_device *pdev, { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; - int i; adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); if (!adg) { @@ -483,13 +503,7 @@ int rsnd_adg_probe(struct platform_device *pdev, adg->mod.ops = &adg_ops; adg->mod.priv = priv; - adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); - adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); - adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); - adg->clk[CLKI] = devm_clk_get(dev, "clk_i"); - - for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); + rsnd_adg_get_clkin(priv, adg); rsnd_adg_ssi_clk_init(priv, adg); -- cgit v1.2.3-59-g8ed1b From 2a46db4a3787edb0dc07276f21f33bbaf01938f1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Sep 2015 07:04:45 +0000 Subject: ASoC: rsnd: add AUDIO_CLKOUT support Renesas sound has AUDIO_CLKOUT (in Gen1/Gen2) AUDIO_CLKOUT1/2/3 (in Gen3) This patch support these patches as clock provider. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/renesas,rsnd.txt | 3 + sound/soc/sh/rcar/adg.c | 98 +++++++++++++++++++++- 2 files changed, 97 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index bf6fd1af0a11..c57cbd65736c 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -34,6 +34,9 @@ Required properties: see below for detail. - #sound-dai-cells : it must be 0 if your system is using single DAI it must be 1 if your system is using multi DAI +- #clock-cells : it must be 0 if your system has audio_clkout + it must be 1 if your system has audio_clkout0/1/2/3 +- clock-frequency : for all audio_clkout0/1/2/3 SSI subnode properties: - interrupts : Should contain SSI interrupt for PIO transfer diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d4fb11a3ce64..3fecb87f45ba 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -7,6 +7,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ +#include #include "rsnd.h" #define CLKA 0 @@ -15,6 +16,12 @@ #define CLKI 3 #define CLKMAX 4 +#define CLKOUT 0 +#define CLKOUT1 1 +#define CLKOUT2 2 +#define CLKOUT3 3 +#define CLKOUTMAX 4 + #define BRRx_MASK(x) (0x3FF & x) static struct rsnd_mod_ops adg_ops = { @@ -23,6 +30,8 @@ static struct rsnd_mod_ops adg_ops = { struct rsnd_adg { struct clk *clk[CLKMAX]; + struct clk *clkout[CLKOUTMAX]; + struct clk_onecell_data onecell; struct rsnd_mod mod; int rbga_rate_for_441khz; /* RBGA */ @@ -34,6 +43,11 @@ struct rsnd_adg { (i < CLKMAX) && \ ((pos) = adg->clk[i]); \ i++) +#define for_each_rsnd_clkout(pos, adg, i) \ + for (i = 0; \ + (i < CLKOUTMAX) && \ + ((pos) = adg->clkout[i]); \ + i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) static u32 rsnd_adg_calculate_rbgx(unsigned long div) @@ -416,14 +430,25 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); } -static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +static void rsnd_adg_get_clkout(struct rsnd_priv *priv, + struct rsnd_adg *adg) { struct clk *clk; struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct device *dev = rsnd_priv_to_dev(priv); - unsigned long rate, div; + struct device_node *np = dev->of_node; u32 ckr, rbgx, rbga, rbgb; + u32 rate, req_rate, div; + uint32_t count = 0; + unsigned long req_48kHz_rate, req_441kHz_rate; int i; + const char *parent_clk_name = NULL; + static const char * const clkout_name[] = { + [CLKOUT] = "audio_clkout", + [CLKOUT1] = "audio_clkout1", + [CLKOUT2] = "audio_clkout2", + [CLKOUT3] = "audio_clkout3", + }; int brg_table[] = { [CLKA] = 0x0, [CLKB] = 0x1, @@ -431,6 +456,20 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) [CLKI] = 0x2, }; + of_property_read_u32(np, "#clock-cells", &count); + + /* + * ADG supports BRRA/BRRB output only + * this means all clkout0/1/2/3 will be same rate + */ + of_property_read_u32(np, "clock-frequency", &req_rate); + req_48kHz_rate = 0; + req_441kHz_rate = 0; + if (0 == (req_rate % 44100)) + req_441kHz_rate = req_rate; + if (0 == (req_rate % 48000)) + req_48kHz_rate = req_rate; + /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. @@ -454,22 +493,72 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) /* RBGA */ if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { div = 6; + if (req_441kHz_rate) + div = rate / req_441kHz_rate; rbgx = rsnd_adg_calculate_rbgx(div); if (BRRx_MASK(rbgx) == rbgx) { rbga = rbgx; adg->rbga_rate_for_441khz = rate / div; ckr |= brg_table[i] << 20; + if (req_441kHz_rate) + parent_clk_name = __clk_get_name(clk); } } /* RBGB */ if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { div = 6; + if (req_48kHz_rate) + div = rate / req_48kHz_rate; rbgx = rsnd_adg_calculate_rbgx(div); if (BRRx_MASK(rbgx) == rbgx) { rbgb = rbgx; adg->rbgb_rate_for_48khz = rate / div; ckr |= brg_table[i] << 16; + if (req_48kHz_rate) { + parent_clk_name = __clk_get_name(clk); + ckr |= 0x80000000; + } + } + } + } + + /* + * ADG supports BRRA/BRRB output only. + * this means all clkout0/1/2/3 will be * same rate + */ + + /* + * for clkout + */ + if (!count) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, + (parent_clk_name) ? + 0 : CLK_IS_ROOT, req_rate); + if (!IS_ERR(clk)) { + adg->clkout[CLKOUT] = clk; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + } + } + /* + * for clkout0/1/2/3 + */ + else { + for (i = 0; i < CLKOUTMAX; i++) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, + (parent_clk_name) ? + 0 : CLK_IS_ROOT, + req_rate); + if (!IS_ERR(clk)) { + adg->onecell.clks = adg->clkout; + adg->onecell.clk_num = CLKOUTMAX; + + adg->clkout[i] = clk; + + of_clk_add_provider(np, of_clk_src_onecell_get, + &adg->onecell); } } } @@ -478,6 +567,8 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) rsnd_mod_write(adg_mod, BRRA, rbga); rsnd_mod_write(adg_mod, BRRB, rbgb); + for_each_rsnd_clkout(clk, adg, i) + dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk)); dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", ckr, rbga, rbgb); } @@ -504,8 +595,7 @@ int rsnd_adg_probe(struct platform_device *pdev, adg->mod.priv = priv; rsnd_adg_get_clkin(priv, adg); - - rsnd_adg_ssi_clk_init(priv, adg); + rsnd_adg_get_clkout(priv, adg); priv->adg = adg; -- cgit v1.2.3-59-g8ed1b From 03d3964c517b24b11923978e2ed4a2a19b702f8a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Sep 2015 12:31:24 +0800 Subject: ASoC: SPEAr: Make SND_SPEAR_SOC select SND_SOC_GENERIC_DMAENGINE_PCM devm_snd_dmaengine_pcm_register() is guarded by CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/spear/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 0a53053495f3..4fb91412ebec 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate -- cgit v1.2.3-59-g8ed1b From 3e8f5263bd83fd0fe7dacba2de347b17ac99fc91 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:46 +0300 Subject: ASoC: tlv320aic3x: Improve tdm support Before this patch the set_tdm_slots() callback did not store the value of slot width anywhere. The tdm support only worked if selected slot width was equal to the sample width. With this patch all sample widths that fit into the slot width are supported. There unused bits are filled unnecessarily in the capture direction, but the other end of the i2s bus should be able to ignore them. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic3x.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1a82b19b2644..f1c9fffcd3a6 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -80,6 +80,7 @@ struct aic3x_priv { unsigned int sysclk; unsigned int dai_fmt; unsigned int tdm_delay; + unsigned int slot_width; struct list_head list; int master; int gpio_reset; @@ -1025,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; int clk; + int width = aic3x->slot_width; + + if (!width) + width = params_width(params); /* select data word length */ data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); - switch (params_width(params)) { + switch (width) { case 16: break; case 20: @@ -1170,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); int delay = 0; + int width = aic3x->slot_width; + + if (!width) + width = substream->runtime->sample_bits; /* TDM slot selection only valid in DSP_A/_B mode */ if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A) - delay += (aic3x->tdm_delay + 1); + delay += (aic3x->tdm_delay*width + 1); else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B) - delay += aic3x->tdm_delay; + delay += aic3x->tdm_delay*width; /* Configure data delay */ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay); @@ -1296,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, return -EINVAL; } - aic3x->tdm_delay = lsb * slot_width; + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(codec->dev, "Unsupported slot width %d\n", slot_width); + return -EINVAL; + } + + + aic3x->tdm_delay = lsb; + aic3x->slot_width = slot_width; /* DOUT in high-impedance on inactive bit clocks */ snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, -- cgit v1.2.3-59-g8ed1b From dd55ff8346a972cca1ad056c8258ee96d090633e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:44 +0300 Subject: ASoC: davinci-mcasp: Add set_tdm_slots() support Implements set_tdm_slot() callback for mcasp. Channel constraints are updated according to the configured tdm mask and slots each time set_tdm_slot() is called. The special case when slot width is set to zero is allowed and it means that slot width is the same as the sample width. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 255 ++++++++++++++++++++++++++------------ 1 file changed, 174 insertions(+), 81 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index add6bb99661d..fa47a39fac86 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -80,6 +80,8 @@ struct davinci_mcasp { /* McASP specific data */ int tdm_slots; + u32 tdm_mask[2]; + int slot_width; u8 op_mode; u8 num_serializer; u8 *serial_dir; @@ -596,6 +598,84 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; } +/* All serializers must have equal number of channels */ +static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, + int serializers) +{ + struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; + unsigned int *list = (unsigned int *) cl->list; + int slots = mcasp->tdm_slots; + int i, count = 0; + + if (mcasp->tdm_mask[stream]) + slots = hweight32(mcasp->tdm_mask[stream]); + + for (i = 2; i <= slots; i++) + list[count++] = i; + + for (i = 2; i <= serializers; i++) + list[count++] = i*slots; + + cl->count = count; + + return 0; +} + +static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp) +{ + int rx_serializers = 0, tx_serializers = 0, ret, i; + + for (i = 0; i < mcasp->num_serializer; i++) + if (mcasp->serial_dir[i] == TX_MODE) + tx_serializers++; + else if (mcasp->serial_dir[i] == RX_MODE) + rx_serializers++; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK, + tx_serializers); + if (ret) + return ret; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE, + rx_serializers); + + return ret; +} + + +static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(mcasp->dev, + "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + if (tx_mask >= (1<= (1<dev, + "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n", + tx_mask, rx_mask, slots); + return -EINVAL; + } + + if (slot_width && + (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) { + dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + mcasp->tdm_slots = slots; + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask; + mcasp->slot_width = slot_width; + + return davinci_mcasp_set_ch_constraints(mcasp); +} + static int davinci_config_channel_size(struct davinci_mcasp *mcasp, int word_length) { @@ -632,6 +712,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, */ rx_rotate = (slot_length - word_length) / 4; word_length = slot_length; + } else if (mcasp->slot_width) { + rx_rotate = (mcasp->slot_width - word_length) / 4; + word_length = mcasp->slot_width; } /* mapping of the XSSZ bit-field as described in the datasheet */ @@ -777,33 +860,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, /* * If more than one serializer is needed, then use them with - * their specified tdm_slots count. Otherwise, one serializer - * can cope with the transaction using as many slots as channels - * in the stream, requires channels symmetry + * all the specified tdm_slots. Otherwise, one serializer can + * cope with the transaction using just as many slots as there + * are channels in the stream. */ - active_serializers = (channels + total_slots - 1) / total_slots; - if (active_serializers == 1) - active_slots = channels; - else - active_slots = total_slots; - - for (i = 0; i < active_slots; i++) - mask |= (1 << i); + if (mcasp->tdm_mask[stream]) { + active_slots = hweight32(mcasp->tdm_mask[stream]); + active_serializers = (channels + active_slots - 1) / + active_slots; + if (active_serializers == 1) { + active_slots = channels; + for (i = 0; i < total_slots; i++) { + if ((1 << i) & mcasp->tdm_mask[stream]) { + mask |= (1 << i); + if (--active_slots <= 0) + break; + } + } + } + } else { + active_serializers = (channels + total_slots - 1) / total_slots; + if (active_serializers == 1) + active_slots = channels; + else + active_slots = total_slots; + for (i = 0; i < active_slots; i++) + mask |= (1 << i); + } mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); if (!mcasp->dat_port) busel = TXSEL; - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(total_slots), FSXMOD(0x1FF)); - - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(total_slots), FSRMOD(0x1FF)); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(total_slots), FSXMOD(0x1FF)); + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(total_slots), FSRMOD(0x1FF)); + } return 0; } @@ -923,6 +1023,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int sbits = params_width(params); int ppm, div; + if (mcasp->slot_width) + sbits = mcasp->slot_width; + div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, &ppm); if (ppm) @@ -1028,6 +1131,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval range; int i; + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + snd_interval_any(&range); range.empty = 1; @@ -1070,10 +1176,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { if (snd_mask_test(fmt, i)) { - uint bclk_freq = snd_pcm_format_width(i)*slots*rate; + uint sbits = snd_pcm_format_width(i); int ppm; - davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + + davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate, + &ppm); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { snd_mask_set(&nfmt, i); count++; @@ -1095,6 +1205,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir; + int tdm_slots = mcasp->tdm_slots; + + if (mcasp->tdm_mask[substream->stream]) + tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); mcasp->substreams[substream->stream] = substream; @@ -1115,7 +1229,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, max_channels++; } ruledata->serializers = max_channels; - max_channels *= mcasp->tdm_slots; + max_channels *= tdm_slots; /* * If the already active stream has less channels than the calculated * limnit based on the seirializers * tdm_slots, we need to use that as @@ -1125,15 +1239,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, */ if (mcasp->channels && mcasp->channels < max_channels) max_channels = mcasp->channels; + /* + * But we can always allow channels upto the amount of + * the available tdm_slots. + */ + if (max_channels < tdm_slots) + max_channels = tdm_slots; snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels); - if (mcasp->chconstr[substream->stream].count) - snd_pcm_hw_constraint_list(substream->runtime, - 0, SNDRV_PCM_HW_PARAM_CHANNELS, - &mcasp->chconstr[substream->stream]); + snd_pcm_hw_constraint_list(substream->runtime, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mcasp->chconstr[substream->stream]); + + if (mcasp->slot_width) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + 8, mcasp->slot_width); /* * If we rely on implicit BCLK divider setting we should @@ -1185,6 +1309,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { .set_fmt = davinci_mcasp_set_dai_fmt, .set_clkdiv = davinci_mcasp_set_clkdiv, .set_sysclk = davinci_mcasp_set_sysclk, + .set_tdm_slot = davinci_mcasp_set_tdm_slot, }; static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) @@ -1514,59 +1639,6 @@ nodata: return pdata; } -/* All serializers must have equal number of channels */ -static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, - struct snd_pcm_hw_constraint_list *cl, - int serializers) -{ - unsigned int *list; - int i, count = 0; - - if (serializers <= 1) - return 0; - - list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (mcasp->tdm_slots + serializers - 2), - GFP_KERNEL); - if (!list) - return -ENOMEM; - - for (i = 2; i <= mcasp->tdm_slots; i++) - list[count++] = i; - - for (i = 2; i <= serializers; i++) - list[count++] = i*mcasp->tdm_slots; - - cl->count = count; - cl->list = list; - - return 0; -} - - -static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) -{ - int rx_serializers = 0, tx_serializers = 0, ret, i; - - for (i = 0; i < mcasp->num_serializer; i++) - if (mcasp->serial_dir[i] == TX_MODE) - tx_serializers++; - else if (mcasp->serial_dir[i] == RX_MODE) - rx_serializers++; - - ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ - SNDRV_PCM_STREAM_PLAYBACK], - tx_serializers); - if (ret) - return ret; - - ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ - SNDRV_PCM_STREAM_CAPTURE], - rx_serializers); - - return ret; -} - enum { PCM_EDMA, PCM_SDMA, @@ -1783,7 +1855,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; } - ret = davinci_mcasp_init_ch_constraints(mcasp); + /* Allocate memory for long enough list for all possible + * scenarios. Maximum number tdm slots is 32 and there cannot + * be more serializers than given in the configuration. The + * serializer directions could be taken into account, but it + * would make code much more complex and save only couple of + * bytes. + */ + mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = + devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (32 + mcasp->num_serializer - 2), + GFP_KERNEL); + + mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = + devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (32 + mcasp->num_serializer - 2), + GFP_KERNEL); + + if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || + !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) + return -ENOMEM; + + ret = davinci_mcasp_set_ch_constraints(mcasp); if (ret) goto err; -- cgit v1.2.3-59-g8ed1b From 6131084a0bc966107021d8c89489f9cd1663b902 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Sep 2015 21:27:43 +0300 Subject: ASoC: simple-card: Add tdm slot mask support to simple-card Adds DT binding for explicitly choosing a tdm mask for DAI and uses it in simple-card. The API for snd_soc_of_parse_tdm_slot() has also been changed. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tdm-slot.txt | 11 +++++++++- include/sound/simple_card.h | 2 ++ include/sound/soc.h | 2 ++ sound/soc/generic/simple-card.c | 8 +++++-- sound/soc/soc-core.c | 25 ++++++++++++++++++++++ 5 files changed, 45 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.txt b/Documentation/devicetree/bindings/sound/tdm-slot.txt index 6a2c84247f91..34cf70e2cbc4 100644 --- a/Documentation/devicetree/bindings/sound/tdm-slot.txt +++ b/Documentation/devicetree/bindings/sound/tdm-slot.txt @@ -4,11 +4,15 @@ This specifies audio DAI's TDM slot. TDM slot properties: dai-tdm-slot-num : Number of slots in use. -dai-tdm-slot-width : Width in bits for each slot. +dai-tdm-slot-width : Width in bits for each slot. +dai-tdm-slot-tx-mask : Transmit direction slot mask, optional +dai-tdm-slot-rx-mask : Receive direction slot mask, optional For instance: dai-tdm-slot-num = <2>; dai-tdm-slot-width = <8>; + dai-tdm-slot-tx-mask = <0 1>; + dai-tdm-slot-rx-mask = <1 0>; And for each spcified driver, there could be one .of_xlate_tdm_slot_mask() to specify a explicit mapping of the channels and the slots. If it's absent @@ -18,3 +22,8 @@ tx and rx masks. For snd_soc_of_xlate_tdm_slot_mask(), the tx and rx masks will use a 1 bit for an active slot as default, and the default active bits are at the LSB of the masks. + +The explicit masks are given as array of integers, where the first +number presents bit-0 (LSB), second presents bit-1, etc. Any non zero +number is considered 1 and 0 is 0. snd_soc_of_xlate_tdm_slot_mask() +does not do anything, if either mask is set non zero value. diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index b9b4f289fe6b..0399352f3a62 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -19,6 +19,8 @@ struct asoc_simple_dai { unsigned int sysclk; int slots; int slot_width; + unsigned int tx_slot_mask; + unsigned int rx_slot_mask; struct clk *clk; }; diff --git a/include/sound/soc.h b/include/sound/soc.h index 884e728b09d9..a76622d7bb2f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1601,6 +1601,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_tdm_slot(struct device_node *np, + unsigned int *tx_mask, + unsigned int *rx_mask, unsigned int *slots, unsigned int *slot_width); void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3ff76d419436..54c33204541f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -151,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, } if (set->slots) { - ret = snd_soc_dai_set_tdm_slot(dai, 0, 0, + ret = snd_soc_dai_set_tdm_slot(dai, + set->tx_slot_mask, + set->rx_slot_mask, set->slots, set->slot_width); if (ret && ret != -ENOTSUPP) { @@ -243,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np, return ret; /* Parse TDM slot */ - ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); + ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask, + &dai->rx_slot_mask, + &dai->slots, &dai->slot_width); if (ret) return ret; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6173d15236c3..c5e21ca0c015 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3291,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +static int snd_soc_of_get_slot_mask(struct device_node *np, + const char *prop_name, + unsigned int *mask) +{ + u32 val; + const u32 *of_slot_mask = of_get_property(np, prop_name, &val); + int i; + + if (!of_slot_mask) + return 0; + val /= sizeof(u32); + for (i = 0; i < val; i++) + if (be32_to_cpup(&of_slot_mask[i])) + *mask |= (1 << i); + + return val; +} + int snd_soc_of_parse_tdm_slot(struct device_node *np, + unsigned int *tx_mask, + unsigned int *rx_mask, unsigned int *slots, unsigned int *slot_width) { u32 val; int ret; + if (tx_mask) + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); + if (rx_mask) + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); + if (of_property_read_bool(np, "dai-tdm-slot-num")) { ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); if (ret) -- cgit v1.2.3-59-g8ed1b From 2fc171e69e0dc0f5cce805ec40923c4e7ff78e94 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 10 Sep 2015 18:01:45 +0530 Subject: ASoC: Intel: remove unused function The function get_current_pipe_id() was not being used. Signed-off-by: Sudip Mukherjee Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 683e50116152..5e9c316c142a 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -368,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream, kfree(stream); } -static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) -{ - struct sst_data *sst = snd_soc_dai_get_drvdata(dai); - struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; - struct sst_runtime_stream *stream = - substream->runtime->private_data; - u32 str_id = stream->stream_info.str_id; - unsigned int pipe_id; - - pipe_id = map[str_id].device_id; - - dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n", - pipe_id, str_id); - return pipe_id; -} - static int sst_media_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { -- cgit v1.2.3-59-g8ed1b From 45fb6b6f2aa3f6b22b81078db0dba4b26c9d0bdb Mon Sep 17 00:00:00 2001 From: Emilio López Date: Sat, 12 Sep 2015 15:26:24 +0200 Subject: ASoC: sunxi: add support for the on-chip codec on early Allwinner SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sun4i, sun5i and sun7i SoC families have a built-in codec, capable of both audio capture and playback. While this is called a codec by Allwinner, it really is an in-SoC combination of a codec and a DAI, with its own DAC/ADC and amplifiers in a single memory-mapped controller. The capture part has been left out for now, and will be added eventually. Signed-off-by: Emilio López Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sunxi/Kconfig | 11 + sound/soc/sunxi/Makefile | 2 + sound/soc/sunxi/sun4i-codec.c | 720 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 735 insertions(+) create mode 100644 sound/soc/sunxi/Kconfig create mode 100644 sound/soc/sunxi/Makefile create mode 100644 sound/soc/sunxi/sun4i-codec.c (limited to 'sound') diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 225bfda414e9..7de792b06007 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -58,6 +58,7 @@ source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sti/Kconfig" +source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 134aca150a50..af0a5714e107 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sti/ +obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig new file mode 100644 index 000000000000..84c72ec6ad73 --- /dev/null +++ b/sound/soc/sunxi/Kconfig @@ -0,0 +1,11 @@ +menu "Allwinner SoC Audio support" + +config SND_SUN4I_CODEC + tristate "Allwinner A10 Codec Support" + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Select Y or M to add support for the Codec embedded in the Allwinner + A10 and affiliated SoCs. + +endmenu diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile new file mode 100644 index 000000000000..ea8a08c881d6 --- /dev/null +++ b/sound/soc/sunxi/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o + diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c new file mode 100644 index 000000000000..6e83e62ef039 --- /dev/null +++ b/sound/soc/sunxi/sun4i-codec.c @@ -0,0 +1,720 @@ +/* + * Copyright 2014 Emilio López + * Copyright 2014 Jon Smirl + * Copyright 2015 Maxime Ripard + * + * Based on the Allwinner SDK driver, released under the GPL. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Codec DAC register offsets and bit fields */ +#define SUN4I_CODEC_DAC_DPC (0x00) +#define SUN4I_CODEC_DAC_DPC_EN_DA (31) +#define SUN4I_CODEC_DAC_DPC_DVOL (12) +#define SUN4I_CODEC_DAC_FIFOC (0x04) +#define SUN4I_CODEC_DAC_FIFOC_DAC_FS (29) +#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION (28) +#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT (26) +#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE (24) +#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT (21) +#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL (8) +#define SUN4I_CODEC_DAC_FIFOC_MONO_EN (6) +#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS (5) +#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN (4) +#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) +#define SUN4I_CODEC_DAC_FIFOS (0x08) +#define SUN4I_CODEC_DAC_TXDATA (0x0c) +#define SUN4I_CODEC_DAC_ACTL (0x10) +#define SUN4I_CODEC_DAC_ACTL_DACAENR (31) +#define SUN4I_CODEC_DAC_ACTL_DACAENL (30) +#define SUN4I_CODEC_DAC_ACTL_MIXEN (29) +#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS (15) +#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS (14) +#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS (13) +#define SUN4I_CODEC_DAC_ACTL_DACPAS (8) +#define SUN4I_CODEC_DAC_ACTL_MIXPAS (7) +#define SUN4I_CODEC_DAC_ACTL_PA_MUTE (6) +#define SUN4I_CODEC_DAC_ACTL_PA_VOL (0) +#define SUN4I_CODEC_DAC_TUNE (0x14) +#define SUN4I_CODEC_DAC_DEBUG (0x18) + +/* Codec ADC register offsets and bit fields */ +#define SUN4I_CODEC_ADC_FIFOC (0x1c) +#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) +#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24) +#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8) +#define SUN4I_CODEC_ADC_FIFOC_MONO_EN (7) +#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS (6) +#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN (4) +#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) +#define SUN4I_CODEC_ADC_FIFOS (0x20) +#define SUN4I_CODEC_ADC_RXDATA (0x24) +#define SUN4I_CODEC_ADC_ACTL (0x28) +#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) +#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) +#define SUN4I_CODEC_ADC_ACTL_PREG1EN (29) +#define SUN4I_CODEC_ADC_ACTL_PREG2EN (28) +#define SUN4I_CODEC_ADC_ACTL_VMICEN (27) +#define SUN4I_CODEC_ADC_ACTL_VADCG (20) +#define SUN4I_CODEC_ADC_ACTL_ADCIS (17) +#define SUN4I_CODEC_ADC_ACTL_PA_EN (4) +#define SUN4I_CODEC_ADC_ACTL_DDE (3) +#define SUN4I_CODEC_ADC_DEBUG (0x2c) + +/* Other various ADC registers */ +#define SUN4I_CODEC_DAC_TXCNT (0x30) +#define SUN4I_CODEC_ADC_RXCNT (0x34) +#define SUN4I_CODEC_AC_SYS_VERI (0x38) +#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c) + +struct sun4i_codec { + struct device *dev; + struct regmap *regmap; + struct clk *clk_apb; + struct clk *clk_module; + + struct snd_dmaengine_dai_dma_data playback_dma_data; +}; + +static void sun4i_codec_start_playback(struct sun4i_codec *scodec) +{ + /* + * FIXME: according to the BSP, we might need to drive a PA + * GPIO high here on some boards + */ + + /* Flush TX FIFO */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH), + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); + + /* Enable DAC DRQ */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN), + BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN)); +} + +static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) +{ + /* + * FIXME: according to the BSP, we might need to drive a PA + * GPIO low here on some boards + */ + + /* Disable DAC DRQ */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN), + 0); +} + +static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENOTSUPP; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + sun4i_codec_start_playback(scodec); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sun4i_codec_stop_playback(scodec); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int sun4i_codec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + u32 val; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENOTSUPP; + + /* Flush the TX FIFO */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH), + BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH)); + + /* Set TX FIFO Empty Trigger Level */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + 0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL, + 0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL); + + if (substream->runtime->rate > 32000) + /* Use 64 bits FIR filter */ + val = 0; + else + /* Use 32 bits FIR filter */ + val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION); + + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION), + val); + + /* Send zeros when we have an underrun */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT), + 0); + + return 0; +} + +static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) +{ + unsigned int rate = params_rate(params); + + switch (rate) { + case 176400: + case 88200: + case 44100: + case 33075: + case 22050: + case 14700: + case 11025: + case 7350: + return 22579200; + + case 192000: + case 96000: + case 48000: + case 32000: + case 24000: + case 16000: + case 12000: + case 8000: + return 24576000; + + default: + return 0; + } +} + +static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params) +{ + unsigned int rate = params_rate(params); + + switch (rate) { + case 192000: + case 176400: + return 6; + + case 96000: + case 88200: + return 7; + + case 48000: + case 44100: + return 0; + + case 32000: + case 33075: + return 1; + + case 24000: + case 22050: + return 2; + + case 16000: + case 14700: + return 3; + + case 12000: + case 11025: + return 4; + + case 8000: + case 7350: + return 5; + + default: + return -EINVAL; + } +} + +static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + unsigned long clk_freq; + int hwrate; + u32 val; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENOTSUPP; + + clk_freq = sun4i_codec_get_mod_freq(params); + if (!clk_freq) + return -EINVAL; + + if (clk_set_rate(scodec->clk_module, clk_freq)) + return -EINVAL; + + hwrate = sun4i_codec_get_hw_rate(params); + if (hwrate < 0) + return hwrate; + + /* Set DAC sample rate */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + 7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS, + hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS); + + /* Set the number of channels we want to use */ + if (params_channels(params) == 1) + val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN); + else + val = 0; + + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN), + val); + + /* Set the number of sample bits to either 16 or 24 bits */ + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS), + BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS)); + + /* Set TX FIFO mode to padding the LSBs with 0 */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE), + 0); + + scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } else { + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS), + 0); + + /* Set TX FIFO mode to repeat the MSB */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE), + BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE)); + + scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + } + + return 0; +} + +static int sun4i_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + + /* + * Stop issuing DRQ when we have room for less than 16 samples + * in our TX FIFO + */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, + 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT, + 3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT); + + return clk_prepare_enable(scodec->clk_module); +} + +static void sun4i_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(scodec->clk_module); +} + +static const struct snd_soc_dai_ops sun4i_codec_dai_ops = { + .startup = sun4i_codec_startup, + .shutdown = sun4i_codec_shutdown, + .trigger = sun4i_codec_trigger, + .hw_params = sun4i_codec_hw_params, + .prepare = sun4i_codec_prepare, +}; + +static struct snd_soc_dai_driver sun4i_codec_dai = { + .name = "Codec", + .ops = &sun4i_codec_dai_ops, + .playback = { + .stream_name = "Codec Playback", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, +}; + +/*** Codec ***/ +static const struct snd_kcontrol_new sun4i_codec_pa_mute = + SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); + +static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); + +static const struct snd_kcontrol_new sun4i_codec_widgets[] = { + SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, + sun4i_codec_pa_volume_scale), +}; + +static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0), +}; + +static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = { + SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0), + SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0), +}; + +static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0), + SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0), +}; + +static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { + /* Digital parts of the DACs */ + SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_EN_DA, 0, + NULL, 0), + + /* Analog parts of the DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_DACAENL, 0), + SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_DACAENR, 0), + + /* Mixers */ + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + sun4i_codec_left_mixer_controls, + ARRAY_SIZE(sun4i_codec_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + sun4i_codec_right_mixer_controls, + ARRAY_SIZE(sun4i_codec_right_mixer_controls)), + + /* Global Mixer Enable */ + SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, + SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), + + /* Pre-Amplifier */ + SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_PA_EN, 0, + sun4i_codec_pa_mixer_controls, + ARRAY_SIZE(sun4i_codec_pa_mixer_controls)), + SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0, + &sun4i_codec_pa_mute), + + SND_SOC_DAPM_OUTPUT("HP Right"), + SND_SOC_DAPM_OUTPUT("HP Left"), +}; + +static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { + /* Left DAC Routes */ + { "Left DAC", NULL, "DAC" }, + + /* Right DAC Routes */ + { "Right DAC", NULL, "DAC" }, + + /* Right Mixer Routes */ + { "Right Mixer", NULL, "Mixer Enable" }, + { "Right Mixer", "Left DAC Playback Switch", "Left DAC" }, + { "Right Mixer", "Right DAC Playback Switch", "Right DAC" }, + + /* Left Mixer Routes */ + { "Left Mixer", NULL, "Mixer Enable" }, + { "Left Mixer", "Left DAC Playback Switch", "Left DAC" }, + + /* Pre-Amplifier Mixer Routes */ + { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" }, + { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" }, + { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" }, + { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" }, + + /* PA -> HP path */ + { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" }, + { "HP Right", NULL, "Pre-Amplifier Mute" }, + { "HP Left", NULL, "Pre-Amplifier Mute" }, +}; + +static struct snd_soc_codec_driver sun4i_codec_codec = { + .controls = sun4i_codec_widgets, + .num_controls = ARRAY_SIZE(sun4i_codec_widgets), + .dapm_widgets = sun4i_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets), + .dapm_routes = sun4i_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes), +}; + +static const struct snd_soc_component_driver sun4i_codec_component = { + .name = "sun4i-codec", +}; + +#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000 +#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static int sun4i_codec_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); + + snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, + NULL); + + return 0; +} + +static struct snd_soc_dai_driver dummy_cpu_dai = { + .name = "sun4i-codec-cpu-dai", + .probe = sun4i_codec_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SUN4I_CODEC_RATES, + .formats = SUN4I_CODEC_FORMATS, + .sig_bits = 24, + }, +}; + +static const struct regmap_config sun4i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL, +}; + +static const struct of_device_id sun4i_codec_of_match[] = { + { .compatible = "allwinner,sun4i-a10-codec" }, + { .compatible = "allwinner,sun7i-a20-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); + +static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, + int *num_links) +{ + struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link), + GFP_KERNEL); + if (!link) + return NULL; + + link->name = "cdc"; + link->stream_name = "CDC PCM"; + link->codec_dai_name = "Codec"; + link->cpu_dai_name = dev_name(dev); + link->codec_name = dev_name(dev); + link->platform_name = dev_name(dev); + link->dai_fmt = SND_SOC_DAIFMT_I2S; + + *num_links = 1; + + return link; +}; + +static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return NULL; + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return NULL; + + card->dev = dev; + card->name = "sun4i-codec"; + + ret = snd_soc_of_parse_audio_routing(card, "routing"); + if (ret) { + dev_err(dev, "Failed to create our audio routing\n"); + return NULL; + } + + return card; +}; + +static int sun4i_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct sun4i_codec *scodec; + struct resource *res; + void __iomem *base; + int ret; + + scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL); + if (!scodec) + return -ENOMEM; + + scodec->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Failed to map the registers\n"); + return PTR_ERR(base); + } + + scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_codec_regmap_config); + if (IS_ERR(scodec->regmap)) { + dev_err(&pdev->dev, "Failed to create our regmap\n"); + return PTR_ERR(scodec->regmap); + } + + /* Get the clocks from the DT */ + scodec->clk_apb = devm_clk_get(&pdev->dev, "apb"); + if (IS_ERR(scodec->clk_apb)) { + dev_err(&pdev->dev, "Failed to get the APB clock\n"); + return PTR_ERR(scodec->clk_apb); + } + + scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); + if (IS_ERR(scodec->clk_module)) { + dev_err(&pdev->dev, "Failed to get the module clock\n"); + return PTR_ERR(scodec->clk_module); + } + + /* Enable the bus clock */ + if (clk_prepare_enable(scodec->clk_apb)) { + dev_err(&pdev->dev, "Failed to enable the APB clock\n"); + return -EINVAL; + } + + /* DMA configuration for TX FIFO */ + scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; + scodec->playback_dma_data.maxburst = 4; + scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec, + &sun4i_codec_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register our codec\n"); + goto err_clk_disable; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &sun4i_codec_component, + &dummy_cpu_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register our DAI\n"); + goto err_unregister_codec; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); + goto err_unregister_codec; + } + + card = sun4i_codec_create_card(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "Failed to create our card\n"); + goto err_unregister_codec; + } + + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, scodec); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "Failed to register our card\n"); + goto err_unregister_codec; + } + + return 0; + +err_unregister_codec: + snd_soc_unregister_codec(&pdev->dev); +err_clk_disable: + clk_disable_unprepare(scodec->clk_apb); + return ret; +} + +static int sun4i_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + snd_soc_unregister_codec(&pdev->dev); + clk_disable_unprepare(scodec->clk_apb); + + return 0; +} + +static struct platform_driver sun4i_codec_driver = { + .driver = { + .name = "sun4i-codec", + .owner = THIS_MODULE, + .of_match_table = sun4i_codec_of_match, + }, + .probe = sun4i_codec_probe, + .remove = sun4i_codec_remove, +}; +module_platform_driver(sun4i_codec_driver); + +MODULE_DESCRIPTION("Allwinner A10 codec driver"); +MODULE_AUTHOR("Emilio López "); +MODULE_AUTHOR("Jon Smirl "); +MODULE_AUTHOR("Maxime Ripard "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From e2f8445176c9e6e6b152b792d08abca8308bb8ff Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 02:41:05 +0000 Subject: ASoC: rsnd: SND_SOC_RCAR depends on COMMON_CLK 2a46db4a3("ASoC: rsnd: add AUDIO_CLKOUT support") uses of_clk_add_provider() which is requesting struct clk_onecell_data. But it is COMMON_CLK feature. SND_SOC_RCAR depends on COMMON_CLK This patch also solved compile error of 7486d80f7("ASoC: rsnd: remove unneeded sh_clk header") Reported-by: kbuild test robot Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 07114b0b0dc1..6ca90aaf141f 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" depends on DMA_OF + depends on COMMON_CLK select SND_SIMPLE_CARD select REGMAP_MMIO help -- cgit v1.2.3-59-g8ed1b From 544637bf3f4ddc1971740026e1cf58d8516b53fa Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 02:42:42 +0000 Subject: ASoC: ak4642: exchange macro name to avoid redefinition 71a0138ab("ASoC: ak4642: enable to use MCKO as fixed rate output pin on DT") added new FS() macro, but x86 already has it in arch/x86/include/uapi/asm/ptrace-abi.h This patch exchange FS() to FSs() to avoid redefinition warning Reported-by: kbuild test robot Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4642.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index b5c4981c9f4c..cda27c22812a 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -130,8 +130,8 @@ #define I2S (3 << 0) /* MD_CTL2 */ -#define FS(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) -#define PS(val) ((val & 0x3) << 6) +#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) +#define PSs(val) ((val & 0x3) << 6) /* MD_CTL3 */ #define BST1 (1 << 3) @@ -458,7 +458,8 @@ static int ak4642_set_mcko(struct snd_soc_codec *codec, for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) { for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) { if (frequency == ps_list[ps] * fs_list[fs]) { - snd_soc_write(codec, MD_CTL2, PS(ps) | FS(fs)); + snd_soc_write(codec, MD_CTL2, + PSs(ps) | FSs(fs)); return 0; } } -- cgit v1.2.3-59-g8ed1b From 462c30bc8b3457e2904c45babf4cb06c3b38ed4e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 02:44:37 +0000 Subject: ASoC: rsnd: fixup clkout_name[] index error for single AUDIO_CLKOUT SoC 2a46db4a3("ASoC: rsnd: add AUDIO_CLKOUT support") added AUDIO_CLKOUT support for ADG. But single AUDIO_CLKOUT needs clkout_name[CLKOUT], not clkout_name[i]. Kernel will have NULL pointer access without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 3fecb87f45ba..c4ebbb7a7b6f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -532,7 +532,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, * for clkout */ if (!count) { - clk = clk_register_fixed_rate(dev, clkout_name[i], + clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], parent_clk_name, (parent_clk_name) ? 0 : CLK_IS_ROOT, req_rate); -- cgit v1.2.3-59-g8ed1b From bee261b8964d7751fdd63d7f636c42741294a30c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 13:59:40 +0100 Subject: ASoC: arizona: Add default cases for event switches Since the addition of the WILL_PMU / WILL_PMD several of the switches in arizona.c no longer cover all cases or have a default case. Whilst this isn't causing any problems in the interests of robustness add default cases. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8a2221ab3d10..1bc19f1e5e70 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, 0x4f5, 0x0da); } break; + default: + break; } return 0; @@ -725,6 +727,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES); if (reg == 0) arizona_in_set_vu(codec, 0); + break; + default: + break; } return 0; @@ -806,6 +811,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, break; } break; + default: + break; } return 0; -- cgit v1.2.3-59-g8ed1b From 002b083b8da96fd6c546fab2608803e7ce47627d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 13:59:41 +0100 Subject: ASoC: arizona: Add utility function to check if an input is analog We will occasionally require to take different action based on if an input is analog or digital so add a helper function to return if an input is analog. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 9 +++++++++ sound/soc/codecs/arizona.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 1bc19f1e5e70..ac21b85ff75f 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -691,6 +691,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena) ARIZONA_IN_VU, val); } +bool arizona_input_analog(struct snd_soc_codec *codec, int shift) +{ + unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8); + unsigned int val = snd_soc_read(codec, reg); + + return !(val & ARIZONA_IN1_MODE_MASK); +} +EXPORT_SYMBOL_GPL(arizona_input_analog); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index ada0a418ff4b..7b68d05a0939 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -294,4 +294,6 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai); int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff); +extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); + #endif -- cgit v1.2.3-59-g8ed1b From ccaadda319ffa42385d8eabde0efc3c544bebce3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 17 Sep 2015 10:45:24 +0100 Subject: ASoC: wm5110: Add additional analogue input enable for early revs Earlier revisions of the wm5110/8280 silicon require a slightly more complex procedure to enable analogue inputs. This patch adds this into the driver. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 187 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 163 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 9756578fc752..c04c0bc6f58a 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -38,6 +38,12 @@ struct wm5110_priv { struct arizona_priv core; struct arizona_fll fll[2]; + + unsigned int in_value; + int in_pre_pending; + int in_post_pending; + + unsigned int in_pga_cache[6]; }; static const struct wm_adsp_region wm5110_dsp1_regions[] = { @@ -428,6 +434,127 @@ err: return ret; } +static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = dapm->card; + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_get_volsw_range(kcontrol, ucontrol); + + mutex_unlock(&card->dapm_mutex); + + return ret; +} + +static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = dapm->card; + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_put_volsw_range(kcontrol, ucontrol); + + mutex_unlock(&card->dapm_mutex); + + return ret; +} + +static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + unsigned int reg, mask; + struct reg_sequence analog_seq[] = { + { 0x80, 0x3 }, + { 0x35d, 0 }, + { 0x80, 0x0 }, + }; + + reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4); + mask = ARIZONA_IN1L_PGA_VOL_MASK; + + switch (event) { + case SND_SOC_DAPM_WILL_PMU: + wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2); + wm5110->in_pre_pending++; + wm5110->in_post_pending++; + return 0; + case SND_SOC_DAPM_PRE_PMU: + wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg); + + snd_soc_update_bits(codec, reg, mask, + 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT); + + wm5110->in_pre_pending--; + if (wm5110->in_pre_pending == 0) { + analog_seq[1].def = wm5110->in_value; + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + + msleep(55); + + wm5110->in_value = 0; + } + + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, reg, mask, + wm5110->in_pga_cache[w->shift]); + + wm5110->in_post_pending--; + if (wm5110->in_post_pending == 0) + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + break; + default: + break; + } + + return 0; +} + +static int wm5110_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + + switch (arizona->rev) { + case 0 ... 4: + if (arizona_input_analog(codec, w->shift)) + wm5110_in_analog_ev(w, kcontrol, event); + + break; + default: + break; + } + + return arizona_in_ev(w, kcontrol, event); +} + static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); @@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]), SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]), -SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, - ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, - ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, - ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), @@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, - 0, NULL, 0, arizona_in_ev, + 0, NULL, 0, wm5110_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | -- cgit v1.2.3-59-g8ed1b From 6c84e5917f83334f9196a50cfa31e72207b1a7f6 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 17 Sep 2015 13:13:38 +0300 Subject: ASoC: soc-core: Fix sparse warning in be32_to_cpup() call Store return value of of_get_property() to a pointer of __be32 type as device tree has big endian type. This fixes a sparse warning couple of lines later when be32_to_cpup() is used to convert from big endian to cpu endian. The whole conversion is not really necessary, as we are only checking if the value is zero or not, but I wanted to add it to remind in the future that the data has to be converted before use. Compiler should optimize the unnecessary operations away. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c5e21ca0c015..3b471f9c98c6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3296,7 +3296,7 @@ static int snd_soc_of_get_slot_mask(struct device_node *np, unsigned int *mask) { u32 val; - const u32 *of_slot_mask = of_get_property(np, prop_name, &val); + const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); int i; if (!of_slot_mask) -- cgit v1.2.3-59-g8ed1b From 8985729578cb42f9e781a8e38e5b6b1ee90c1018 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 17 Sep 2015 10:47:33 +0200 Subject: ASoC: qcom: change PTR_ERR argument Apply PTR_ERR to the value that was recently assigned. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,y; @@ if (IS_ERR(x) || ...) { ... when any when != IS_ERR(...) ( PTR_ERR(x) | * PTR_ERR(y) ) ... when any } // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 97bc2023f08a..e5101e0d2d37 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -438,7 +438,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", - __func__, PTR_ERR(drvdata->mi2s_bit_clk[i])); + __func__, + PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); } } -- cgit v1.2.3-59-g8ed1b From 14a998be08e5286741021d3c81cc81e8d6d8a270 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 17 Sep 2015 10:39:05 +0300 Subject: ASoC: davinci-mcasp: Get rid of bclk_lrclk_ratio in private data The slot_width is for essentially same thing. Instead of storing bclk_lrclk_ratio, just store the slot_width. Comments has been updated accordingly and some variable names changed to more descriptive. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 56 ++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fa47a39fac86..452f2a36e51c 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -87,7 +87,6 @@ struct davinci_mcasp { u8 *serial_dir; u8 version; u8 bclk_div; - u16 bclk_lrclk_ratio; int streams; u32 irq_request[2]; int dma_request[2]; @@ -558,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, mcasp->bclk_div = div; break; - case 2: /* BCLK/LRCLK ratio */ - mcasp->bclk_lrclk_ratio = div; + case 2: /* + * BCLK/LRCLK ratio descries how many bit-clock cycles + * fit into one frame. The clock ratio is given for a + * full period of data (for I2S format both left and + * right channels), so it has to be divided by number + * of tdm-slots (for I2S - divided by 2). + * Instead of storing this ratio, we calculate a new + * tdm_slot width by dividing the the ratio by the + * number of configured tdm slots. + */ + mcasp->slot_width = div / mcasp->tdm_slots; + if (div % mcasp->tdm_slots) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", + __func__, div, mcasp->tdm_slots); break; default: @@ -677,11 +689,13 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, } static int davinci_config_channel_size(struct davinci_mcasp *mcasp, - int word_length) + int sample_width) { u32 fmt; - u32 tx_rotate = (word_length / 4) & 0x7; - u32 mask = (1ULL << word_length) - 1; + u32 tx_rotate = (sample_width / 4) & 0x7; + u32 mask = (1ULL << sample_width) - 1; + u32 slot_width = sample_width; + /* * For captured data we should not rotate, inversion and masking is * enoguh to get the data to the right position: @@ -694,31 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, u32 rx_rotate = 0; /* - * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv() - * callback, take it into account here. That allows us to for example - * send 32 bits per channel to the codec, while only 16 of them carry - * audio payload. - * The clock ratio is given for a full period of data (for I2S format - * both left and right channels), so it has to be divided by number of - * tdm-slots (for I2S - divided by 2). + * Setting the tdm slot width either with set_clkdiv() or + * set_tdm_slot() allows us to for example send 32 bits per + * channel to the codec, while only 16 of them carry audio + * payload. */ - if (mcasp->bclk_lrclk_ratio) { - u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; - + if (mcasp->slot_width) { /* - * When we have more bclk then it is needed for the data, we - * need to use the rotation to move the received samples to have - * correct alignment. + * When we have more bclk then it is needed for the + * data, we need to use the rotation to move the + * received samples to have correct alignment. */ - rx_rotate = (slot_length - word_length) / 4; - word_length = slot_length; - } else if (mcasp->slot_width) { - rx_rotate = (mcasp->slot_width - word_length) / 4; - word_length = mcasp->slot_width; + slot_width = mcasp->slot_width; + rx_rotate = (slot_width - sample_width) / 4; } /* mapping of the XSSZ bit-field as described in the datasheet */ - fmt = (word_length >> 1) - 1; + fmt = (slot_width >> 1) - 1; if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), -- cgit v1.2.3-59-g8ed1b From 31f4f0c640bd2d144ab16666c76d45f173f952f5 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Thu, 17 Sep 2015 04:55:55 +0800 Subject: ASoC: sunxi: fix platform_no_drv_owner.cocci warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/soc/sunxi/sun4i-codec.c:708:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Emilio López Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 6e83e62ef039..8d59d83b5aa4 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -705,7 +705,6 @@ static int sun4i_codec_remove(struct platform_device *pdev) static struct platform_driver sun4i_codec_driver = { .driver = { .name = "sun4i-codec", - .owner = THIS_MODULE, .of_match_table = sun4i_codec_of_match, }, .probe = sun4i_codec_probe, -- cgit v1.2.3-59-g8ed1b From b07570628471777aabb5695284e1af4533e502da Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2015 08:26:36 +0000 Subject: ASoC: add ak4613 support Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4613.txt | 17 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4613.c | 469 +++++++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ak4613.txt create mode 100644 sound/soc/codecs/ak4613.c (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt new file mode 100644 index 000000000000..15a919522b42 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4613.txt @@ -0,0 +1,17 @@ +AK4613 I2C transmitter + +This device supports I2C mode only. + +Required properties: + +- compatible : "asahi-kasei,ak4613" +- reg : The chip select number on the I2C bus + +Example: + +&i2c { + ak4613: ak4613@0x10 { + compatible = "asahi-kasei,ak4613"; + reg = <0x10>; + }; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733ecd17f..a92e4d4b2eee 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_AK4554 + select SND_SOC_AK4613 if I2C select SND_SOC_AK4641 if I2C select SND_SOC_AK4642 if I2C select SND_SOC_AK4671 if I2C @@ -319,6 +320,10 @@ config SND_SOC_AK4535 config SND_SOC_AK4554 tristate "AKM AK4554 CODEC" +config SND_SOC_AK4613 + tristate "AKM AK4613 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077954ae..5b6c8af38a39 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o +snd-soc-ak4613-objs := ak4613.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o @@ -216,6 +217,7 @@ obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o +obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c new file mode 100644 index 000000000000..fd96a8f9e2d3 --- /dev/null +++ b/sound/soc/codecs/ak4613.c @@ -0,0 +1,469 @@ +/* + * ak4613.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * Kuninori Morimoto + * + * Based on ak4642.c by Kuninori Morimoto + * Based on wm8731.c by Richard Purdie + * Based on ak4535.c by Richard Purdie + * Based on wm8753.c by Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PW_MGMT1 0x00 /* Power Management 1 */ +#define PW_MGMT2 0x01 /* Power Management 2 */ +#define PW_MGMT3 0x02 /* Power Management 3 */ +#define CTRL1 0x03 /* Control 1 */ +#define CTRL2 0x04 /* Control 2 */ +#define DEMP1 0x05 /* De-emphasis1 */ +#define DEMP2 0x06 /* De-emphasis2 */ +#define OFD 0x07 /* Overflow Detect */ +#define ZRD 0x08 /* Zero Detect */ +#define ICTRL 0x09 /* Input Control */ +#define OCTRL 0x0a /* Output Control */ +#define LOUT1 0x0b /* LOUT1 Volume Control */ +#define ROUT1 0x0c /* ROUT1 Volume Control */ +#define LOUT2 0x0d /* LOUT2 Volume Control */ +#define ROUT2 0x0e /* ROUT2 Volume Control */ +#define LOUT3 0x0f /* LOUT3 Volume Control */ +#define ROUT3 0x10 /* ROUT3 Volume Control */ +#define LOUT4 0x11 /* LOUT4 Volume Control */ +#define ROUT4 0x12 /* ROUT4 Volume Control */ +#define LOUT5 0x13 /* LOUT5 Volume Control */ +#define ROUT5 0x14 /* ROUT5 Volume Control */ +#define LOUT6 0x15 /* LOUT6 Volume Control */ +#define ROUT6 0x16 /* ROUT6 Volume Control */ + +/* PW_MGMT1 */ +#define RSTN BIT(0) +#define PMDAC BIT(1) +#define PMADC BIT(2) +#define PMVR BIT(3) + +/* PW_MGMT2 */ +#define PMAD_ALL 0x7 + +/* PW_MGMT3 */ +#define PMDA_ALL 0x3f + +/* CTRL1 */ +#define DIF0 BIT(3) +#define DIF1 BIT(4) +#define DIF2 BIT(5) +#define TDM0 BIT(6) +#define TDM1 BIT(7) +#define NO_FMT (0xff) +#define FMT_MASK (0xf8) + +/* CTRL2 */ +#define DFS_NORMAL_SPEED (0 << 2) +#define DFS_DOUBLE_SPEED (1 << 2) +#define DFS_QUAD_SPEED (2 << 2) + +struct ak4613_priv { + struct mutex lock; + + unsigned int fmt; + u8 fmt_ctrl; + int cnt; +}; + +struct ak4613_formats { + unsigned int width; + unsigned int fmt; +}; + +struct ak4613_interface { + struct ak4613_formats capture; + struct ak4613_formats playback; +}; + +static const struct reg_default ak4613_reg[] = { + { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, + { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, + { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 }, + { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 }, + { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, +}; + +#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3) +#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } +static const struct ak4613_interface ak4613_iface[] = { + /* capture */ /* playback */ + [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) }, + [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) }, + [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) }, + [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) }, + [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) }, +}; + +static const struct regmap_config ak4613_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x16, + .reg_defaults = ak4613_reg, + .num_reg_defaults = ARRAY_SIZE(ak4613_reg), +}; + +static const struct of_device_id ak4613_of_match[] = { + { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4613_of_match); + +static const struct i2c_device_id ak4613_i2c_id[] = { + { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id); + +static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = { + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("LOUT4"), + SND_SOC_DAPM_OUTPUT("LOUT5"), + SND_SOC_DAPM_OUTPUT("LOUT6"), + + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + SND_SOC_DAPM_OUTPUT("ROUT4"), + SND_SOC_DAPM_OUTPUT("ROUT5"), + SND_SOC_DAPM_OUTPUT("ROUT6"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0), + SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0), + SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0), + SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0), + SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0), +}; + +static const struct snd_soc_dapm_route ak4613_intercon[] = { + {"LOUT1", NULL, "DAC1"}, + {"LOUT2", NULL, "DAC2"}, + {"LOUT3", NULL, "DAC3"}, + {"LOUT4", NULL, "DAC4"}, + {"LOUT5", NULL, "DAC5"}, + {"LOUT6", NULL, "DAC6"}, + + {"ROUT1", NULL, "DAC1"}, + {"ROUT2", NULL, "DAC2"}, + {"ROUT3", NULL, "DAC3"}, + {"ROUT4", NULL, "DAC4"}, + {"ROUT5", NULL, "DAC5"}, + {"ROUT6", NULL, "DAC6"}, + + {"DAC1", NULL, "Playback"}, + {"DAC2", NULL, "Playback"}, + {"DAC3", NULL, "Playback"}, + {"DAC4", NULL, "Playback"}, + {"DAC5", NULL, "Playback"}, + {"DAC6", NULL, "Playback"}, + + {"Capture", NULL, "ADC1"}, + {"Capture", NULL, "ADC2"}, + + {"ADC1", NULL, "LIN1"}, + {"ADC2", NULL, "LIN2"}, + + {"ADC1", NULL, "RIN1"}, + {"ADC2", NULL, "RIN2"}, +}; + +static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + struct device *dev = codec->dev; + + mutex_lock(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); + priv->cnt = 0; + } + if (!priv->cnt) + priv->fmt_ctrl = NO_FMT; + mutex_unlock(&priv->lock); +} + +static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + fmt &= SND_SOC_DAIFMT_FORMAT_MASK; + + switch (fmt) { + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + priv->fmt = fmt; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + const struct ak4613_formats *fmts; + struct device *dev = codec->dev; + unsigned int width = params_width(params); + unsigned int fmt = priv->fmt; + unsigned int rate; + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int i, ret; + u8 fmt_ctrl, ctrl2; + + rate = params_rate(params); + switch (rate) { + case 32000: + case 44100: + case 48000: + ctrl2 = DFS_NORMAL_SPEED; + break; + case 88200: + case 96000: + ctrl2 = DFS_DOUBLE_SPEED; + break; + case 176400: + case 192000: + ctrl2 = DFS_QUAD_SPEED; + break; + default: + return -EINVAL; + } + + /* + * FIXME + * + * It doesn't support TDM at this point + */ + fmt_ctrl = NO_FMT; + for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) { + fmts = (is_play) ? &ak4613_iface[i].playback : + &ak4613_iface[i].capture; + + if (fmts->fmt != fmt) + continue; + + if (fmt == SND_SOC_DAIFMT_RIGHT_J) { + if (fmts->width != width) + continue; + } else { + if (fmts->width < width) + continue; + } + + fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i); + break; + } + + ret = -EINVAL; + if (fmt_ctrl == NO_FMT) + goto hw_params_end; + + mutex_lock(&priv->lock); + if ((priv->fmt_ctrl == NO_FMT) || + (priv->fmt_ctrl == fmt_ctrl)) { + priv->fmt_ctrl = fmt_ctrl; + priv->cnt++; + ret = 0; + } + mutex_unlock(&priv->lock); + + if (ret < 0) + goto hw_params_end; + + snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); + snd_soc_write(codec, CTRL2, ctrl2); + +hw_params_end: + if (ret < 0) + dev_warn(dev, "unsupported data width/format combination\n"); + + return ret; +} + +static int ak4613_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u8 mgmt1 = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + mgmt1 |= RSTN; + /* fall through */ + case SND_SOC_BIAS_PREPARE: + mgmt1 |= PMADC | PMDAC; + /* fall through */ + case SND_SOC_BIAS_STANDBY: + mgmt1 |= PMVR; + /* fall through */ + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_write(codec, PW_MGMT1, mgmt1); + + return 0; +} + +static const struct snd_soc_dai_ops ak4613_dai_ops = { + .shutdown = ak4613_dai_shutdown, + .set_fmt = ak4613_dai_set_fmt, + .hw_params = ak4613_dai_hw_params, +}; + +#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_64000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4613_dai = { + .name = "ak4613-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .ops = &ak4613_dai_ops, + .symmetric_rates = 1, +}; + +static int ak4613_resume(struct snd_soc_codec *codec) +{ + struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + + regcache_mark_dirty(regmap); + return regcache_sync(regmap); +} + +static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { + .resume = ak4613_resume, + .set_bias_level = ak4613_set_bias_level, + .dapm_widgets = ak4613_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), + .dapm_routes = ak4613_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), +}; + +static int ak4613_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; + const struct regmap_config *regmap_cfg; + struct regmap *regmap; + struct ak4613_priv *priv; + + regmap_cfg = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4613_of_match, dev); + if (of_id) + regmap_cfg = of_id->data; + } else { + regmap_cfg = (const struct regmap_config *)id->driver_data; + } + + if (!regmap_cfg) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->fmt_ctrl = NO_FMT; + priv->cnt = 0; + + mutex_init(&priv->lock); + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return snd_soc_register_codec(dev, &soc_codec_dev_ak4613, + &ak4613_dai, 1); +} + +static int ak4613_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver ak4613_i2c_driver = { + .driver = { + .name = "ak4613-codec", + .owner = THIS_MODULE, + .of_match_table = ak4613_of_match, + }, + .probe = ak4613_i2c_probe, + .remove = ak4613_i2c_remove, + .id_table = ak4613_i2c_id, +}; + +module_i2c_driver(ak4613_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4613 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From e3a4d958d78e4f1709f3d4611849617ac9222a0c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 18 Sep 2015 00:52:44 +0000 Subject: ASoC: ak4613: add Digital Playback Volume support For L/ROUT1 to L/ROUT6 Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4613.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index fd96a8f9e2d3..07a266460ec3 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -22,6 +22,7 @@ #include #include #include +#include #define PW_MGMT1 0x00 /* Power Management 1 */ #define PW_MGMT2 0x01 /* Power Management 2 */ @@ -91,6 +92,31 @@ struct ak4613_interface { struct ak4613_formats playback; }; +/* + * Playback Volume + * + * max : 0x00 : 0 dB + * ( 0.5 dB step ) + * min : 0xFE : -127.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1); + +static const struct snd_kcontrol_new ak4613_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6, + 0, 0xFF, 1, out_tlv), +}; + static const struct reg_default ak4613_reg[] = { { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, @@ -397,6 +423,8 @@ static int ak4613_resume(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { .resume = ak4613_resume, .set_bias_level = ak4613_set_bias_level, + .controls = ak4613_snd_controls, + .num_controls = ARRAY_SIZE(ak4613_snd_controls), .dapm_widgets = ak4613_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), .dapm_routes = ak4613_intercon, -- cgit v1.2.3-59-g8ed1b From 9d70594760a94523c550d2cc0f47b3c0af63c8e9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 18 Sep 2015 15:25:32 +0800 Subject: ASoC: rockchip: Fix missing CLKDEV_LOOKUP dependency Make SND_SOC_ROCKCHIP_MAX98090 and SND_SOC_ROCKCHIP_RT5645 depend on CLKDEV_LOOKUP to fix below build warning: warning: (SND_SOC_ROCKCHIP_MAX98090 && SND_SOC_ROCKCHIP_RT5645) selects SND_SOC_ROCKCHIP_I2S which has unmet direct dependencies (SOUND && !M68K && !UML && SND && SND_SOC && CLKDEV_LOOKUP && SND_SOC_ROCKCHIP) Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/rockchip/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 58bae8e2cf5f..570905709d3a 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -17,7 +17,7 @@ config SND_SOC_ROCKCHIP_I2S config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98090 select SND_SOC_TS3A227E @@ -27,7 +27,7 @@ config SND_SOC_ROCKCHIP_MAX98090 config SND_SOC_ROCKCHIP_RT5645 tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB + depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP select SND_SOC_ROCKCHIP_I2S select SND_SOC_RT5645 help -- cgit v1.2.3-59-g8ed1b From 5b97c0f18a1781f50db96baa020f913886d1972a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Mon, 17 Aug 2015 22:56:44 +0530 Subject: ASoC: Intel: Skylake: Remove unused CPU dais We need to create CPU DAI for each endpoint instance. For this we should have one DMIC DAI, one HDA DAI and SSP DAI. Thus, DMIC23, HDA-SPK/AMIC was not required so this patch removes them Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7d617bf493bc..bea26730873c 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -509,17 +509,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, -{ - .name = "DMIC23 Pin", - .ops = &skl_dmic_dai_ops, - .capture = { - .stream_name = "DMIC23 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, - }, -}, { .name = "HD-Codec Pin", .ops = &skl_link_dai_ops, @@ -538,28 +527,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }, -{ - .name = "HD-Codec-SPK Pin", - .ops = &skl_link_dai_ops, - .playback = { - .stream_name = "HD-Codec-SPK Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -{ - .name = "HD-Codec-AMIC Pin", - .ops = &skl_link_dai_ops, - .capture = { - .stream_name = "HD-Codec-AMIC Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, }; static int skl_platform_open(struct snd_pcm_substream *substream) -- cgit v1.2.3-59-g8ed1b From 9529138276c852297967b5d3cc2f6dda3ddb9526 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Sep 2015 14:06:38 +0300 Subject: ASoC: hdmi: Remove obsolete dummy HDMI codec The hdmi stub codec has not been used since refactoring of OMAP HDMI audio support. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 -- sound/soc/codecs/Makefile | 2 - sound/soc/codecs/hdmi.c | 109 ---------------------------------------------- 3 files changed, 115 deletions(-) delete mode 100644 sound/soc/codecs/hdmi.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733ecd17f..0142396bb42c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -79,7 +79,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C - select SND_SOC_HDMI_CODEC select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 @@ -442,9 +441,6 @@ config SND_SOC_BT_SCO config SND_SOC_DMIC tristate -config SND_SOC_HDMI_CODEC - tristate "HDMI stub CODEC" - config SND_SOC_ES8328 tristate "Everest Semi ES8328 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077954ae..7d7cc1b049c2 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -72,7 +72,6 @@ snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o -snd-soc-hdmi-codec-objs := hdmi.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o @@ -264,7 +263,6 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o -obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c deleted file mode 100644 index bd42ad34e004..000000000000 --- a/sound/soc/codecs/hdmi.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ALSA SoC codec driver for HDMI audio codecs. - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Ricardo Neri - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#include -#include -#include -#include - -#define DRV_NAME "hdmi-audio-codec" - -static const struct snd_soc_dapm_widget hdmi_widgets[] = { - SND_SOC_DAPM_INPUT("RX"), - SND_SOC_DAPM_OUTPUT("TX"), -}; - -static const struct snd_soc_dapm_route hdmi_routes[] = { - { "Capture", NULL, "RX" }, - { "TX", NULL, "Playback" }, -}; - -static struct snd_soc_dai_driver hdmi_codec_dai = { - .name = "hdmi-hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, - .sig_bits = 24, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - -}; - -#ifdef CONFIG_OF -static const struct of_device_id hdmi_audio_codec_ids[] = { - { .compatible = "linux,hdmi-audio", }, - { } -}; -MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids); -#endif - -static struct snd_soc_codec_driver hdmi_codec = { - .dapm_widgets = hdmi_widgets, - .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), - .dapm_routes = hdmi_routes, - .num_dapm_routes = ARRAY_SIZE(hdmi_routes), - .ignore_pmdown_time = true, -}; - -static int hdmi_codec_probe(struct platform_device *pdev) -{ - return snd_soc_register_codec(&pdev->dev, &hdmi_codec, - &hdmi_codec_dai, 1); -} - -static int hdmi_codec_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - return 0; -} - -static struct platform_driver hdmi_codec_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(hdmi_audio_codec_ids), - }, - - .probe = hdmi_codec_probe, - .remove = hdmi_codec_remove, -}; - -module_platform_driver(hdmi_codec_driver); - -MODULE_AUTHOR("Ricardo Neri "); -MODULE_DESCRIPTION("ASoC generic HDMI codec driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3-59-g8ed1b From cde79035c6cf578dd33dfea3e39666efc27cbcf2 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 24 Aug 2015 14:16:51 +0200 Subject: ASoC: Handle multiple codecs with split playback / capture Add the capability to use multiple codecs on the same DAI linke where one codec is used for playback and another one is used for capture. Tested on a setup using an SSM2518 for playback and an ICS43432 I2S MEMS microphone for capture. No verification is made that the playback and capture codec setups are compatible in terms of number of TDM slots (where applicable). Signed-off-by: Ricard Wanderlof Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 70e4b9d8bdcd..317395824cd7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -34,6 +34,24 @@ #define DPCM_MAX_BE_USERS 8 +/* + * snd_soc_dai_stream_valid() - check if a DAI supports the given stream + * + * Returns true if the DAI supports the indicated stream type. + */ +static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_pcm_stream *codec_stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &dai->driver->playback; + else + codec_stream = &dai->driver->capture; + + /* If the codec specifies any rate at all, it supports the stream. */ + return codec_stream->rates; +} + /** * snd_soc_runtime_activate() - Increment active count for PCM runtime components * @rtd: ASoC PCM runtime that is activated @@ -371,6 +389,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) /* first calculate min/max only for CODECs in the DAI link */ for (i = 0; i < rtd->num_codecs; i++) { + + /* + * Skip CODECs which don't support the current stream type. + * Otherwise, since the rate, channel, and format values will + * zero in that case, we would have no usable settings left, + * causing the resulting setup to fail. + * At least one CODEC should match, otherwise we should have + * bailed out on a higher level, since there would be no + * CODEC to support the transfer direction in that case. + */ + if (!snd_soc_dai_stream_valid(rtd->codec_dais[i], + substream->stream)) + continue; + codec_dai_drv = rtd->codec_dais[i]->driver; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) codec_stream = &codec_dai_drv->playback; @@ -827,6 +859,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; struct snd_pcm_hw_params codec_params; + /* + * Skip CODECs which don't support the current stream type, + * the idea being that if a CODEC is not used for the currently + * set up transfer direction, it should not need to be + * configured, especially since the configuration used might + * not even be supported by that CODEC. There may be cases + * however where a CODEC needs to be set up although it is + * actually not being used for the transfer, e.g. if a + * capture-only CODEC is acting as an LRCLK and/or BCLK master + * for the DAI link including a playback-only CODEC. + * If this becomes necessary, we will have to augment the + * machine driver setup with information on how to act, so + * we can do the right thing here. + */ + if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) + continue; + /* copy params for each codec */ codec_params = *params; -- cgit v1.2.3-59-g8ed1b From e9159e7577cfd1ac8b3c5ea8916ad37bcd87e629 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 21 Sep 2015 14:12:00 +0800 Subject: ASoC: rt5645: Add dmi for Broadwell Add DMI data for Buddy project. Signed-off-by: Bard Liao Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index dbc1d76d9d4e..e83068886c70 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3209,6 +3209,31 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { { } }; +static struct rt5645_platform_data buddy_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, +}; + +static int buddy_quirk_cb(const struct dmi_system_id *id) +{ + rt5645_pdata = &buddy_platform_data; + + return 1; +} + +static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = { + { + .ident = "Chrome Buddy", + .callback = buddy_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"), + }, + }, + { } +}; + + static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) { rt5645->pdata.in2_diff = device_property_read_bool(dev, @@ -3241,7 +3266,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (pdata) rt5645->pdata = *pdata; - else if (dmi_check_system(dmi_platform_intel_braswell)) + else if (dmi_check_system(dmi_platform_intel_braswell) || + dmi_check_system(dmi_platform_intel_broadwell)) rt5645->pdata = *rt5645_pdata; else rt5645_parse_dt(rt5645, &i2c->dev); -- cgit v1.2.3-59-g8ed1b From 917536aeb88d34e06c1353b0dd144f0987bb66bd Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 21 Sep 2015 14:12:01 +0800 Subject: ASoC: rt5645: Add jd_invert for Broadwell Broadwell can not triger the IRQ falling and rising simultaneously, so it can not detect jack-in and jack-out simultaneously. We add a flag "jd_invert" to platform data. If this flag is set, codec IRQ will be set to invert that forces IRQ as pulse when jack-in and jack-out. Signed-off-by: John Lin Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/rt5645.h | 2 ++ sound/soc/codecs/rt5645.c | 7 +++++++ sound/soc/codecs/rt5645.h | 4 ++++ 3 files changed, 13 insertions(+) (limited to 'sound') diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h index 22734bc3ffd4..a5cf6152e778 100644 --- a/include/sound/rt5645.h +++ b/include/sound/rt5645.h @@ -21,6 +21,8 @@ struct rt5645_platform_data { /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ unsigned int jd_mode; + /* Invert JD when jack insert */ + bool jd_invert; }; #endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e83068886c70..b0d96b6d21de 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2829,6 +2829,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; } + if (rt5645->pdata.jd_invert) + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); } else { /* jack out */ rt5645->jack_type = 0; @@ -2844,6 +2847,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_disable_pin(dapm, "LDO2"); snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); + if (rt5645->pdata.jd_invert) + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR); } return rt5645->jack_type; @@ -3213,6 +3219,7 @@ static struct rt5645_platform_data buddy_platform_data = { .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, .jd_mode = 3, + .jd_invert = true, }; static int buddy_quirk_cb(const struct dmi_system_id *id) diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 0e4cfc6ac649..90325d9256ff 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -1626,6 +1626,10 @@ #define RT5645_OT_P_NOR (0x0 << 10) #define RT5645_OT_P_INV (0x1 << 10) #define RT5645_IRQ_JD_1_1_EN (0x1 << 9) +#define RT5645_JD_1_1_MASK (0x1 << 7) +#define RT5645_JD_1_1_SFT 7 +#define RT5645_JD_1_1_NOR (0x0 << 7) +#define RT5645_JD_1_1_INV (0x1 << 7) /* IRQ Control 2 (0xbe) */ #define RT5645_IRQ_MB1_OC_MASK (0x1 << 15) -- cgit v1.2.3-59-g8ed1b From de3f8fdf7393e4259b68ae8cea806c39d602769e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 23 Sep 2015 14:35:27 +0800 Subject: ASoC: rt5645: Remove the repeated definitions Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 90325d9256ff..f45861c49ef2 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -777,8 +777,6 @@ #define RT5645_PWR_CLS_D_R_BIT 9 #define RT5645_PWR_CLS_D_L (0x1 << 8) #define RT5645_PWR_CLS_D_L_BIT 8 -#define RT5645_PWR_ADC_R (0x1 << 1) -#define RT5645_PWR_ADC_R_BIT 1 #define RT5645_PWR_DAC_L2 (0x1 << 7) #define RT5645_PWR_DAC_L2_BIT 7 #define RT5645_PWR_DAC_R2 (0x1 << 6) -- cgit v1.2.3-59-g8ed1b From 13f3a46d2a6cbdff1bbb06fac6de7dce753db8e1 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 20 Sep 2015 21:18:55 +0900 Subject: ALSA: oxfw: add Mackie Onyx Satellite quirk to inform wrong value in 'dbs' field in tx CIP header Mackie Onyx Satellite has two usage; standalone and with base station. These two modes has different stream formats. In standalone mode, rx data block includes 2 Multi Bit Linear Audio (MBLA) data channels and tx data block includes 2. With base station, they're 6 and 2. Although, with base station, the actual tx packet include wrong value in 'dbs' field in its CIP header. This quirk causes packet streaming layer to detect packet discontinuity and to stop PCM substream. This is a response of 'single' subfunction 'extended stream format information' command in AV/C Stream Format Information Specification 1.1. It means that a data block in tx packets includes 2 MBLA data channels. $ ./firewire-request /dev/fw1 fcp 0x01ffbfc001000000ffffffff response: 000: 0c ff bf c0 01 00 00 00 ff 00 90 40 03 02 01 02 response: 010: 06 Current OXFW driver parses the response and detects stream formats correctly. $ cat /proc/asound/card1/firewire/formation ... Output Stream from device: Rate PCM MIDI * 48000 2 0 44100 2 0 88200 2 0 96000 2 0 On the other hand, in actual tx CIP, the 'dbs' field has 6. But the number of quadlets in CIP payload is not multiple of 6. Seeing the subtraction of two successive payload quadlets, it should be multiple of 2. payload CIP CIP quadlets header0 header1 24 00060052 9002ffff 24 0006005e 9002ffff 26 0006006a 9002ffff 24 00060077 9002ffff 24 00060083 9002ffff 26 0006008f 9002ffff 24 0006009c 9002ffff 24 000600a8 9002ffff 26 000600b4 9002ffff 24 000600c1 9002ffff This commit adds support for this quirk to OXFW driver, by using CIP_WRONG_DBS flag. When this flag is set, packet streaming layer uses the value of its 'data_block_quadlets' member instead of the value in CIP header. This value is already set by OXFW driver and no discontinuity is detected. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-stream.c | 5 ++++- sound/firewire/oxfw/oxfw.c | 29 +++++++++++++++++++++++++++++ sound/firewire/oxfw/oxfw.h | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 77ad5b98e806..863eb4ae7246 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -238,9 +238,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, * packets. As a result, next isochronous packet includes more data * blocks than IEC 61883-6 defines. */ - if (stream == &oxfw->tx_stream) + if (stream == &oxfw->tx_stream) { oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK | CIP_JUMBO_PAYLOAD; + if (oxfw->wrong_dbs) + oxfw->tx_stream.flags |= CIP_WRONG_DBS; + } end: return err; } diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 8c6ce019f437..d606e3a9ce97 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -19,6 +19,8 @@ #define VENDOR_BEHRINGER 0x001564 #define VENDOR_LACIE 0x00d04b +#define MODEL_SATELLITE 0x00200f + #define SPECIFIER_1394TA 0x00a02d #define VERSION_AVC 0x010001 @@ -129,6 +131,31 @@ static void oxfw_card_free(struct snd_card *card) mutex_destroy(&oxfw->mutex); } +static void detect_quirks(struct snd_oxfw *oxfw) +{ + struct fw_device *fw_dev = fw_parent_device(oxfw->unit); + struct fw_csr_iterator it; + int key, val; + int vendor, model; + + /* Seek from Root Directory of Config ROM. */ + vendor = model = 0; + fw_csr_iterator_init(&it, fw_dev->config_rom + 5); + while (fw_csr_iterator_next(&it, &key, &val)) { + if (key == CSR_VENDOR) + vendor = val; + else if (key == CSR_MODEL) + model = val; + } + + /* + * Mackie Onyx Satellite with base station has a quirk to report a wrong + * value in 'dbs' field of CIP header against its format information. + */ + if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE) + oxfw->wrong_dbs = true; +} + static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { @@ -157,6 +184,8 @@ static int oxfw_probe(struct fw_unit *unit, if (err < 0) goto error; + detect_quirks(oxfw); + err = name_card(oxfw); if (err < 0) goto error; diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index cace5ad4fe76..7b5dad819ca4 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -49,6 +49,7 @@ struct snd_oxfw { struct mutex mutex; spinlock_t lock; + bool wrong_dbs; bool has_output; u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; -- cgit v1.2.3-59-g8ed1b From 145d92e77e6f34f53773fdb06789816ec2b6c7b3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 23 Sep 2015 12:42:28 +0300 Subject: ALSA: core: check for underflow in snd_pcm_sw_params() As far as I can see, having an invalid ->tstamp_mode is harmless, but adding a check silences a static checker warning. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 75888dd38a7f..4863af5e7b26 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -650,7 +650,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, } snd_pcm_stream_unlock_irq(substream); - if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) + if (params->tstamp_mode < 0 || + params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) return -EINVAL; if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) && params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST) -- cgit v1.2.3-59-g8ed1b From 0f7e17740612ca0f3b8baf91645075d07bb52b2c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 24 Sep 2015 23:34:01 +0800 Subject: ASoC: rl6347a: Clean up unneeded inclusion of header files Also move the include of sound/hda_verbs.h to rl6347a.h because it is used in rl6347a.h. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rl6347a.c | 19 +------------------ sound/soc/codecs/rl6347a.h | 2 ++ sound/soc/codecs/rt286.c | 1 - sound/soc/codecs/rt298.c | 1 - 4 files changed, 3 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c index 91d5166bd3a1..a4b910efbd45 100644 --- a/sound/soc/codecs/rl6347a.c +++ b/sound/soc/codecs/rl6347a.c @@ -11,25 +11,8 @@ */ #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "rl6347a.h" diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h index 1cb56e50b7f3..e127919cb36b 100644 --- a/sound/soc/codecs/rl6347a.h +++ b/sound/soc/codecs/rl6347a.h @@ -12,6 +12,8 @@ #ifndef __RL6347A_H__ #define __RL6347A_H__ +#include + #define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) #define RL6347A_VENDOR_REGISTERS 0x20 diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index bd9365885f73..1fbdb4fe0ca9 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -29,7 +29,6 @@ #include #include #include -#include #include "rl6347a.h" #include "rt286.h" diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 3c2f0f8d6266..2a6c2ba0f0d8 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "rl6347a.h" #include "rt298.h" -- cgit v1.2.3-59-g8ed1b From ddd621fbba35178643a39559c9688a373285bbc0 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Wed, 2 Sep 2015 14:11:40 +0800 Subject: ALSA: hda - display audio call sync_audio_rate callback For display audio, call the sync_audio_rate callback function to do the synchronization between gfx driver and audio driver. Signed-off-by: Libin Yang Reviewed-by: Takashi Iwai Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index acbfbe087ee8..3a2d4a5a1714 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1775,6 +1775,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) return non_pcm; } +/* There is a fixed mapping between audio pin node and display port + * on current Intel platforms: + * Pin Widget 5 - PORT B (port = 1 in i915 driver) + * Pin Widget 6 - PORT C (port = 2 in i915 driver) + * Pin Widget 7 - PORT D (port = 3 in i915 driver) + */ +static int intel_pin2port(hda_nid_t pin_nid) +{ + return pin_nid - 4; +} /* * HDMI callbacks @@ -1791,6 +1801,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, int pin_idx = hinfo_to_pin_index(codec, hinfo); struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hda_nid_t pin_nid = per_pin->pin_nid; + struct snd_pcm_runtime *runtime = substream->runtime; + struct i915_audio_component *acomp = codec->bus->core.audio_component; bool non_pcm; int pinctl; @@ -1807,6 +1819,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx); } + /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ + /* Todo: add DP1.2 MST audio support later */ + if (acomp && acomp->ops && acomp->ops->sync_audio_rate) + acomp->ops->sync_audio_rate(acomp->dev, + intel_pin2port(pin_nid), + runtime->rate); + non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); mutex_lock(&per_pin->lock); per_pin->channels = substream->runtime->channels; -- cgit v1.2.3-59-g8ed1b From e87359efcaf45efc41dbaa6ec25eb26705300cee Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 28 Sep 2015 13:06:20 +0300 Subject: ALSA: usb-audio: harmless underflow in snd_audigy2nx_led_put() We want to verify that "value" is either zero or one, so we test if it is greater than one. Unfortunately, this is a signed int so it could also be negative. I think this is harmless but it introduces a static checker warning. Let's make "value" unsigned. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index d3608c0a29f3..fe91184ce832 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -338,7 +338,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct usb_mixer_interface *mixer = list->mixer; int index = kcontrol->private_value & 0xff; - int value = ucontrol->value.integer.value[0]; + unsigned int value = ucontrol->value.integer.value[0]; int old_value = kcontrol->private_value >> 8; int err; -- cgit v1.2.3-59-g8ed1b From 10b2b6dc1a6bb287411045c788f76d53f96c11bc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:48 +0900 Subject: ALSA: firewire-lib: arrange structure for AMDTP stream In later commit, some members related to AM824 data format will be moved from AMDTP stream structure to data block structure. This commit is a preparation for it. Additionally, current layout of AMDTP stream structure is a bit mess by several extensions. This commit also arranges the layout. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.h | 58 +++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index b2cf9e75693b..4640d2b35fb8 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -117,52 +117,56 @@ struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; enum amdtp_stream_direction direction; - struct fw_iso_context *context; struct mutex mutex; - enum cip_sfc sfc; - unsigned int data_block_quadlets; - unsigned int pcm_channels; - unsigned int midi_ports; - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; - u8 midi_position; - - unsigned int syt_interval; - unsigned int transfer_delay; - unsigned int source_node_id_field; + /* For packet processing. */ + struct fw_iso_context *context; struct iso_packets_buffer buffer; - - struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; - int packet_index; + + /* For CIP headers. */ + unsigned int source_node_id_field; + unsigned int data_block_quadlets; unsigned int data_block_counter; + /* quirk: fixed interval of dbc between previos/current packets. */ + unsigned int tx_dbc_interval; + /* quirk: indicate the value of dbc field in a first packet. */ + unsigned int tx_first_dbc; + /* Internal flags. */ + enum cip_sfc sfc; + unsigned int syt_interval; + unsigned int transfer_delay; unsigned int data_block_state; - unsigned int last_syt_offset; unsigned int syt_offset_state; + /* For a PCM substream processing. */ + struct snd_pcm_substream *pcm; + struct tasklet_struct period_tasklet; unsigned int pcm_buffer_pointer; unsigned int pcm_period_pointer; bool pointer_flush; - bool double_pcm_frames; + /* To wait for first packet. */ + bool callbacked; + wait_queue_head_t callback_wait; + struct amdtp_stream *sync_slave; + + /* For AM824 processing. */ struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; int midi_fifo_limit; int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned int pcm_channels; + unsigned int midi_ports; - /* quirk: fixed interval of dbc between previos/current packets. */ - unsigned int tx_dbc_interval; - /* quirk: indicate the value of dbc field in a first packet. */ - unsigned int tx_first_dbc; + u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; + bool double_pcm_frames; - bool callbacked; - wait_queue_head_t callback_wait; - struct amdtp_stream *sync_slave; + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, -- cgit v1.2.3-59-g8ed1b From 547e631ce3886175a33b5ccf67729bdd18e9b7e0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:49 +0900 Subject: ALSA: firewire-lib: return error code when amdtp_stream_set_parameters() detects error Currently, amdtp_stream_set_parameters() returns no error even if wrong arguments are given. This is not good for streaming layer because drivers can continue processing ignoring capability of streaming layer. This commit changes this function to return error code. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 22 ++++++++++++---------- sound/firewire/amdtp.h | 8 ++++---- sound/firewire/bebob/bebob_stream.c | 13 +++++++++---- sound/firewire/dice/dice-stream.c | 5 ++++- sound/firewire/fireworks/fireworks_stream.c | 6 ++++-- sound/firewire/oxfw/oxfw-stream.c | 5 ++++- 6 files changed, 37 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 2a153d260836..2bacb5173ff8 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -197,10 +197,10 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports) +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports) { unsigned int i, sfc, midi_channels; @@ -209,15 +209,15 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, if (WARN_ON(amdtp_stream_running(s)) | WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) | WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) - return; + return -EINVAL; - for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) + for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { if (amdtp_rate_table[sfc] == rate) - goto sfc_found; - WARN_ON(1); - return; + break; + } + if (sfc == ARRAY_SIZE(amdtp_rate_table)) + return -EINVAL; -sfc_found: s->pcm_channels = pcm_channels; s->sfc = sfc; s->data_block_quadlets = s->pcm_channels + midi_channels; @@ -243,6 +243,8 @@ sfc_found: * (The value here is adjusted for midi_ratelimit_per_packet().) */ s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; + + return 0; } EXPORT_SYMBOL(amdtp_stream_set_parameters); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 4640d2b35fb8..3fb8db7ecd68 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -174,10 +174,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum cip_flags flags); void amdtp_stream_destroy(struct amdtp_stream *s); -void amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 5be5242e1ed8..c642b79e7ed4 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -427,12 +427,17 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) index = get_formation_index(rate); pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; - amdtp_stream_set_parameters(&bebob->tx_stream, - rate, pcm_channels, midi_channels * 8); + err = amdtp_stream_set_parameters(&bebob->tx_stream, rate, + pcm_channels, midi_channels * 8); + if (err < 0) + goto end; + pcm_channels = bebob->rx_stream_formations[index].pcm; midi_channels = bebob->rx_stream_formations[index].midi; - amdtp_stream_set_parameters(&bebob->rx_stream, - rate, pcm_channels, midi_channels * 8); + err = amdtp_stream_set_parameters(&bebob->rx_stream, rate, + pcm_channels, midi_channels * 8); + if (err < 0) + goto end; /* establish connections for both streams */ err = cmp_connection_establish(&bebob->out_conn, diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 07dbd01d7a6b..c96306adcae0 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -133,7 +133,10 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, stream->double_pcm_frames = false; } - amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + if (err < 0) + goto end; + if (mode > 1) { pcm_chs /= 2; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 7e353f1f7bff..dfefccff3c55 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, midi_ports = efw->midi_in_ports; } - amdtp_stream_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports); + err = amdtp_stream_set_parameters(stream, sampling_rate, + pcm_channels, midi_ports); + if (err < 0) + goto end; /* establish connection via CMP */ err = cmp_connection_establish(conn, diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 77ad5b98e806..d119468fedf4 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -155,7 +155,10 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, err = -EINVAL; goto end; } - amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports); + err = amdtp_stream_set_parameters(stream, rate, + pcm_channels, midi_ports); + if (err < 0) + goto end; err = cmp_connection_establish(conn, amdtp_stream_get_max_payload(stream)); -- cgit v1.2.3-59-g8ed1b From 27ec83b5c6ac08599240ec9a95286e79d6ea9e51 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:50 +0900 Subject: ALSA: firewire-lib: add an argument for Dice's dual wire mode In IEC 61883-6, one data block represents one event. In ALSA, the event is one PCM frame. Therefore, when processing one data block, current implementation counts one PCM frame. On the other hand, Dice platform has a quirk called as 'dual wire' at higher sampling rate. In detail, see comment of commit 6eb6c81eee2a ("ALSA: dice: Split stream functionality into a file"). Currently, to handle this quirk, AMDTP stream structure has a 'double_pcm_frames' member. When this is enabled, two PCM frames are counted. Each driver set this flag by accessing the structure member directly. In future commit, some members related to AM824 data block will be moved to specific structure, to separate packet streaming layer and data block processing layer. The access will be limited by opaque pointer. For this reason, this commit adds an argument into amdtp_stream_set_parameter() to set the flag. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 4 +++- sound/firewire/amdtp.h | 3 ++- sound/firewire/bebob/bebob_stream.c | 6 ++++-- sound/firewire/dice/dice-stream.c | 12 ++++++------ sound/firewire/fireworks/fireworks_stream.c | 2 +- sound/firewire/oxfw/oxfw-stream.c | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 2bacb5173ff8..094982f7c0fd 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -193,6 +193,7 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * @pcm_channels: the number of PCM samples in each data block, to be encoded * as AM824 multi-bit linear audio * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * @double_pcm_frames: one data block transfers two PCM frames * * The parameters must be set before the stream is started, and must not be * changed while the stream is running. @@ -200,7 +201,8 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports) + unsigned int midi_ports, + bool double_pcm_frames) { unsigned int i, sfc, midi_channels; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 3fb8db7ecd68..2c9130695780 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -177,7 +177,8 @@ void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports); + unsigned int midi_ports, + bool double_pcm_frames); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index c642b79e7ed4..920a3b8844ee 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -428,14 +428,16 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; err = amdtp_stream_set_parameters(&bebob->tx_stream, rate, - pcm_channels, midi_channels * 8); + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; pcm_channels = bebob->rx_stream_formations[index].pcm; midi_channels = bebob->rx_stream_formations[index].midi; err = amdtp_stream_set_parameters(&bebob->rx_stream, rate, - pcm_channels, midi_channels * 8); + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index c96306adcae0..e4c6c20d70a0 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, { struct fw_iso_resources *resources; unsigned int i, mode, pcm_chs, midi_ports; + bool double_pcm_frames; int err; err = snd_dice_stream_get_rate_mode(dice, rate, &mode); @@ -125,19 +126,18 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, * For this quirk, blocking mode is required and PCM buffer size should * be aligned to SYT_INTERVAL. */ - if (mode > 1) { + double_pcm_frames = mode > 1; + if (double_pcm_frames) { rate /= 2; pcm_chs *= 2; - stream->double_pcm_frames = true; - } else { - stream->double_pcm_frames = false; } - err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports, + false); if (err < 0) goto end; - if (mode > 1) { + if (double_pcm_frames) { pcm_chs /= 2; for (i = 0; i < pcm_chs; i++) { diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index dfefccff3c55..85a72e63913d 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -74,7 +74,7 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, } err = amdtp_stream_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports); + pcm_channels, midi_ports, false); if (err < 0) goto end; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index d119468fedf4..318f78e1a313 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -156,7 +156,7 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, goto end; } err = amdtp_stream_set_parameters(stream, rate, - pcm_channels, midi_ports); + pcm_channels, midi_ports, false); if (err < 0) goto end; -- cgit v1.2.3-59-g8ed1b From 6a4e89ff0f4e46c1a9d0c28e786952e70516a5f4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:51 +0900 Subject: ALSA: firewire-lib: add a member of frame_multiplier instead of double_pcm_frames In future commit, interface between data block processing layer and packet stream processing layer is defined. These two layers communicate the number of data blocks and the number of PCM frames. The data block processing layer has a responsibility for calculating the number of PCM frames. Therefore, 'dual wire' of Dice quirk should be handled in data block processing layer. This commit adds a member of 'frame_multiplier'. This member represents the ratio of the number of PCM frames against the number of data blocks. Usually, the value of this member is 1, while it's 2 in Dice's 'dual wire'. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 22 ++++++++++++---------- sound/firewire/amdtp.h | 3 ++- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 094982f7c0fd..0bb5e14b6627 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -225,6 +225,16 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + s->frame_multiplier = 2; + else + s->frame_multiplier = 1; + s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ @@ -584,14 +594,6 @@ static void update_pcm_pointers(struct amdtp_stream *s, { unsigned int ptr; - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk to transfer - * two PCM frames in one data block. - */ - if (s->double_pcm_frames) - frames *= 2; - ptr = s->pcm_buffer_pointer + frames; if (ptr >= pcm->runtime->buffer_size) ptr -= pcm->runtime->buffer_size; @@ -685,7 +687,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, return -EIO; if (pcm) - update_pcm_pointers(s, pcm, data_blocks); + update_pcm_pointers(s, pcm, data_blocks * s->frame_multiplier); /* No need to return the number of handled data blocks. */ return 0; @@ -788,7 +790,7 @@ end: return -EIO; if (pcm) - update_pcm_pointers(s, pcm, *data_blocks); + update_pcm_pointers(s, pcm, *data_blocks * s->frame_multiplier); return 0; } diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 2c9130695780..65229258bd24 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -162,11 +162,12 @@ struct amdtp_stream { u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; u8 midi_position; - bool double_pcm_frames; void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); + + unsigned int frame_multiplier; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, -- cgit v1.2.3-59-g8ed1b From 20e445771ea109bc0ce9e717cbf2475e56a141a8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:52 +0900 Subject: ALSA: firewire-lib: add helper functions as interfaces between packet streaming layer and data block processing layer ALSA PCM framework uses PCM buffer with a concept of 'period' to synchronize userspace operations to hardware for nearly-realtime processing. Each driver implements snd_pcm_period_elapsed() to tell across of the period boundary to ALSA PCM middleware. To call the function, some drivers utilize hardware interrupt handlers, the others count handled PCM frames. Drivers for sound units on IEEE 1394 bus are the latter. They use two buffers; PCM buffer and DMA buffer for IEEE 1394 isochronous packet. PCM frames are copied between these two buffers and 'amdtp_stream' structure counts the handled PCM frames. Then, snd_pcm_period_elapsed() is called if required. Essentially, packet streaming layer should not be responsible for PCM frame processing. The PCM frame processing should be handled in each data block processing layer as a result of handling data blocks. Although, PCM frame counting is a common work for all of protocols which ALSA firewire stack is going to support. This commit adds two new helper functions as interfaces between packet streaming layer to data block processing layer. In future, each data block processing layer implements these functions. The packet streaming layer calls data block processing layer per packet by calling the functions. The data block processing layer processes data blocks and PCM frames, and returns the number of processed PCM frames. Then the packet streaming layer calculates handled PCM frames and calls snd_pcm_period_elapsed(). Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 82 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 27 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 0bb5e14b6627..5a3a4529194a 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -657,28 +657,42 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } +unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + if (s->midi_ports) + write_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, unsigned int syt) { __be32 *buffer; unsigned int payload_length; + unsigned int pcm_frames; struct snd_pcm_substream *pcm; buffer = s->buffer.packets[s->packet_index].buffer; + pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | s->data_block_counter); buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | (s->sfc << CIP_FDF_SHIFT) | syt); - buffer += 2; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - s->transfer_samples(s, pcm, buffer, data_blocks); - else - write_pcm_silence(s, buffer, data_blocks); - if (s->midi_ports) - write_midi_messages(s, buffer, data_blocks); s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; @@ -686,20 +700,41 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, if (queue_out_packet(s, payload_length, false) < 0) return -EIO; - if (pcm) - update_pcm_pointers(s, pcm, data_blocks * s->frame_multiplier); + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); /* No need to return the number of handled data blocks. */ return 0; } +unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + pcm_frames = 0; + } + + if (s->midi_ports) + read_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + static int handle_in_packet(struct amdtp_stream *s, unsigned int payload_quadlets, __be32 *buffer, - unsigned int *data_blocks) + unsigned int *data_blocks, unsigned int syt) { u32 cip_header[2]; unsigned int data_block_quadlets, data_block_counter, dbc_interval; - struct snd_pcm_substream *pcm = NULL; + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; bool lost; cip_header[0] = be32_to_cpu(buffer[0]); @@ -716,6 +751,7 @@ static int handle_in_packet(struct amdtp_stream *s, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); *data_blocks = 0; + pcm_frames = 0; goto end; } @@ -769,16 +805,7 @@ static int handle_in_packet(struct amdtp_stream *s, return -EIO; } - if (*data_blocks > 0) { - buffer += 2; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - s->transfer_samples(s, pcm, buffer, *data_blocks); - - if (s->midi_ports) - read_midi_messages(s, buffer, *data_blocks); - } + pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); if (s->flags & CIP_DBC_IS_END_EVENT) s->data_block_counter = data_block_counter; @@ -789,8 +816,9 @@ end: if (queue_in_packet(s) < 0) return -EIO; - if (pcm) - update_pcm_pointers(s, pcm, *data_blocks * s->frame_multiplier); + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); return 0; } @@ -860,15 +888,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, break; } + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks) < 0) { + &data_blocks, syt) < 0) { s->packet_index = -1; break; } /* Process sync slave stream */ if (s->sync_slave && s->sync_slave->callbacked) { - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_out_packet(s->sync_slave, data_blocks, syt) < 0) { s->packet_index = -1; -- cgit v1.2.3-59-g8ed1b From 414ba022a5287e9473c7fa2b4aa1b1025e9ca8be Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:53 +0900 Subject: ALSA: firewire-lib: add support arbitrary value for fmt/fdf fields in CIP header Some vendor specific protocol uses its own value for fmt/fdf fields in CIP header. This commit support to set arbitrary values for the fields. In IEC 61883-6, NO-DATA code is defined for FDF field. A packet with this code includes no data even if it includes some data blocks. This commit still leaves a condition to handle this special packet. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 31 +++++++++++++++++++++++-------- sound/firewire/amdtp.h | 2 ++ 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5a3a4529194a..b251f4b2b51c 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -59,8 +59,8 @@ * Audio and Music transfer protocol specific parameters * only "Clock-based rate control mode" is supported */ -#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT) -#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3)) +#define CIP_FMT_AM 0x10 +#define AMDTP_FDF_AM824 0x00 #define AMDTP_FDF_NO_DATA 0xff /* TODO: make these configurable */ @@ -94,6 +94,8 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = NULL; + s->fmt = CIP_FMT_AM; + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -225,6 +227,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; + s->fdf = AMDTP_FDF_AM824 | s->sfc; + /* * In IEC 61883-6, one data block represents one event. In ALSA, one * event equals to one PCM frame. But Dice has a quirk at higher @@ -691,8 +695,10 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | s->data_block_counter); - buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | - (s->sfc << CIP_FDF_SHIFT) | syt); + buffer[1] = cpu_to_be32(CIP_EOH | + ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | + ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | + (syt & CIP_SYT_MASK)); s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; @@ -732,6 +738,7 @@ static int handle_in_packet(struct amdtp_stream *s, unsigned int *data_blocks, unsigned int syt) { u32 cip_header[2]; + unsigned int fmt, fdf; unsigned int data_block_quadlets, data_block_counter, dbc_interval; struct snd_pcm_substream *pcm; unsigned int pcm_frames; @@ -745,8 +752,7 @@ static int handle_in_packet(struct amdtp_stream *s, * For convenience, also check FMT field is AM824 or not. */ if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || - ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) || - ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) { + ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) { dev_info_ratelimited(&s->unit->device, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); @@ -755,10 +761,19 @@ static int handle_in_packet(struct amdtp_stream *s, goto end; } + /* Check valid protocol or not. */ + fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; + if (fmt != s->fmt) { + dev_err(&s->unit->device, + "Detect unexpected protocol: %08x %08x\n", + cip_header[0], cip_header[1]); + return -EIO; + } + /* Calculate data blocks */ + fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; if (payload_quadlets < 3 || - ((cip_header[1] & CIP_FDF_MASK) == - (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) { + (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { *data_blocks = 0; } else { data_block_quadlets = diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 65229258bd24..883bb1a7e25f 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -128,6 +128,8 @@ struct amdtp_stream { unsigned int source_node_id_field; unsigned int data_block_quadlets; unsigned int data_block_counter; + unsigned int fmt; + unsigned int fdf; /* quirk: fixed interval of dbc between previos/current packets. */ unsigned int tx_dbc_interval; /* quirk: indicate the value of dbc field in a first packet. */ -- cgit v1.2.3-59-g8ed1b From d67c46b953749aef223496ec95b7bf93e40887dc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:54 +0900 Subject: ALSA: firewire-lib: rename 'amdtp' to 'amdtp-stream' to prepare for functional separation In later commit, data block processing layer will be newly added. This layer will be named as 'amdtp-am824'. This commit renames current amdtp file to amdtp-stream, to distinguish it from the new layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Makefile | 2 +- sound/firewire/amdtp-stream.c | 1157 ++++++++++++++++++++++++++++++++++ sound/firewire/amdtp-stream.h | 306 +++++++++ sound/firewire/amdtp.c | 1157 ---------------------------------- sound/firewire/amdtp.h | 306 --------- sound/firewire/bebob/bebob.h | 2 +- sound/firewire/dice/dice.h | 2 +- sound/firewire/fcp.c | 2 +- sound/firewire/fireworks/fireworks.h | 2 +- sound/firewire/oxfw/oxfw.h | 2 +- 10 files changed, 1469 insertions(+), 1469 deletions(-) create mode 100644 sound/firewire/amdtp-stream.c create mode 100644 sound/firewire/amdtp-stream.h delete mode 100644 sound/firewire/amdtp.c delete mode 100644 sound/firewire/amdtp.h (limited to 'sound') diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 8b37f084b2ab..102e342ddc38 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,5 +1,5 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ - fcp.o cmp.o amdtp.o + fcp.o cmp.o amdtp-stream.o snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c new file mode 100644 index 000000000000..ec65ebf8f04a --- /dev/null +++ b/sound/firewire/amdtp-stream.c @@ -0,0 +1,1157 @@ +/* + * Audio and Music Data Transmission Protocol (IEC 61883-6) streams + * with Common Isochronous Packet (IEC 61883-1) headers + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amdtp-stream.h" + +#define TICKS_PER_CYCLE 3072 +#define CYCLES_PER_SECOND 8000 +#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) + +/* + * Nominally 3125 bytes/second, but the MIDI port's clock might be + * 1% too slow, and the bus clock 100 ppm too fast. + */ +#define MIDI_BYTES_PER_SECOND 3093 + +/* + * Several devices look only at the first eight data blocks. + * In any case, this is more than enough for the MIDI data rate. + */ +#define MAX_MIDI_RX_BLOCKS 8 + +#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ + +/* isochronous header parameters */ +#define ISO_DATA_LENGTH_SHIFT 16 +#define TAG_CIP 1 + +/* common isochronous packet header parameters */ +#define CIP_EOH_SHIFT 31 +#define CIP_EOH (1u << CIP_EOH_SHIFT) +#define CIP_EOH_MASK 0x80000000 +#define CIP_SID_SHIFT 24 +#define CIP_SID_MASK 0x3f000000 +#define CIP_DBS_MASK 0x00ff0000 +#define CIP_DBS_SHIFT 16 +#define CIP_DBC_MASK 0x000000ff +#define CIP_FMT_SHIFT 24 +#define CIP_FMT_MASK 0x3f000000 +#define CIP_FDF_MASK 0x00ff0000 +#define CIP_FDF_SHIFT 16 +#define CIP_SYT_MASK 0x0000ffff +#define CIP_SYT_NO_INFO 0xffff + +/* + * Audio and Music transfer protocol specific parameters + * only "Clock-based rate control mode" is supported + */ +#define CIP_FMT_AM 0x10 +#define AMDTP_FDF_AM824 0x00 +#define AMDTP_FDF_NO_DATA 0xff + +/* TODO: make these configurable */ +#define INTERRUPT_INTERVAL 16 +#define QUEUE_LENGTH 48 + +#define IN_PACKET_HEADER_SIZE 4 +#define OUT_PACKET_HEADER_SIZE 0 + +static void pcm_period_tasklet(unsigned long data); + +/** + * amdtp_stream_init - initialize an AMDTP stream structure + * @s: the AMDTP stream to initialize + * @unit: the target of the stream + * @dir: the direction of stream + * @flags: the packet transmission method to use + */ +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags) +{ + s->unit = unit; + s->direction = dir; + s->flags = flags; + s->context = ERR_PTR(-1); + mutex_init(&s->mutex); + tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); + s->packet_index = 0; + + init_waitqueue_head(&s->callback_wait); + s->callbacked = false; + s->sync_slave = NULL; + + s->fmt = CIP_FMT_AM; + + return 0; +} +EXPORT_SYMBOL(amdtp_stream_init); + +/** + * amdtp_stream_destroy - free stream resources + * @s: the AMDTP stream to destroy + */ +void amdtp_stream_destroy(struct amdtp_stream *s) +{ + WARN_ON(amdtp_stream_running(s)); + mutex_destroy(&s->mutex); +} +EXPORT_SYMBOL(amdtp_stream_destroy); + +const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { + [CIP_SFC_32000] = 8, + [CIP_SFC_44100] = 8, + [CIP_SFC_48000] = 8, + [CIP_SFC_88200] = 16, + [CIP_SFC_96000] = 16, + [CIP_SFC_176400] = 32, + [CIP_SFC_192000] = 32, +}; +EXPORT_SYMBOL(amdtp_syt_intervals); + +const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = { + [CIP_SFC_32000] = 32000, + [CIP_SFC_44100] = 44100, + [CIP_SFC_48000] = 48000, + [CIP_SFC_88200] = 88200, + [CIP_SFC_96000] = 96000, + [CIP_SFC_176400] = 176400, + [CIP_SFC_192000] = 192000, +}; +EXPORT_SYMBOL(amdtp_rate_table); + +/** + * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream + * @s: the AMDTP stream, which must be initialized. + * @runtime: the PCM substream runtime + */ +int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime) +{ + int err; + + /* AM824 in IEC 61883-6 can deliver 24bit data */ + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + goto end; + + /* + * Currently firewire-lib processes 16 packets in one software + * interrupt callback. This equals to 2msec but actually the + * interval of the interrupts has a jitter. + * Additionally, even if adding a constraint to fit period size to + * 2msec, actual calculated frames per period doesn't equal to 2msec, + * depending on sampling rate. + * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec. + * Here let us use 5msec for safe period interrupt. + */ + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 5000, UINT_MAX); + if (err < 0) + goto end; + + /* Non-Blocking stream has no more constraints */ + if (!(s->flags & CIP_BLOCKING)) + goto end; + + /* + * One AMDTP packet can include some frames. In blocking mode, the + * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, + * depending on its sampling rate. For accurate period interrupt, it's + * preferrable to align period/buffer sizes to current SYT_INTERVAL. + * + * TODO: These constraints can be improved with proper rules. + * Currently apply LCM of SYT_INTERVALs. + */ + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); + if (err < 0) + goto end; + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); +end: + return err; +} +EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); + +/** + * amdtp_stream_set_parameters - set stream parameters + * @s: the AMDTP stream to configure + * @rate: the sample rate + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * @double_pcm_frames: one data block transfers two PCM frames + * + * The parameters must be set before the stream is started, and must not be + * changed while the stream is running. + */ +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames) +{ + unsigned int i, sfc, midi_channels; + + midi_channels = DIV_ROUND_UP(midi_ports, 8); + + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) || + WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) + return -EINVAL; + + for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { + if (amdtp_rate_table[sfc] == rate) + break; + } + if (sfc == ARRAY_SIZE(amdtp_rate_table)) + return -EINVAL; + + s->pcm_channels = pcm_channels; + s->sfc = sfc; + s->data_block_quadlets = s->pcm_channels + midi_channels; + s->midi_ports = midi_ports; + + s->fdf = AMDTP_FDF_AM824 | s->sfc; + + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + s->frame_multiplier = 2; + else + s->frame_multiplier = 1; + + s->syt_interval = amdtp_syt_intervals[sfc]; + + /* default buffering in the device */ + s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + if (s->flags & CIP_BLOCKING) + /* additional buffering needed to adjust for no-data packets */ + s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + + /* init the position map for PCM and MIDI channels */ + for (i = 0; i < pcm_channels; i++) + s->pcm_positions[i] = i; + s->midi_position = s->pcm_channels; + + /* + * We do not know the actual MIDI FIFO size of most devices. Just + * assume two bytes, i.e., one byte can be received over the bus while + * the previous one is transmitted over MIDI. + * (The value here is adjusted for midi_ratelimit_per_packet().) + */ + s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; + + return 0; +} +EXPORT_SYMBOL(amdtp_stream_set_parameters); + +/** + * amdtp_stream_get_max_payload - get the stream's packet size + * @s: the AMDTP stream + * + * This function must not be called before the stream has been configured + * with amdtp_stream_set_parameters(). + */ +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) +{ + unsigned int multiplier = 1; + + if (s->flags & CIP_JUMBO_PAYLOAD) + multiplier = 5; + + return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier; +} +EXPORT_SYMBOL(amdtp_stream_get_max_payload); + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + +/** + * amdtp_stream_set_pcm_format - set the PCM format + * @s: the AMDTP stream to configure + * @format: the format of the ALSA PCM device + * + * The sample format must be set after the other parameters (rate/PCM channels/ + * MIDI) and before the stream is started, and must not be changed while the + * stream is running. + */ +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format) +{ + if (WARN_ON(amdtp_stream_pcm_running(s))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + if (s->direction == AMDTP_OUT_STREAM) { + s->transfer_samples = write_pcm_s16; + break; + } + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S32: + if (s->direction == AMDTP_OUT_STREAM) + s->transfer_samples = write_pcm_s32; + else + s->transfer_samples = read_pcm_s32; + break; + } +} +EXPORT_SYMBOL(amdtp_stream_set_pcm_format); + +/** + * amdtp_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP stream + * + * This function should be called from the PCM device's .prepare callback. + */ +void amdtp_stream_pcm_prepare(struct amdtp_stream *s) +{ + tasklet_kill(&s->period_tasklet); + s->pcm_buffer_pointer = 0; + s->pcm_period_pointer = 0; + s->pointer_flush = true; +} +EXPORT_SYMBOL(amdtp_stream_pcm_prepare); + +static unsigned int calculate_data_blocks(struct amdtp_stream *s, + unsigned int syt) +{ + unsigned int phase, data_blocks; + + /* Blocking mode. */ + if (s->flags & CIP_BLOCKING) { + /* This module generate empty packet for 'no data'. */ + if (syt == CIP_SYT_NO_INFO) + data_blocks = 0; + else + data_blocks = s->syt_interval; + /* Non-blocking mode. */ + } else { + if (!cip_sfc_is_base_44100(s->sfc)) { + /* Sample_rate / 8000 is an integer, and precomputed. */ + data_blocks = s->data_block_state; + } else { + phase = s->data_block_state; + + /* + * This calculates the number of data blocks per packet so that + * 1) the overall rate is correct and exactly synchronized to + * the bus clock, and + * 2) packets with a rounded-up number of blocks occur as early + * as possible in the sequence (to prevent underruns of the + * device's buffer). + */ + if (s->sfc == CIP_SFC_44100) + /* 6 6 5 6 5 6 5 ... */ + data_blocks = 5 + ((phase & 1) ^ + (phase == 0 || phase >= 40)); + else + /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ + data_blocks = 11 * (s->sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (s->sfc >> 1))) + phase = 0; + s->data_block_state = phase; + } + } + + return data_blocks; +} + +static unsigned int calculate_syt(struct amdtp_stream *s, + unsigned int cycle) +{ + unsigned int syt_offset, phase, index, syt; + + if (s->last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(s->sfc)) + syt_offset = s->last_syt_offset + s->syt_offset_state; + else { + /* + * The time, in ticks, of the n'th SYT_INTERVAL sample is: + * n * SYT_INTERVAL * 24576000 / sample_rate + * Modulo TICKS_PER_CYCLE, the difference between successive + * elements is about 1386.23. Rounding the results of this + * formula to the SYT precision results in a sequence of + * differences that begins with: + * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... + * This code generates _exactly_ the same sequence. + */ + phase = s->syt_offset_state; + index = phase % 13; + syt_offset = s->last_syt_offset; + syt_offset += 1386 + ((index && !(index & 3)) || + phase == 146); + if (++phase >= 147) + phase = 0; + s->syt_offset_state = phase; + } + } else + syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; + s->last_syt_offset = syt_offset; + + if (syt_offset < TICKS_PER_CYCLE) { + syt_offset += s->transfer_delay; + syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; + syt += syt_offset % TICKS_PER_CYCLE; + + return syt & CIP_SYT_MASK; + } else { + return CIP_SYT_NO_INFO; + } +} + +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u32 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src >> 8) | 0x40000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u16 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src << 8) | 0x42000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + channels = s->pcm_channels; + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void write_pcm_silence(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i, c; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) + buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +/* + * To avoid sending MIDI bytes at too high a rate, assume that the receiving + * device has a FIFO, and track how much it is filled. This values increases + * by one whenever we send one byte in a packet, but the FIFO empties at + * a constant rate independent of our packet rate. One packet has syt_interval + * samples, so the number of bytes that empty out of the FIFO, per packet(!), + * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing + * fractional values, the values in midi_fifo_used[] are measured in bytes + * multiplied by the sample rate. + */ +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + int used; + + used = s->midi_fifo_used[port]; + if (used == 0) /* common shortcut */ + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + s->midi_fifo_used[port] = used; + + return used < s->midi_fifo_limit; +} + +static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) +{ + s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; +} + +static void write_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int f, port; + u8 *b; + + for (f = 0; f < frames; f++) { + b = (u8 *)&buffer[s->midi_position]; + + port = (s->data_block_counter + f) % 8; + if (f < MAX_MIDI_RX_BLOCKS && + midi_ratelimit_per_packet(s, port) && + s->midi[port] != NULL && + snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { + midi_rate_use_one_byte(s, port); + b[0] = 0x81; + } else { + b[0] = 0x80; + b[1] = 0; + } + b[2] = 0; + b[3] = 0; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < frames; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[s->midi_position]; + + len = b[0] - 0x80; + if ((1 <= len) && (len <= 3) && (s->midi[port])) + snd_rawmidi_receive(s->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } +} + +static void update_pcm_pointers(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + unsigned int frames) +{ + unsigned int ptr; + + ptr = s->pcm_buffer_pointer + frames; + if (ptr >= pcm->runtime->buffer_size) + ptr -= pcm->runtime->buffer_size; + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; + + s->pcm_period_pointer += frames; + if (s->pcm_period_pointer >= pcm->runtime->period_size) { + s->pcm_period_pointer -= pcm->runtime->period_size; + s->pointer_flush = false; + tasklet_hi_schedule(&s->period_tasklet); + } +} + +static void pcm_period_tasklet(unsigned long data) +{ + struct amdtp_stream *s = (void *)data; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + + if (pcm) + snd_pcm_period_elapsed(pcm); +} + +static int queue_packet(struct amdtp_stream *s, + unsigned int header_length, + unsigned int payload_length, bool skip) +{ + struct fw_iso_packet p = {0}; + int err = 0; + + if (IS_ERR(s->context)) + goto end; + + p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + p.tag = TAG_CIP; + p.header_length = header_length; + p.payload_length = (!skip) ? payload_length : 0; + p.skip = skip; + err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, + s->buffer.packets[s->packet_index].offset); + if (err < 0) { + dev_err(&s->unit->device, "queueing error: %d\n", err); + goto end; + } + + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; +end: + return err; +} + +static inline int queue_out_packet(struct amdtp_stream *s, + unsigned int payload_length, bool skip) +{ + return queue_packet(s, OUT_PACKET_HEADER_SIZE, + payload_length, skip); +} + +static inline int queue_in_packet(struct amdtp_stream *s) +{ + return queue_packet(s, IN_PACKET_HEADER_SIZE, + amdtp_stream_get_max_payload(s), false); +} + +unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + if (s->midi_ports) + write_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + +static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, + unsigned int syt) +{ + __be32 *buffer; + unsigned int payload_length; + unsigned int pcm_frames; + struct snd_pcm_substream *pcm; + + buffer = s->buffer.packets[s->packet_index].buffer; + pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); + + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | + (s->data_block_quadlets << CIP_DBS_SHIFT) | + s->data_block_counter); + buffer[1] = cpu_to_be32(CIP_EOH | + ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | + ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | + (syt & CIP_SYT_MASK)); + + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + + payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + if (queue_out_packet(s, payload_length, false) < 0) + return -EIO; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); + + /* No need to return the number of handled data blocks. */ + return 0; +} + +unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + pcm_frames = 0; + } + + if (s->midi_ports) + read_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + +static int handle_in_packet(struct amdtp_stream *s, + unsigned int payload_quadlets, __be32 *buffer, + unsigned int *data_blocks, unsigned int syt) +{ + u32 cip_header[2]; + unsigned int fmt, fdf; + unsigned int data_block_quadlets, data_block_counter, dbc_interval; + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; + bool lost; + + cip_header[0] = be32_to_cpu(buffer[0]); + cip_header[1] = be32_to_cpu(buffer[1]); + + /* + * This module supports 'Two-quadlet CIP header with SYT field'. + * For convenience, also check FMT field is AM824 or not. + */ + if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || + ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) { + dev_info_ratelimited(&s->unit->device, + "Invalid CIP header for AMDTP: %08X:%08X\n", + cip_header[0], cip_header[1]); + *data_blocks = 0; + pcm_frames = 0; + goto end; + } + + /* Check valid protocol or not. */ + fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; + if (fmt != s->fmt) { + dev_err(&s->unit->device, + "Detect unexpected protocol: %08x %08x\n", + cip_header[0], cip_header[1]); + return -EIO; + } + + /* Calculate data blocks */ + fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; + if (payload_quadlets < 3 || + (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { + *data_blocks = 0; + } else { + data_block_quadlets = + (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; + /* avoid division by zero */ + if (data_block_quadlets == 0) { + dev_err(&s->unit->device, + "Detect invalid value in dbs field: %08X\n", + cip_header[0]); + return -EPROTO; + } + if (s->flags & CIP_WRONG_DBS) + data_block_quadlets = s->data_block_quadlets; + + *data_blocks = (payload_quadlets - 2) / data_block_quadlets; + } + + /* Check data block counter continuity */ + data_block_counter = cip_header[0] & CIP_DBC_MASK; + if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && + s->data_block_counter != UINT_MAX) + data_block_counter = s->data_block_counter; + + if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && + data_block_counter == s->tx_first_dbc) || + s->data_block_counter == UINT_MAX) { + lost = false; + } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { + lost = data_block_counter != s->data_block_counter; + } else { + if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) + dbc_interval = s->tx_dbc_interval; + else + dbc_interval = *data_blocks; + + lost = data_block_counter != + ((s->data_block_counter + dbc_interval) & 0xff); + } + + if (lost) { + dev_err(&s->unit->device, + "Detect discontinuity of CIP: %02X %02X\n", + s->data_block_counter, data_block_counter); + return -EIO; + } + + pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); + + if (s->flags & CIP_DBC_IS_END_EVENT) + s->data_block_counter = data_block_counter; + else + s->data_block_counter = + (data_block_counter + *data_blocks) & 0xff; +end: + if (queue_in_packet(s) < 0) + return -EIO; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); + + return 0; +} + +static void out_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + unsigned int i, syt, packets = header_length / 4; + unsigned int data_blocks; + + if (s->packet_index < 0) + return; + + /* + * Compute the cycle of the last queued packet. + * (We need only the four lowest bits for the SYT, so we can ignore + * that bits 0-11 must wrap around at 3072.) + */ + cycle += QUEUE_LENGTH - packets; + + for (i = 0; i < packets; ++i) { + syt = calculate_syt(s, ++cycle); + data_blocks = calculate_data_blocks(s, syt); + + if (handle_out_packet(s, data_blocks, syt) < 0) { + s->packet_index = -1; + amdtp_stream_pcm_abort(s); + return; + } + } + + fw_iso_context_queue_flush(s->context); +} + +static void in_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + unsigned int p, syt, packets; + unsigned int payload_quadlets, max_payload_quadlets; + unsigned int data_blocks; + __be32 *buffer, *headers = header; + + if (s->packet_index < 0) + return; + + /* The number of packets in buffer */ + packets = header_length / IN_PACKET_HEADER_SIZE; + + /* For buffer-over-run prevention. */ + max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; + + for (p = 0; p < packets; p++) { + buffer = s->buffer.packets[s->packet_index].buffer; + + /* The number of quadlets in this packet */ + payload_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + if (payload_quadlets > max_payload_quadlets) { + dev_err(&s->unit->device, + "Detect jumbo payload: %02x %02x\n", + payload_quadlets, max_payload_quadlets); + s->packet_index = -1; + break; + } + + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + if (handle_in_packet(s, payload_quadlets, buffer, + &data_blocks, syt) < 0) { + s->packet_index = -1; + break; + } + + /* Process sync slave stream */ + if (s->sync_slave && s->sync_slave->callbacked) { + if (handle_out_packet(s->sync_slave, + data_blocks, syt) < 0) { + s->packet_index = -1; + break; + } + } + } + + /* Queueing error or detecting discontinuity */ + if (s->packet_index < 0) { + amdtp_stream_pcm_abort(s); + + /* Abort sync slave. */ + if (s->sync_slave) { + s->sync_slave->packet_index = -1; + amdtp_stream_pcm_abort(s->sync_slave); + } + return; + } + + /* when sync to device, flush the packets for slave stream */ + if (s->sync_slave && s->sync_slave->callbacked) + fw_iso_context_queue_flush(s->sync_slave->context); + + fw_iso_context_queue_flush(s->context); +} + +/* processing is done by master callback */ +static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + return; +} + +/* this is executed one time */ +static void amdtp_stream_first_callback(struct fw_iso_context *context, + u32 cycle, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + + /* + * For in-stream, first packet has come. + * For out-stream, prepared to transmit first packet + */ + s->callbacked = true; + wake_up(&s->callback_wait); + + if (s->direction == AMDTP_IN_STREAM) + context->callback.sc = in_stream_callback; + else if (s->flags & CIP_SYNC_TO_DEVICE) + context->callback.sc = slave_stream_callback; + else + context->callback.sc = out_stream_callback; + + context->callback.sc(context, cycle, header_length, header, s); +} + +/** + * amdtp_stream_start - start transferring packets + * @s: the AMDTP stream to start + * @channel: the isochronous channel on the bus + * @speed: firewire speed code + * + * The stream cannot be started until it has been configured with + * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI + * device can be started. + */ +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) +{ + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; + unsigned int header_size; + enum dma_data_direction dir; + int type, tag, err; + + mutex_lock(&s->mutex); + + if (WARN_ON(amdtp_stream_running(s) || + (s->data_block_quadlets < 1))) { + err = -EBADFD; + goto err_unlock; + } + + if (s->direction == AMDTP_IN_STREAM && + s->flags & CIP_SKIP_INIT_DBC_CHECK) + s->data_block_counter = UINT_MAX; + else + s->data_block_counter = 0; + s->data_block_state = initial_state[s->sfc].data_block; + s->syt_offset_state = initial_state[s->sfc].syt_offset; + s->last_syt_offset = TICKS_PER_CYCLE; + + /* initialize packet buffer */ + if (s->direction == AMDTP_IN_STREAM) { + dir = DMA_FROM_DEVICE; + type = FW_ISO_CONTEXT_RECEIVE; + header_size = IN_PACKET_HEADER_SIZE; + } else { + dir = DMA_TO_DEVICE; + type = FW_ISO_CONTEXT_TRANSMIT; + header_size = OUT_PACKET_HEADER_SIZE; + } + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_stream_get_max_payload(s), dir); + if (err < 0) + goto err_unlock; + + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, + type, channel, speed, header_size, + amdtp_stream_first_callback, s); + if (IS_ERR(s->context)) { + err = PTR_ERR(s->context); + if (err == -EBUSY) + dev_err(&s->unit->device, + "no free stream on this controller\n"); + goto err_buffer; + } + + amdtp_stream_update(s); + + s->packet_index = 0; + do { + if (s->direction == AMDTP_IN_STREAM) + err = queue_in_packet(s); + else + err = queue_out_packet(s, 0, true); + if (err < 0) + goto err_context; + } while (s->packet_index > 0); + + /* NOTE: TAG1 matches CIP. This just affects in stream. */ + tag = FW_ISO_CONTEXT_MATCH_TAG1; + if (s->flags & CIP_EMPTY_WITH_TAG0) + tag |= FW_ISO_CONTEXT_MATCH_TAG0; + + s->callbacked = false; + err = fw_iso_context_start(s->context, -1, 0, tag); + if (err < 0) + goto err_context; + + mutex_unlock(&s->mutex); + + return 0; + +err_context: + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); +err_buffer: + iso_packets_buffer_destroy(&s->buffer, s->unit); +err_unlock: + mutex_unlock(&s->mutex); + + return err; +} +EXPORT_SYMBOL(amdtp_stream_start); + +/** + * amdtp_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP stream that transports the PCM data + * + * Returns the current buffer position, in frames. + */ +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) +{ + /* this optimization is allowed to be racy */ + if (s->pointer_flush && amdtp_stream_running(s)) + fw_iso_context_flush_completions(s->context); + else + s->pointer_flush = true; + + return ACCESS_ONCE(s->pcm_buffer_pointer); +} +EXPORT_SYMBOL(amdtp_stream_pcm_pointer); + +/** + * amdtp_stream_update - update the stream after a bus reset + * @s: the AMDTP stream + */ +void amdtp_stream_update(struct amdtp_stream *s) +{ + /* Precomputing. */ + ACCESS_ONCE(s->source_node_id_field) = + (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & + CIP_SID_MASK; +} +EXPORT_SYMBOL(amdtp_stream_update); + +/** + * amdtp_stream_stop - stop sending packets + * @s: the AMDTP stream to stop + * + * All PCM and MIDI devices of the stream must be stopped before the stream + * itself can be stopped. + */ +void amdtp_stream_stop(struct amdtp_stream *s) +{ + mutex_lock(&s->mutex); + + if (!amdtp_stream_running(s)) { + mutex_unlock(&s->mutex); + return; + } + + tasklet_kill(&s->period_tasklet); + fw_iso_context_stop(s->context); + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); + iso_packets_buffer_destroy(&s->buffer, s->unit); + + s->callbacked = false; + + mutex_unlock(&s->mutex); +} +EXPORT_SYMBOL(amdtp_stream_stop); + +/** + * amdtp_stream_pcm_abort - abort the running PCM device + * @s: the AMDTP stream about to be stopped + * + * If the isochronous stream needs to be stopped asynchronously, call this + * function first to stop the PCM device. + */ +void amdtp_stream_pcm_abort(struct amdtp_stream *s) +{ + struct snd_pcm_substream *pcm; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + snd_pcm_stop_xrun(pcm); +} +EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h new file mode 100644 index 000000000000..883bb1a7e25f --- /dev/null +++ b/sound/firewire/amdtp-stream.h @@ -0,0 +1,306 @@ +#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED +#define SOUND_FIREWIRE_AMDTP_H_INCLUDED + +#include +#include +#include +#include +#include "packets-buffer.h" + +/** + * enum cip_flags - describes details of the streaming protocol + * @CIP_NONBLOCKING: In non-blocking mode, each packet contains + * sample_rate/8000 samples, with rounding up or down to adjust + * for clock skew and left-over fractional samples. This should + * be used if supported by the device. + * @CIP_BLOCKING: In blocking mode, each packet contains either zero or + * SYT_INTERVAL samples, with these two types alternating so that + * the overall sample rate comes out right. + * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is + * generated by in packets. Defaultly this driver generates timestamp. + * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0. + * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet + * corresponds to the end of event in the packet. Out of IEC 61883. + * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets. + * The value of data_block_quadlets is used instead of reported value. + * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is + * skipped for detecting discontinuity. + * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first + * packet is not continuous from an initial value. + * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty + * packet is wrong but the others are correct. + * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an + * packet is larger than IEC 61883-6 defines. Current implementation + * allows 5 times as large as IEC 61883-6 defines. + */ +enum cip_flags { + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, + CIP_SYNC_TO_DEVICE = 0x02, + CIP_EMPTY_WITH_TAG0 = 0x04, + CIP_DBC_IS_END_EVENT = 0x08, + CIP_WRONG_DBS = 0x10, + CIP_SKIP_DBC_ZERO_CHECK = 0x20, + CIP_SKIP_INIT_DBC_CHECK = 0x40, + CIP_EMPTY_HAS_WRONG_DBC = 0x80, + CIP_JUMBO_PAYLOAD = 0x100, +}; + +/** + * enum cip_sfc - supported Sampling Frequency Codes (SFCs) + * @CIP_SFC_32000: 32,000 data blocks + * @CIP_SFC_44100: 44,100 data blocks + * @CIP_SFC_48000: 48,000 data blocks + * @CIP_SFC_88200: 88,200 data blocks + * @CIP_SFC_96000: 96,000 data blocks + * @CIP_SFC_176400: 176,400 data blocks + * @CIP_SFC_192000: 192,000 data blocks + * @CIP_SFC_COUNT: the number of supported SFCs + * + * These values are used to show nominal Sampling Frequency Code in + * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002, + * this code means the number of events per second. Actually the code + * represents the number of data blocks transferred per second in an AMDTP + * stream. + * + * In IEC 61883-6:2005, some extensions were added to support more types of + * data such as 'One Bit LInear Audio', therefore the meaning of SFC became + * different depending on the types. + * + * Currently our implementation is compatible with IEC 61883-6:2002. + */ +enum cip_sfc { + CIP_SFC_32000 = 0, + CIP_SFC_44100 = 1, + CIP_SFC_48000 = 2, + CIP_SFC_88200 = 3, + CIP_SFC_96000 = 4, + CIP_SFC_176400 = 5, + CIP_SFC_192000 = 6, + CIP_SFC_COUNT +}; + +#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 + +#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ + SNDRV_PCM_FMTBIT_S32) + + +/* + * This module supports maximum 64 PCM channels for one PCM stream + * This is for our convenience. + */ +#define AMDTP_MAX_CHANNELS_FOR_PCM 64 + +/* + * AMDTP packet can include channels for MIDI conformant data. + * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. + * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. + * + * This module supports maximum 1 MIDI conformant data channels. + * Then this AMDTP packets can transfer maximum 8 MIDI data streams. + */ +#define AMDTP_MAX_CHANNELS_FOR_MIDI 1 + +struct fw_unit; +struct fw_iso_context; +struct snd_pcm_substream; +struct snd_pcm_runtime; +struct snd_rawmidi_substream; + +enum amdtp_stream_direction { + AMDTP_OUT_STREAM = 0, + AMDTP_IN_STREAM +}; + +struct amdtp_stream { + struct fw_unit *unit; + enum cip_flags flags; + enum amdtp_stream_direction direction; + struct mutex mutex; + + /* For packet processing. */ + struct fw_iso_context *context; + struct iso_packets_buffer buffer; + int packet_index; + + /* For CIP headers. */ + unsigned int source_node_id_field; + unsigned int data_block_quadlets; + unsigned int data_block_counter; + unsigned int fmt; + unsigned int fdf; + /* quirk: fixed interval of dbc between previos/current packets. */ + unsigned int tx_dbc_interval; + /* quirk: indicate the value of dbc field in a first packet. */ + unsigned int tx_first_dbc; + + /* Internal flags. */ + enum cip_sfc sfc; + unsigned int syt_interval; + unsigned int transfer_delay; + unsigned int data_block_state; + unsigned int last_syt_offset; + unsigned int syt_offset_state; + + /* For a PCM substream processing. */ + struct snd_pcm_substream *pcm; + struct tasklet_struct period_tasklet; + unsigned int pcm_buffer_pointer; + unsigned int pcm_period_pointer; + bool pointer_flush; + + /* To wait for first packet. */ + bool callbacked; + wait_queue_head_t callback_wait; + struct amdtp_stream *sync_slave; + + /* For AM824 processing. */ + struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_limit; + int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned int pcm_channels; + unsigned int midi_ports; + + u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; + + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + + unsigned int frame_multiplier; +}; + +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, + enum cip_flags flags); +void amdtp_stream_destroy(struct amdtp_stream *s); + +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames); +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); + +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); +void amdtp_stream_update(struct amdtp_stream *s); +void amdtp_stream_stop(struct amdtp_stream *s); + +int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime); +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); +void amdtp_stream_pcm_prepare(struct amdtp_stream *s); +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); +void amdtp_stream_pcm_abort(struct amdtp_stream *s); + +extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; +extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT]; + +/** + * amdtp_stream_running - check stream is running or not + * @s: the AMDTP stream + * + * If this function returns true, the stream is running. + */ +static inline bool amdtp_stream_running(struct amdtp_stream *s) +{ + return !IS_ERR(s->context); +} + +/** + * amdtp_streaming_error - check for streaming error + * @s: the AMDTP stream + * + * If this function returns true, the stream's packet queue has stopped due to + * an asynchronous error. + */ +static inline bool amdtp_streaming_error(struct amdtp_stream *s) +{ + return s->packet_index < 0; +} + +/** + * amdtp_stream_pcm_running - check PCM substream is running or not + * @s: the AMDTP stream + * + * If this function returns true, PCM substream in the AMDTP stream is running. + */ +static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s) +{ + return !!s->pcm; +} + +/** + * amdtp_stream_pcm_trigger - start/stop playback from a PCM device + * @s: the AMDTP stream + * @pcm: the PCM device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of PCM data. This function should be called from the PCM + * device's .trigger callback. + */ +static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, + struct snd_pcm_substream *pcm) +{ + ACCESS_ONCE(s->pcm) = pcm; +} + +/** + * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device + * @s: the AMDTP stream + * @port: index of MIDI port + * @midi: the MIDI device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of MIDI data. This function should be called from the MIDI + * device's .trigger callback. + */ +static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s, + unsigned int port, + struct snd_rawmidi_substream *midi) +{ + if (port < s->midi_ports) + ACCESS_ONCE(s->midi[port]) = midi; +} + +static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) +{ + return sfc & 1; +} + +static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, + struct amdtp_stream *master, + struct amdtp_stream *slave) +{ + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master->flags |= CIP_SYNC_TO_DEVICE; + slave->flags |= CIP_SYNC_TO_DEVICE; + master->sync_slave = slave; + } else { + master->flags &= ~CIP_SYNC_TO_DEVICE; + slave->flags &= ~CIP_SYNC_TO_DEVICE; + master->sync_slave = NULL; + } + + slave->sync_slave = NULL; +} + +/** + * amdtp_stream_wait_callback - sleep till callbacked or timeout + * @s: the AMDTP stream + * @timeout: msec till timeout + * + * If this function return false, the AMDTP stream should be stopped. + */ +static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, + unsigned int timeout) +{ + return wait_event_timeout(s->callback_wait, + s->callbacked == true, + msecs_to_jiffies(timeout)) > 0; +} + +#endif diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c deleted file mode 100644 index b251f4b2b51c..000000000000 --- a/sound/firewire/amdtp.c +++ /dev/null @@ -1,1157 +0,0 @@ -/* - * Audio and Music Data Transmission Protocol (IEC 61883-6) streams - * with Common Isochronous Packet (IEC 61883-1) headers - * - * Copyright (c) Clemens Ladisch - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "amdtp.h" - -#define TICKS_PER_CYCLE 3072 -#define CYCLES_PER_SECOND 8000 -#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) - -/* - * Nominally 3125 bytes/second, but the MIDI port's clock might be - * 1% too slow, and the bus clock 100 ppm too fast. - */ -#define MIDI_BYTES_PER_SECOND 3093 - -/* - * Several devices look only at the first eight data blocks. - * In any case, this is more than enough for the MIDI data rate. - */ -#define MAX_MIDI_RX_BLOCKS 8 - -#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ - -/* isochronous header parameters */ -#define ISO_DATA_LENGTH_SHIFT 16 -#define TAG_CIP 1 - -/* common isochronous packet header parameters */ -#define CIP_EOH_SHIFT 31 -#define CIP_EOH (1u << CIP_EOH_SHIFT) -#define CIP_EOH_MASK 0x80000000 -#define CIP_SID_SHIFT 24 -#define CIP_SID_MASK 0x3f000000 -#define CIP_DBS_MASK 0x00ff0000 -#define CIP_DBS_SHIFT 16 -#define CIP_DBC_MASK 0x000000ff -#define CIP_FMT_SHIFT 24 -#define CIP_FMT_MASK 0x3f000000 -#define CIP_FDF_MASK 0x00ff0000 -#define CIP_FDF_SHIFT 16 -#define CIP_SYT_MASK 0x0000ffff -#define CIP_SYT_NO_INFO 0xffff - -/* - * Audio and Music transfer protocol specific parameters - * only "Clock-based rate control mode" is supported - */ -#define CIP_FMT_AM 0x10 -#define AMDTP_FDF_AM824 0x00 -#define AMDTP_FDF_NO_DATA 0xff - -/* TODO: make these configurable */ -#define INTERRUPT_INTERVAL 16 -#define QUEUE_LENGTH 48 - -#define IN_PACKET_HEADER_SIZE 4 -#define OUT_PACKET_HEADER_SIZE 0 - -static void pcm_period_tasklet(unsigned long data); - -/** - * amdtp_stream_init - initialize an AMDTP stream structure - * @s: the AMDTP stream to initialize - * @unit: the target of the stream - * @dir: the direction of stream - * @flags: the packet transmission method to use - */ -int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, enum cip_flags flags) -{ - s->unit = unit; - s->direction = dir; - s->flags = flags; - s->context = ERR_PTR(-1); - mutex_init(&s->mutex); - tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); - s->packet_index = 0; - - init_waitqueue_head(&s->callback_wait); - s->callbacked = false; - s->sync_slave = NULL; - - s->fmt = CIP_FMT_AM; - - return 0; -} -EXPORT_SYMBOL(amdtp_stream_init); - -/** - * amdtp_stream_destroy - free stream resources - * @s: the AMDTP stream to destroy - */ -void amdtp_stream_destroy(struct amdtp_stream *s) -{ - WARN_ON(amdtp_stream_running(s)); - mutex_destroy(&s->mutex); -} -EXPORT_SYMBOL(amdtp_stream_destroy); - -const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { - [CIP_SFC_32000] = 8, - [CIP_SFC_44100] = 8, - [CIP_SFC_48000] = 8, - [CIP_SFC_88200] = 16, - [CIP_SFC_96000] = 16, - [CIP_SFC_176400] = 32, - [CIP_SFC_192000] = 32, -}; -EXPORT_SYMBOL(amdtp_syt_intervals); - -const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = { - [CIP_SFC_32000] = 32000, - [CIP_SFC_44100] = 44100, - [CIP_SFC_48000] = 48000, - [CIP_SFC_88200] = 88200, - [CIP_SFC_96000] = 96000, - [CIP_SFC_176400] = 176400, - [CIP_SFC_192000] = 192000, -}; -EXPORT_SYMBOL(amdtp_rate_table); - -/** - * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream - * @s: the AMDTP stream, which must be initialized. - * @runtime: the PCM substream runtime - */ -int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, - struct snd_pcm_runtime *runtime) -{ - int err; - - /* AM824 in IEC 61883-6 can deliver 24bit data */ - err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - if (err < 0) - goto end; - - /* - * Currently firewire-lib processes 16 packets in one software - * interrupt callback. This equals to 2msec but actually the - * interval of the interrupts has a jitter. - * Additionally, even if adding a constraint to fit period size to - * 2msec, actual calculated frames per period doesn't equal to 2msec, - * depending on sampling rate. - * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec. - * Here let us use 5msec for safe period interrupt. - */ - err = snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 5000, UINT_MAX); - if (err < 0) - goto end; - - /* Non-Blocking stream has no more constraints */ - if (!(s->flags & CIP_BLOCKING)) - goto end; - - /* - * One AMDTP packet can include some frames. In blocking mode, the - * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, - * depending on its sampling rate. For accurate period interrupt, it's - * preferrable to align period/buffer sizes to current SYT_INTERVAL. - * - * TODO: These constraints can be improved with proper rules. - * Currently apply LCM of SYT_INTERVALs. - */ - err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); - if (err < 0) - goto end; - err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); -end: - return err; -} -EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); - -/** - * amdtp_stream_set_parameters - set stream parameters - * @s: the AMDTP stream to configure - * @rate: the sample rate - * @pcm_channels: the number of PCM samples in each data block, to be encoded - * as AM824 multi-bit linear audio - * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) - * @double_pcm_frames: one data block transfers two PCM frames - * - * The parameters must be set before the stream is started, and must not be - * changed while the stream is running. - */ -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames) -{ - unsigned int i, sfc, midi_channels; - - midi_channels = DIV_ROUND_UP(midi_ports, 8); - - if (WARN_ON(amdtp_stream_running(s)) | - WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) | - WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) - return -EINVAL; - - for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { - if (amdtp_rate_table[sfc] == rate) - break; - } - if (sfc == ARRAY_SIZE(amdtp_rate_table)) - return -EINVAL; - - s->pcm_channels = pcm_channels; - s->sfc = sfc; - s->data_block_quadlets = s->pcm_channels + midi_channels; - s->midi_ports = midi_ports; - - s->fdf = AMDTP_FDF_AM824 | s->sfc; - - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk at higher - * sampling rate to transfer two PCM frames in one data block. - */ - if (double_pcm_frames) - s->frame_multiplier = 2; - else - s->frame_multiplier = 1; - - s->syt_interval = amdtp_syt_intervals[sfc]; - - /* default buffering in the device */ - s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; - if (s->flags & CIP_BLOCKING) - /* additional buffering needed to adjust for no-data packets */ - s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; - - /* init the position map for PCM and MIDI channels */ - for (i = 0; i < pcm_channels; i++) - s->pcm_positions[i] = i; - s->midi_position = s->pcm_channels; - - /* - * We do not know the actual MIDI FIFO size of most devices. Just - * assume two bytes, i.e., one byte can be received over the bus while - * the previous one is transmitted over MIDI. - * (The value here is adjusted for midi_ratelimit_per_packet().) - */ - s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; - - return 0; -} -EXPORT_SYMBOL(amdtp_stream_set_parameters); - -/** - * amdtp_stream_get_max_payload - get the stream's packet size - * @s: the AMDTP stream - * - * This function must not be called before the stream has been configured - * with amdtp_stream_set_parameters(). - */ -unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) -{ - unsigned int multiplier = 1; - - if (s->flags & CIP_JUMBO_PAYLOAD) - multiplier = 5; - - return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier; -} -EXPORT_SYMBOL(amdtp_stream_get_max_payload); - -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - -/** - * amdtp_stream_set_pcm_format - set the PCM format - * @s: the AMDTP stream to configure - * @format: the format of the ALSA PCM device - * - * The sample format must be set after the other parameters (rate/PCM channels/ - * MIDI) and before the stream is started, and must not be changed while the - * stream is running. - */ -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format) -{ - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - s->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - s->transfer_samples = write_pcm_s32; - else - s->transfer_samples = read_pcm_s32; - break; - } -} -EXPORT_SYMBOL(amdtp_stream_set_pcm_format); - -/** - * amdtp_stream_pcm_prepare - prepare PCM device for running - * @s: the AMDTP stream - * - * This function should be called from the PCM device's .prepare callback. - */ -void amdtp_stream_pcm_prepare(struct amdtp_stream *s) -{ - tasklet_kill(&s->period_tasklet); - s->pcm_buffer_pointer = 0; - s->pcm_period_pointer = 0; - s->pointer_flush = true; -} -EXPORT_SYMBOL(amdtp_stream_pcm_prepare); - -static unsigned int calculate_data_blocks(struct amdtp_stream *s, - unsigned int syt) -{ - unsigned int phase, data_blocks; - - /* Blocking mode. */ - if (s->flags & CIP_BLOCKING) { - /* This module generate empty packet for 'no data'. */ - if (syt == CIP_SYT_NO_INFO) - data_blocks = 0; - else - data_blocks = s->syt_interval; - /* Non-blocking mode. */ - } else { - if (!cip_sfc_is_base_44100(s->sfc)) { - /* Sample_rate / 8000 is an integer, and precomputed. */ - data_blocks = s->data_block_state; - } else { - phase = s->data_block_state; - - /* - * This calculates the number of data blocks per packet so that - * 1) the overall rate is correct and exactly synchronized to - * the bus clock, and - * 2) packets with a rounded-up number of blocks occur as early - * as possible in the sequence (to prevent underruns of the - * device's buffer). - */ - if (s->sfc == CIP_SFC_44100) - /* 6 6 5 6 5 6 5 ... */ - data_blocks = 5 + ((phase & 1) ^ - (phase == 0 || phase >= 40)); - else - /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ - data_blocks = 11 * (s->sfc >> 1) + (phase == 0); - if (++phase >= (80 >> (s->sfc >> 1))) - phase = 0; - s->data_block_state = phase; - } - } - - return data_blocks; -} - -static unsigned int calculate_syt(struct amdtp_stream *s, - unsigned int cycle) -{ - unsigned int syt_offset, phase, index, syt; - - if (s->last_syt_offset < TICKS_PER_CYCLE) { - if (!cip_sfc_is_base_44100(s->sfc)) - syt_offset = s->last_syt_offset + s->syt_offset_state; - else { - /* - * The time, in ticks, of the n'th SYT_INTERVAL sample is: - * n * SYT_INTERVAL * 24576000 / sample_rate - * Modulo TICKS_PER_CYCLE, the difference between successive - * elements is about 1386.23. Rounding the results of this - * formula to the SYT precision results in a sequence of - * differences that begins with: - * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... - * This code generates _exactly_ the same sequence. - */ - phase = s->syt_offset_state; - index = phase % 13; - syt_offset = s->last_syt_offset; - syt_offset += 1386 + ((index && !(index & 3)) || - phase == 146); - if (++phase >= 147) - phase = 0; - s->syt_offset_state = phase; - } - } else - syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; - s->last_syt_offset = syt_offset; - - if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += s->transfer_delay; - syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; - syt += syt_offset % TICKS_PER_CYCLE; - - return syt & CIP_SYT_MASK; - } else { - return CIP_SYT_NO_INFO; - } -} - -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u32 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src >> 8) | 0x40000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src << 8) | 0x42000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - u32 *dst; - - channels = s->pcm_channels; - dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; - dst++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - dst = (void *)runtime->dma_area; - } -} - -static void write_pcm_silence(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int i, c; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < s->pcm_channels; ++c) - buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); - buffer += s->data_block_quadlets; - } -} - -/* - * To avoid sending MIDI bytes at too high a rate, assume that the receiving - * device has a FIFO, and track how much it is filled. This values increases - * by one whenever we send one byte in a packet, but the FIFO empties at - * a constant rate independent of our packet rate. One packet has syt_interval - * samples, so the number of bytes that empty out of the FIFO, per packet(!), - * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing - * fractional values, the values in midi_fifo_used[] are measured in bytes - * multiplied by the sample rate. - */ -static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) -{ - int used; - - used = s->midi_fifo_used[port]; - if (used == 0) /* common shortcut */ - return true; - - used -= MIDI_BYTES_PER_SECOND * s->syt_interval; - used = max(used, 0); - s->midi_fifo_used[port] = used; - - return used < s->midi_fifo_limit; -} - -static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) -{ - s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; -} - -static void write_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int f, port; - u8 *b; - - for (f = 0; f < frames; f++) { - b = (u8 *)&buffer[s->midi_position]; - - port = (s->data_block_counter + f) % 8; - if (f < MAX_MIDI_RX_BLOCKS && - midi_ratelimit_per_packet(s, port) && - s->midi[port] != NULL && - snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { - midi_rate_use_one_byte(s, port); - b[0] = 0x81; - } else { - b[0] = 0x80; - b[1] = 0; - } - b[2] = 0; - b[3] = 0; - - buffer += s->data_block_quadlets; - } -} - -static void read_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int f, port; - int len; - u8 *b; - - for (f = 0; f < frames; f++) { - port = (s->data_block_counter + f) % 8; - b = (u8 *)&buffer[s->midi_position]; - - len = b[0] - 0x80; - if ((1 <= len) && (len <= 3) && (s->midi[port])) - snd_rawmidi_receive(s->midi[port], b + 1, len); - - buffer += s->data_block_quadlets; - } -} - -static void update_pcm_pointers(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - unsigned int frames) -{ - unsigned int ptr; - - ptr = s->pcm_buffer_pointer + frames; - if (ptr >= pcm->runtime->buffer_size) - ptr -= pcm->runtime->buffer_size; - ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; - - s->pcm_period_pointer += frames; - if (s->pcm_period_pointer >= pcm->runtime->period_size) { - s->pcm_period_pointer -= pcm->runtime->period_size; - s->pointer_flush = false; - tasklet_hi_schedule(&s->period_tasklet); - } -} - -static void pcm_period_tasklet(unsigned long data) -{ - struct amdtp_stream *s = (void *)data; - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - - if (pcm) - snd_pcm_period_elapsed(pcm); -} - -static int queue_packet(struct amdtp_stream *s, - unsigned int header_length, - unsigned int payload_length, bool skip) -{ - struct fw_iso_packet p = {0}; - int err = 0; - - if (IS_ERR(s->context)) - goto end; - - p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); - p.tag = TAG_CIP; - p.header_length = header_length; - p.payload_length = (!skip) ? payload_length : 0; - p.skip = skip; - err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, - s->buffer.packets[s->packet_index].offset); - if (err < 0) { - dev_err(&s->unit->device, "queueing error: %d\n", err); - goto end; - } - - if (++s->packet_index >= QUEUE_LENGTH) - s->packet_index = 0; -end: - return err; -} - -static inline int queue_out_packet(struct amdtp_stream *s, - unsigned int payload_length, bool skip) -{ - return queue_packet(s, OUT_PACKET_HEADER_SIZE, - payload_length, skip); -} - -static inline int queue_in_packet(struct amdtp_stream *s) -{ - return queue_packet(s, IN_PACKET_HEADER_SIZE, - amdtp_stream_get_max_payload(s), false); -} - -unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - write_pcm_silence(s, buffer, data_blocks); - pcm_frames = 0; - } - - if (s->midi_ports) - write_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - -static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, - unsigned int syt) -{ - __be32 *buffer; - unsigned int payload_length; - unsigned int pcm_frames; - struct snd_pcm_substream *pcm; - - buffer = s->buffer.packets[s->packet_index].buffer; - pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); - - buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | - (s->data_block_quadlets << CIP_DBS_SHIFT) | - s->data_block_counter); - buffer[1] = cpu_to_be32(CIP_EOH | - ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | - ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | - (syt & CIP_SYT_MASK)); - - s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; - - payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - if (queue_out_packet(s, payload_length, false) < 0) - return -EIO; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - /* No need to return the number of handled data blocks. */ - return 0; -} - -unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - pcm_frames = 0; - } - - if (s->midi_ports) - read_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - -static int handle_in_packet(struct amdtp_stream *s, - unsigned int payload_quadlets, __be32 *buffer, - unsigned int *data_blocks, unsigned int syt) -{ - u32 cip_header[2]; - unsigned int fmt, fdf; - unsigned int data_block_quadlets, data_block_counter, dbc_interval; - struct snd_pcm_substream *pcm; - unsigned int pcm_frames; - bool lost; - - cip_header[0] = be32_to_cpu(buffer[0]); - cip_header[1] = be32_to_cpu(buffer[1]); - - /* - * This module supports 'Two-quadlet CIP header with SYT field'. - * For convenience, also check FMT field is AM824 or not. - */ - if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || - ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) { - dev_info_ratelimited(&s->unit->device, - "Invalid CIP header for AMDTP: %08X:%08X\n", - cip_header[0], cip_header[1]); - *data_blocks = 0; - pcm_frames = 0; - goto end; - } - - /* Check valid protocol or not. */ - fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; - if (fmt != s->fmt) { - dev_err(&s->unit->device, - "Detect unexpected protocol: %08x %08x\n", - cip_header[0], cip_header[1]); - return -EIO; - } - - /* Calculate data blocks */ - fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; - if (payload_quadlets < 3 || - (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { - *data_blocks = 0; - } else { - data_block_quadlets = - (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; - /* avoid division by zero */ - if (data_block_quadlets == 0) { - dev_err(&s->unit->device, - "Detect invalid value in dbs field: %08X\n", - cip_header[0]); - return -EPROTO; - } - if (s->flags & CIP_WRONG_DBS) - data_block_quadlets = s->data_block_quadlets; - - *data_blocks = (payload_quadlets - 2) / data_block_quadlets; - } - - /* Check data block counter continuity */ - data_block_counter = cip_header[0] & CIP_DBC_MASK; - if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && - s->data_block_counter != UINT_MAX) - data_block_counter = s->data_block_counter; - - if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && - data_block_counter == s->tx_first_dbc) || - s->data_block_counter == UINT_MAX) { - lost = false; - } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { - lost = data_block_counter != s->data_block_counter; - } else { - if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) - dbc_interval = s->tx_dbc_interval; - else - dbc_interval = *data_blocks; - - lost = data_block_counter != - ((s->data_block_counter + dbc_interval) & 0xff); - } - - if (lost) { - dev_err(&s->unit->device, - "Detect discontinuity of CIP: %02X %02X\n", - s->data_block_counter, data_block_counter); - return -EIO; - } - - pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); - - if (s->flags & CIP_DBC_IS_END_EVENT) - s->data_block_counter = data_block_counter; - else - s->data_block_counter = - (data_block_counter + *data_blocks) & 0xff; -end: - if (queue_in_packet(s) < 0) - return -EIO; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - return 0; -} - -static void out_stream_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, - void *private_data) -{ - struct amdtp_stream *s = private_data; - unsigned int i, syt, packets = header_length / 4; - unsigned int data_blocks; - - if (s->packet_index < 0) - return; - - /* - * Compute the cycle of the last queued packet. - * (We need only the four lowest bits for the SYT, so we can ignore - * that bits 0-11 must wrap around at 3072.) - */ - cycle += QUEUE_LENGTH - packets; - - for (i = 0; i < packets; ++i) { - syt = calculate_syt(s, ++cycle); - data_blocks = calculate_data_blocks(s, syt); - - if (handle_out_packet(s, data_blocks, syt) < 0) { - s->packet_index = -1; - amdtp_stream_pcm_abort(s); - return; - } - } - - fw_iso_context_queue_flush(s->context); -} - -static void in_stream_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, - void *private_data) -{ - struct amdtp_stream *s = private_data; - unsigned int p, syt, packets; - unsigned int payload_quadlets, max_payload_quadlets; - unsigned int data_blocks; - __be32 *buffer, *headers = header; - - if (s->packet_index < 0) - return; - - /* The number of packets in buffer */ - packets = header_length / IN_PACKET_HEADER_SIZE; - - /* For buffer-over-run prevention. */ - max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; - - for (p = 0; p < packets; p++) { - buffer = s->buffer.packets[s->packet_index].buffer; - - /* The number of quadlets in this packet */ - payload_quadlets = - (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; - if (payload_quadlets > max_payload_quadlets) { - dev_err(&s->unit->device, - "Detect jumbo payload: %02x %02x\n", - payload_quadlets, max_payload_quadlets); - s->packet_index = -1; - break; - } - - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks, syt) < 0) { - s->packet_index = -1; - break; - } - - /* Process sync slave stream */ - if (s->sync_slave && s->sync_slave->callbacked) { - if (handle_out_packet(s->sync_slave, - data_blocks, syt) < 0) { - s->packet_index = -1; - break; - } - } - } - - /* Queueing error or detecting discontinuity */ - if (s->packet_index < 0) { - amdtp_stream_pcm_abort(s); - - /* Abort sync slave. */ - if (s->sync_slave) { - s->sync_slave->packet_index = -1; - amdtp_stream_pcm_abort(s->sync_slave); - } - return; - } - - /* when sync to device, flush the packets for slave stream */ - if (s->sync_slave && s->sync_slave->callbacked) - fw_iso_context_queue_flush(s->sync_slave->context); - - fw_iso_context_queue_flush(s->context); -} - -/* processing is done by master callback */ -static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, - void *private_data) -{ - return; -} - -/* this is executed one time */ -static void amdtp_stream_first_callback(struct fw_iso_context *context, - u32 cycle, size_t header_length, - void *header, void *private_data) -{ - struct amdtp_stream *s = private_data; - - /* - * For in-stream, first packet has come. - * For out-stream, prepared to transmit first packet - */ - s->callbacked = true; - wake_up(&s->callback_wait); - - if (s->direction == AMDTP_IN_STREAM) - context->callback.sc = in_stream_callback; - else if (s->flags & CIP_SYNC_TO_DEVICE) - context->callback.sc = slave_stream_callback; - else - context->callback.sc = out_stream_callback; - - context->callback.sc(context, cycle, header_length, header, s); -} - -/** - * amdtp_stream_start - start transferring packets - * @s: the AMDTP stream to start - * @channel: the isochronous channel on the bus - * @speed: firewire speed code - * - * The stream cannot be started until it has been configured with - * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI - * device can be started. - */ -int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) -{ - static const struct { - unsigned int data_block; - unsigned int syt_offset; - } initial_state[] = { - [CIP_SFC_32000] = { 4, 3072 }, - [CIP_SFC_48000] = { 6, 1024 }, - [CIP_SFC_96000] = { 12, 1024 }, - [CIP_SFC_192000] = { 24, 1024 }, - [CIP_SFC_44100] = { 0, 67 }, - [CIP_SFC_88200] = { 0, 67 }, - [CIP_SFC_176400] = { 0, 67 }, - }; - unsigned int header_size; - enum dma_data_direction dir; - int type, tag, err; - - mutex_lock(&s->mutex); - - if (WARN_ON(amdtp_stream_running(s) || - (s->data_block_quadlets < 1))) { - err = -EBADFD; - goto err_unlock; - } - - if (s->direction == AMDTP_IN_STREAM && - s->flags & CIP_SKIP_INIT_DBC_CHECK) - s->data_block_counter = UINT_MAX; - else - s->data_block_counter = 0; - s->data_block_state = initial_state[s->sfc].data_block; - s->syt_offset_state = initial_state[s->sfc].syt_offset; - s->last_syt_offset = TICKS_PER_CYCLE; - - /* initialize packet buffer */ - if (s->direction == AMDTP_IN_STREAM) { - dir = DMA_FROM_DEVICE; - type = FW_ISO_CONTEXT_RECEIVE; - header_size = IN_PACKET_HEADER_SIZE; - } else { - dir = DMA_TO_DEVICE; - type = FW_ISO_CONTEXT_TRANSMIT; - header_size = OUT_PACKET_HEADER_SIZE; - } - err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), dir); - if (err < 0) - goto err_unlock; - - s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, - type, channel, speed, header_size, - amdtp_stream_first_callback, s); - if (IS_ERR(s->context)) { - err = PTR_ERR(s->context); - if (err == -EBUSY) - dev_err(&s->unit->device, - "no free stream on this controller\n"); - goto err_buffer; - } - - amdtp_stream_update(s); - - s->packet_index = 0; - do { - if (s->direction == AMDTP_IN_STREAM) - err = queue_in_packet(s); - else - err = queue_out_packet(s, 0, true); - if (err < 0) - goto err_context; - } while (s->packet_index > 0); - - /* NOTE: TAG1 matches CIP. This just affects in stream. */ - tag = FW_ISO_CONTEXT_MATCH_TAG1; - if (s->flags & CIP_EMPTY_WITH_TAG0) - tag |= FW_ISO_CONTEXT_MATCH_TAG0; - - s->callbacked = false; - err = fw_iso_context_start(s->context, -1, 0, tag); - if (err < 0) - goto err_context; - - mutex_unlock(&s->mutex); - - return 0; - -err_context: - fw_iso_context_destroy(s->context); - s->context = ERR_PTR(-1); -err_buffer: - iso_packets_buffer_destroy(&s->buffer, s->unit); -err_unlock: - mutex_unlock(&s->mutex); - - return err; -} -EXPORT_SYMBOL(amdtp_stream_start); - -/** - * amdtp_stream_pcm_pointer - get the PCM buffer position - * @s: the AMDTP stream that transports the PCM data - * - * Returns the current buffer position, in frames. - */ -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) -{ - /* this optimization is allowed to be racy */ - if (s->pointer_flush && amdtp_stream_running(s)) - fw_iso_context_flush_completions(s->context); - else - s->pointer_flush = true; - - return ACCESS_ONCE(s->pcm_buffer_pointer); -} -EXPORT_SYMBOL(amdtp_stream_pcm_pointer); - -/** - * amdtp_stream_update - update the stream after a bus reset - * @s: the AMDTP stream - */ -void amdtp_stream_update(struct amdtp_stream *s) -{ - /* Precomputing. */ - ACCESS_ONCE(s->source_node_id_field) = - (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & - CIP_SID_MASK; -} -EXPORT_SYMBOL(amdtp_stream_update); - -/** - * amdtp_stream_stop - stop sending packets - * @s: the AMDTP stream to stop - * - * All PCM and MIDI devices of the stream must be stopped before the stream - * itself can be stopped. - */ -void amdtp_stream_stop(struct amdtp_stream *s) -{ - mutex_lock(&s->mutex); - - if (!amdtp_stream_running(s)) { - mutex_unlock(&s->mutex); - return; - } - - tasklet_kill(&s->period_tasklet); - fw_iso_context_stop(s->context); - fw_iso_context_destroy(s->context); - s->context = ERR_PTR(-1); - iso_packets_buffer_destroy(&s->buffer, s->unit); - - s->callbacked = false; - - mutex_unlock(&s->mutex); -} -EXPORT_SYMBOL(amdtp_stream_stop); - -/** - * amdtp_stream_pcm_abort - abort the running PCM device - * @s: the AMDTP stream about to be stopped - * - * If the isochronous stream needs to be stopped asynchronously, call this - * function first to stop the PCM device. - */ -void amdtp_stream_pcm_abort(struct amdtp_stream *s) -{ - struct snd_pcm_substream *pcm; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - snd_pcm_stop_xrun(pcm); -} -EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h deleted file mode 100644 index 883bb1a7e25f..000000000000 --- a/sound/firewire/amdtp.h +++ /dev/null @@ -1,306 +0,0 @@ -#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED -#define SOUND_FIREWIRE_AMDTP_H_INCLUDED - -#include -#include -#include -#include -#include "packets-buffer.h" - -/** - * enum cip_flags - describes details of the streaming protocol - * @CIP_NONBLOCKING: In non-blocking mode, each packet contains - * sample_rate/8000 samples, with rounding up or down to adjust - * for clock skew and left-over fractional samples. This should - * be used if supported by the device. - * @CIP_BLOCKING: In blocking mode, each packet contains either zero or - * SYT_INTERVAL samples, with these two types alternating so that - * the overall sample rate comes out right. - * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is - * generated by in packets. Defaultly this driver generates timestamp. - * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0. - * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet - * corresponds to the end of event in the packet. Out of IEC 61883. - * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets. - * The value of data_block_quadlets is used instead of reported value. - * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is - * skipped for detecting discontinuity. - * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first - * packet is not continuous from an initial value. - * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty - * packet is wrong but the others are correct. - * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an - * packet is larger than IEC 61883-6 defines. Current implementation - * allows 5 times as large as IEC 61883-6 defines. - */ -enum cip_flags { - CIP_NONBLOCKING = 0x00, - CIP_BLOCKING = 0x01, - CIP_SYNC_TO_DEVICE = 0x02, - CIP_EMPTY_WITH_TAG0 = 0x04, - CIP_DBC_IS_END_EVENT = 0x08, - CIP_WRONG_DBS = 0x10, - CIP_SKIP_DBC_ZERO_CHECK = 0x20, - CIP_SKIP_INIT_DBC_CHECK = 0x40, - CIP_EMPTY_HAS_WRONG_DBC = 0x80, - CIP_JUMBO_PAYLOAD = 0x100, -}; - -/** - * enum cip_sfc - supported Sampling Frequency Codes (SFCs) - * @CIP_SFC_32000: 32,000 data blocks - * @CIP_SFC_44100: 44,100 data blocks - * @CIP_SFC_48000: 48,000 data blocks - * @CIP_SFC_88200: 88,200 data blocks - * @CIP_SFC_96000: 96,000 data blocks - * @CIP_SFC_176400: 176,400 data blocks - * @CIP_SFC_192000: 192,000 data blocks - * @CIP_SFC_COUNT: the number of supported SFCs - * - * These values are used to show nominal Sampling Frequency Code in - * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002, - * this code means the number of events per second. Actually the code - * represents the number of data blocks transferred per second in an AMDTP - * stream. - * - * In IEC 61883-6:2005, some extensions were added to support more types of - * data such as 'One Bit LInear Audio', therefore the meaning of SFC became - * different depending on the types. - * - * Currently our implementation is compatible with IEC 61883-6:2002. - */ -enum cip_sfc { - CIP_SFC_32000 = 0, - CIP_SFC_44100 = 1, - CIP_SFC_48000 = 2, - CIP_SFC_88200 = 3, - CIP_SFC_96000 = 4, - CIP_SFC_176400 = 5, - CIP_SFC_192000 = 6, - CIP_SFC_COUNT -}; - -#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 - -#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ - SNDRV_PCM_FMTBIT_S32) - - -/* - * This module supports maximum 64 PCM channels for one PCM stream - * This is for our convenience. - */ -#define AMDTP_MAX_CHANNELS_FOR_PCM 64 - -/* - * AMDTP packet can include channels for MIDI conformant data. - * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. - * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. - * - * This module supports maximum 1 MIDI conformant data channels. - * Then this AMDTP packets can transfer maximum 8 MIDI data streams. - */ -#define AMDTP_MAX_CHANNELS_FOR_MIDI 1 - -struct fw_unit; -struct fw_iso_context; -struct snd_pcm_substream; -struct snd_pcm_runtime; -struct snd_rawmidi_substream; - -enum amdtp_stream_direction { - AMDTP_OUT_STREAM = 0, - AMDTP_IN_STREAM -}; - -struct amdtp_stream { - struct fw_unit *unit; - enum cip_flags flags; - enum amdtp_stream_direction direction; - struct mutex mutex; - - /* For packet processing. */ - struct fw_iso_context *context; - struct iso_packets_buffer buffer; - int packet_index; - - /* For CIP headers. */ - unsigned int source_node_id_field; - unsigned int data_block_quadlets; - unsigned int data_block_counter; - unsigned int fmt; - unsigned int fdf; - /* quirk: fixed interval of dbc between previos/current packets. */ - unsigned int tx_dbc_interval; - /* quirk: indicate the value of dbc field in a first packet. */ - unsigned int tx_first_dbc; - - /* Internal flags. */ - enum cip_sfc sfc; - unsigned int syt_interval; - unsigned int transfer_delay; - unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; - - /* For a PCM substream processing. */ - struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; - unsigned int pcm_buffer_pointer; - unsigned int pcm_period_pointer; - bool pointer_flush; - - /* To wait for first packet. */ - bool callbacked; - wait_queue_head_t callback_wait; - struct amdtp_stream *sync_slave; - - /* For AM824 processing. */ - struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; - int midi_fifo_limit; - int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; - unsigned int pcm_channels; - unsigned int midi_ports; - - u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; - u8 midi_position; - - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - - unsigned int frame_multiplier; -}; - -int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, - enum cip_flags flags); -void amdtp_stream_destroy(struct amdtp_stream *s); - -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames); -unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); - -int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); -void amdtp_stream_update(struct amdtp_stream *s); -void amdtp_stream_stop(struct amdtp_stream *s); - -int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, - struct snd_pcm_runtime *runtime); -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); -void amdtp_stream_pcm_prepare(struct amdtp_stream *s); -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); -void amdtp_stream_pcm_abort(struct amdtp_stream *s); - -extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; -extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT]; - -/** - * amdtp_stream_running - check stream is running or not - * @s: the AMDTP stream - * - * If this function returns true, the stream is running. - */ -static inline bool amdtp_stream_running(struct amdtp_stream *s) -{ - return !IS_ERR(s->context); -} - -/** - * amdtp_streaming_error - check for streaming error - * @s: the AMDTP stream - * - * If this function returns true, the stream's packet queue has stopped due to - * an asynchronous error. - */ -static inline bool amdtp_streaming_error(struct amdtp_stream *s) -{ - return s->packet_index < 0; -} - -/** - * amdtp_stream_pcm_running - check PCM substream is running or not - * @s: the AMDTP stream - * - * If this function returns true, PCM substream in the AMDTP stream is running. - */ -static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s) -{ - return !!s->pcm; -} - -/** - * amdtp_stream_pcm_trigger - start/stop playback from a PCM device - * @s: the AMDTP stream - * @pcm: the PCM device to be started, or %NULL to stop the current device - * - * Call this function on a running isochronous stream to enable the actual - * transmission of PCM data. This function should be called from the PCM - * device's .trigger callback. - */ -static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, - struct snd_pcm_substream *pcm) -{ - ACCESS_ONCE(s->pcm) = pcm; -} - -/** - * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device - * @s: the AMDTP stream - * @port: index of MIDI port - * @midi: the MIDI device to be started, or %NULL to stop the current device - * - * Call this function on a running isochronous stream to enable the actual - * transmission of MIDI data. This function should be called from the MIDI - * device's .trigger callback. - */ -static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s, - unsigned int port, - struct snd_rawmidi_substream *midi) -{ - if (port < s->midi_ports) - ACCESS_ONCE(s->midi[port]) = midi; -} - -static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) -{ - return sfc & 1; -} - -static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, - struct amdtp_stream *master, - struct amdtp_stream *slave) -{ - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master->flags |= CIP_SYNC_TO_DEVICE; - slave->flags |= CIP_SYNC_TO_DEVICE; - master->sync_slave = slave; - } else { - master->flags &= ~CIP_SYNC_TO_DEVICE; - slave->flags &= ~CIP_SYNC_TO_DEVICE; - master->sync_slave = NULL; - } - - slave->sync_slave = NULL; -} - -/** - * amdtp_stream_wait_callback - sleep till callbacked or timeout - * @s: the AMDTP stream - * @timeout: msec till timeout - * - * If this function return false, the AMDTP stream should be stopped. - */ -static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, - unsigned int timeout) -{ - return wait_event_timeout(s->callback_wait, - s->callbacked == true, - msecs_to_jiffies(timeout)) > 0; -} - -#endif diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index d23caca7f369..72a1c5e404fd 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -31,7 +31,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../cmp.h" /* basic register addresses on DM1000/DM1100/DM1500 */ diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index ecf5dc862235..29578c19e977 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -34,7 +34,7 @@ #include #include -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../iso-resources.h" #include "../lib.h" #include "dice-interface.h" diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c index 0619597e3a3f..cce19768f43d 100644 --- a/sound/firewire/fcp.c +++ b/sound/firewire/fcp.c @@ -17,7 +17,7 @@ #include #include "fcp.h" #include "lib.h" -#include "amdtp.h" +#include "amdtp-stream.h" #define CTS_AVC 0x00 diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 084d414b228c..d54f17134495 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -29,7 +29,7 @@ #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../cmp.h" #include "../lib.h" diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index cace5ad4fe76..2c3d20b61cbe 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -28,7 +28,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../cmp.h" struct device_info { -- cgit v1.2.3-59-g8ed1b From 5955815e71ff9c773b156680c781c87728e37bea Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:55 +0900 Subject: ALSA: firewire-lib: add data block processing layer for AM824 format This commit adds data block processing layer for AM824 format. The new layer initializes streaming layer with its value for fmt field. Currently, most implementation of data block processing still remains streaming layer. In later commits, these codes will be moved to the layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Makefile | 2 +- sound/firewire/amdtp-am824.c | 26 ++++++++++++++++++++++++++ sound/firewire/amdtp-am824.h | 8 ++++++++ sound/firewire/amdtp-stream.c | 7 ++++--- sound/firewire/amdtp-stream.h | 3 ++- sound/firewire/bebob/bebob.h | 2 +- sound/firewire/bebob/bebob_stream.c | 8 ++++---- sound/firewire/dice/dice-stream.c | 2 +- sound/firewire/dice/dice.h | 2 +- sound/firewire/fireworks/fireworks.h | 2 +- sound/firewire/fireworks/fireworks_stream.c | 2 +- sound/firewire/oxfw/oxfw-stream.c | 2 +- sound/firewire/oxfw/oxfw.h | 2 +- 13 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 sound/firewire/amdtp-am824.c create mode 100644 sound/firewire/amdtp-am824.h (limited to 'sound') diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 102e342ddc38..6a8a71371efd 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,5 +1,5 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ - fcp.o cmp.o amdtp-stream.o + fcp.o cmp.o amdtp-stream.o amdtp-am824.o snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c new file mode 100644 index 000000000000..da4b643eff40 --- /dev/null +++ b/sound/firewire/amdtp-am824.c @@ -0,0 +1,26 @@ +/* + * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6) + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "amdtp-am824.h" + +#define CIP_FMT_AM 0x10 + +/** + * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 + * data block + * @s: the AMDTP stream to initialize + * @unit: the target of the stream + * @dir: the direction of stream + * @flags: the packet transmission method to use + */ +int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags) +{ + return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM); +} +EXPORT_SYMBOL_GPL(amdtp_am824_init); diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h new file mode 100644 index 000000000000..ed96ac56628f --- /dev/null +++ b/sound/firewire/amdtp-am824.h @@ -0,0 +1,8 @@ +#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED +#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED + +#include "amdtp-stream.h" + +int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags); +#endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index ec65ebf8f04a..2254eec4521b 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -78,9 +77,11 @@ static void pcm_period_tasklet(unsigned long data); * @unit: the target of the stream * @dir: the direction of stream * @flags: the packet transmission method to use + * @fmt: the value of fmt field in CIP header */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, enum cip_flags flags) + enum amdtp_stream_direction dir, enum cip_flags flags, + unsigned int fmt) { s->unit = unit; s->direction = dir; @@ -94,7 +95,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = NULL; - s->fmt = CIP_FMT_AM; + s->fmt = fmt; return 0; } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 883bb1a7e25f..5f96affe37b7 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "packets-buffer.h" @@ -174,7 +175,7 @@ struct amdtp_stream { int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, - enum cip_flags flags); + enum cip_flags flags, unsigned int fmt); void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 72a1c5e404fd..d3c9d8de289b 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -31,7 +31,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../cmp.h" /* basic register addresses on DM1000/DM1100/DM1500 */ diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 920a3b8844ee..34bc3a419993 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -537,8 +537,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) if (err < 0) goto end; - err = amdtp_stream_init(&bebob->tx_stream, bebob->unit, - AMDTP_IN_STREAM, CIP_BLOCKING); + err = amdtp_am824_init(&bebob->tx_stream, bebob->unit, + AMDTP_IN_STREAM, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(&bebob->tx_stream); destroy_both_connections(bebob); @@ -566,8 +566,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) if (bebob->maudio_special_quirk) bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC; - err = amdtp_stream_init(&bebob->rx_stream, bebob->unit, - AMDTP_OUT_STREAM, CIP_BLOCKING); + err = amdtp_am824_init(&bebob->rx_stream, bebob->unit, + AMDTP_OUT_STREAM, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(&bebob->tx_stream); amdtp_stream_destroy(&bebob->rx_stream); diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index e4c6c20d70a0..917860e88b66 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -305,7 +305,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) goto end; resources->channels_mask = 0x00000000ffffffffuLL; - err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING); + err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 29578c19e977..101550ac1a24 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -34,7 +34,7 @@ #include #include -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../iso-resources.h" #include "../lib.h" #include "dice-interface.h" diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index d54f17134495..c7cb7deafe48 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -29,7 +29,7 @@ #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../cmp.h" #include "../lib.h" diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 85a72e63913d..8cac5b987cc1 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream) if (err < 0) goto end; - err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING); + err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(stream); cmp_connection_destroy(conn); diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 318f78e1a313..83683414793f 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -228,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, if (err < 0) goto end; - err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); + err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); if (err < 0) { amdtp_stream_destroy(stream); cmp_connection_destroy(conn); diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 2c3d20b61cbe..2441459d2e58 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -28,7 +28,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../cmp.h" struct device_info { -- cgit v1.2.3-59-g8ed1b From 51c29fd21389d9995a8a18a91eeb8dd1220a2119 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:56 +0900 Subject: ALSA: firewire-lib: rename parameter setting function for AM824 with FDF field The value of FDF field in CIP header is protocol-dependent. Thus, it's better to allow data block processing layer to decide the value in any timing. In AM824 data format, the value of FDF field in CIP header indicates N-flag and Nominal Sampling Frequency Code (sfc). The N-flag is for switching 'Clock-based rate control mode' and 'Command-based rate control mode'. In our implementation, 'Clock-based rate control mode' is just supported. Therefore, When sampling transfer frequency is decided, then the FDF can be set. This commit replaces 'amdtp_stream_set_parameters' with 'amdtp_am824_set_parameters' to set the FDF. This is the same timing to decide the ration between the number of data blocks and the number of PCM frames. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 42 +++++++++++++++++++++++++++++ sound/firewire/amdtp-am824.h | 5 ++++ sound/firewire/amdtp-stream.c | 21 ++------------- sound/firewire/amdtp-stream.h | 3 +-- sound/firewire/bebob/bebob_stream.c | 12 ++++----- sound/firewire/dice/dice-stream.c | 4 +-- sound/firewire/fireworks/fireworks_stream.c | 4 +-- sound/firewire/oxfw/oxfw-stream.c | 4 +-- 8 files changed, 62 insertions(+), 33 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index da4b643eff40..f5edcbfa3157 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -10,6 +10,48 @@ #define CIP_FMT_AM 0x10 +/* "Clock-based rate control mode" is just supported. */ +#define AMDTP_FDF_AM824 0x00 + +/** + * amdtp_am824_set_parameters - set stream parameters + * @s: the AMDTP stream to configure + * @rate: the sample rate + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * @double_pcm_frames: one data block transfers two PCM frames + * + * The parameters must be set before the stream is started, and must not be + * changed while the stream is running. + */ +int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames) +{ + int err; + + err = amdtp_stream_set_parameters(s, rate, pcm_channels, midi_ports); + if (err < 0) + return err; + + s->fdf = AMDTP_FDF_AM824 | s->sfc; + + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + s->frame_multiplier = 2; + else + s->frame_multiplier = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index ed96ac56628f..cfc0d61e0db4 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -3,6 +3,11 @@ #include "amdtp-stream.h" +int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames); + int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags); #endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 2254eec4521b..53d84e9bf241 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -54,12 +54,8 @@ #define CIP_SYT_MASK 0x0000ffff #define CIP_SYT_NO_INFO 0xffff -/* - * Audio and Music transfer protocol specific parameters - * only "Clock-based rate control mode" is supported - */ +/* Audio and Music transfer protocol specific parameters */ #define CIP_FMT_AM 0x10 -#define AMDTP_FDF_AM824 0x00 #define AMDTP_FDF_NO_DATA 0xff /* TODO: make these configurable */ @@ -204,8 +200,7 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames) + unsigned int midi_ports) { unsigned int i, sfc, midi_channels; @@ -228,18 +223,6 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; - s->fdf = AMDTP_FDF_AM824 | s->sfc; - - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk at higher - * sampling rate to transfer two PCM frames in one data block. - */ - if (double_pcm_frames) - s->frame_multiplier = 2; - else - s->frame_multiplier = 1; - s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 5f96affe37b7..d915940a14c9 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -181,8 +181,7 @@ void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames); + unsigned int midi_ports); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 34bc3a419993..fa510f30cf8f 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -427,17 +427,17 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) index = get_formation_index(rate); pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; - err = amdtp_stream_set_parameters(&bebob->tx_stream, rate, - pcm_channels, midi_channels * 8, - false); + err = amdtp_am824_set_parameters(&bebob->tx_stream, rate, + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; pcm_channels = bebob->rx_stream_formations[index].pcm; midi_channels = bebob->rx_stream_formations[index].midi; - err = amdtp_stream_set_parameters(&bebob->rx_stream, rate, - pcm_channels, midi_channels * 8, - false); + err = amdtp_am824_set_parameters(&bebob->rx_stream, rate, + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 917860e88b66..89483818785e 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -132,8 +132,8 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, pcm_chs *= 2; } - err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports, - false); + err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, + double_pcm_frames); if (err < 0) goto end; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 8cac5b987cc1..759f6e3ed44a 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -73,8 +73,8 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, midi_ports = efw->midi_in_ports; } - err = amdtp_stream_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports, false); + err = amdtp_am824_set_parameters(stream, sampling_rate, + pcm_channels, midi_ports, false); if (err < 0) goto end; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 83683414793f..4982befc9c25 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -155,8 +155,8 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, err = -EINVAL; goto end; } - err = amdtp_stream_set_parameters(stream, rate, - pcm_channels, midi_ports, false); + err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, + false); if (err < 0) goto end; -- cgit v1.2.3-59-g8ed1b From bc8500da3e62cd6a896407a1b330a2eb21817c22 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:57 +0900 Subject: ALSA: firewire-lib: move PCM substream constraint to AM824 layer In IEC 61883-6, PCM frames are transferred in Multi Bit Linear Audio data channel. The data channel transfers 16/20/24 bit PCM samples. Thus, PCM substream has a constrain about it. This commit moves codes related to the constraint from packet streaming layer to AM824 data block processing layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 20 ++++++++++++++++++++ sound/firewire/amdtp-am824.h | 5 +++++ sound/firewire/amdtp-stream.c | 5 ----- sound/firewire/bebob/bebob_pcm.c | 2 +- sound/firewire/dice/dice-pcm.c | 2 +- sound/firewire/fireworks/fireworks_pcm.c | 2 +- sound/firewire/oxfw/oxfw-pcm.c | 2 +- 7 files changed, 29 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index f5edcbfa3157..7adaba356579 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -52,6 +52,26 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, } EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); +/** + * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream + * @s: the AMDTP stream for AM824 data block, must be initialized. + * @runtime: the PCM substream runtime + * + */ +int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime) +{ + int err; + + err = amdtp_stream_add_pcm_hw_constraints(s, runtime); + if (err < 0) + return err; + + /* AM824 in IEC 61883-6 can deliver 24bit data. */ + return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); +} +EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints); + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index cfc0d61e0db4..4aaed9048d82 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -1,6 +1,8 @@ #ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED #define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED +#include + #include "amdtp-stream.h" int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, @@ -8,6 +10,9 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, bool double_pcm_frames); +int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime); + int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags); #endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 53d84e9bf241..def4fb4b1cce 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -140,11 +140,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, { int err; - /* AM824 in IEC 61883-6 can deliver 24bit data */ - err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - if (err < 0) - goto end; - /* * Currently firewire-lib processes 16 packets in one software * interrupt callback. This equals to 2msec but actually the diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index c0f018a61fdc..50610ca4b480 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(s, runtime); + err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 4e67b1da0fe6..f5f30079311f 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); + err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); end: return err; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index c30b2ffa8dfb..f186ab70f429 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(s, runtime); + err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 9c73930d0278..bddd28743a65 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); + err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); end: return err; } -- cgit v1.2.3-59-g8ed1b From f65be911c0e13fab28919e17c9fa00c38dc4ff2f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:58 +0900 Subject: ALSA: firewire-lib: add helper functions to set positions of data channels In IEC 61883-6, several types of data are available in AM824 format. The data is transferred in each data channel. The position of data channel in data block differs depending on model. Current implementation has an array to map the index of data channel in an data block to the position of actual data channel. The implementation allows each driver to access the mapping directly. In later commit, the mapping is in specific structure pushed into an opaque pointer. Helper functions are required. This commit adds the helper functions for this purpose. In IEC 61883-6, AM824 format supports many data types, while this specification easily causes over-engineering. Current AM824 implementation is allowed to handle two types of data, Multi Bit Linear Audio data (=PCM samples) and MIDI conformant data (=MIDI messages). Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 28 ++++++++++++++++++++++++++++ sound/firewire/amdtp-am824.h | 6 ++++++ sound/firewire/bebob/bebob_stream.c | 5 +++-- sound/firewire/dice/dice-stream.c | 5 +++-- 4 files changed, 40 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 7adaba356579..5d99a9921d50 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -52,6 +52,34 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, } EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); +/** + * amdtp_am824_set_pcm_position - set an index of data channel for a channel + * of PCM frame + * @s: the AMDTP stream + * @index: the index of data channel in an data block + * @position: the channel of PCM frame + */ +void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index, + unsigned int position) +{ + if (index < s->pcm_channels) + s->pcm_positions[index] = position; +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position); + +/** + * amdtp_am824_set_midi_position - set a index of data channel for MIDI + * conformant data channel + * @s: the AMDTP stream + * @position: the index of data channel in an data block + */ +void amdtp_am824_set_midi_position(struct amdtp_stream *s, + unsigned int position) +{ + s->midi_position = position; +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position); + /** * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream * @s: the AMDTP stream for AM824 data block, must be initialized. diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 4aaed9048d82..7c21e9972fad 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -10,6 +10,12 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, bool double_pcm_frames); +void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index, + unsigned int position); + +void amdtp_am824_set_midi_position(struct amdtp_stream *s, + unsigned int position); + int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index fa510f30cf8f..cf6402d371e7 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) err = -ENOSYS; goto end; } - s->midi_position = stm_pos; + amdtp_am824_set_midi_position(s, stm_pos); midi = stm_pos; break; /* for PCM data channel */ @@ -358,7 +358,8 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) err = -ENOSYS; goto end; } - s->pcm_positions[location] = stm_pos; + amdtp_am824_set_pcm_position(s, location, + stm_pos); break; } } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 89483818785e..2108f7f1a764 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -141,8 +141,9 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, pcm_chs /= 2; for (i = 0; i < pcm_chs; i++) { - stream->pcm_positions[i] = i * 2; - stream->pcm_positions[i + pcm_chs] = i * 2 + 1; + amdtp_am824_set_pcm_position(stream, i, i * 2); + amdtp_am824_set_pcm_position(stream, i + pcm_chs, + i * 2 + 1); } } -- cgit v1.2.3-59-g8ed1b From 03e2a67eed7bf2e4c701587080bc8d60dd45209c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:59 +0900 Subject: ALSA: firewire-lib: move MIDI trigger helper function to AM824 layer In IEC 61883-6, MIDI messages are transferred in MIDI conformant data channel. Essentially, packet streaming layer is not responsible for MIDI functionality. This commit moves MIDI trigger helper function from the layer to AM824 layer. The rest of codes related to MIDI functionality will be moved in later commits. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 18 ++++++++++++++++++ sound/firewire/amdtp-am824.h | 4 ++++ sound/firewire/amdtp-stream.h | 18 ------------------ sound/firewire/bebob/bebob_midi.c | 16 ++++++++-------- sound/firewire/dice/dice-midi.c | 12 ++++++------ sound/firewire/fireworks/fireworks_midi.c | 12 ++++++------ sound/firewire/oxfw/oxfw-midi.c | 16 ++++++++-------- 7 files changed, 50 insertions(+), 46 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 5d99a9921d50..540a101661e2 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -100,6 +100,24 @@ int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, } EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints); +/** + * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device + * @s: the AMDTP stream + * @port: index of MIDI port + * @midi: the MIDI device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of MIDI data. This function should be called from the MIDI + * device's .trigger callback. + */ +void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi) +{ + if (port < s->midi_ports) + ACCESS_ONCE(s->midi[port]) = midi; +} +EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger); + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 7c21e9972fad..65e6093a4460 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -2,6 +2,7 @@ #define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED #include +#include #include "amdtp-stream.h" @@ -19,6 +20,9 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s, int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); +void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi); + int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags); #endif diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index d915940a14c9..984f02ac2aed 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -248,24 +248,6 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, ACCESS_ONCE(s->pcm) = pcm; } -/** - * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device - * @s: the AMDTP stream - * @port: index of MIDI port - * @midi: the MIDI device to be started, or %NULL to stop the current device - * - * Call this function on a running isochronous stream to enable the actual - * transmission of MIDI data. This function should be called from the MIDI - * device's .trigger callback. - */ -static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s, - unsigned int port, - struct snd_rawmidi_substream *midi) -{ - if (port < s->midi_ports) - ACCESS_ONCE(s->midi[port]) = midi; -} - static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) { return sfc & 1; diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 5681143925cd..90d95be499b0 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&bebob->lock, flags); if (up) - amdtp_stream_midi_trigger(&bebob->tx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&bebob->tx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&bebob->tx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&bebob->tx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&bebob->lock, flags); } @@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&bebob->lock, flags); if (up) - amdtp_stream_midi_trigger(&bebob->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&bebob->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&bebob->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&bebob->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&bebob->lock, flags); } diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index fe43ce791f84..151b09f240f2 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_stream_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream, substrm->number, substrm); else - amdtp_stream_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream, substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); @@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_stream_midi_trigger(&dice->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&dice->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&dice->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&dice->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); } diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index cf9c65260439..fba01bbba456 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&efw->lock, flags); if (up) - amdtp_stream_midi_trigger(&efw->tx_stream, + amdtp_am824_midi_trigger(&efw->tx_stream, substrm->number, substrm); else - amdtp_stream_midi_trigger(&efw->tx_stream, + amdtp_am824_midi_trigger(&efw->tx_stream, substrm->number, NULL); spin_unlock_irqrestore(&efw->lock, flags); @@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&efw->lock, flags); if (up) - amdtp_stream_midi_trigger(&efw->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&efw->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&efw->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&efw->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&efw->lock, flags); } diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 540a30338516..37a86cf69cbf 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&oxfw->lock, flags); if (up) - amdtp_stream_midi_trigger(&oxfw->tx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&oxfw->tx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&oxfw->tx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&oxfw->tx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&oxfw->lock, flags); } @@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&oxfw->lock, flags); if (up) - amdtp_stream_midi_trigger(&oxfw->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&oxfw->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&oxfw->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&oxfw->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&oxfw->lock, flags); } -- cgit v1.2.3-59-g8ed1b From 85130cb43e78a7bdb2ade10131563d89fbbddf9d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:22:00 +0900 Subject: ALSA: firewire-lib: rename PCM format helper function Setting the format of PCM substream to AMDTP stream structure is important to set a handler to copy actual PCM samples between buffers. The processing should be in data block processing layer because essentially it has no relationship to packet streaming. This commit renames PCM format setting function to prepare for integrating AM824 layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-stream.c | 7 +++---- sound/firewire/amdtp-stream.h | 4 ++-- sound/firewire/bebob/bebob_pcm.c | 8 ++++---- sound/firewire/dice/dice-pcm.c | 6 ++---- sound/firewire/fireworks/fireworks_pcm.c | 6 ++++-- sound/firewire/oxfw/oxfw-pcm.c | 4 ++-- 6 files changed, 17 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index def4fb4b1cce..245002e9b0cb 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -272,7 +272,7 @@ static void read_pcm_s32(struct amdtp_stream *s, __be32 *buffer, unsigned int frames); /** - * amdtp_stream_set_pcm_format - set the PCM format + * amdtp_am824_set_pcm_format - set the PCM format * @s: the AMDTP stream to configure * @format: the format of the ALSA PCM device * @@ -280,8 +280,7 @@ static void read_pcm_s32(struct amdtp_stream *s, * MIDI) and before the stream is started, and must not be changed while the * stream is running. */ -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format) +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) { if (WARN_ON(amdtp_stream_pcm_running(s))) return; @@ -305,7 +304,7 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, break; } } -EXPORT_SYMBOL(amdtp_stream_set_pcm_format); +EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); /** * amdtp_stream_pcm_prepare - prepare PCM device for running diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 984f02ac2aed..3fb82c753f7b 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -190,8 +190,8 @@ void amdtp_stream_stop(struct amdtp_stream *s); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 50610ca4b480..8d8de4c3bfb5 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&bebob->substreams_counter); - amdtp_stream_set_pcm_format(&bebob->tx_stream, - params_format(hw_params)); + + amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); return 0; } @@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&bebob->substreams_counter); - amdtp_stream_set_pcm_format(&bebob->rx_stream, - params_format(hw_params)); + + amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); return 0; } diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index f5f30079311f..8cbcd5d59b45 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_stream_set_pcm_format(&dice->tx_stream, - params_format(hw_params)); + amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params)); return 0; } @@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_stream_set_pcm_format(&dice->rx_stream, - params_format(hw_params)); + amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params)); return 0; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index f186ab70f429..109fb1bc8d9d 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&efw->capture_substreams); - amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params)); + + amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params)); return 0; } @@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&efw->playback_substreams); - amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params)); + + amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params)); return 0; } diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index bddd28743a65..217fe376b766 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); return 0; } @@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); return 0; } -- cgit v1.2.3-59-g8ed1b From 49c7b3fcd9f0a0125e8cd8212d5576382198eeb2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:22:01 +0900 Subject: ALSA: firewire-lib: rename macros with AM824 prefix This commit renames some macros just related to AM824 format. In later commit, they're moved to AM824 layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-stream.c | 4 ++-- sound/firewire/amdtp-stream.h | 14 +++++++------- sound/firewire/bebob/bebob_pcm.c | 4 ++-- sound/firewire/bebob/bebob_stream.c | 6 +++--- sound/firewire/dice/dice-pcm.c | 4 ++-- sound/firewire/fireworks/fireworks.c | 12 ++++++------ sound/firewire/fireworks/fireworks_pcm.c | 4 ++-- sound/firewire/oxfw/oxfw-pcm.c | 4 ++-- sound/firewire/oxfw/oxfw-stream.c | 4 ++-- 9 files changed, 28 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 245002e9b0cb..c61e0ec5c0f5 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -202,8 +202,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, midi_channels = DIV_ROUND_UP(midi_ports, 8); if (WARN_ON(amdtp_stream_running(s)) || - WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) || - WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) + WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) || + WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) return -EINVAL; for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 3fb82c753f7b..71f4f751fabc 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -81,9 +81,9 @@ enum cip_sfc { CIP_SFC_COUNT }; -#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 +#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 -#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ +#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ SNDRV_PCM_FMTBIT_S32) @@ -91,7 +91,7 @@ enum cip_sfc { * This module supports maximum 64 PCM channels for one PCM stream * This is for our convenience. */ -#define AMDTP_MAX_CHANNELS_FOR_PCM 64 +#define AM824_MAX_CHANNELS_FOR_PCM 64 /* * AMDTP packet can include channels for MIDI conformant data. @@ -101,7 +101,7 @@ enum cip_sfc { * This module supports maximum 1 MIDI conformant data channels. * Then this AMDTP packets can transfer maximum 8 MIDI data streams. */ -#define AMDTP_MAX_CHANNELS_FOR_MIDI 1 +#define AM824_MAX_CHANNELS_FOR_MIDI 1 struct fw_unit; struct fw_iso_context; @@ -157,13 +157,13 @@ struct amdtp_stream { struct amdtp_stream *sync_slave; /* For AM824 processing. */ - struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8]; int midi_fifo_limit; - int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8]; unsigned int pcm_channels; unsigned int midi_ports; - u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; u8 midi_position; void (*transfer_samples)(struct amdtp_stream *s, diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 8d8de4c3bfb5..2fdc1f10a2c7 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob, SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { - runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index cf6402d371e7..a2baa478d4ba 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -354,7 +354,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) case 0x09: /* Digital */ default: location = pcm + sec_loc; - if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) { + if (location >= AM824_MAX_CHANNELS_FOR_PCM) { err = -ENOSYS; goto end; } @@ -872,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len, } } - if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM || - formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI) + if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM || + formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI) return -ENOSYS; return 0; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 8cbcd5d59b45..9b3431999fc8 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice, SNDRV_PCM_INFO_BLOCK_TRANSFER; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - hw->formats = AMDTP_IN_PCM_FORMAT_BITS; + hw->formats = AM824_IN_PCM_FORMAT_BITS; stream = &dice->tx_stream; pcm_channels = dice->tx_channels; } else { - hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + hw->formats = AM824_OUT_PCM_FORMAT_BITS; stream = &dice->rx_stream; pcm_channels = dice->rx_channels; } diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index c94a432f7cc6..d5b19bc11e59 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw) efw->midi_out_ports = hwinfo->midi_out_ports; efw->midi_in_ports = hwinfo->midi_in_ports; - if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) { + if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) { err = -ENOSYS; goto end; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 109fb1bc8d9d..d27135bac513 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw, SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &efw->tx_stream; pcm_channels = efw->pcm_capture_channels; } else { - runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &efw->rx_stream; pcm_channels = efw->pcm_playback_channels; } diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 217fe376b766..8d233417695d 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw, SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; stream = &oxfw->tx_stream; formats = oxfw->tx_stream_formats; } else { - runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; stream = &oxfw->rx_stream; formats = oxfw->rx_stream_formats; } diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 4982befc9c25..0c40c00daf12 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -483,8 +483,8 @@ int snd_oxfw_stream_parse_format(u8 *format, } } - if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM || - formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI) + if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM || + formation->midi > AM824_MAX_CHANNELS_FOR_MIDI) return -ENOSYS; return 0; -- cgit v1.2.3-59-g8ed1b From df075feefbd347f13fba5198294cda619532c237 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:22:02 +0900 Subject: ALSA: firewire-lib: complete AM824 data block processing layer This commit moves the codes related to data block processing from packet streaming layer to AM824 layer. Each driver initializes amdtp stream structure for AM824 data block by calling amdtp_am824_init(). Then, a memory block is allocated for AM824 specific structure. This memory block is released by calling amdtp_stream_destroy(). When setting streaming parameters, it calls amdtp_am824_set_parameters(). When starting packet streaming, it calls amdtp_stream_start(). When stopping packet streaming, it calls amdtp_stream_stop(). Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 349 ++++++++++++++++++++++++++++++++++++++++-- sound/firewire/amdtp-am824.h | 24 +++ sound/firewire/amdtp-stream.c | 316 +++----------------------------------- sound/firewire/amdtp-stream.h | 62 ++------ 4 files changed, 401 insertions(+), 350 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 540a101661e2..fe4b83f65831 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -1,11 +1,14 @@ /* * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6) * + * Copyright (c) Clemens Ladisch * Copyright (c) 2015 Takashi Sakamoto * * Licensed under the terms of the GNU General Public License, version 2. */ +#include + #include "amdtp-am824.h" #define CIP_FMT_AM 0x10 @@ -13,6 +16,35 @@ /* "Clock-based rate control mode" is just supported. */ #define AMDTP_FDF_AM824 0x00 +/* + * Nominally 3125 bytes/second, but the MIDI port's clock might be + * 1% too slow, and the bus clock 100 ppm too fast. + */ +#define MIDI_BYTES_PER_SECOND 3093 + +/* + * Several devices look only at the first eight data blocks. + * In any case, this is more than enough for the MIDI data rate. + */ +#define MAX_MIDI_RX_BLOCKS 8 + +struct amdtp_am824 { + struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_limit; + int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned int pcm_channels; + unsigned int midi_ports; + + u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; + + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + + unsigned int frame_multiplier; +}; + /** * amdtp_am824_set_parameters - set stream parameters * @s: the AMDTP stream to configure @@ -30,23 +62,58 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, bool double_pcm_frames) { + struct amdtp_am824 *p = s->protocol; + unsigned int midi_channels; + unsigned int i; int err; - err = amdtp_stream_set_parameters(s, rate, pcm_channels, midi_ports); + if (amdtp_stream_running(s)) + return -EINVAL; + + if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) + return -EINVAL; + + midi_channels = DIV_ROUND_UP(midi_ports, 8); + if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI) + return -EINVAL; + + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) || + WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) + return -EINVAL; + + err = amdtp_stream_set_parameters(s, rate, + pcm_channels + midi_channels); if (err < 0) return err; s->fdf = AMDTP_FDF_AM824 | s->sfc; + p->pcm_channels = pcm_channels; + p->midi_ports = midi_ports; + /* * In IEC 61883-6, one data block represents one event. In ALSA, one * event equals to one PCM frame. But Dice has a quirk at higher * sampling rate to transfer two PCM frames in one data block. */ if (double_pcm_frames) - s->frame_multiplier = 2; + p->frame_multiplier = 2; else - s->frame_multiplier = 1; + p->frame_multiplier = 1; + + /* init the position map for PCM and MIDI channels */ + for (i = 0; i < pcm_channels; i++) + p->pcm_positions[i] = i; + p->midi_position = p->pcm_channels; + + /* + * We do not know the actual MIDI FIFO size of most devices. Just + * assume two bytes, i.e., one byte can be received over the bus while + * the previous one is transmitted over MIDI. + * (The value here is adjusted for midi_ratelimit_per_packet().) + */ + p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; return 0; } @@ -62,8 +129,10 @@ EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index, unsigned int position) { - if (index < s->pcm_channels) - s->pcm_positions[index] = position; + struct amdtp_am824 *p = s->protocol; + + if (index < p->pcm_channels) + p->pcm_positions[index] = position; } EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position); @@ -76,10 +145,139 @@ EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position); void amdtp_am824_set_midi_position(struct amdtp_stream *s, unsigned int position) { - s->midi_position = position; + struct amdtp_am824 *p = s->protocol; + + p->midi_position = position; } EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position); +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u32 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[p->pcm_positions[c]] = + cpu_to_be32((*src >> 8) | 0x40000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u16 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[p->pcm_positions[c]] = + cpu_to_be32((*src << 8) | 0x42000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + channels = p->pcm_channels; + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void write_pcm_silence(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + unsigned int i, c, channels = p->pcm_channels; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) + buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +/** + * amdtp_am824_set_pcm_format - set the PCM format + * @s: the AMDTP stream to configure + * @format: the format of the ALSA PCM device + * + * The sample format must be set after the other parameters (rate/PCM channels/ + * MIDI) and before the stream is started, and must not be changed while the + * stream is running. + */ +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) +{ + struct amdtp_am824 *p = s->protocol; + + if (WARN_ON(amdtp_stream_pcm_running(s))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + if (s->direction == AMDTP_OUT_STREAM) { + p->transfer_samples = write_pcm_s16; + break; + } + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S32: + if (s->direction == AMDTP_OUT_STREAM) + p->transfer_samples = write_pcm_s32; + else + p->transfer_samples = read_pcm_s32; + break; + } +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); + /** * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream * @s: the AMDTP stream for AM824 data block, must be initialized. @@ -113,11 +311,135 @@ EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints); void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, struct snd_rawmidi_substream *midi) { - if (port < s->midi_ports) - ACCESS_ONCE(s->midi[port]) = midi; + struct amdtp_am824 *p = s->protocol; + + if (port < p->midi_ports) + ACCESS_ONCE(p->midi[port]) = midi; } EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger); +/* + * To avoid sending MIDI bytes at too high a rate, assume that the receiving + * device has a FIFO, and track how much it is filled. This values increases + * by one whenever we send one byte in a packet, but the FIFO empties at + * a constant rate independent of our packet rate. One packet has syt_interval + * samples, so the number of bytes that empty out of the FIFO, per packet(!), + * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing + * fractional values, the values in midi_fifo_used[] are measured in bytes + * multiplied by the sample rate. + */ +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_am824 *p = s->protocol; + int used; + + used = p->midi_fifo_used[port]; + if (used == 0) /* common shortcut */ + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + p->midi_fifo_used[port] = used; + + return used < p->midi_fifo_limit; +} + +static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_am824 *p = s->protocol; + + p->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; +} + +static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + unsigned int f, port; + u8 *b; + + for (f = 0; f < frames; f++) { + b = (u8 *)&buffer[p->midi_position]; + + port = (s->data_block_counter + f) % 8; + if (f < MAX_MIDI_RX_BLOCKS && + midi_ratelimit_per_packet(s, port) && + p->midi[port] != NULL && + snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) { + midi_rate_use_one_byte(s, port); + b[0] = 0x81; + } else { + b[0] = 0x80; + b[1] = 0; + } + b[2] = 0; + b[3] = 0; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < frames; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[p->midi_position]; + + len = b[0] - 0x80; + if ((1 <= len) && (len <= 3) && (p->midi[port])) + snd_rawmidi_receive(p->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } +} + +unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + p->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * p->frame_multiplier; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + if (p->midi_ports) + write_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + +unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + p->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * p->frame_multiplier; + } else { + pcm_frames = 0; + } + + if (p->midi_ports) + read_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block @@ -129,6 +451,15 @@ EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger); int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags) { - return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM); + amdtp_stream_process_data_blocks_t process_data_blocks; + + if (dir == AMDTP_IN_STREAM) + process_data_blocks = process_tx_data_blocks; + else + process_data_blocks = process_rx_data_blocks; + + return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM, + process_data_blocks, + sizeof(struct amdtp_am824)); } EXPORT_SYMBOL_GPL(amdtp_am824_init); diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 65e6093a4460..73b07b3109db 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -6,6 +6,27 @@ #include "amdtp-stream.h" +#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 + +#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ + SNDRV_PCM_FMTBIT_S32) + +/* + * This module supports maximum 64 PCM channels for one PCM stream + * This is for our convenience. + */ +#define AM824_MAX_CHANNELS_FOR_PCM 64 + +/* + * AMDTP packet can include channels for MIDI conformant data. + * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. + * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. + * + * This module supports maximum 1 MIDI conformant data channels. + * Then this AMDTP packets can transfer maximum 8 MIDI data streams. + */ +#define AM824_MAX_CHANNELS_FOR_MIDI 1 + int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, unsigned int midi_ports, @@ -20,6 +41,9 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s, int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); + void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, struct snd_rawmidi_substream *midi); diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index c61e0ec5c0f5..fa10b58a0957 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -13,25 +13,12 @@ #include #include #include -#include #include "amdtp-stream.h" #define TICKS_PER_CYCLE 3072 #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) -/* - * Nominally 3125 bytes/second, but the MIDI port's clock might be - * 1% too slow, and the bus clock 100 ppm too fast. - */ -#define MIDI_BYTES_PER_SECOND 3093 - -/* - * Several devices look only at the first eight data blocks. - * In any case, this is more than enough for the MIDI data rate. - */ -#define MAX_MIDI_RX_BLOCKS 8 - #define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ /* isochronous header parameters */ @@ -74,11 +61,22 @@ static void pcm_period_tasklet(unsigned long data); * @dir: the direction of stream * @flags: the packet transmission method to use * @fmt: the value of fmt field in CIP header + * @process_data_blocks: callback handler to process data blocks + * @protocol_size: the size to allocate newly for protocol */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags, - unsigned int fmt) + unsigned int fmt, + amdtp_stream_process_data_blocks_t process_data_blocks, + unsigned int protocol_size) { + if (process_data_blocks == NULL) + return -EINVAL; + + s->protocol = kzalloc(protocol_size, GFP_KERNEL); + if (!s->protocol) + return -ENOMEM; + s->unit = unit; s->direction = dir; s->flags = flags; @@ -92,6 +90,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->sync_slave = NULL; s->fmt = fmt; + s->process_data_blocks = process_data_blocks; return 0; } @@ -104,6 +103,7 @@ EXPORT_SYMBOL(amdtp_stream_init); void amdtp_stream_destroy(struct amdtp_stream *s) { WARN_ON(amdtp_stream_running(s)); + kfree(s->protocol); mutex_destroy(&s->mutex); } EXPORT_SYMBOL(amdtp_stream_destroy); @@ -184,27 +184,15 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * amdtp_stream_set_parameters - set stream parameters * @s: the AMDTP stream to configure * @rate: the sample rate - * @pcm_channels: the number of PCM samples in each data block, to be encoded - * as AM824 multi-bit linear audio - * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) - * @double_pcm_frames: one data block transfers two PCM frames + * @data_block_quadlets: the size of a data block in quadlet unit * * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports) +int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int data_block_quadlets) { - unsigned int i, sfc, midi_channels; - - midi_channels = DIV_ROUND_UP(midi_ports, 8); - - if (WARN_ON(amdtp_stream_running(s)) || - WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) || - WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) - return -EINVAL; + unsigned int sfc; for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { if (amdtp_rate_table[sfc] == rate) @@ -213,11 +201,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, if (sfc == ARRAY_SIZE(amdtp_rate_table)) return -EINVAL; - s->pcm_channels = pcm_channels; s->sfc = sfc; - s->data_block_quadlets = s->pcm_channels + midi_channels; - s->midi_ports = midi_ports; - + s->data_block_quadlets = data_block_quadlets; s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ @@ -226,19 +211,6 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; - /* init the position map for PCM and MIDI channels */ - for (i = 0; i < pcm_channels; i++) - s->pcm_positions[i] = i; - s->midi_position = s->pcm_channels; - - /* - * We do not know the actual MIDI FIFO size of most devices. Just - * assume two bytes, i.e., one byte can be received over the bus while - * the previous one is transmitted over MIDI. - * (The value here is adjusted for midi_ratelimit_per_packet().) - */ - s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; - return 0; } EXPORT_SYMBOL(amdtp_stream_set_parameters); @@ -261,51 +233,6 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_get_max_payload); -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - -/** - * amdtp_am824_set_pcm_format - set the PCM format - * @s: the AMDTP stream to configure - * @format: the format of the ALSA PCM device - * - * The sample format must be set after the other parameters (rate/PCM channels/ - * MIDI) and before the stream is started, and must not be changed while the - * stream is running. - */ -void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) -{ - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - s->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - s->transfer_samples = write_pcm_s32; - else - s->transfer_samples = read_pcm_s32; - break; - } -} -EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); - /** * amdtp_stream_pcm_prepare - prepare PCM device for running * @s: the AMDTP stream @@ -408,168 +335,6 @@ static unsigned int calculate_syt(struct amdtp_stream *s, } } -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u32 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src >> 8) | 0x40000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src << 8) | 0x42000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - u32 *dst; - - channels = s->pcm_channels; - dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; - dst++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - dst = (void *)runtime->dma_area; - } -} - -static void write_pcm_silence(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int i, c; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < s->pcm_channels; ++c) - buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); - buffer += s->data_block_quadlets; - } -} - -/* - * To avoid sending MIDI bytes at too high a rate, assume that the receiving - * device has a FIFO, and track how much it is filled. This values increases - * by one whenever we send one byte in a packet, but the FIFO empties at - * a constant rate independent of our packet rate. One packet has syt_interval - * samples, so the number of bytes that empty out of the FIFO, per packet(!), - * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing - * fractional values, the values in midi_fifo_used[] are measured in bytes - * multiplied by the sample rate. - */ -static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) -{ - int used; - - used = s->midi_fifo_used[port]; - if (used == 0) /* common shortcut */ - return true; - - used -= MIDI_BYTES_PER_SECOND * s->syt_interval; - used = max(used, 0); - s->midi_fifo_used[port] = used; - - return used < s->midi_fifo_limit; -} - -static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) -{ - s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; -} - -static void write_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int f, port; - u8 *b; - - for (f = 0; f < frames; f++) { - b = (u8 *)&buffer[s->midi_position]; - - port = (s->data_block_counter + f) % 8; - if (f < MAX_MIDI_RX_BLOCKS && - midi_ratelimit_per_packet(s, port) && - s->midi[port] != NULL && - snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { - midi_rate_use_one_byte(s, port); - b[0] = 0x81; - } else { - b[0] = 0x80; - b[1] = 0; - } - b[2] = 0; - b[3] = 0; - - buffer += s->data_block_quadlets; - } -} - -static void read_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int f, port; - int len; - u8 *b; - - for (f = 0; f < frames; f++) { - port = (s->data_block_counter + f) % 8; - b = (u8 *)&buffer[s->midi_position]; - - len = b[0] - 0x80; - if ((1 <= len) && (len <= 3) && (s->midi[port])) - snd_rawmidi_receive(s->midi[port], b + 1, len); - - buffer += s->data_block_quadlets; - } -} - static void update_pcm_pointers(struct amdtp_stream *s, struct snd_pcm_substream *pcm, unsigned int frames) @@ -639,26 +404,6 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } -unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - write_pcm_silence(s, buffer, data_blocks); - pcm_frames = 0; - } - - if (s->midi_ports) - write_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, unsigned int syt) { @@ -668,7 +413,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, struct snd_pcm_substream *pcm; buffer = s->buffer.packets[s->packet_index].buffer; - pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); + pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | @@ -692,25 +437,6 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, return 0; } -unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - pcm_frames = 0; - } - - if (s->midi_ports) - read_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - static int handle_in_packet(struct amdtp_stream *s, unsigned int payload_quadlets, __be32 *buffer, unsigned int *data_blocks, unsigned int syt) @@ -798,7 +524,7 @@ static int handle_in_packet(struct amdtp_stream *s, return -EIO; } - pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); + pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt); if (s->flags & CIP_DBC_IS_END_EVENT) s->data_block_counter = data_block_counter; diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 71f4f751fabc..8775704a3665 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -81,39 +81,22 @@ enum cip_sfc { CIP_SFC_COUNT }; -#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 - -#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ - SNDRV_PCM_FMTBIT_S32) - - -/* - * This module supports maximum 64 PCM channels for one PCM stream - * This is for our convenience. - */ -#define AM824_MAX_CHANNELS_FOR_PCM 64 - -/* - * AMDTP packet can include channels for MIDI conformant data. - * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. - * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. - * - * This module supports maximum 1 MIDI conformant data channels. - * Then this AMDTP packets can transfer maximum 8 MIDI data streams. - */ -#define AM824_MAX_CHANNELS_FOR_MIDI 1 - struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; struct snd_pcm_runtime; -struct snd_rawmidi_substream; enum amdtp_stream_direction { AMDTP_OUT_STREAM = 0, AMDTP_IN_STREAM }; +struct amdtp_stream; +typedef unsigned int (*amdtp_stream_process_data_blocks_t)( + struct amdtp_stream *s, + __be32 *buffer, + unsigned int data_blocks, + unsigned int *syt); struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; @@ -156,32 +139,20 @@ struct amdtp_stream { wait_queue_head_t callback_wait; struct amdtp_stream *sync_slave; - /* For AM824 processing. */ - struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8]; - int midi_fifo_limit; - int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8]; - unsigned int pcm_channels; - unsigned int midi_ports; - - u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; - u8 midi_position; - - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - - unsigned int frame_multiplier; + /* For backends to process data blocks. */ + void *protocol; + amdtp_stream_process_data_blocks_t process_data_blocks; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, - enum cip_flags flags, unsigned int fmt); + enum amdtp_stream_direction dir, enum cip_flags flags, + unsigned int fmt, + amdtp_stream_process_data_blocks_t process_data_blocks, + unsigned int protocol_size); void amdtp_stream_destroy(struct amdtp_stream *s); -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); +int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int data_block_quadlets); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); @@ -190,8 +161,7 @@ void amdtp_stream_stop(struct amdtp_stream *s); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_am824_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); + void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); -- cgit v1.2.3-59-g8ed1b From 094435d41df823dde850e193add57534a5b709c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Sep 2015 12:57:42 +0200 Subject: ALSA: pcm: Avoid double hw_free calls at releasing a stream snd_pcm_release_substream() always calls hw_free op when the stream was opened. This is superfluous in most cases because it's been already released via explicit hw_free ioctl. Although this double call is usually OK as this callback should be written to be called multiple times, it's better to avoid superfluous calls. Reported-by: Vinod Koul Tested-by: Jeeja Kp Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4863af5e7b26..139887011ba2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2227,7 +2227,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) snd_pcm_drop(substream); if (substream->hw_opened) { - if (substream->ops->hw_free != NULL) + if (substream->ops->hw_free && + substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) substream->ops->hw_free(substream); substream->ops->close(substream); substream->hw_opened = 0; -- cgit v1.2.3-59-g8ed1b From 02e6ef9fba3ed85e31b13cbd6bb2185412b44c9e Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 29 Sep 2015 22:46:10 +0800 Subject: ALSA: firewire-lib: process_rx_data_blocks() can be static Signed-off-by: Fengguang Wu Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index fe4b83f65831..bebddc60fde8 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -399,8 +399,8 @@ static void read_midi_messages(struct amdtp_stream *s, } } -unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) +static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) { struct amdtp_am824 *p = s->protocol; struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); @@ -420,8 +420,8 @@ unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, return pcm_frames; } -unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) +static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) { struct amdtp_am824 *p = s->protocol; struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); -- cgit v1.2.3-59-g8ed1b From 9edf723fd85822c7b7d8ef4f41a74c5a33eeca0c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:16 +0900 Subject: ALSA: firewire-digi00x: add skeleton for Digi 002/003 family This commit adds a new driver for Digidesign 002/003 family. This commit just creates/removes card instance according to bus event. More functions will be added in following commits. Digidesign 002/003 family consists of: * Agere FW802B for IEEE 1394 PHY layer * PDI 1394L40 for IEEE 1394 LINK layer and IEC 61883 interface * ALTERA ACEX EP1K50 for IEC 61883 layer and DSP controller * ADSP-21065L for signal processing [minor cleanup using skip_spaces() by tiwai] Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Kconfig | 14 ++++ sound/firewire/Makefile | 1 + sound/firewire/digi00x/Makefile | 2 + sound/firewire/digi00x/digi00x.c | 138 +++++++++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x.h | 32 +++++++++ 5 files changed, 187 insertions(+) create mode 100644 sound/firewire/digi00x/Makefile create mode 100644 sound/firewire/digi00x/digi00x.c create mode 100644 sound/firewire/digi00x/digi00x.h (limited to 'sound') diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 8850b7de1d38..74f17a8263db 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -120,4 +120,18 @@ config SND_BEBOB To compile this driver as a module, choose M here: the module will be called snd-bebob. +config SND_FIREWIRE_DIGI00X + tristate "Digidesign Digi 002/003 family support" + select SND_FIREWIRE_LIB + help + Say Y here to include support for Digidesign Digi 002/003 family. + * Digi 002 Console + * Digi 002 Rack + * Digi 003 Console + * Digi 003 Rack + * Digi 003 Rack+ + + To compile this driver as a module, choose M here: the module + will be called snd-firewire-digi00x. + endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 6a8a71371efd..5325d15d7018 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_FIREWORKS) += fireworks/ obj-$(CONFIG_SND_BEBOB) += bebob/ +obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/ diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile new file mode 100644 index 000000000000..13fec5bd4a81 --- /dev/null +++ b/sound/firewire/digi00x/Makefile @@ -0,0 +1,2 @@ +snd-firewire-digi00x-objs := digi00x.o +obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c new file mode 100644 index 000000000000..260967e54519 --- /dev/null +++ b/sound/firewire/digi00x/digi00x.c @@ -0,0 +1,138 @@ +/* + * digi00x.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "digi00x.h" + +MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver"); +MODULE_AUTHOR("Takashi Sakamoto "); +MODULE_LICENSE("GPL v2"); + +#define VENDOR_DIGIDESIGN 0x00a07e +#define MODEL_DIGI00X 0x000002 + +static int name_card(struct snd_dg00x *dg00x) +{ + struct fw_device *fw_dev = fw_parent_device(dg00x->unit); + char name[32] = {0}; + char *model; + int err; + + err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name, + sizeof(name)); + if (err < 0) + return err; + + model = skip_spaces(name); + + strcpy(dg00x->card->driver, "Digi00x"); + strcpy(dg00x->card->shortname, model); + strcpy(dg00x->card->mixername, model); + snprintf(dg00x->card->longname, sizeof(dg00x->card->longname), + "Digidesign %s, GUID %08x%08x at %s, S%d", model, + cpu_to_be32(fw_dev->config_rom[3]), + cpu_to_be32(fw_dev->config_rom[4]), + dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed); + + return 0; +} + +static void dg00x_card_free(struct snd_card *card) +{ + struct snd_dg00x *dg00x = card->private_data; + + fw_unit_put(dg00x->unit); + + mutex_destroy(&dg00x->mutex); +} + +static int snd_dg00x_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_card *card; + struct snd_dg00x *dg00x; + int err; + + /* create card */ + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(struct snd_dg00x), &card); + if (err < 0) + return err; + card->private_free = dg00x_card_free; + + /* initialize myself */ + dg00x = card->private_data; + dg00x->card = card; + dg00x->unit = fw_unit_get(unit); + + mutex_init(&dg00x->mutex); + + err = name_card(dg00x); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, dg00x); + + return err; +error: + snd_card_free(card); + return err; +} + +static void snd_dg00x_update(struct fw_unit *unit) +{ + return; +} + +static void snd_dg00x_remove(struct fw_unit *unit) +{ + struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); + + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(dg00x->card); +} + +static const struct ieee1394_device_id snd_dg00x_id_table[] = { + /* Both of 002/003 use the same ID. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = VENDOR_DIGIDESIGN, + .model_id = MODEL_DIGI00X, + }, + {} +}; +MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table); + +static struct fw_driver dg00x_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "snd-firewire-digi00x", + .bus = &fw_bus_type, + }, + .probe = snd_dg00x_probe, + .update = snd_dg00x_update, + .remove = snd_dg00x_remove, + .id_table = snd_dg00x_id_table, +}; + +static int __init snd_dg00x_init(void) +{ + return driver_register(&dg00x_driver.driver); +} + +static void __exit snd_dg00x_exit(void) +{ + driver_unregister(&dg00x_driver.driver); +} + +module_init(snd_dg00x_init); +module_exit(snd_dg00x_exit); diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h new file mode 100644 index 000000000000..0bb2ca3ae471 --- /dev/null +++ b/sound/firewire/digi00x/digi00x.h @@ -0,0 +1,32 @@ +/* + * digi00x.h - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef SOUND_DIGI00X_H_INCLUDED +#define SOUND_DIGI00X_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../lib.h" + +struct snd_dg00x { + struct snd_card *card; + struct fw_unit *unit; + + struct mutex mutex; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 163ae6f3f3f059ab44311792af5a73f70f747263 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:17 +0900 Subject: ALSA: firewire-digi00x: add data block processing layer Digi 002/003 family uses its own format for data blocks. The format is quite similar to AM824 in IEC 61883-6, while there're some differences: * The Valid Bit Length (VBL) code is always 0x40 in Multi-bit Linear Audio (MBLA) data channel. * The first data channel includes MIDI messages, against IEC 61883-6 recommendation. * The Counter field is always zero in MIDI conformant data channel. * Sequence multiplexing in IEC 61883-6 is not applied to the MIDI conformant data channel. * PCM samples are scrambled in received AMDTP packets. We call the way as Double-Oh-Three (DOT). The algorithm was discovered by Robin Gareus and Damien Zammit in 2012. This commit adds data processing layer to satisfy these differences. There's a quirk about transmission mode for received packets. When this driver applies non-blocking mode to outgoing packets with isochronous channel 2 or more, after 15 to 20 seconds since playbacking, any PCM samples causes noisy sound on the device. With isochronous channel 0 or 1, this doesn't occur. As long as I investigated, this quirk is not observed when applying blocking mode to the received packets. This driver applies blocking mode to outgoing packets, while non-blocking mode to incoming packgets. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/amdtp-dot.c | 330 +++++++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x.h | 14 ++ 3 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/digi00x/amdtp-dot.c (limited to 'sound') diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 13fec5bd4a81..87c4cfd2e8b0 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,2 +1,2 @@ -snd-firewire-digi00x-objs := digi00x.o +snd-firewire-digi00x-objs := amdtp-dot.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c new file mode 100644 index 000000000000..e6731d33c480 --- /dev/null +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -0,0 +1,330 @@ +/* + * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * Copyright (C) 2012 Robin Gareus + * Copyright (C) 2012 Damien Zammit + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include "digi00x.h" + +#define CIP_FMT_AM 0x10 + +/* 'Clock-based rate control mode' is just supported. */ +#define AMDTP_FDF_AM824 0x00 + +/* + * The double-oh-three algorithm was discovered by Robin Gareus and Damien + * Zammit in 2012, with reverse-engineering for Digi 003 Rack. + */ +struct dot_state { + __u8 carry; + __u8 idx; + unsigned int off; +}; + +struct amdtp_dot { + unsigned int pcm_channels; + struct dot_state state; + + unsigned int midi_ports; + + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +}; + +/* + * double-oh-three look up table + * + * @param idx index byte (audio-sample data) 0x00..0xff + * @param off channel offset shift + * @return salt to XOR with given data + */ +#define BYTE_PER_SAMPLE (4) +#define MAGIC_DOT_BYTE (2) +#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE) +static const __u8 dot_scrt(const __u8 idx, const unsigned int off) +{ + /* + * the length of the added pattern only depends on the lower nibble + * of the last non-zero data + */ + static const __u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14, + 12, 10, 8, 6, 4, 2, 0}; + + /* + * the lower nibble of the salt. Interleaved sequence. + * this is walked backwards according to len[] + */ + static const __u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4, + 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf}; + + /* circular list for the salt's hi nibble. */ + static const __u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4, + 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa}; + + /* + * start offset for upper nibble mapping. + * note: 9 is /special/. In the case where the high nibble == 0x9, + * hir[] is not used and - coincidentally - the salt's hi nibble is + * 0x09 regardless of the offset. + */ + static const __u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4, + 3, 0x00, 14, 13, 8, 9, 10, 2}; + + const __u8 ln = idx & 0xf; + const __u8 hn = (idx >> 4) & 0xf; + const __u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15]; + + if (len[ln] < off) + return 0x00; + + return ((nib[14 + off - len[ln]]) | (hr << 4)); +} + +static void dot_encode_step(struct dot_state *state, __be32 *const buffer) +{ + __u8 * const data = (__u8 *) buffer; + + if (data[MAGIC_DOT_BYTE] != 0x00) { + state->off = 0; + state->idx = data[MAGIC_DOT_BYTE] ^ state->carry; + } + data[MAGIC_DOT_BYTE] ^= state->carry; + state->carry = dot_scrt(state->idx, ++(state->off)); +} + +int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int pcm_channels, unsigned int midi_ports) +{ + struct amdtp_dot *p = s->protocol; + int err; + + if (amdtp_stream_running(s)) + return -EBUSY; + + /* + * A first data channel is for MIDI conformant data channel, the rest is + * Multi Bit Linear Audio data channel. + */ + err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1); + if (err < 0) + return err; + + s->fdf = AMDTP_FDF_AM824 | s->sfc; + + p->pcm_channels = pcm_channels; + p->midi_ports = midi_ports; + + return 0; +} + +static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_dot *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u32 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + buffer++; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000); + dot_encode_step(&p->state, &buffer[c]); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_dot *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u16 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + buffer++; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[c] = cpu_to_be32((*src << 8) | 0x40000000); + dot_encode_step(&p->state, &buffer[c]); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_dot *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + channels = p->pcm_channels; + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + buffer++; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int channels, i, c; + + channels = p->pcm_channels; + + buffer++; + for (i = 0; i < data_blocks; ++i) { + for (c = 0; c < channels; ++c) + buffer[c] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime) +{ + int err; + + /* This protocol delivers 24 bit data in 32bit data channel. */ + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + + return amdtp_stream_add_pcm_hw_constraints(s, runtime); +} + +void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) +{ + struct amdtp_dot *p = s->protocol; + + if (WARN_ON(amdtp_stream_pcm_running(s))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + if (s->direction == AMDTP_OUT_STREAM) { + p->transfer_samples = write_pcm_s16; + break; + } + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S32: + if (s->direction == AMDTP_OUT_STREAM) + p->transfer_samples = write_pcm_s32; + else + p->transfer_samples = read_pcm_s32; + break; + } +} + +static unsigned int process_tx_data_blocks(struct amdtp_stream *s, + __be32 *buffer, + unsigned int data_blocks, + unsigned int *syt) +{ + struct amdtp_dot *p = (struct amdtp_dot *)s->protocol; + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) { + p->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks; + } else { + pcm_frames = 0; + } + + /* A place holder for MIDI processing. */ + + return pcm_frames; +} + +static unsigned int process_rx_data_blocks(struct amdtp_stream *s, + __be32 *buffer, + unsigned int data_blocks, + unsigned int *syt) +{ + struct amdtp_dot *p = (struct amdtp_dot *)s->protocol; + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) { + p->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + /* A place holder for MIDI processing. */ + + return pcm_frames; +} + +int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir) +{ + amdtp_stream_process_data_blocks_t process_data_blocks; + enum cip_flags flags; + + /* Use different mode between incoming/outgoing. */ + if (dir == AMDTP_IN_STREAM) { + flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK; + process_data_blocks = process_tx_data_blocks; + } else { + flags = CIP_BLOCKING; + process_data_blocks = process_rx_data_blocks; + } + + return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM, + process_data_blocks, sizeof(struct amdtp_dot)); +} + +void amdtp_dot_reset(struct amdtp_stream *s) +{ + struct amdtp_dot *p = s->protocol; + + p->state.carry = 0x00; + p->state.idx = 0x00; + p->state.off = 0; +} diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 0bb2ca3ae471..fe21d7778004 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -19,8 +19,12 @@ #include #include +#include +#include #include "../lib.h" +#include "../iso-resources.h" +#include "../amdtp-stream.h" struct snd_dg00x { struct snd_card *card; @@ -29,4 +33,14 @@ struct snd_dg00x { struct mutex mutex; }; +int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir); +int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); +void amdtp_dot_reset(struct amdtp_stream *s); +int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime); +void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); + #endif -- cgit v1.2.3-59-g8ed1b From 3a2a17974eef10766ffbd7d3e9f5191fbb3c9f33 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:18 +0900 Subject: ALSA: firewire-digi00x: add stream functionality This commit adds a functionality to manage streaming. The streaming is not controlled by CMP in IEC 61883-6. It's controlled by IEEE 1394 write transaction to certain addresses. Several clock sources are available, while there're no differences about packet transmission. The value of SYT field in transmitted packets is always zero. Thus, streams in both direction don't build synchronization. And the device always requires received packets to transmit packets. This driver keeps to transfer outgoing stream even if they're not required. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/digi00x-stream.c | 381 ++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x.c | 12 +- sound/firewire/digi00x/digi00x.h | 68 ++++++ 4 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 sound/firewire/digi00x/digi00x-stream.c (limited to 'sound') diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 87c4cfd2e8b0..32d8ebe7ce6b 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,2 +1,2 @@ -snd-firewire-digi00x-objs := amdtp-dot.o digi00x.o +snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c new file mode 100644 index 000000000000..3278c3e08560 --- /dev/null +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -0,0 +1,381 @@ +/* + * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "digi00x.h" + +#define CALLBACK_TIMEOUT 500 + +const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = { + [SND_DG00X_RATE_44100] = 44100, + [SND_DG00X_RATE_48000] = 48000, + [SND_DG00X_RATE_88200] = 88200, + [SND_DG00X_RATE_96000] = 96000, +}; + +/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */ +const unsigned int +snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = { + /* Analog/ADAT/SPDIF */ + [SND_DG00X_RATE_44100] = (8 + 8 + 2), + [SND_DG00X_RATE_48000] = (8 + 8 + 2), + /* Analog/SPDIF */ + [SND_DG00X_RATE_88200] = (8 + 2), + [SND_DG00X_RATE_96000] = (8 + 2), +}; + +int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate) +{ + u32 data; + __be32 reg; + int err; + + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + data = be32_to_cpu(reg) & 0x0f; + if (data < ARRAY_SIZE(snd_dg00x_stream_rates)) + *rate = snd_dg00x_stream_rates[data]; + else + err = -EIO; + + return err; +} + +int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate) +{ + __be32 reg; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) { + if (rate == snd_dg00x_stream_rates[i]) + break; + } + if (i == ARRAY_SIZE(snd_dg00x_stream_rates)) + return -EINVAL; + + reg = cpu_to_be32(i); + return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, + ®, sizeof(reg), 0); +} + +int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x, + enum snd_dg00x_clock *clock) +{ + __be32 reg; + int err; + + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + *clock = be32_to_cpu(reg) & 0x0f; + if (*clock >= SND_DG00X_CLOCK_COUNT) + err = -EIO; + + return err; +} + +int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect) +{ + __be32 reg; + int err; + + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL, + ®, sizeof(reg), 0); + if (err >= 0) + *detect = be32_to_cpu(reg) > 0; + + return err; +} + +int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x, + unsigned int *rate) +{ + u32 data; + __be32 reg; + int err; + + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + data = be32_to_cpu(reg) & 0x0f; + if (data < ARRAY_SIZE(snd_dg00x_stream_rates)) + *rate = snd_dg00x_stream_rates[data]; + /* This means desync. */ + else + err = -EBUSY; + + return err; +} + +static void finish_session(struct snd_dg00x *dg00x) +{ + __be32 data = cpu_to_be32(0x00000003); + + snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, + &data, sizeof(data), 0); +} + +static int begin_session(struct snd_dg00x *dg00x) +{ + __be32 data; + u32 curr; + int err; + + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE, + &data, sizeof(data), 0); + if (err < 0) + goto error; + curr = be32_to_cpu(data); + + if (curr == 0) + curr = 2; + + curr--; + while (curr > 0) { + data = cpu_to_be32(curr); + err = snd_fw_transaction(dg00x->unit, + TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + + DG00X_OFFSET_STREAMING_SET, + &data, sizeof(data), 0); + if (err < 0) + goto error; + + msleep(20); + curr--; + } + + return 0; +error: + finish_session(dg00x); + return err; +} + +static void release_resources(struct snd_dg00x *dg00x) +{ + __be32 data = 0; + + /* Unregister isochronous channels for both direction. */ + snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, + &data, sizeof(data), 0); + + /* Release isochronous resources. */ + fw_iso_resources_free(&dg00x->tx_resources); + fw_iso_resources_free(&dg00x->rx_resources); +} + +static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) +{ + unsigned int i; + __be32 data; + int err; + + /* Check sampling rate. */ + for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { + if (snd_dg00x_stream_rates[i] == rate) + break; + } + if (i == SND_DG00X_RATE_COUNT) + return -EINVAL; + + /* Keep resources for out-stream. */ + err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate, + snd_dg00x_stream_pcm_channels[i], 0); + if (err < 0) + return err; + err = fw_iso_resources_allocate(&dg00x->rx_resources, + amdtp_stream_get_max_payload(&dg00x->rx_stream), + fw_parent_device(dg00x->unit)->max_speed); + if (err < 0) + return err; + + /* Keep resources for in-stream. */ + err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate, + snd_dg00x_stream_pcm_channels[i], 0); + if (err < 0) + return err; + err = fw_iso_resources_allocate(&dg00x->tx_resources, + amdtp_stream_get_max_payload(&dg00x->tx_stream), + fw_parent_device(dg00x->unit)->max_speed); + if (err < 0) + goto error; + + /* Register isochronous channels for both direction. */ + data = cpu_to_be32((dg00x->tx_resources.channel << 16) | + dg00x->rx_resources.channel); + err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, + &data, sizeof(data), 0); + if (err < 0) + goto error; + + return 0; +error: + release_resources(dg00x); + return err; +} + +int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) +{ + int err; + + /* For out-stream. */ + err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit); + if (err < 0) + goto error; + err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM); + if (err < 0) + goto error; + + /* For in-stream. */ + err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit); + if (err < 0) + goto error; + err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM); + if (err < 0) + goto error; + + return 0; +error: + snd_dg00x_stream_destroy_duplex(dg00x); + return err; +} + +/* + * This function should be called before starting streams or after stopping + * streams. + */ +void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) +{ + amdtp_stream_destroy(&dg00x->rx_stream); + fw_iso_resources_destroy(&dg00x->rx_resources); + + amdtp_stream_destroy(&dg00x->tx_stream); + fw_iso_resources_destroy(&dg00x->tx_resources); +} + +int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate) +{ + unsigned int curr_rate; + int err = 0; + + if (dg00x->substreams_counter == 0) + goto end; + + /* Check current sampling rate. */ + err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); + if (err < 0) + goto error; + if (curr_rate != rate || + amdtp_streaming_error(&dg00x->tx_stream) || + amdtp_streaming_error(&dg00x->rx_stream)) { + finish_session(dg00x); + + amdtp_stream_stop(&dg00x->tx_stream); + amdtp_stream_stop(&dg00x->rx_stream); + release_resources(dg00x); + } + + /* + * No packets are transmitted without receiving packets, reagardless of + * which source of clock is used. + */ + if (!amdtp_stream_running(&dg00x->rx_stream)) { + err = snd_dg00x_stream_set_local_rate(dg00x, rate); + if (err < 0) + goto error; + + err = keep_resources(dg00x, rate); + if (err < 0) + goto error; + + err = begin_session(dg00x); + if (err < 0) + goto error; + + err = amdtp_stream_start(&dg00x->rx_stream, + dg00x->rx_resources.channel, + fw_parent_device(dg00x->unit)->max_speed); + if (err < 0) + goto error; + + if (!amdtp_stream_wait_callback(&dg00x->rx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; + } + } + + /* + * The value of SYT field in transmitted packets is always 0x0000. Thus, + * duplex streams with timestamp synchronization cannot be built. + */ + if (!amdtp_stream_running(&dg00x->tx_stream)) { + err = amdtp_stream_start(&dg00x->tx_stream, + dg00x->tx_resources.channel, + fw_parent_device(dg00x->unit)->max_speed); + if (err < 0) + goto error; + + if (!amdtp_stream_wait_callback(&dg00x->tx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; + } + } +end: + return err; +error: + finish_session(dg00x); + + amdtp_stream_stop(&dg00x->tx_stream); + amdtp_stream_stop(&dg00x->rx_stream); + release_resources(dg00x); + + return err; +} + +void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) +{ + if (dg00x->substreams_counter > 0) + return; + + amdtp_stream_stop(&dg00x->tx_stream); + amdtp_stream_stop(&dg00x->rx_stream); + finish_session(dg00x); + release_resources(dg00x); + + /* + * Just after finishing the session, the device may lost transmitting + * functionality for a short time. + */ + msleep(50); +} + +void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) +{ + fw_iso_resources_update(&dg00x->tx_resources); + fw_iso_resources_update(&dg00x->rx_resources); + + amdtp_stream_update(&dg00x->tx_stream); + amdtp_stream_update(&dg00x->rx_stream); +} diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 260967e54519..5bda4c4c87f0 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -45,6 +45,8 @@ static void dg00x_card_free(struct snd_card *card) { struct snd_dg00x *dg00x = card->private_data; + snd_dg00x_stream_destroy_duplex(dg00x); + fw_unit_put(dg00x->unit); mutex_destroy(&dg00x->mutex); @@ -75,6 +77,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_dg00x_stream_init_duplex(dg00x); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; @@ -89,7 +95,11 @@ error: static void snd_dg00x_update(struct fw_unit *unit) { - return; + struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); + + mutex_lock(&dg00x->mutex); + snd_dg00x_stream_update_duplex(dg00x); + mutex_unlock(&dg00x->mutex); } static void snd_dg00x_remove(struct fw_unit *unit) diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index fe21d7778004..685d5f01ee0d 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -31,6 +31,57 @@ struct snd_dg00x { struct fw_unit *unit; struct mutex mutex; + + struct amdtp_stream tx_stream; + struct fw_iso_resources tx_resources; + + struct amdtp_stream rx_stream; + struct fw_iso_resources rx_resources; + + unsigned int substreams_counter; +}; + +#define DG00X_ADDR_BASE 0xffffe0000000ull + +#define DG00X_OFFSET_STREAMING_STATE 0x0000 +#define DG00X_OFFSET_STREAMING_SET 0x0004 +#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008 +/* For LSB of the address 0x000c */ +/* unknown 0x0010 */ +#define DG00X_OFFSET_MESSAGE_ADDR 0x0014 +/* For LSB of the address 0x0018 */ +/* unknown 0x001c */ +/* unknown 0x0020 */ +/* not used 0x0024--0x00ff */ +#define DG00X_OFFSET_ISOC_CHANNELS 0x0100 +/* unknown 0x0104 */ +/* unknown 0x0108 */ +/* unknown 0x010c */ +#define DG00X_OFFSET_LOCAL_RATE 0x0110 +#define DG00X_OFFSET_EXTERNAL_RATE 0x0114 +#define DG00X_OFFSET_CLOCK_SOURCE 0x0118 +#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c +/* unknown 0x0120 */ +/* Mixer control on/off 0x0124 */ +/* unknown 0x0128 */ +#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c +/* unknown 0x0138 */ +#define DG00X_OFFSET_MMC 0x0400 + +enum snd_dg00x_rate { + SND_DG00X_RATE_44100 = 0, + SND_DG00X_RATE_48000, + SND_DG00X_RATE_88200, + SND_DG00X_RATE_96000, + SND_DG00X_RATE_COUNT, +}; + +enum snd_dg00x_clock { + SND_DG00X_CLOCK_INTERNAL = 0, + SND_DG00X_CLOCK_SPDIF, + SND_DG00X_CLOCK_ADAT, + SND_DG00X_CLOCK_WORD, + SND_DG00X_CLOCK_COUNT, }; int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -43,4 +94,21 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); +extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT]; +extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT]; +int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x, + unsigned int *rate); +int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, + unsigned int *rate); +int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate); +int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x, + enum snd_dg00x_clock *clock); +int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, + bool *detect); +int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x); +int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate); +void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x); +void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x); +void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x); + #endif -- cgit v1.2.3-59-g8ed1b From 927f17dc3a517128f07bbc1634fb25ce2587dc6f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:19 +0900 Subject: ALSA: firewire-digi00x: add proc node to show clock status This commit adds proc node to show current clock status for debugging. As long as testing Digi 002 rack, registers can show local clock rate, local clock source. When external clock input such as S/PDIF is connected, the registers show the detection and external clock rate. Additionally, the registers show the mode of optical digital input interface. Although, a tester with Digi 003 rack reports this makes no sense. Further investigation is required for Digi 003 series. Besides, in Digi 002 rack, the S/PDIF format must be IEC 60958-4, so-called professional. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/Makefile | 3 +- sound/firewire/digi00x/digi00x-proc.c | 99 +++++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x.c | 2 + sound/firewire/digi00x/digi00x.h | 8 +++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/digi00x/digi00x-proc.c (limited to 'sound') diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 32d8ebe7ce6b..3e0f028d6b6b 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,2 +1,3 @@ -snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x.o +snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ + digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c new file mode 100644 index 000000000000..a1d601f31165 --- /dev/null +++ b/sound/firewire/digi00x/digi00x-proc.c @@ -0,0 +1,99 @@ +/* + * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "digi00x.h" + +static int get_optical_iface_mode(struct snd_dg00x *dg00x, + enum snd_dg00x_optical_mode *mode) +{ + __be32 data; + int err; + + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE, + &data, sizeof(data), 0); + if (err >= 0) + *mode = be32_to_cpu(data) & 0x01; + + return err; +} + +static void proc_read_clock(struct snd_info_entry *entry, + struct snd_info_buffer *buf) +{ + static const char *const source_name[] = { + [SND_DG00X_CLOCK_INTERNAL] = "internal", + [SND_DG00X_CLOCK_SPDIF] = "s/pdif", + [SND_DG00X_CLOCK_ADAT] = "adat", + [SND_DG00X_CLOCK_WORD] = "word clock", + }; + static const char *const optical_name[] = { + [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat", + [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif", + }; + struct snd_dg00x *dg00x = entry->private_data; + enum snd_dg00x_optical_mode mode; + unsigned int rate; + enum snd_dg00x_clock clock; + bool detect; + + if (get_optical_iface_mode(dg00x, &mode) < 0) + return; + if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0) + return; + if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0) + return; + + snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]); + snd_iprintf(buf, "Sampling Rate: %d\n", rate); + snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]); + + if (clock == SND_DG00X_CLOCK_INTERNAL) + return; + + if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0) + return; + snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not"); + if (!detect) + return; + + if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0) + snd_iprintf(buf, "External sampling rate: %d\n", rate); +} + +void snd_dg00x_proc_init(struct snd_dg00x *dg00x) +{ + struct snd_info_entry *root, *entry; + + /* + * All nodes are automatically removed at snd_card_disconnect(), + * by following to link list. + */ + root = snd_info_create_card_entry(dg00x->card, "firewire", + dg00x->card->proc_root); + if (root == NULL) + return; + + root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(root) < 0) { + snd_info_free_entry(root); + return; + } + + entry = snd_info_create_card_entry(dg00x->card, "clock", root); + if (entry == NULL) { + snd_info_free_entry(root); + return; + } + + snd_info_set_text_ops(entry, dg00x, proc_read_clock); + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + snd_info_free_entry(root); + } +} diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 5bda4c4c87f0..a5f66fd1fb18 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -81,6 +81,8 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error; + snd_dg00x_proc_init(dg00x); + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 685d5f01ee0d..9ee259f8daa7 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -84,6 +85,12 @@ enum snd_dg00x_clock { SND_DG00X_CLOCK_COUNT, }; +enum snd_dg00x_optical_mode { + SND_DG00X_OPT_IFACE_MODE_ADAT = 0, + SND_DG00X_OPT_IFACE_MODE_SPDIF, + SND_DG00X_OPT_IFACE_MODE_COUNT, +}; + int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir); int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, @@ -111,4 +118,5 @@ void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x); +void snd_dg00x_proc_init(struct snd_dg00x *dg00x); #endif -- cgit v1.2.3-59-g8ed1b From 0120d0f1fe3bbf6c6a450056be56a70c409cf7a3 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:20 +0900 Subject: ALSA: firewire-digi00x: add PCM functionality This commit adds PCM functionality to transmit/receive PCM samples. Any PCM substreams are jointed because incoming/outgoing AMDTP streams are bound. When one of PCM substream is running or external clock source is selected, current sampling rate is used. Else, the sampling rate is changed as an userspace application requests. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/digi00x-pcm.c | 362 +++++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x.c | 4 + sound/firewire/digi00x/digi00x.h | 3 + 4 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/digi00x/digi00x-pcm.c (limited to 'sound') diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 3e0f028d6b6b..3c0ff6fff1f0 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,3 +1,3 @@ snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ - digi00x.o + digi00x-pcm.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c new file mode 100644 index 000000000000..87c9aa234890 --- /dev/null +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -0,0 +1,362 @@ +/* + * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "digi00x.h" + +static int hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval t = { + .min = UINT_MAX, .max = 0, .integer = 1, + }; + unsigned int i; + + for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { + if (!snd_interval_test(c, + snd_dg00x_stream_pcm_channels[i])) + continue; + + t.min = min(t.min, snd_dg00x_stream_rates[i]); + t.max = max(t.max, snd_dg00x_stream_rates[i]); + } + + return snd_interval_refine(r, &t); +} + +static int hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval t = { + .min = UINT_MAX, .max = 0, .integer = 1, + }; + unsigned int i; + + for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { + if (!snd_interval_test(r, snd_dg00x_stream_rates[i])) + continue; + + t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]); + t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]); + } + + return snd_interval_refine(c, &t); +} + +static int pcm_init_hw_params(struct snd_dg00x *dg00x, + struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .channels_min = 10, + .channels_max = 18, + .period_bytes_min = 4 * 18, + .period_bytes_max = 4 * 18 * 2048, + .buffer_bytes_max = 4 * 18 * 2048 * 2, + .periods_min = 2, + .periods_max = UINT_MAX, + }; + struct amdtp_stream *s; + int err; + + substream->runtime->hw = hardware; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; + s = &dg00x->tx_stream; + } else { + substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32; + s = &dg00x->rx_stream; + } + + err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + + err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + + return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime); +} + +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + enum snd_dg00x_clock clock; + bool detect; + unsigned int rate; + int err; + + err = pcm_init_hw_params(dg00x, substream); + if (err < 0) + return err; + + /* Check current clock source. */ + err = snd_dg00x_stream_get_clock(dg00x, &clock); + if (err < 0) + return err; + if (clock != SND_DG00X_CLOCK_INTERNAL) { + err = snd_dg00x_stream_check_external_clock(dg00x, &detect); + if (err < 0) + return err; + if (!detect) { + err = -EBUSY; + return err; + } + } + + if ((clock != SND_DG00X_CLOCK_INTERNAL) || + amdtp_stream_pcm_running(&dg00x->rx_stream) || + amdtp_stream_pcm_running(&dg00x->tx_stream)) { + err = snd_dg00x_stream_get_external_rate(dg00x, &rate); + if (err < 0) + return err; + substream->runtime->hw.rate_min = rate; + substream->runtime->hw.rate_max = rate; + } + + snd_pcm_set_sync(substream); + + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int pcm_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dg00x *dg00x = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&dg00x->mutex); + dg00x->substreams_counter++; + mutex_unlock(&dg00x->mutex); + } + + amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params)); + + return 0; +} + +static int pcm_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dg00x *dg00x = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&dg00x->mutex); + dg00x->substreams_counter++; + mutex_unlock(&dg00x->mutex); + } + + amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params)); + + return 0; +} + +static int pcm_capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + + mutex_lock(&dg00x->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + dg00x->substreams_counter--; + + snd_dg00x_stream_stop_duplex(dg00x); + + mutex_unlock(&dg00x->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + + mutex_lock(&dg00x->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + dg00x->substreams_counter--; + + snd_dg00x_stream_stop_duplex(dg00x); + + mutex_unlock(&dg00x->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + mutex_lock(&dg00x->mutex); + + err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&dg00x->tx_stream); + + mutex_unlock(&dg00x->mutex); + + return err; +} + +static int pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + mutex_lock(&dg00x->mutex); + + err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); + if (err >= 0) { + amdtp_stream_pcm_prepare(&dg00x->rx_stream); + amdtp_dot_reset(&dg00x->rx_stream); + } + + mutex_unlock(&dg00x->mutex); + + return err; +} + +static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dg00x *dg00x = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dg00x *dg00x = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) +{ + struct snd_dg00x *dg00x = sbstrm->private_data; + + return amdtp_stream_pcm_pointer(&dg00x->tx_stream); +} + +static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) +{ + struct snd_dg00x *dg00x = sbstrm->private_data; + + return amdtp_stream_pcm_pointer(&dg00x->rx_stream); +} + +static struct snd_pcm_ops pcm_capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, +}; + +static struct snd_pcm_ops pcm_playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm); + if (err < 0) + return err; + + pcm->private_data = dg00x; + snprintf(pcm->name, sizeof(pcm->name), + "%s PCM", dg00x->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + + return 0; +} diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index a5f66fd1fb18..166c1d43123f 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -83,6 +83,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, snd_dg00x_proc_init(dg00x); + err = snd_dg00x_create_pcm_devices(dg00x); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 9ee259f8daa7..199064504a55 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -119,4 +119,7 @@ void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x); void snd_dg00x_proc_init(struct snd_dg00x *dg00x); + +int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x); + #endif -- cgit v1.2.3-59-g8ed1b From 660dd3d52ead45b8e60dcf966daf304de2121a28 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:21 +0900 Subject: ALSA: firewire-digi00x: add hwdep interface This commit adds hwdep interface so as the other sound drivers for units on IEEE 1394 bus have. This interface is designed for mixer/control applications. By using this interface, an application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 +- include/uapi/sound/firewire.h | 1 + sound/firewire/Kconfig | 1 + sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/digi00x-hwdep.c | 192 ++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x-pcm.c | 23 +++- sound/firewire/digi00x/digi00x-stream.c | 39 +++++++ sound/firewire/digi00x/digi00x.c | 6 + sound/firewire/digi00x/digi00x.h | 14 +++ 9 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 sound/firewire/digi00x/digi00x-hwdep.c (limited to 'sound') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index a45be6bdcf5b..aa329132f6c4 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -100,9 +100,10 @@ enum { SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */ SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */ SNDRV_HWDEP_IFACE_FW_OXFW, /* Oxford OXFW970/971 based device */ + SNDRV_HWDEP_IFACE_FW_DIGI00X, /* Digidesign Digi 002/003 family */ /* Don't forget to change the following: */ - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DIGI00X }; struct snd_hwdep_info { diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 49122df3b56b..f67d228f731b 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -56,6 +56,7 @@ union snd_firewire_event { #define SNDRV_FIREWIRE_TYPE_FIREWORKS 2 #define SNDRV_FIREWIRE_TYPE_BEBOB 3 #define SNDRV_FIREWIRE_TYPE_OXFW 4 +#define SNDRV_FIREWIRE_TYPE_DIGI00X 5 /* RME, MOTU, ... */ struct snd_firewire_get_info { diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 74f17a8263db..e61445e2ceaa 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -123,6 +123,7 @@ config SND_BEBOB config SND_FIREWIRE_DIGI00X tristate "Digidesign Digi 002/003 family support" select SND_FIREWIRE_LIB + select SND_HWDEP help Say Y here to include support for Digidesign Digi 002/003 family. * Digi 002 Console diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 3c0ff6fff1f0..5bc3c7780a51 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,3 +1,3 @@ snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ - digi00x-pcm.o digi00x.o + digi00x-pcm.o digi00x-hwdep.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c new file mode 100644 index 000000000000..d629e415dc8b --- /dev/null +++ b/sound/firewire/digi00x/digi00x-hwdep.c @@ -0,0 +1,192 @@ +/* + * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * This codes give three functionality. + * + * 1.get firewire node information + * 2.get notification about starting/stopping stream + * 3.lock/unlock stream + */ + +#include "digi00x.h" + +static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, + loff_t *offset) +{ + struct snd_dg00x *dg00x = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dg00x->lock); + + while (!dg00x->dev_lock_changed) { + prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dg00x->lock); + schedule(); + finish_wait(&dg00x->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dg00x->lock); + } + + memset(&event, 0, sizeof(event)); + if (dg00x->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = (dg00x->dev_lock_count > 0); + dg00x->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } + + spin_unlock_irq(&dg00x->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_dg00x *dg00x = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dg00x->hwdep_wait, wait); + + spin_lock_irq(&dg00x->lock); + if (dg00x->dev_lock_changed) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dg00x->lock); + + return events; +} + +static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(dg00x->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DIGI00X; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int hwdep_lock(struct snd_dg00x *dg00x) +{ + int err; + + spin_lock_irq(&dg00x->lock); + + if (dg00x->dev_lock_count == 0) { + dg00x->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dg00x->lock); + + return err; +} + +static int hwdep_unlock(struct snd_dg00x *dg00x) +{ + int err; + + spin_lock_irq(&dg00x->lock); + + if (dg00x->dev_lock_count == -1) { + dg00x->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dg00x->lock); + + return err; +} + +static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_dg00x *dg00x = hwdep->private_data; + + spin_lock_irq(&dg00x->lock); + if (dg00x->dev_lock_count == -1) + dg00x->dev_lock_count = 0; + spin_unlock_irq(&dg00x->lock); + + return 0; +} + +static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_dg00x *dg00x = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(dg00x, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(dg00x); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(dg00x); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +static const struct snd_hwdep_ops hwdep_ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, +}; + +int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) +{ + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep); + if (err < 0) + return err; + + strcpy(hwdep->name, "Digi00x"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; + hwdep->ops = hwdep_ops; + hwdep->private_data = dg00x; + hwdep->exclusive = true; + + return err; +} diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 87c9aa234890..cac28f70aef7 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -118,21 +118,25 @@ static int pcm_open(struct snd_pcm_substream *substream) unsigned int rate; int err; + err = snd_dg00x_stream_lock_try(dg00x); + if (err < 0) + goto end; + err = pcm_init_hw_params(dg00x, substream); if (err < 0) - return err; + goto err_locked; /* Check current clock source. */ err = snd_dg00x_stream_get_clock(dg00x, &clock); if (err < 0) - return err; + goto err_locked; if (clock != SND_DG00X_CLOCK_INTERNAL) { err = snd_dg00x_stream_check_external_clock(dg00x, &detect); if (err < 0) - return err; + goto err_locked; if (!detect) { err = -EBUSY; - return err; + goto err_locked; } } @@ -141,18 +145,25 @@ static int pcm_open(struct snd_pcm_substream *substream) amdtp_stream_pcm_running(&dg00x->tx_stream)) { err = snd_dg00x_stream_get_external_rate(dg00x, &rate); if (err < 0) - return err; + goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); - +end: + return err; +err_locked: + snd_dg00x_stream_lock_release(dg00x); return err; } static int pcm_close(struct snd_pcm_substream *substream) { + struct snd_dg00x *dg00x = substream->private_data; + + snd_dg00x_stream_lock_release(dg00x); + return 0; } diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 3278c3e08560..8aac31be3132 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -379,3 +379,42 @@ void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) amdtp_stream_update(&dg00x->tx_stream); amdtp_stream_update(&dg00x->rx_stream); } + +void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x) +{ + dg00x->dev_lock_changed = true; + wake_up(&dg00x->hwdep_wait); +} + +int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x) +{ + int err; + + spin_lock_irq(&dg00x->lock); + + /* user land lock this */ + if (dg00x->dev_lock_count < 0) { + err = -EBUSY; + goto end; + } + + /* this is the first time */ + if (dg00x->dev_lock_count++ == 0) + snd_dg00x_stream_lock_changed(dg00x); + err = 0; +end: + spin_unlock_irq(&dg00x->lock); + return err; +} + +void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x) +{ + spin_lock_irq(&dg00x->lock); + + if (WARN_ON(dg00x->dev_lock_count <= 0)) + goto end; + if (--dg00x->dev_lock_count == 0) + snd_dg00x_stream_lock_changed(dg00x); +end: + spin_unlock_irq(&dg00x->lock); +} diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 166c1d43123f..cd93030df1cd 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -72,6 +72,8 @@ static int snd_dg00x_probe(struct fw_unit *unit, dg00x->unit = fw_unit_get(unit); mutex_init(&dg00x->mutex); + spin_lock_init(&dg00x->lock); + init_waitqueue_head(&dg00x->hwdep_wait); err = name_card(dg00x); if (err < 0) @@ -87,6 +89,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_dg00x_create_hwdep_device(dg00x); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 199064504a55..5ba531806262 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "../lib.h" #include "../iso-resources.h" @@ -32,6 +34,7 @@ struct snd_dg00x { struct fw_unit *unit; struct mutex mutex; + spinlock_t lock; struct amdtp_stream tx_stream; struct fw_iso_resources tx_resources; @@ -40,6 +43,12 @@ struct snd_dg00x { struct fw_iso_resources rx_resources; unsigned int substreams_counter; + + /* for uapi */ + int dev_lock_count; + bool dev_lock_changed; + wait_queue_head_t hwdep_wait; + }; #define DG00X_ADDR_BASE 0xffffe0000000ull @@ -118,8 +127,13 @@ void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x); +void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x); +int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x); +void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x); + void snd_dg00x_proc_init(struct snd_dg00x *dg00x); int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x); +int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x); #endif -- cgit v1.2.3-59-g8ed1b From 44b7308871ac6fd85fc840bfa3ddb466fe7aff23 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 30 Sep 2015 09:39:22 +0900 Subject: ALSA: firewire-digi00x: add support for asynchronous messaging Digi 002/003 family uses asynchronous transaction for messaging. The address to transmit this message is stored on a certain register. This commit allocates a range of address on OHCI 1394 host controller to handle the messaging. As long as I know, the purpose of this message seems to notify lost of synchronization. While, the meaning of content of the message is not clear. Actual examples of this messaging: * When clock source is set as internal: - 0x00007051 - 0x00007052 - 0x00007054 - 0x00007057 - 0x00007058 * When clock source is set as somewhat external: - 0x00009000 - 0x00009010 - 0x00009020 - 0x00009021 - 0x00009022 The lost often occurs when using internal clock source. In this case, users hear sounds with quite short gap every several minutes. In fact, the lost is recovered temporarily. When using with external clock source, the lost seems not to occur. The mechanism is not clear yet. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/uapi/sound/firewire.h | 7 +++ sound/firewire/digi00x/Makefile | 3 +- sound/firewire/digi00x/digi00x-hwdep.c | 12 ++++- sound/firewire/digi00x/digi00x-transaction.c | 81 ++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x.c | 7 +++ sound/firewire/digi00x/digi00x.h | 7 +++ 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 sound/firewire/digi00x/digi00x-transaction.c (limited to 'sound') diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index f67d228f731b..deb041cb9af0 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -9,6 +9,7 @@ #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475 +#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE 0x746e736c struct snd_firewire_event_common { unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ @@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response { __be32 response[0]; /* some responses */ }; +struct snd_firewire_event_digi00x_message { + unsigned int type; + __u32 message; /* Digi00x-specific message */ +}; + union snd_firewire_event { struct snd_firewire_event_common common; struct snd_firewire_event_lock_status lock_status; struct snd_firewire_event_dice_notification dice_notification; struct snd_firewire_event_efw_response efw_response; + struct snd_firewire_event_digi00x_message digi00x_message; }; diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 5bc3c7780a51..28e3d137ef57 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,3 +1,4 @@ snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ - digi00x-pcm.o digi00x-hwdep.o digi00x.o + digi00x-pcm.o digi00x-hwdep.o \ + digi00x-transaction.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c index d629e415dc8b..f188e4758fd2 100644 --- a/sound/firewire/digi00x/digi00x-hwdep.c +++ b/sound/firewire/digi00x/digi00x-hwdep.c @@ -12,6 +12,7 @@ * 1.get firewire node information * 2.get notification about starting/stopping stream * 3.lock/unlock stream + * 4.get asynchronous messaging */ #include "digi00x.h" @@ -25,7 +26,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&dg00x->lock); - while (!dg00x->dev_lock_changed) { + while (!dg00x->dev_lock_changed && dg00x->msg == 0) { prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&dg00x->lock); schedule(); @@ -42,6 +43,13 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, dg00x->dev_lock_changed = false; count = min_t(long, count, sizeof(event.lock_status)); + } else { + event.digi00x_message.type = + SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE; + event.digi00x_message.message = dg00x->msg; + dg00x->msg = 0; + + count = min_t(long, count, sizeof(event.digi00x_message)); } spin_unlock_irq(&dg00x->lock); @@ -61,7 +69,7 @@ static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &dg00x->hwdep_wait, wait); spin_lock_irq(&dg00x->lock); - if (dg00x->dev_lock_changed) + if (dg00x->dev_lock_changed || dg00x->msg) events = POLLIN | POLLRDNORM; else events = 0; diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c new file mode 100644 index 000000000000..49372901a1e1 --- /dev/null +++ b/sound/firewire/digi00x/digi00x-transaction.c @@ -0,0 +1,81 @@ +/* + * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include "digi00x.h" + +static void handle_unknown_message(struct snd_dg00x *dg00x, + unsigned long long offset, __be32 *buf) +{ + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + dg00x->msg = be32_to_cpu(*buf); + spin_unlock_irqrestore(&dg00x->lock, flags); + + wake_up(&dg00x->hwdep_wait); +} + +static void handle_message(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct snd_dg00x *dg00x = callback_data; + __be32 *buf = (__be32 *)data; + + if (offset == dg00x->async_handler.offset) + handle_unknown_message(dg00x, offset, buf); + + fw_send_response(card, request, RCODE_COMPLETE); +} + +int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x) +{ + struct fw_device *device = fw_parent_device(dg00x->unit); + __be32 data[2]; + + /* Unknown. 4bytes. */ + data[0] = cpu_to_be32((device->card->node_id << 16) | + (dg00x->async_handler.offset >> 32)); + data[1] = cpu_to_be32(dg00x->async_handler.offset); + return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, + &data, sizeof(data), 0); +} + +int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) +{ + static const struct fw_address_region resp_register_region = { + .start = 0xffffe0000000ull, + .end = 0xffffe000ffffull, + }; + int err; + + dg00x->async_handler.length = 4; + dg00x->async_handler.address_callback = handle_message; + dg00x->async_handler.callback_data = dg00x; + + err = fw_core_add_address_handler(&dg00x->async_handler, + &resp_register_region); + if (err < 0) + return err; + + err = snd_dg00x_transaction_reregister(dg00x); + if (err < 0) { + fw_core_remove_address_handler(&dg00x->async_handler); + dg00x->async_handler.address_callback = NULL; + } + + return err; +} + +void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) +{ + fw_core_remove_address_handler(&dg00x->async_handler); +} diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index cd93030df1cd..34937a26c198 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -46,6 +46,7 @@ static void dg00x_card_free(struct snd_card *card) struct snd_dg00x *dg00x = card->private_data; snd_dg00x_stream_destroy_duplex(dg00x); + snd_dg00x_transaction_unregister(dg00x); fw_unit_put(dg00x->unit); @@ -93,6 +94,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_dg00x_transaction_register(dg00x); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; @@ -109,6 +114,8 @@ static void snd_dg00x_update(struct fw_unit *unit) { struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); + snd_dg00x_transaction_reregister(dg00x); + mutex_lock(&dg00x->mutex); snd_dg00x_stream_update_duplex(dg00x); mutex_unlock(&dg00x->mutex); diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 5ba531806262..b960c868b59b 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -49,6 +49,9 @@ struct snd_dg00x { bool dev_lock_changed; wait_queue_head_t hwdep_wait; + /* For asynchronous messages. */ + struct fw_address_handler async_handler; + u32 msg; }; #define DG00X_ADDR_BASE 0xffffe0000000ull @@ -110,6 +113,10 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); +int snd_dg00x_transaction_register(struct snd_dg00x *dg00x); +int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x); +void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x); + extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT]; extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT]; int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x, -- cgit v1.2.3-59-g8ed1b From 98a4b665bc91480cc4c054093a59249e153ac17e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 29 Sep 2015 21:43:17 +0200 Subject: ASoC: sun4i-codec: pass through clk_set_rate error The current code, disregarding the clk_set_rate error code, was always returning -EINVAL. Fix that and return the code in order to have more clue about what's going on. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 8d59d83b5aa4..77408af5d38b 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -283,7 +283,7 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); unsigned long clk_freq; - int hwrate; + int ret, hwrate; u32 val; if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) @@ -293,8 +293,9 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, if (!clk_freq) return -EINVAL; - if (clk_set_rate(scodec->clk_module, clk_freq)) - return -EINVAL; + ret = clk_set_rate(scodec->clk_module, clk_freq); + if (ret) + return ret; hwrate = sun4i_codec_get_hw_rate(params); if (hwrate < 0) -- cgit v1.2.3-59-g8ed1b From debb97248741597fa9f38f563f2b9a17250ee4c5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 29 Sep 2015 21:43:18 +0200 Subject: ASoC: sun4i-codec: Remove redundant SND_PCM_RATE_KNOT The SND_PCM_RATE_KNOT covers all the rate settings, even though some that we don't support, while we also list all the rate we support. Simply remove it. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 77408af5d38b..47780552dcd0 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -389,8 +389,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { .rate_max = 192000, .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000 | - SNDRV_PCM_RATE_KNOT, + SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 24, -- cgit v1.2.3-59-g8ed1b From 58ceb57ec1be928bec2faeca11fe0752f930669d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 30 Sep 2015 22:51:52 +0200 Subject: ASoC: pxa: pxa-pcm-lib: switch over to snd-soc-dmaengine-pcm This patch removes the old PXA DMA API usage and switches over to generic functions provided by snd-soc-dmaengine-pcm. More cleanups may be done on top of this, and some function stubs can now be removed completetly. However, the intention here was to keep the transition as small as possible. This was tested on the mioa701 pxa27x board. Signed-off-by: Daniel Mack [trivial change from mmp-dma to pxa-dma] Signed-off-by: Robert Jarzmik Signed-off-by: Mark Brown --- include/sound/pxa2xx-lib.h | 1 - sound/arm/pxa2xx-ac97.c | 13 ++- sound/arm/pxa2xx-pcm-lib.c | 201 ++++++++------------------------------------ sound/arm/pxa2xx-pcm.c | 12 +-- sound/arm/pxa2xx-pcm.h | 2 - sound/soc/pxa/pxa2xx-ac97.c | 49 +++++++---- sound/soc/pxa/pxa2xx-i2s.c | 3 + sound/soc/pxa/pxa2xx-pcm.c | 32 ------- 8 files changed, 85 insertions(+), 228 deletions(-) (limited to 'sound') diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 56e818e4a1cb..6ef629bde164 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -12,7 +12,6 @@ extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream); extern int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd); extern snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream); extern int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream); -extern void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id); extern int __pxa2xx_pcm_open(struct snd_pcm_substream *substream); extern int __pxa2xx_pcm_close(struct snd_pcm_substream *substream); extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 38590b322c54..fbd5dad0c484 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct pxad_param pxa2xx_ac97_pcm_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 12, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct pxad_param pxa2xx_ac97_pcm_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 11, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 01f8fdc42b1b..e9b98af6b52c 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,8 +16,6 @@ #include #include -#include - #include "pxa2xx-pcm.h" static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { @@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { .period_bytes_min = 32, .period_bytes_max = 8192 - 32, .periods_min = 1, - .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), + .periods_max = 256, .buffer_bytes_max = 128 * 1024, .fifo_size = 32, }; @@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd = runtime->private_data; - size_t totsize = params_buffer_bytes(params); - size_t period = params_period_bytes(params); - pxa_dma_desc *dma_desc; - dma_addr_t dma_buff_phys, next_desc_phys; - u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_params; + struct dma_slave_config config; + int ret; - /* temporary transition hack */ - switch (rtd->params->addr_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - dcmd |= DCMD_WIDTH1; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - dcmd |= DCMD_WIDTH2; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - dcmd |= DCMD_WIDTH4; - break; - default: - /* can't happen */ - break; - } + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; - switch (rtd->params->maxburst) { - case 8: - dcmd |= DCMD_BURST8; - break; - case 16: - dcmd |= DCMD_BURST16; - break; - case 32: - dcmd |= DCMD_BURST32; - break; - } + ret = snd_hwparams_to_dma_slave_config(substream, params, &config); + if (ret) + return ret; - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = totsize; + snd_dmaengine_pcm_set_config_from_dai_data(substream, + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), + &config); - dma_desc = rtd->dma_desc_array; - next_desc_phys = rtd->dma_desc_array_phys; - dma_buff_phys = runtime->dma_addr; - do { - next_desc_phys += sizeof(pxa_dma_desc); - dma_desc->ddadr = next_desc_phys; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->addr; - } else { - dma_desc->dsadr = rtd->params->addr; - dma_desc->dtadr = dma_buff_phys; - } - if (period > totsize) - period = totsize; - dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; - dma_desc++; - dma_buff_phys += period; - } while (totsize -= period); - dma_desc[-1].ddadr = rtd->dma_desc_array_phys; + ret = dmaengine_slave_config(chan, &config); + if (ret) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); return 0; } @@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - - if (rtd && rtd->params && rtd->params->filter_data) { - unsigned long req = *(unsigned long *) rtd->params->filter_data; - DRCMR(req) = 0; - } - snd_pcm_set_runtime_buffer(substream, NULL); return 0; } @@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; - DCSR(prtd->dma_ch) = DCSR_RUN; - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - DCSR(prtd->dma_ch) &= ~DCSR_RUN; - break; - - case SNDRV_PCM_TRIGGER_RESUME: - DCSR(prtd->dma_ch) |= DCSR_RUN; - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; - DCSR(prtd->dma_ch) |= DCSR_RUN; - break; - - default: - ret = -EINVAL; - } - - return ret; + return snd_dmaengine_pcm_trigger(substream, cmd); } EXPORT_SYMBOL(pxa2xx_pcm_trigger); snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *prtd = runtime->private_data; - - dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); - snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); - - if (x == runtime->buffer_size) - x = 0; - return x; + return snd_dmaengine_pcm_pointer(substream); } EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - unsigned long req; - - if (!prtd || !prtd->params) - return 0; - - if (prtd->dma_ch == -1) - return -EINVAL; - - DCSR(prtd->dma_ch) &= ~DCSR_RUN; - DCSR(prtd->dma_ch) = 0; - DCMD(prtd->dma_ch) = 0; - req = *(unsigned long *) prtd->params->filter_data; - DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; - return 0; } EXPORT_SYMBOL(__pxa2xx_pcm_prepare); -void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) -{ - struct snd_pcm_substream *substream = dev_id; - int dcsr; - - dcsr = DCSR(dma_ch); - DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; - - if (dcsr & DCSR_ENDINTR) { - snd_pcm_period_elapsed(substream); - } else { - printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", - dma_ch, dcsr); - snd_pcm_stop_xrun(substream); - } -} -EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); - int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd; + struct snd_dmaengine_dai_dma_data *dma_params; int ret; runtime->hw = pxa2xx_pcm_hardware; + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; + /* * For mysterious reasons (and despite what the manual says) * playback samples are lost if the DMA count is not a multiple @@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (ret) - goto out; + return ret; ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); if (ret) - goto out; + return ret; ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) - goto out; - - ret = -ENOMEM; - rtd = kzalloc(sizeof(*rtd), GFP_KERNEL); - if (!rtd) - goto out; - rtd->dma_desc_array = - dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, - &rtd->dma_desc_array_phys, GFP_KERNEL); - if (!rtd->dma_desc_array) - goto err1; + return ret; - rtd->dma_ch = -1; - runtime->private_data = rtd; - return 0; - - err1: - kfree(rtd); - out: - return ret; + return snd_dmaengine_pcm_open_request_chan(substream, + pxad_filter_fn, + dma_params->filter_data); } EXPORT_SYMBOL(__pxa2xx_pcm_open); int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *rtd = runtime->private_data; - - dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, - rtd->dma_desc_array, rtd->dma_desc_array_phys); - kfree(rtd); - return 0; + return snd_dmaengine_pcm_close_release_chan(substream); } EXPORT_SYMBOL(__pxa2xx_pcm_close); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 83be8e3f095e..83fcfac97739 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma("dma", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - goto err2; - rtd->dma_ch = ret; ret = client->startup(substream); if (!ret) - goto out; + goto err2; + + return 0; - pxa_free_dma(rtd->dma_ch); err2: __pxa2xx_pcm_close(substream); out: @@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) { struct pxa2xx_pcm_client *client = substream->private_data; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - pxa_free_dma(rtd->dma_ch); client->shutdown(substream); return __pxa2xx_pcm_close(substream); diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 00330985beec..8fa2b7c9e6b8 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,8 +13,6 @@ struct pxa2xx_runtime_data { int dma_ch; struct snd_dmaengine_dai_dma_data *params; - struct pxa_dma_desc *dma_desc_array; - dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 9e4b04e0fbd1..f3de615aacd7 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11; +static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 11, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12; +static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 12, +}; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 10, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 9, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = { + .prio = PXAD_PRIO_LOWEST, + .drcmr = 8, +}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .addr = __PREG(MCDR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, @@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; -static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, return 0; } -static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, return 0; } -static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) +static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; @@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, SNDRV_PCM_RATE_48000) static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { - .hw_params = pxa2xx_ac97_hw_params, + .startup = pxa2xx_ac97_hifi_startup, }; static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { - .hw_params = pxa2xx_ac97_hw_aux_params, + .startup = pxa2xx_ac97_aux_startup, }; static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { - .hw_params = pxa2xx_ac97_hw_mic_params, + .startup = pxa2xx_ac97_mic_startup, }; /* diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 6b4e40036910..0389cf7b4b1e 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai) /* Along with FIFO servicing */ SAIMR &= ~(SAIMR_RFS | SAIMR_TFS); + snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out, + &pxa2xx_i2s_pcm_stereo_in); + return 0; } diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 29a3fdbb7b59..9f390398d518 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -15,8 +15,6 @@ #include #include -#include - #include #include #include @@ -27,11 +25,8 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_dmaengine_dai_dma_data *dma; - int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, if (!dma) return 0; - /* this may get called several times by oss emulation - * with different params */ - if (prtd->params == NULL) { - prtd->params = dma; - ret = pxa_request_dma("name", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - return ret; - prtd->dma_ch = ret; - } else if (prtd->params != dma) { - pxa_free_dma(prtd->dma_ch); - prtd->params = dma; - ret = pxa_request_dma("name", DMA_PRIO_LOW, - pxa2xx_pcm_dma_irq, substream); - if (ret < 0) - return ret; - prtd->dma_ch = ret; - } - return __pxa2xx_pcm_hw_params(substream, params); } static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { - struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - __pxa2xx_pcm_hw_free(substream); - if (prtd->dma_ch >= 0) { - pxa_free_dma(prtd->dma_ch); - prtd->dma_ch = -1; - prtd->params = NULL; - } - return 0; } -- cgit v1.2.3-59-g8ed1b From e29fd55d8d23ea7afcadd15a87700583e7da5035 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 1 Oct 2015 14:47:10 +0800 Subject: ASoC: rt5645: Add the control of ClassD modulator Speaker Gain Ratio Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 12 ++++++++++++ sound/soc/codecs/rt5645.h | 4 ++++ 2 files changed, 16 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index b0d96b6d21de..1f7045bc23a7 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -469,6 +469,14 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) ); +/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */ +static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv, + 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0), + 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0) +); + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -476,6 +484,10 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL, RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + /* ClassD modulator Speaker Gain Ratio */ + SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO, + RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv), + /* Headphone Output Volume */ SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index f45861c49ef2..d0e0ce52c287 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -668,6 +668,10 @@ #define RT5645_M_SV_R_SPM_R (0x1 << 0) #define RT5645_M_SV_R_SPM_R_SFT 0 +/* SPOMIX Ratio Control (0x4a) */ +#define RT5645_SPK_G_CLSD_MASK (0x7 << 0) +#define RT5645_SPK_G_CLSD_SFT 0 + /* Mono Output Mixer Control (0x4c) */ #define RT5645_M_OV_L_MM (0x1 << 9) #define RT5645_M_OV_L_MM_SFT 9 -- cgit v1.2.3-59-g8ed1b From c0949b278515da948597b4a1a2726f42591ef385 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:11 +0900 Subject: ALSA: firewire-tascam: add skeleton for TASCAM FireWire series This commit adds a new driver for TASCAM FireWire series. In this commit, this driver just creates/removes card instance according to bus event. More functionalities will be added in following commits. TASCAM FireWire series consists of: * PDI 1394P23 for IEEE 1394 PHY layer * PDI 1394L40 for IEEE 1394 LINK layer and IEC 61883 interface * XILINX XC9536XL * XILINX Spartan-II XC2S100 * ATMEL AT91M42800A Ilya Zimnovich had investigated TASCAM FireWire series in 2011, and discover some features of his FW-1804. You can see a part of his research in FFADO project. http://subversion.ffado.org/wiki/Tascam A part of my work are based on Ilya's investigation, while this series doesn't support the FW-1804, because of a lack of config ROM information and its protocol detail, especially for PCM channels. I observed that FW-1884 and FW-1082 don't work properly with 1394 OHCI controller based on VT6315. The controller can actually communicate packets to these models, while these models generate no sounds. It may be due to the PHY/LINK layer issues. Using 1394 OHCI controller produced by the other vendors such as Texas Instruments may work. Or adding another node on the bus. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Kconfig | 11 +++ sound/firewire/Makefile | 1 + sound/firewire/tascam/Makefile | 2 + sound/firewire/tascam/tascam.c | 155 +++++++++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.h | 33 +++++++++ 5 files changed, 202 insertions(+) create mode 100644 sound/firewire/tascam/Makefile create mode 100644 sound/firewire/tascam/tascam.c create mode 100644 sound/firewire/tascam/tascam.h (limited to 'sound') diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index e61445e2ceaa..bb3f2610c6e9 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -135,4 +135,15 @@ config SND_FIREWIRE_DIGI00X To compile this driver as a module, choose M here: the module will be called snd-firewire-digi00x. +config SND_FIREWIRE_TASCAM + tristate "TASCAM FireWire series support" + select SND_FIREWIRE_LIB + help + Say Y here to include support for TASCAM. + * FW-1884 + * FW-1082 + + To compile this driver as a module, choose M here: the module + will be called snd-firewire-tascam. + endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 5325d15d7018..6ae50f50db62 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_FIREWORKS) += fireworks/ obj-$(CONFIG_SND_BEBOB) += bebob/ obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/ +obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/ diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile new file mode 100644 index 000000000000..627129b98a5c --- /dev/null +++ b/sound/firewire/tascam/Makefile @@ -0,0 +1,2 @@ +snd-firewire-tascam-objs := tascam.o +obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c new file mode 100644 index 000000000000..9f2d2a33f58a --- /dev/null +++ b/sound/firewire/tascam/tascam.c @@ -0,0 +1,155 @@ +/* + * tascam.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "tascam.h" + +MODULE_DESCRIPTION("TASCAM FireWire series Driver"); +MODULE_AUTHOR("Takashi Sakamoto "); +MODULE_LICENSE("GPL v2"); + +static int check_name(struct snd_tscm *tscm) +{ + struct fw_device *fw_dev = fw_parent_device(tscm->unit); + char vendor[8]; + char model[8]; + __u32 data; + + /* Retrieve model name. */ + data = be32_to_cpu(fw_dev->config_rom[28]); + memcpy(model, &data, 4); + data = be32_to_cpu(fw_dev->config_rom[29]); + memcpy(model + 4, &data, 4); + model[7] = '\0'; + + /* Retrieve vendor name. */ + data = be32_to_cpu(fw_dev->config_rom[23]); + memcpy(vendor, &data, 4); + data = be32_to_cpu(fw_dev->config_rom[24]); + memcpy(vendor + 4, &data, 4); + vendor[7] = '\0'; + + strcpy(tscm->card->driver, "FW-TASCAM"); + strcpy(tscm->card->shortname, model); + strcpy(tscm->card->mixername, model); + snprintf(tscm->card->longname, sizeof(tscm->card->longname), + "%s %s, GUID %08x%08x at %s, S%d", vendor, model, + cpu_to_be32(fw_dev->config_rom[3]), + cpu_to_be32(fw_dev->config_rom[4]), + dev_name(&tscm->unit->device), 100 << fw_dev->max_speed); + + return 0; +} + +static void tscm_card_free(struct snd_card *card) +{ + struct snd_tscm *tscm = card->private_data; + + fw_unit_put(tscm->unit); + + mutex_destroy(&tscm->mutex); +} + +static int snd_tscm_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_card *card; + struct snd_tscm *tscm; + int err; + + /* create card */ + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(struct snd_tscm), &card); + if (err < 0) + return err; + card->private_free = tscm_card_free; + + /* initialize myself */ + tscm = card->private_data; + tscm->card = card; + tscm->unit = fw_unit_get(unit); + + mutex_init(&tscm->mutex); + + err = check_name(tscm); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, tscm); + + return err; +error: + snd_card_free(card); + return err; +} + +static void snd_tscm_update(struct fw_unit *unit) +{ + return; +} + +static void snd_tscm_remove(struct fw_unit *unit) +{ + struct snd_tscm *tscm = dev_get_drvdata(&unit->device); + + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(tscm->card); +} + +static const struct ieee1394_device_id snd_tscm_id_table[] = { + /* FW-1082 */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = 0x00022e, + .specifier_id = 0x00022e, + .version = 0x800003, + }, + /* FW-1884 */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = 0x00022e, + .specifier_id = 0x00022e, + .version = 0x800000, + }, + /* FW-1804 mey be supported if IDs are clear. */ + /* FE-08 requires reverse-engineering because it just has faders. */ + {} +}; +MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table); + +static struct fw_driver tscm_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "snd-firewire-tascam", + .bus = &fw_bus_type, + }, + .probe = snd_tscm_probe, + .update = snd_tscm_update, + .remove = snd_tscm_remove, + .id_table = snd_tscm_id_table, +}; + +static int __init snd_tscm_init(void) +{ + return driver_register(&tscm_driver.driver); +} + +static void __exit snd_tscm_exit(void) +{ + driver_unregister(&tscm_driver.driver); +} + +module_init(snd_tscm_init); +module_exit(snd_tscm_exit); diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h new file mode 100644 index 000000000000..d2f4f67cf636 --- /dev/null +++ b/sound/firewire/tascam/tascam.h @@ -0,0 +1,33 @@ +/* + * tascam.h - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef SOUND_TASCAM_H_INCLUDED +#define SOUND_TASCAM_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../lib.h" + +struct snd_tscm { + struct snd_card *card; + struct fw_unit *unit; + + struct mutex mutex; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 6f81ba19ace2282f2560f5ec3a827ec5370825cb Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:12 +0900 Subject: ALSA: firewire-tascam: add a structure for model-dependent parameters. TASCAM FireWire series doesn't tell drivers their capabilities, thus the drivers should have model-dependent parameters and apply it to detected devices. This commit adds a structure to represent such parameters. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam.c | 36 ++++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.h | 13 +++++++++++++ 2 files changed, 49 insertions(+) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index 9f2d2a33f58a..9ac09cbb3b05 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -12,6 +12,39 @@ MODULE_DESCRIPTION("TASCAM FireWire series Driver"); MODULE_AUTHOR("Takashi Sakamoto "); MODULE_LICENSE("GPL v2"); +static struct snd_tscm_spec model_specs[] = { + { + .name = "FW-1884", + .has_adat = true, + .has_spdif = true, + .pcm_capture_analog_channels = 8, + .pcm_playback_analog_channels = 8, + .midi_capture_ports = 4, + .midi_playback_ports = 4, + .is_controller = true, + }, + { + .name = "FW-1804", + .has_adat = true, + .has_spdif = true, + .pcm_capture_analog_channels = 8, + .pcm_playback_analog_channels = 2, + .midi_capture_ports = 2, + .midi_playback_ports = 4, + .is_controller = false, + }, + { + .name = "FW-1082", + .has_adat = false, + .has_spdif = true, + .pcm_capture_analog_channels = 8, + .pcm_playback_analog_channels = 2, + .midi_capture_ports = 2, + .midi_playback_ports = 2, + .is_controller = true, + }, +}; + static int check_name(struct snd_tscm *tscm) { struct fw_device *fw_dev = fw_parent_device(tscm->unit); @@ -72,6 +105,7 @@ static int snd_tscm_probe(struct fw_unit *unit, tscm = card->private_data; tscm->card = card; tscm->unit = fw_unit_get(unit); + tscm->spec = (const struct snd_tscm_spec *)entry->driver_data; mutex_init(&tscm->mutex); @@ -113,6 +147,7 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = { .vendor_id = 0x00022e, .specifier_id = 0x00022e, .version = 0x800003, + .driver_data = (kernel_ulong_t)&model_specs[2], }, /* FW-1884 */ { @@ -122,6 +157,7 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = { .vendor_id = 0x00022e, .specifier_id = 0x00022e, .version = 0x800000, + .driver_data = (kernel_ulong_t)&model_specs[0], }, /* FW-1804 mey be supported if IDs are clear. */ /* FE-08 requires reverse-engineering because it just has faders. */ diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index d2f4f67cf636..e12f8b55b8cd 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -23,11 +23,24 @@ #include "../lib.h" +struct snd_tscm_spec { + const char *const name; + bool has_adat; + bool has_spdif; + unsigned int pcm_capture_analog_channels; + unsigned int pcm_playback_analog_channels; + unsigned int midi_capture_ports; + unsigned int midi_playback_ports; + bool is_controller; +}; + struct snd_tscm { struct snd_card *card; struct fw_unit *unit; struct mutex mutex; + + const struct snd_tscm_spec *spec; }; #endif -- cgit v1.2.3-59-g8ed1b From 96e5fbb0dd128496378dbc404c2a894e49c1ac21 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:13 +0900 Subject: ALSA: firewire-tascam: add proc node to show firmware information TASCAM FireWire series has certain registers for firmware information. This commit adds proc node to show the information. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/Makefile | 2 +- sound/firewire/tascam/tascam-proc.c | 88 +++++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.c | 2 + sound/firewire/tascam/tascam.h | 10 +++++ 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/tascam/tascam-proc.c (limited to 'sound') diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index 627129b98a5c..1555206c7716 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,2 +1,2 @@ -snd-firewire-tascam-objs := tascam.o +snd-firewire-tascam-objs := tascam-proc.o tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c new file mode 100644 index 000000000000..bfd4a4c06914 --- /dev/null +++ b/sound/firewire/tascam/tascam-proc.c @@ -0,0 +1,88 @@ +/* + * tascam-proc.h - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./tascam.h" + +static void proc_read_firmware(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_tscm *tscm = entry->private_data; + __be32 data; + unsigned int reg, fpga, arm, hw; + int err; + + err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER, + &data, sizeof(data), 0); + if (err < 0) + return; + reg = be32_to_cpu(data); + + err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA, + &data, sizeof(data), 0); + if (err < 0) + return; + fpga = be32_to_cpu(data); + + err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM, + &data, sizeof(data), 0); + if (err < 0) + return; + arm = be32_to_cpu(data); + + err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW, + &data, sizeof(data), 0); + if (err < 0) + return; + hw = be32_to_cpu(data); + + snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg); + snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga); + snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm); + snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw); +} + +static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root, + const char *name, + void (*op)(struct snd_info_entry *e, + struct snd_info_buffer *b)) +{ + struct snd_info_entry *entry; + + entry = snd_info_create_card_entry(tscm->card, name, root); + if (entry == NULL) + return; + + snd_info_set_text_ops(entry, tscm, op); + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); +} + +void snd_tscm_proc_init(struct snd_tscm *tscm) +{ + struct snd_info_entry *root; + + /* + * All nodes are automatically removed at snd_card_disconnect(), + * by following to link list. + */ + root = snd_info_create_card_entry(tscm->card, "firewire", + tscm->card->proc_root); + if (root == NULL) + return; + root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(root) < 0) { + snd_info_free_entry(root); + return; + } + + add_node(tscm, root, "firmware", proc_read_firmware); +} diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index 9ac09cbb3b05..d7418c065f4e 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -113,6 +113,8 @@ static int snd_tscm_probe(struct fw_unit *unit, if (err < 0) goto error; + snd_tscm_proc_init(tscm); + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index e12f8b55b8cd..9ecc5505723f 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -20,6 +20,7 @@ #include #include +#include #include "../lib.h" @@ -43,4 +44,13 @@ struct snd_tscm { const struct snd_tscm_spec *spec; }; +#define TSCM_ADDR_BASE 0xffff00000000ull + +#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000 +#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004 +#define TSCM_OFFSET_FIRMWARE_ARM 0x0008 +#define TSCM_OFFSET_FIRMWARE_HW 0x000c + +void snd_tscm_proc_init(struct snd_tscm *tscm); + #endif -- cgit v1.2.3-59-g8ed1b From 47faeea25ef3a39f2224377a1fc76581cd18c044 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:14 +0900 Subject: ALSA: firewire-tascam: add data block processing layer TASCAM FireWire series uses non-blocking transmission for AMDTP packet streaming, while the format of data blocks is unique. The CIP headers includes specific value in FMT field and no SYT information. In transmitted packets, the first data channel represents event counter, and the last data channel has status and control information. The rest has 24bit PCM samples with right padding. In received packets, all of data channels include 16, 24, 32bit PCM samples. There's no other kind of information. This commit adds support for this protocol. For convenience, the size of PCM samples in outgoing packet is limited by 16 and 24bit. The status and control information will be supported in future commits. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/Makefile | 2 +- sound/firewire/tascam/amdtp-tascam.c | 243 +++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.h | 10 ++ 3 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/tascam/amdtp-tascam.c (limited to 'sound') diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index 1555206c7716..d06c737ae261 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,2 +1,2 @@ -snd-firewire-tascam-objs := tascam-proc.o tascam.o +snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c new file mode 100644 index 000000000000..9dd0fccd5ccc --- /dev/null +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -0,0 +1,243 @@ +/* + * amdtp-tascam.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include "tascam.h" + +#define AMDTP_FMT_TSCM_TX 0x1e +#define AMDTP_FMT_TSCM_RX 0x3e + +struct amdtp_tscm { + unsigned int pcm_channels; + + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +}; + +int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) +{ + struct amdtp_tscm *p = s->protocol; + unsigned int data_channels; + + if (amdtp_stream_running(s)) + return -EBUSY; + + data_channels = p->pcm_channels; + + /* Packets in in-stream have extra 2 data channels. */ + if (s->direction == AMDTP_IN_STREAM) + data_channels += 2; + + return amdtp_stream_set_parameters(s, rate, data_channels); +} + +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_tscm *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u32 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[c] = cpu_to_be32(*src); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_tscm *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u16 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[c] = cpu_to_be32(*src << 16); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_tscm *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + channels = p->pcm_channels; + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + /* The first data channel is for event counter. */ + buffer += 1; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[c]); + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_tscm *p = s->protocol; + unsigned int channels, i, c; + + channels = p->pcm_channels; + + for (i = 0; i < data_blocks; ++i) { + for (c = 0; c < channels; ++c) + buffer[c] = 0x00000000; + buffer += s->data_block_quadlets; + } +} + +int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime) +{ + int err; + + /* + * Our implementation allows this protocol to deliver 24 bit sample in + * 32bit data channel. + */ + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + + return amdtp_stream_add_pcm_hw_constraints(s, runtime); +} + +void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) +{ + struct amdtp_tscm *p = s->protocol; + + if (WARN_ON(amdtp_stream_pcm_running(s))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + if (s->direction == AMDTP_OUT_STREAM) { + p->transfer_samples = write_pcm_s16; + break; + } + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S32: + if (s->direction == AMDTP_OUT_STREAM) + p->transfer_samples = write_pcm_s32; + else + p->transfer_samples = read_pcm_s32; + break; + } +} + +static unsigned int process_tx_data_blocks(struct amdtp_stream *s, + __be32 *buffer, + unsigned int data_blocks, + unsigned int *syt) +{ + struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol; + struct snd_pcm_substream *pcm; + + pcm = ACCESS_ONCE(s->pcm); + if (data_blocks > 0 && pcm) + p->transfer_samples(s, pcm, buffer, data_blocks); + + /* A place holder for control messages. */ + + return data_blocks; +} + +static unsigned int process_rx_data_blocks(struct amdtp_stream *s, + __be32 *buffer, + unsigned int data_blocks, + unsigned int *syt) +{ + struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol; + struct snd_pcm_substream *pcm; + + /* This field is not used. */ + *syt = 0x0000; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + p->transfer_samples(s, pcm, buffer, data_blocks); + else + write_pcm_silence(s, buffer, data_blocks); + + return data_blocks; +} + +int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, unsigned int pcm_channels) +{ + amdtp_stream_process_data_blocks_t process_data_blocks; + struct amdtp_tscm *p; + unsigned int fmt; + int err; + + if (dir == AMDTP_IN_STREAM) { + fmt = AMDTP_FMT_TSCM_TX; + process_data_blocks = process_tx_data_blocks; + } else { + fmt = AMDTP_FMT_TSCM_RX; + process_data_blocks = process_rx_data_blocks; + } + + err = amdtp_stream_init(s, unit, dir, + CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, + process_data_blocks, sizeof(struct amdtp_tscm)); + if (err < 0) + return 0; + + /* Use fixed value for FDF field. */ + s->fdf = 0x00; + + /* This protocol uses fixed number of data channels for PCM samples. */ + p = s->protocol; + p->pcm_channels = pcm_channels; + + return 0; +} diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 9ecc5505723f..195be0bfb054 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -21,8 +21,11 @@ #include #include #include +#include +#include #include "../lib.h" +#include "../amdtp-stream.h" struct snd_tscm_spec { const char *const name; @@ -51,6 +54,13 @@ struct snd_tscm { #define TSCM_OFFSET_FIRMWARE_ARM 0x0008 #define TSCM_OFFSET_FIRMWARE_HW 0x000c +int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, unsigned int pcm_channels); +int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate); +int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime); +void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); + void snd_tscm_proc_init(struct snd_tscm *tscm); #endif -- cgit v1.2.3-59-g8ed1b From 35efa5c489de63a9bdbb7ea4e66dcfadcca951b4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:15 +0900 Subject: ALSA: firewire-tascam: add streaming functionality This commit adds streaming functionality for both direction. To utilize the sequence of the number of data blocks in packets, full duplex with synchronization is applied. Besides, TASCAM FireWire series allows drivers to decide which PCM data channels are enabled. For convenience, this driver always enable whole the data channels. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/Makefile | 3 +- sound/firewire/tascam/tascam-stream.c | 457 ++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.c | 12 +- sound/firewire/tascam/tascam.h | 36 +++ 4 files changed, 506 insertions(+), 2 deletions(-) create mode 100644 sound/firewire/tascam/tascam-stream.c (limited to 'sound') diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index d06c737ae261..0a1c38732805 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,2 +1,3 @@ -snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam.o +snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \ + tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c new file mode 100644 index 000000000000..0732f7f0c736 --- /dev/null +++ b/sound/firewire/tascam/tascam-stream.c @@ -0,0 +1,457 @@ +/* + * tascam-stream.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include "tascam.h" + +#define CALLBACK_TIMEOUT 500 + +static int get_clock(struct snd_tscm *tscm, u32 *data) +{ + __be32 reg; + int err; + + err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, + ®, sizeof(reg), 0); + if (err >= 0) + *data = be32_to_cpu(reg); + + return err; +} + +static int set_clock(struct snd_tscm *tscm, unsigned int rate, + enum snd_tscm_clock clock) +{ + u32 data; + __be32 reg; + int err; + + err = get_clock(tscm, &data); + if (err < 0) + return err; + data &= 0x0000ffff; + + if (rate > 0) { + data &= 0x000000ff; + /* Base rate. */ + if ((rate % 44100) == 0) { + data |= 0x00000100; + /* Multiplier. */ + if (rate / 44100 == 2) + data |= 0x00008000; + } else if ((rate % 48000) == 0) { + data |= 0x00000200; + /* Multiplier. */ + if (rate / 48000 == 2) + data |= 0x00008000; + } else { + return -EAGAIN; + } + } + + if (clock != INT_MAX) { + data &= 0x0000ff00; + data |= clock + 1; + } + + reg = cpu_to_be32(data); + + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + if (data & 0x00008000) + reg = cpu_to_be32(0x0000001a); + else + reg = cpu_to_be32(0x0000000d); + + return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE, + ®, sizeof(reg), 0); +} + +int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate) +{ + u32 data = 0x0; + unsigned int trials = 0; + int err; + + while (data == 0x0 || trials++ < 5) { + err = get_clock(tscm, &data); + if (err < 0) + return err; + + data = (data & 0xff000000) >> 24; + } + + /* Check base rate. */ + if ((data & 0x0f) == 0x01) + *rate = 44100; + else if ((data & 0x0f) == 0x02) + *rate = 48000; + else + return -EAGAIN; + + /* Check multiplier. */ + if ((data & 0xf0) == 0x80) + *rate *= 2; + else if ((data & 0xf0) != 0x00) + return -EAGAIN; + + return err; +} + +int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock) +{ + u32 data; + int err; + + err = get_clock(tscm, &data); + if (err < 0) + return err; + + *clock = ((data & 0x00ff0000) >> 16) - 1; + if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT) + return -EIO; + + return 0; +} + +static int enable_data_channels(struct snd_tscm *tscm) +{ + __be32 reg; + u32 data; + unsigned int i; + int err; + + data = 0; + for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i) + data |= BIT(i); + if (tscm->spec->has_adat) + data |= 0x0000ff00; + if (tscm->spec->has_spdif) + data |= 0x00030000; + + reg = cpu_to_be32(data); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + data = 0; + for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i) + data |= BIT(i); + if (tscm->spec->has_adat) + data |= 0x0000ff00; + if (tscm->spec->has_spdif) + data |= 0x00030000; + + reg = cpu_to_be32(data); + return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS, + ®, sizeof(reg), 0); +} + +static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate) +{ + __be32 reg; + int err; + + /* Set an option for unknown purpose. */ + reg = cpu_to_be32(0x00200000); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + err = enable_data_channels(tscm); + if (err < 0) + return err; + + return set_clock(tscm, rate, INT_MAX); +} + +static void finish_session(struct snd_tscm *tscm) +{ + __be32 reg; + + reg = 0; + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, + ®, sizeof(reg), 0); + + reg = 0; + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, + ®, sizeof(reg), 0); + +} + +static int begin_session(struct snd_tscm *tscm) +{ + __be32 reg; + int err; + + reg = cpu_to_be32(0x00000001); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + reg = cpu_to_be32(0x00000001); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* Set an option for unknown purpose. */ + reg = cpu_to_be32(0x00002000); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* Start multiplexing PCM samples on packets. */ + reg = cpu_to_be32(0x00000001); + return snd_fw_transaction(tscm->unit, + TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON, + ®, sizeof(reg), 0); +} + +static void release_resources(struct snd_tscm *tscm) +{ + __be32 reg; + + /* Unregister channels. */ + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, + ®, sizeof(reg), 0); + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, + ®, sizeof(reg), 0); + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, + ®, sizeof(reg), 0); + + /* Release isochronous resources. */ + fw_iso_resources_free(&tscm->tx_resources); + fw_iso_resources_free(&tscm->rx_resources); +} + +static int keep_resources(struct snd_tscm *tscm, unsigned int rate) +{ + __be32 reg; + int err; + + /* Keep resources for in-stream. */ + err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate); + if (err < 0) + return err; + err = fw_iso_resources_allocate(&tscm->tx_resources, + amdtp_stream_get_max_payload(&tscm->tx_stream), + fw_parent_device(tscm->unit)->max_speed); + if (err < 0) + goto error; + + /* Keep resources for out-stream. */ + err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate); + if (err < 0) + return err; + err = fw_iso_resources_allocate(&tscm->rx_resources, + amdtp_stream_get_max_payload(&tscm->rx_stream), + fw_parent_device(tscm->unit)->max_speed); + if (err < 0) + return err; + + /* Register the isochronous channel for transmitting stream. */ + reg = cpu_to_be32(tscm->tx_resources.channel); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, + ®, sizeof(reg), 0); + if (err < 0) + goto error; + + /* Unknown */ + reg = cpu_to_be32(0x00000002); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, + ®, sizeof(reg), 0); + if (err < 0) + goto error; + + /* Register the isochronous channel for receiving stream. */ + reg = cpu_to_be32(tscm->rx_resources.channel); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, + ®, sizeof(reg), 0); + if (err < 0) + goto error; + + return 0; +error: + release_resources(tscm); + return err; +} + +int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) +{ + unsigned int pcm_channels; + int err; + + /* For out-stream. */ + err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit); + if (err < 0) + return err; + pcm_channels = tscm->spec->pcm_playback_analog_channels; + if (tscm->spec->has_adat) + pcm_channels += 8; + if (tscm->spec->has_spdif) + pcm_channels += 2; + err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM, + pcm_channels); + if (err < 0) + return err; + + /* For in-stream. */ + err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit); + if (err < 0) + return err; + pcm_channels = tscm->spec->pcm_capture_analog_channels; + if (tscm->spec->has_adat) + pcm_channels += 8; + if (tscm->spec->has_spdif) + pcm_channels += 2; + err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM, + pcm_channels); + if (err < 0) + amdtp_stream_destroy(&tscm->rx_stream); + + return 0; +} + +/* At bus reset, streaming is stopped and some registers are clear. */ +void snd_tscm_stream_update_duplex(struct snd_tscm *tscm) +{ + amdtp_stream_pcm_abort(&tscm->tx_stream); + amdtp_stream_stop(&tscm->tx_stream); + + amdtp_stream_pcm_abort(&tscm->rx_stream); + amdtp_stream_stop(&tscm->rx_stream); +} + +/* + * This function should be called before starting streams or after stopping + * streams. + */ +void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) +{ + amdtp_stream_destroy(&tscm->rx_stream); + amdtp_stream_destroy(&tscm->tx_stream); + + fw_iso_resources_destroy(&tscm->rx_resources); + fw_iso_resources_destroy(&tscm->tx_resources); +} + +int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) +{ + unsigned int curr_rate; + int err; + + if (tscm->substreams_counter == 0) + return 0; + + err = snd_tscm_stream_get_rate(tscm, &curr_rate); + if (err < 0) + return err; + if (curr_rate != rate || + amdtp_streaming_error(&tscm->tx_stream) || + amdtp_streaming_error(&tscm->rx_stream)) { + finish_session(tscm); + + amdtp_stream_stop(&tscm->tx_stream); + amdtp_stream_stop(&tscm->rx_stream); + + release_resources(tscm); + } + + if (!amdtp_stream_running(&tscm->tx_stream)) { + amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE, + &tscm->tx_stream, &tscm->rx_stream); + err = keep_resources(tscm, rate); + if (err < 0) + goto error; + + err = set_stream_formats(tscm, rate); + if (err < 0) + goto error; + + err = begin_session(tscm); + if (err < 0) + goto error; + + err = amdtp_stream_start(&tscm->tx_stream, + tscm->tx_resources.channel, + fw_parent_device(tscm->unit)->max_speed); + if (err < 0) + goto error; + + if (!amdtp_stream_wait_callback(&tscm->tx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; + } + } + + if (!amdtp_stream_running(&tscm->rx_stream)) { + err = amdtp_stream_start(&tscm->rx_stream, + tscm->rx_resources.channel, + fw_parent_device(tscm->unit)->max_speed); + if (err < 0) + goto error; + + if (!amdtp_stream_wait_callback(&tscm->rx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; + } + } + + return 0; +error: + amdtp_stream_stop(&tscm->tx_stream); + amdtp_stream_stop(&tscm->rx_stream); + + finish_session(tscm); + release_resources(tscm); + + return err; +} + +void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) +{ + if (tscm->substreams_counter > 0) + return; + + amdtp_stream_stop(&tscm->tx_stream); + amdtp_stream_stop(&tscm->rx_stream); + + finish_session(tscm); + release_resources(tscm); +} diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index d7418c065f4e..c2563606ac70 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -82,6 +82,8 @@ static void tscm_card_free(struct snd_card *card) { struct snd_tscm *tscm = card->private_data; + snd_tscm_stream_destroy_duplex(tscm); + fw_unit_put(tscm->unit); mutex_destroy(&tscm->mutex); @@ -115,6 +117,10 @@ static int snd_tscm_probe(struct fw_unit *unit, snd_tscm_proc_init(tscm); + err = snd_tscm_stream_init_duplex(tscm); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; @@ -129,7 +135,11 @@ error: static void snd_tscm_update(struct fw_unit *unit) { - return; + struct snd_tscm *tscm = dev_get_drvdata(&unit->device); + + mutex_lock(&tscm->mutex); + snd_tscm_stream_update_duplex(tscm); + mutex_unlock(&tscm->mutex); } static void snd_tscm_remove(struct fw_unit *unit) diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 195be0bfb054..41fe3a974b90 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -26,6 +26,7 @@ #include "../lib.h" #include "../amdtp-stream.h" +#include "../iso-resources.h" struct snd_tscm_spec { const char *const name; @@ -45,6 +46,12 @@ struct snd_tscm { struct mutex mutex; const struct snd_tscm_spec *spec; + + struct fw_iso_resources tx_resources; + struct fw_iso_resources rx_resources; + struct amdtp_stream tx_stream; + struct amdtp_stream rx_stream; + unsigned int substreams_counter; }; #define TSCM_ADDR_BASE 0xffff00000000ull @@ -54,6 +61,26 @@ struct snd_tscm { #define TSCM_OFFSET_FIRMWARE_ARM 0x0008 #define TSCM_OFFSET_FIRMWARE_HW 0x000c +#define TSCM_OFFSET_ISOC_TX_CH 0x0200 +#define TSCM_OFFSET_UNKNOWN 0x0204 +#define TSCM_OFFSET_START_STREAMING 0x0208 +#define TSCM_OFFSET_ISOC_RX_CH 0x020c +#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */ +#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214 +#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218 +#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c +#define TSCM_OFFSET_ISOC_TX_ON 0x0220 +/* Unknown 0x0224 */ +#define TSCM_OFFSET_CLOCK_STATUS 0x0228 +#define TSCM_OFFSET_SET_OPTION 0x022c + +enum snd_tscm_clock { + SND_TSCM_CLOCK_INTERNAL = 0, + SND_TSCM_CLOCK_WORD = 1, + SND_TSCM_CLOCK_SPDIF = 2, + SND_TSCM_CLOCK_ADAT = 3, +}; + int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, unsigned int pcm_channels); int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate); @@ -61,6 +88,15 @@ int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); +int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate); +int snd_tscm_stream_get_clock(struct snd_tscm *tscm, + enum snd_tscm_clock *clock); +int snd_tscm_stream_init_duplex(struct snd_tscm *tscm); +void snd_tscm_stream_update_duplex(struct snd_tscm *tscm); +void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm); +int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate); +void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm); + void snd_tscm_proc_init(struct snd_tscm *tscm); #endif -- cgit v1.2.3-59-g8ed1b From e453df44f0d6574e99fae990c89a22c6ec6bbb62 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:16 +0900 Subject: ALSA: firewire-tascam: add PCM functionality This commit adds PCM functionality to transmit/receive PCM samples. When one of PCM substreams are running or external clock source is selected, current sampling rate is used. Else, the sampling rate is changed as an userspace application requests. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/Makefile | 2 +- sound/firewire/tascam/tascam-pcm.c | 301 +++++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.c | 4 + sound/firewire/tascam/tascam.h | 2 + 4 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/tascam/tascam-pcm.c (limited to 'sound') diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index 0a1c38732805..f075b9bb723f 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,3 +1,3 @@ snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \ - tascam.o + tascam-pcm.o tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c new file mode 100644 index 000000000000..696b371e3c44 --- /dev/null +++ b/sound/firewire/tascam/tascam-pcm.c @@ -0,0 +1,301 @@ +/* + * tascam-pcm.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "tascam.h" + +static void set_buffer_params(struct snd_pcm_hardware *hw) +{ + hw->period_bytes_min = 4 * hw->channels_min; + hw->period_bytes_max = hw->period_bytes_min * 2048; + hw->buffer_bytes_max = hw->period_bytes_max * 2; + + hw->periods_min = 2; + hw->periods_max = UINT_MAX; +} + +static int pcm_init_hw_params(struct snd_tscm *tscm, + struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .channels_min = 10, + .channels_max = 18, + }; + struct snd_pcm_runtime *runtime = substream->runtime; + struct amdtp_stream *stream; + unsigned int pcm_channels; + + runtime->hw = hardware; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; + stream = &tscm->tx_stream; + pcm_channels = tscm->spec->pcm_capture_analog_channels; + } else { + runtime->hw.formats = + SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; + stream = &tscm->rx_stream; + pcm_channels = tscm->spec->pcm_playback_analog_channels; + } + + if (tscm->spec->has_adat) + pcm_channels += 8; + if (tscm->spec->has_spdif) + pcm_channels += 2; + runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels; + + set_buffer_params(&runtime->hw); + + return amdtp_tscm_add_pcm_hw_constraints(stream, runtime); +} + +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + enum snd_tscm_clock clock; + unsigned int rate; + int err; + + err = pcm_init_hw_params(tscm, substream); + if (err < 0) + return err; + + err = snd_tscm_stream_get_clock(tscm, &clock); + if (clock != SND_TSCM_CLOCK_INTERNAL || + amdtp_stream_pcm_running(&tscm->rx_stream) || + amdtp_stream_pcm_running(&tscm->tx_stream)) { + err = snd_tscm_stream_get_rate(tscm, &rate); + if (err < 0) + return err; + substream->runtime->hw.rate_min = rate; + substream->runtime->hw.rate_max = rate; + } + + snd_pcm_set_sync(substream); + + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int pcm_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_tscm *tscm = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&tscm->mutex); + tscm->substreams_counter++; + mutex_unlock(&tscm->mutex); + } + + amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params)); + + return 0; +} + +static int pcm_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_tscm *tscm = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&tscm->mutex); + tscm->substreams_counter++; + mutex_unlock(&tscm->mutex); + } + + amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params)); + + return 0; +} + +static int pcm_capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + + mutex_lock(&tscm->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + tscm->substreams_counter--; + + snd_tscm_stream_stop_duplex(tscm); + + mutex_unlock(&tscm->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + + mutex_lock(&tscm->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + tscm->substreams_counter--; + + snd_tscm_stream_stop_duplex(tscm); + + mutex_unlock(&tscm->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + mutex_lock(&tscm->mutex); + + err = snd_tscm_stream_start_duplex(tscm, runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&tscm->tx_stream); + + mutex_unlock(&tscm->mutex); + + return err; +} + +static int pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + mutex_lock(&tscm->mutex); + + err = snd_tscm_stream_start_duplex(tscm, runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&tscm->rx_stream); + + mutex_unlock(&tscm->mutex); + + return err; +} + +static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tscm *tscm = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&tscm->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tscm *tscm = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&tscm->rx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) +{ + struct snd_tscm *tscm = sbstrm->private_data; + + return amdtp_stream_pcm_pointer(&tscm->tx_stream); +} + +static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) +{ + struct snd_tscm *tscm = sbstrm->private_data; + + return amdtp_stream_pcm_pointer(&tscm->rx_stream); +} + +static struct snd_pcm_ops pcm_capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, +}; + +static struct snd_pcm_ops pcm_playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm); + if (err < 0) + return err; + + pcm->private_data = tscm; + snprintf(pcm->name, sizeof(pcm->name), + "%s PCM", tscm->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + + return 0; +} diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index c2563606ac70..bb4f70656f2f 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -121,6 +121,10 @@ static int snd_tscm_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_tscm_create_pcm_devices(tscm); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 41fe3a974b90..28c875f7808c 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -99,4 +99,6 @@ void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm); void snd_tscm_proc_init(struct snd_tscm *tscm); +int snd_tscm_create_pcm_devices(struct snd_tscm *tscm); + #endif -- cgit v1.2.3-59-g8ed1b From e5e0c3dd257bf34cf001e10422943f90437f0f1b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 1 Oct 2015 22:02:17 +0900 Subject: ALSA: firewire-tascam: add hwdep interface This commit adds hwdep interface so as the other IEEE 1394 sound devices has. This interface is designed for mixer/control applications. By using this interface, an application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 +- include/uapi/sound/firewire.h | 1 + sound/firewire/Kconfig | 1 + sound/firewire/tascam/Makefile | 2 +- sound/firewire/tascam/tascam-hwdep.c | 201 ++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam-pcm.c | 17 ++- sound/firewire/tascam/tascam-stream.c | 39 +++++++ sound/firewire/tascam/tascam.c | 6 + sound/firewire/tascam/tascam.h | 13 +++ 9 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 sound/firewire/tascam/tascam-hwdep.c (limited to 'sound') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index aa329132f6c4..a82108e5d1c0 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -101,9 +101,10 @@ enum { SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */ SNDRV_HWDEP_IFACE_FW_OXFW, /* Oxford OXFW970/971 based device */ SNDRV_HWDEP_IFACE_FW_DIGI00X, /* Digidesign Digi 002/003 family */ + SNDRV_HWDEP_IFACE_FW_TASCAM, /* TASCAM FireWire series */ /* Don't forget to change the following: */ - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DIGI00X + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM }; struct snd_hwdep_info { diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index deb041cb9af0..db79a12fcc78 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -64,6 +64,7 @@ union snd_firewire_event { #define SNDRV_FIREWIRE_TYPE_BEBOB 3 #define SNDRV_FIREWIRE_TYPE_OXFW 4 #define SNDRV_FIREWIRE_TYPE_DIGI00X 5 +#define SNDRV_FIREWIRE_TYPE_TASCAM 6 /* RME, MOTU, ... */ struct snd_firewire_get_info { diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index bb3f2610c6e9..bee0e5f1a116 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -138,6 +138,7 @@ config SND_FIREWIRE_DIGI00X config SND_FIREWIRE_TASCAM tristate "TASCAM FireWire series support" select SND_FIREWIRE_LIB + select SND_HWDEP help Say Y here to include support for TASCAM. * FW-1884 diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index f075b9bb723f..6beefc2ae8b0 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,3 +1,3 @@ snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \ - tascam-pcm.o tascam.o + tascam-pcm.o tascam-hwdep.o tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c new file mode 100644 index 000000000000..131267c3a042 --- /dev/null +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -0,0 +1,201 @@ +/* + * tascam-hwdep.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * This codes give three functionality. + * + * 1.get firewire node information + * 2.get notification about starting/stopping stream + * 3.lock/unlock stream + */ + +#include "tascam.h" + +static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf, + long count) +{ + union snd_firewire_event event; + + memset(&event, 0, sizeof(event)); + + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = (tscm->dev_lock_count > 0); + tscm->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, + loff_t *offset) +{ + struct snd_tscm *tscm = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&tscm->lock); + + while (!tscm->dev_lock_changed) { + prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&tscm->lock); + schedule(); + finish_wait(&tscm->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&tscm->lock); + } + + memset(&event, 0, sizeof(event)); + count = hwdep_read_locked(tscm, buf, count); + spin_unlock_irq(&tscm->lock); + + return count; +} + +static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_tscm *tscm = hwdep->private_data; + unsigned int events; + + poll_wait(file, &tscm->hwdep_wait, wait); + + spin_lock_irq(&tscm->lock); + if (tscm->dev_lock_changed) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&tscm->lock); + + return events; +} + +static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(tscm->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_TASCAM; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int hwdep_lock(struct snd_tscm *tscm) +{ + int err; + + spin_lock_irq(&tscm->lock); + + if (tscm->dev_lock_count == 0) { + tscm->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&tscm->lock); + + return err; +} + +static int hwdep_unlock(struct snd_tscm *tscm) +{ + int err; + + spin_lock_irq(&tscm->lock); + + if (tscm->dev_lock_count == -1) { + tscm->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&tscm->lock); + + return err; +} + +static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_tscm *tscm = hwdep->private_data; + + spin_lock_irq(&tscm->lock); + if (tscm->dev_lock_count == -1) + tscm->dev_lock_count = 0; + spin_unlock_irq(&tscm->lock); + + return 0; +} + +static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_tscm *tscm = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(tscm, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(tscm); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(tscm); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +static const struct snd_hwdep_ops hwdep_ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, +}; + +int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) +{ + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep); + if (err < 0) + return err; + + strcpy(hwdep->name, "Tascam"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM; + hwdep->ops = hwdep_ops; + hwdep->private_data = tscm; + hwdep->exclusive = true; + + return err; +} diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index 696b371e3c44..380d3db969a5 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -72,9 +72,13 @@ static int pcm_open(struct snd_pcm_substream *substream) unsigned int rate; int err; + err = snd_tscm_stream_lock_try(tscm); + if (err < 0) + goto end; + err = pcm_init_hw_params(tscm, substream); if (err < 0) - return err; + goto err_locked; err = snd_tscm_stream_get_clock(tscm, &clock); if (clock != SND_TSCM_CLOCK_INTERNAL || @@ -82,18 +86,25 @@ static int pcm_open(struct snd_pcm_substream *substream) amdtp_stream_pcm_running(&tscm->tx_stream)) { err = snd_tscm_stream_get_rate(tscm, &rate); if (err < 0) - return err; + goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); - +end: + return err; +err_locked: + snd_tscm_stream_lock_release(tscm); return err; } static int pcm_close(struct snd_pcm_substream *substream) { + struct snd_tscm *tscm = substream->private_data; + + snd_tscm_stream_lock_release(tscm); + return 0; } diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index 0732f7f0c736..0e6dd5c61f53 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -455,3 +455,42 @@ void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) finish_session(tscm); release_resources(tscm); } + +void snd_tscm_stream_lock_changed(struct snd_tscm *tscm) +{ + tscm->dev_lock_changed = true; + wake_up(&tscm->hwdep_wait); +} + +int snd_tscm_stream_lock_try(struct snd_tscm *tscm) +{ + int err; + + spin_lock_irq(&tscm->lock); + + /* user land lock this */ + if (tscm->dev_lock_count < 0) { + err = -EBUSY; + goto end; + } + + /* this is the first time */ + if (tscm->dev_lock_count++ == 0) + snd_tscm_stream_lock_changed(tscm); + err = 0; +end: + spin_unlock_irq(&tscm->lock); + return err; +} + +void snd_tscm_stream_lock_release(struct snd_tscm *tscm) +{ + spin_lock_irq(&tscm->lock); + + if (WARN_ON(tscm->dev_lock_count <= 0)) + goto end; + if (--tscm->dev_lock_count == 0) + snd_tscm_stream_lock_changed(tscm); +end: + spin_unlock_irq(&tscm->lock); +} diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index bb4f70656f2f..ee2f498dcce4 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -110,6 +110,8 @@ static int snd_tscm_probe(struct fw_unit *unit, tscm->spec = (const struct snd_tscm_spec *)entry->driver_data; mutex_init(&tscm->mutex); + spin_lock_init(&tscm->lock); + init_waitqueue_head(&tscm->hwdep_wait); err = check_name(tscm); if (err < 0) @@ -125,6 +127,10 @@ static int snd_tscm_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_tscm_create_hwdep_device(tscm); + if (err < 0) + goto error; + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 28c875f7808c..75a3b9a81fea 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "../lib.h" #include "../amdtp-stream.h" @@ -44,6 +46,7 @@ struct snd_tscm { struct fw_unit *unit; struct mutex mutex; + spinlock_t lock; const struct snd_tscm_spec *spec; @@ -52,6 +55,10 @@ struct snd_tscm { struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; unsigned int substreams_counter; + + int dev_lock_count; + bool dev_lock_changed; + wait_queue_head_t hwdep_wait; }; #define TSCM_ADDR_BASE 0xffff00000000ull @@ -97,8 +104,14 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm); int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate); void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm); +void snd_tscm_stream_lock_changed(struct snd_tscm *tscm); +int snd_tscm_stream_lock_try(struct snd_tscm *tscm); +void snd_tscm_stream_lock_release(struct snd_tscm *tscm); + void snd_tscm_proc_init(struct snd_tscm *tscm); int snd_tscm_create_pcm_devices(struct snd_tscm *tscm); +int snd_tscm_create_hwdep_device(struct snd_tscm *tscm); + #endif -- cgit v1.2.3-59-g8ed1b From 50e6168f27fdc1f915d3341743b46147c626dc6e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 28 Sep 2015 14:01:08 +0100 Subject: ASoC: arizona: add defines for single-input gain control Adds convenience defines for declaring a gain control that has an input mux. These blocks are functionally equivalent to the existing mixer blocks but can only have a single input active at once. Signed-off-by: Richard Fitzgerald Acked-by: Mark Brown Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 7b68d05a0939..b73920ab8650 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -99,6 +99,11 @@ extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; +#define ARIZONA_GAINMUX_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv) + #define ARIZONA_MIXER_CONTROLS(name, base) \ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \ ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ -- cgit v1.2.3-59-g8ed1b From 6ebbce0a6e15fd52ef029f780ec5fb3282fc22e3 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 28 Sep 2015 14:01:09 +0100 Subject: ASoC: wm8998: Initial WM8998 codec driver Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/arizona.c | 33 + sound/soc/codecs/arizona.h | 7 +- sound/soc/codecs/wm8998.c | 1430 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wm8998.h | 23 + 6 files changed, 1500 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/wm8998.c create mode 100644 sound/soc/codecs/wm8998.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733ecd17f..6c30bade64b6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -171,6 +171,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8996 if I2C select SND_SOC_WM8997 if MFD_WM8997 + select SND_SOC_WM8998 if MFD_WM8998 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS @@ -195,9 +196,11 @@ config SND_SOC_ARIZONA default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM8997=y + default y if SND_SOC_WM8998=y default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM8997=m + default m if SND_SOC_WM8998=m config SND_SOC_WM_HUBS tristate @@ -865,6 +868,9 @@ config SND_SOC_WM8996 config SND_SOC_WM8997 tristate +config SND_SOC_WM8998 + tristate + config SND_SOC_WM9081 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077954ae..a9d47693aa56 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -176,6 +176,7 @@ snd-soc-wm8993-objs := wm8993.o snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o snd-soc-wm8995-objs := wm8995.o snd-soc-wm8997-objs := wm8997.o +snd-soc-wm8998-objs := wm8998.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9090-objs := wm9090.o snd-soc-wm9705-objs := wm9705.o @@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o +obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ac21b85ff75f..9929efc6b9aa 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -316,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "Tone Generator 2", "Haptics", "AEC", + "AEC2", "Mic Mute Mixer", "Noise Generator", "IN1L", @@ -423,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x05, 0x06, /* Haptics */ 0x08, /* AEC */ + 0x09, /* AEC2 */ 0x0c, /* Noise mixer */ 0x0d, /* Comfort noise */ 0x10, /* IN1L */ @@ -527,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values); const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); EXPORT_SYMBOL_GPL(arizona_mixer_tlv); +const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + "12kHz", "24kHz", "48kHz", "96kHz", "192kHz", + "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz", + "4kHz", "8kHz", "16kHz", "32kHz", +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_text); + +const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_val); + +const char *arizona_sample_rate_val_to_name(unsigned int rate_val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) { + if (arizona_sample_rate_val[i] == rate_val) + return arizona_sample_rate_text[i]; + } + + return "Illegal"; +} +EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name); + const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = { "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate", }; @@ -1884,6 +1912,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll, if (fll->arizona->rev < 3 || sync) return init_ratio; break; + case WM8998: + case WM1814: + if (sync) + return init_ratio; + break; default: return init_ratio; } diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index b73920ab8650..fea8b8ae8e1a 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -93,7 +93,7 @@ struct arizona_priv { bool dvfs_cached; }; -#define ARIZONA_NUM_MIXER_INPUTS 103 +#define ARIZONA_NUM_MIXER_INPUTS 104 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -214,8 +214,12 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; .num_regs = 1 }) } #define ARIZONA_RATE_ENUM_SIZE 4 +#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14 + extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; +extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; +extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; extern const struct soc_enum arizona_isrc_fsl[]; extern const struct soc_enum arizona_isrc_fsh[]; @@ -301,4 +305,5 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift); +extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val); #endif diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c new file mode 100644 index 000000000000..8782dfb628ab --- /dev/null +++ b/sound/soc/codecs/wm8998.c @@ -0,0 +1,1430 @@ +/* + * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm8998.h" + +struct wm8998_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = snd_soc_read(codec, ARIZONA_ASRC_RATE1); + val &= ARIZONA_ASRC_RATE1_MASK; + val >>= ARIZONA_ASRC_RATE1_SHIFT; + + switch (val) { + case 0: + case 1: + case 2: + val = snd_soc_read(codec, + ARIZONA_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(codec->dev, + "Unsupported ASRC rate1 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, + "Illegal ASRC rate1 selector (0x%x)\n", + val); + return -EINVAL; + } + + val = snd_soc_read(codec, ARIZONA_ASRC_RATE2); + val &= ARIZONA_ASRC_RATE2_MASK; + val >>= ARIZONA_ASRC_RATE2_SHIFT; + + switch (val) { + case 8: + case 9: + val -= 0x8; + val = snd_soc_read(codec, + ARIZONA_ASYNC_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(codec->dev, + "Unsupported ASRC rate2 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, + "Illegal ASRC rate2 selector (0x%x)\n", + val); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, inmode; + unsigned int mode_val, src_val; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + /* L and R registers have same shift and mask */ + inmode = arizona->pdata.inmode[2 * mux]; + src_val = mux << ARIZONA_IN1L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; + + switch (arizona->pdata.inmode[0]) { + case ARIZONA_INMODE_DMIC: + if (mux) + mode_val = 0; /* B always analogue */ + else + mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; + + snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_MODE_MASK, mode_val); + + /* IN1A is digital so L and R must change together */ + /* src_val setting same for both registers */ + snd_soc_update_bits(codec, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, src_val); + snd_soc_update_bits(codec, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_MASK | + ARIZONA_IN1R_SRC_SE_MASK, src_val); + break; + default: + /* both analogue */ + snd_soc_update_bits(codec, + e->reg, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, + src_val); + break; + } + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, inmode, src_val, mode_val; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + inmode = arizona->pdata.inmode[1 + (2 * mux)]; + if (inmode & ARIZONA_INMODE_DMIC) + mode_val = 1 << ARIZONA_IN2_MODE_SHIFT; + else + mode_val = 0; + + src_val = mux << ARIZONA_IN2L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT; + + snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_MODE_MASK, mode_val); + + snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK, + src_val); + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static const char * const wm8998_inmux_texts[] = { + "A", + "B", +}; + +static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_SHIFT, + wm8998_inmux_texts); + +static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_SHIFT, + wm8998_inmux_texts); + +static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_SHIFT, + wm8998_inmux_texts); + +static const struct snd_kcontrol_new wm8998_in1mux[2] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, + snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum, + snd_soc_dapm_get_enum_double, wm8998_in1mux_put), +}; + +static const struct snd_kcontrol_new wm8998_in2mux = + SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum, + snd_soc_dapm_get_enum_double, wm8998_in2mux_put); + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM8998_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0) + +static const struct snd_kcontrol_new wm8998_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L), +WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R), +WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L), +WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R), +WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L), +WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char * const wm8998_aec_loopback_texts[] = { + "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT", + "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR", +}; + +static const unsigned int wm8998_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, + ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, + ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback), +}; + +static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1AL"), +SND_SOC_DAPM_INPUT("IN1AR"), +SND_SOC_DAPM_INPUT("IN1BL"), +SND_SOC_DAPM_INPUT("IN1BR"), +SND_SOC_DAPM_INPUT("IN2A"), +SND_SOC_DAPM_INPUT("IN2B"), + +SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]), +SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]), +SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[0]), + +SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[1]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0), + +ARIZONA_MUX_WIDGETS(EQ1, "EQ1"), +ARIZONA_MUX_WIDGETS(EQ2, "EQ2"), +ARIZONA_MUX_WIDGETS(EQ3, "EQ3"), +ARIZONA_MUX_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"), +ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"), +ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"), +ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"), + +ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"), +ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +SND_SOC_DAPM_OUTPUT("EPOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDATL"), +SND_SOC_DAPM_OUTPUT("SPKDATR"), +SND_SOC_DAPM_OUTPUT("SPDIF"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2 PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "IN1AL", NULL, "SYSCLK" }, + { "IN1AR", NULL, "SYSCLK" }, + { "IN1BL", NULL, "SYSCLK" }, + { "IN1BR", NULL, "SYSCLK" }, + { "IN2A", NULL, "SYSCLK" }, + { "IN2B", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + + { "SLIMRX3", NULL, "Slim2 Playback" }, + { "SLIMRX4", NULL, "Slim2 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + + { "IN1L Mux", "A", "IN1AL" }, + { "IN1R Mux", "A", "IN1AR" }, + { "IN1L Mux", "B", "IN1BL" }, + { "IN1R Mux", "B", "IN1BR" }, + + { "IN2 Mux", "A", "IN2A" }, + { "IN2 Mux", "B", "IN2B" }, + + { "IN1L PGA", NULL, "IN1L Mux" }, + { "IN1R PGA", NULL, "IN1R Mux" }, + { "IN2 PGA", NULL, "IN2 Mux" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"), + ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"), + ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"), + ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"), + + ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"), + ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"), + + ARIZONA_MUX_ROUTES("EQ1", "EQ1"), + ARIZONA_MUX_ROUTES("EQ2", "EQ2"), + ARIZONA_MUX_ROUTES("EQ3", "EQ3"), + ARIZONA_MUX_ROUTES("EQ4", "EQ4"), + + ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC1 Loopback", "HPOUTL", "OUT1L" }, + { "AEC1 Loopback", "HPOUTR", "OUT1R" }, + { "AEC2 Loopback", "HPOUTL", "OUT1L" }, + { "AEC2 Loopback", "HPOUTR", "OUT1R" }, + { "HPOUTL", NULL, "OUT1L" }, + { "HPOUTR", NULL, "OUT1R" }, + + { "AEC1 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC1 Loopback", "LINEOUTR", "OUT2R" }, + { "AEC2 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC2 Loopback", "LINEOUTR", "OUT2R" }, + { "LINEOUTL", NULL, "OUT2L" }, + { "LINEOUTR", NULL, "OUT2R" }, + + { "AEC1 Loopback", "EPOUT", "OUT3" }, + { "AEC2 Loopback", "EPOUT", "OUT3" }, + { "EPOUT", NULL, "OUT3" }, + + { "AEC1 Loopback", "SPKOUTL", "OUT4L" }, + { "AEC2 Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC1 Loopback", "SPKOUTR", "OUT4R" }, + { "AEC2 Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "SPDIF", NULL, "SPD1" }, + + { "AEC1 Loopback", "SPKDATL", "OUT5L" }, + { "AEC1 Loopback", "SPKDATR", "OUT5R" }, + { "AEC2 Loopback", "SPKDATL", "OUT5L" }, + { "AEC2 Loopback", "SPKDATR", "OUT5R" }, + { "SPKDATL", NULL, "OUT5L" }, + { "SPKDATR", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, +}; + +#define WM8998_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8998_dai[] = { + { + .name = "wm8998-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "wm8998-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8998-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM8998_FLL1: + return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout); + case WM8998_FLL2: + return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout); + case WM8998_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref, + Fout); + case WM8998_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +static int wm8998_codec_probe(struct snd_soc_codec *codec) +{ + struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + priv->core.arizona->dapm = dapm; + + arizona_init_spk(codec); + arizona_init_gpio(codec); + + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + + return 0; +} + +static int wm8998_codec_remove(struct snd_soc_codec *codec) +{ + struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define WM8998_DIG_VU 0x0200 + +static unsigned int wm8998_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct regmap *wm8998_get_regmap(struct device *dev) +{ + struct wm8998_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wm8998 = { + .probe = wm8998_codec_probe, + .remove = wm8998_codec_remove, + .get_regmap = wm8998_get_regmap, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8998_set_fll, + + .controls = wm8998_snd_controls, + .num_controls = ARRAY_SIZE(wm8998_snd_controls), + .dapm_widgets = wm8998_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets), + .dapm_routes = wm8998_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes), +}; + +static int wm8998_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8998_priv *wm8998; + int i; + + wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv), + GFP_KERNEL); + if (!wm8998) + return -ENOMEM; + platform_set_drvdata(pdev, wm8998); + + wm8998->core.arizona = arizona; + wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ + + for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++) + wm8998->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8998->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8998->fll[1]); + + for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++) + arizona_init_dai(&wm8998->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8998_digital_vu[i], + WM8998_DIG_VU, WM8998_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998, + wm8998_dai, ARRAY_SIZE(wm8998_dai)); +} + +static int wm8998_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm8998_codec_driver = { + .driver = { + .name = "wm8998-codec", + }, + .probe = wm8998_probe, + .remove = wm8998_remove, +}; + +module_platform_driver(wm8998_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8998 driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:wm8998-codec"); diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h new file mode 100644 index 000000000000..1e8647252162 --- /dev/null +++ b/sound/soc/codecs/wm8998.h @@ -0,0 +1,23 @@ +/* + * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8998_H +#define _WM8998_H + +#include "arizona.h" + +#define WM8998_FLL1 1 +#define WM8998_FLL2 2 +#define WM8998_FLL1_REFCLK 3 +#define WM8998_FLL2_REFCLK 4 + +#endif -- cgit v1.2.3-59-g8ed1b From 5b2688a59af686f7c0a80edc49d7f190365ac090 Mon Sep 17 00:00:00 2001 From: Vedang Patel Date: Wed, 30 Sep 2015 17:28:47 +0800 Subject: ASoC: topology: ABI - Add PCM Support and bump ABI version to 4 The struct snd_soc_tplg_pcm_dai is renamed to snd_soc_tplg_pcm. This struct will now be used to handle data related to PCMs (FE DAI & DAI links). It's not for BE, because BE DAI mappings will be provided by ACPI/FDT data. Remove the unused struct snd_soc_tplg_pcm_cfg_caps. We are using snd_soc_tplg_stream and snd_soc_stream_caps instead. Bump ABI version to 4. Signed-off-by: Vedang Patel Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/uapi/sound/asoc.h | 31 +++++++++++++++---------------- sound/soc/soc-topology.c | 4 ++-- 2 files changed, 17 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 247c50bd60f0..2aa081ca95c1 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -83,7 +83,7 @@ #define SND_SOC_TPLG_NUM_TEXTS 16 /* ABI version */ -#define SND_SOC_TPLG_ABI_VERSION 0x3 +#define SND_SOC_TPLG_ABI_VERSION 0x4 /* Max size of TLV data */ #define SND_SOC_TPLG_TLV_SIZE 32 @@ -378,30 +378,29 @@ struct snd_soc_tplg_dapm_widget { */ } __attribute__((packed)); -struct snd_soc_tplg_pcm_cfg_caps { - struct snd_soc_tplg_stream_caps caps; - struct snd_soc_tplg_stream_config configs[SND_SOC_TPLG_STREAM_CONFIG_MAX]; - __le32 num_configs; /* number of configs */ -} __attribute__((packed)); /* - * Describes SW/FW specific features of PCM or DAI link. + * Describes SW/FW specific features of PCM (FE DAI & DAI link). * - * File block representation for PCM/DAI-Link :- + * File block representation for PCM :- * +-----------------------------------+-----+ * | struct snd_soc_tplg_hdr | 1 | * +-----------------------------------+-----+ - * | struct snd_soc_tplg_dapm_pcm_dai | N | + * | struct snd_soc_tplg_pcm | N | * +-----------------------------------+-----+ */ -struct snd_soc_tplg_pcm_dai { +struct snd_soc_tplg_pcm { __le32 size; /* in bytes of this structure */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 id; /* unique ID - used to match */ - __le32 playback; /* supports playback mode */ - __le32 capture; /* supports capture mode */ - __le32 compress; /* 1 = compressed; 0 = PCM */ - struct snd_soc_tplg_pcm_cfg_caps capconf[2]; /* capabilities and configs */ + char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + __le32 pcm_id; /* unique ID - used to match */ + __le32 dai_id; /* unique ID - used to match */ + __le32 playback; /* supports playback mode */ + __le32 capture; /* supports capture mode */ + __le32 compress; /* 1 = compressed; 0 = PCM */ + struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ + __le32 num_streams; /* number of streams */ + struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */ } __attribute__((packed)); #endif diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 69d01cd925ce..8d7ec80af51b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1558,7 +1558,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_pcm_dai), count, + sizeof(struct snd_soc_tplg_pcm), count, hdr->payload_size, "PCM DAI")) { dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", count); @@ -1566,7 +1566,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); - tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count; + tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); if (dobj == NULL) -- cgit v1.2.3-59-g8ed1b From 6d817c0e9fd7536be76690bfdee88e8a81c16f7d Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 29 Sep 2015 16:44:01 +0100 Subject: ASoC: codecs: Add da7219 codec driver This adds support for the DA7219 audio codec with built-in advanced accessory detect features. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- include/sound/da7219-aad.h | 99 +++ include/sound/da7219.h | 55 ++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/da7219-aad.c | 823 +++++++++++++++++ sound/soc/codecs/da7219-aad.h | 212 +++++ sound/soc/codecs/da7219.c | 1940 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/da7219.h | 820 +++++++++++++++++ 8 files changed, 3955 insertions(+) create mode 100644 include/sound/da7219-aad.h create mode 100644 include/sound/da7219.h create mode 100644 sound/soc/codecs/da7219-aad.c create mode 100644 sound/soc/codecs/da7219-aad.h create mode 100644 sound/soc/codecs/da7219.c create mode 100644 sound/soc/codecs/da7219.h (limited to 'sound') diff --git a/include/sound/da7219-aad.h b/include/sound/da7219-aad.h new file mode 100644 index 000000000000..17802fb86ec4 --- /dev/null +++ b/include/sound/da7219-aad.h @@ -0,0 +1,99 @@ +/* + * da7219-aad.h - DA7322 ASoC Codec AAD Driver Platform Data + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_AAD_PDATA_H +#define __DA7219_AAD_PDATA_H + +enum da7219_aad_micbias_pulse_lvl { + DA7219_AAD_MICBIAS_PULSE_LVL_OFF = 0, + DA7219_AAD_MICBIAS_PULSE_LVL_2_8V = 6, + DA7219_AAD_MICBIAS_PULSE_LVL_2_9V, +}; + +enum da7219_aad_btn_cfg { + DA7219_AAD_BTN_CFG_2MS = 1, + DA7219_AAD_BTN_CFG_5MS, + DA7219_AAD_BTN_CFG_10MS, + DA7219_AAD_BTN_CFG_50MS, + DA7219_AAD_BTN_CFG_100MS, + DA7219_AAD_BTN_CFG_200MS, + DA7219_AAD_BTN_CFG_500MS, +}; + +enum da7219_aad_mic_det_thr { + DA7219_AAD_MIC_DET_THR_200_OHMS = 0, + DA7219_AAD_MIC_DET_THR_500_OHMS, + DA7219_AAD_MIC_DET_THR_750_OHMS, + DA7219_AAD_MIC_DET_THR_1000_OHMS, +}; + +enum da7219_aad_jack_ins_deb { + DA7219_AAD_JACK_INS_DEB_5MS = 0, + DA7219_AAD_JACK_INS_DEB_10MS, + DA7219_AAD_JACK_INS_DEB_20MS, + DA7219_AAD_JACK_INS_DEB_50MS, + DA7219_AAD_JACK_INS_DEB_100MS, + DA7219_AAD_JACK_INS_DEB_200MS, + DA7219_AAD_JACK_INS_DEB_500MS, + DA7219_AAD_JACK_INS_DEB_1S, +}; + +enum da7219_aad_jack_det_rate { + DA7219_AAD_JACK_DET_RATE_32_64MS = 0, + DA7219_AAD_JACK_DET_RATE_64_128MS, + DA7219_AAD_JACK_DET_RATE_128_256MS, + DA7219_AAD_JACK_DET_RATE_256_512MS, +}; + +enum da7219_aad_jack_rem_deb { + DA7219_AAD_JACK_REM_DEB_1MS = 0, + DA7219_AAD_JACK_REM_DEB_5MS, + DA7219_AAD_JACK_REM_DEB_10MS, + DA7219_AAD_JACK_REM_DEB_20MS, +}; + +enum da7219_aad_btn_avg { + DA7219_AAD_BTN_AVG_1 = 0, + DA7219_AAD_BTN_AVG_2, + DA7219_AAD_BTN_AVG_4, + DA7219_AAD_BTN_AVG_8, +}; + +enum da7219_aad_adc_1bit_rpt { + DA7219_AAD_ADC_1BIT_RPT_1 = 0, + DA7219_AAD_ADC_1BIT_RPT_2, + DA7219_AAD_ADC_1BIT_RPT_4, + DA7219_AAD_ADC_1BIT_RPT_8, +}; + +struct da7219_aad_pdata { + int irq; + + enum da7219_aad_micbias_pulse_lvl micbias_pulse_lvl; + u32 micbias_pulse_time; + enum da7219_aad_btn_cfg btn_cfg; + enum da7219_aad_mic_det_thr mic_det_thr; + enum da7219_aad_jack_ins_deb jack_ins_deb; + enum da7219_aad_jack_det_rate jack_det_rate; + enum da7219_aad_jack_rem_deb jack_rem_deb; + + u8 a_d_btn_thr; + u8 d_b_btn_thr; + u8 b_c_btn_thr; + u8 c_mic_btn_thr; + + enum da7219_aad_btn_avg btn_avg; + enum da7219_aad_adc_1bit_rpt adc_1bit_rpt; +}; + +#endif /* __DA7219_AAD_PDATA_H */ diff --git a/include/sound/da7219.h b/include/sound/da7219.h new file mode 100644 index 000000000000..3f39e135312d --- /dev/null +++ b/include/sound/da7219.h @@ -0,0 +1,55 @@ +/* + * da7219.h - DA7219 ASoC Codec Driver Platform Data + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_PDATA_H +#define __DA7219_PDATA_H + +/* LDO */ +enum da7219_ldo_lvl_sel { + DA7219_LDO_LVL_SEL_1_05V = 0, + DA7219_LDO_LVL_SEL_1_10V, + DA7219_LDO_LVL_SEL_1_20V, + DA7219_LDO_LVL_SEL_1_40V, +}; + +/* Mic Bias */ +enum da7219_micbias_voltage { + DA7219_MICBIAS_1_8V = 1, + DA7219_MICBIAS_2_0V, + DA7219_MICBIAS_2_2V, + DA7219_MICBIAS_2_4V, + DA7219_MICBIAS_2_6V, +}; + +/* Mic input type */ +enum da7219_mic_amp_in_sel { + DA7219_MIC_AMP_IN_SEL_DIFF = 0, + DA7219_MIC_AMP_IN_SEL_SE_P, + DA7219_MIC_AMP_IN_SEL_SE_N, +}; + +struct da7219_aad_pdata; + +struct da7219_pdata { + /* Internal LDO */ + enum da7219_ldo_lvl_sel ldo_lvl_sel; + + /* Mic */ + enum da7219_micbias_voltage micbias_lvl; + enum da7219_mic_amp_in_sel mic_amp_in_sel; + + /* AAD */ + struct da7219_aad_pdata *aad_pdata; +}; + +#endif /* __DA7219_PDATA_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733ecd17f..c9a895cc4eff 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C + select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DMIC @@ -430,6 +431,9 @@ config SND_SOC_DA7210 config SND_SOC_DA7213 tristate +config SND_SOC_DA7219 + tristate + config SND_SOC_DA732X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077954ae..eb10a34d5e13 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -49,6 +49,7 @@ snd-soc-cs4349-objs := cs4349.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o +snd-soc-da7219-objs := da7219.o da7219-aad.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-bt-sco-objs := bt-sco.o @@ -241,6 +242,7 @@ obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o +obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c new file mode 100644 index 000000000000..9459593eef13 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.c @@ -0,0 +1,823 @@ +/* + * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * Detection control + */ + +void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + da7219->aad->jack = jack; + da7219->aad->jack_inserted = false; + + /* Send an initial empty report */ + snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK); + + /* Enable/Disable jack detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, + (jack ? DA7219_ACCDET_EN_MASK : 0)); +} +EXPORT_SYMBOL_GPL(da7219_aad_jack_det); + +/* + * Button/HPTest work + */ + +static void da7219_aad_btn_det_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, btn_det_work); + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + u8 statusa, micbias_ctrl; + bool micbias_up = false; + int retries = 0; + + /* Drive headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + /* Make sure mic bias is up */ + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + do { + statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A); + if (statusa & DA7219_MICBIAS_UP_STS_MASK) + micbias_up = true; + else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES) + msleep(DA7219_AAD_MICBIAS_CHK_DELAY); + } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES)); + + if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) + dev_warn(codec->dev, "Mic bias status check timed out"); + + /* + * Mic bias pulse required to enable mic, must be done before enabling + * button detection to prevent erroneous button readings. + */ + if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) { + /* Pulse higher level voltage */ + micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL); + snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_LEVEL_MASK, + da7219_aad->micbias_pulse_lvl); + msleep(da7219_aad->micbias_pulse_time); + snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl); + + } + + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, + da7219_aad->btn_cfg); +} + +static void da7219_aad_hptest_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, hptest_work); + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + u16 tonegen_freq_hptest; + u8 accdet_cfg8; + int report = 0; + + /* Lock DAPM and any Kcontrols that are affected by this test */ + snd_soc_dapm_mutex_lock(dapm); + mutex_lock(&da7219->lock); + + /* Bypass cache so it saves current settings */ + regcache_cache_bypass(da7219->regmap, true); + + /* Make sure Tone Generator is disabled */ + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0); + + /* Enable HPTest block, 1KOhms check */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK, + DA7219_HPTEST_EN_MASK | + DA7219_HPTEST_RES_SEL_1KOHMS); + + /* Set gains to 0db */ + snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB); + snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB); + + /* Disable DAC filters, EQs and soft mute */ + snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK, + 0); + snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK, + 0); + snd_soc_update_bits(codec, DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_MASK, 0); + + /* Enable HP left & right paths */ + snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK, + DA7219_CP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK, + DA7219_DAC_L_SRC_TONEGEN | + DA7219_DAC_R_SRC_TONEGEN); + snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, + DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK, + DA7219_DAC_L_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, + DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK, + DA7219_DAC_R_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_MASK, + DA7219_MIXOUT_L_MIX_SELECT_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_MASK, + DA7219_MIXOUT_R_MIX_SELECT_MASK); + snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_OUTFILT_ST_1L_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1L); + snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R, + DA7219_OUTFILT_ST_1R_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1R); + snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_MASK, + DA7219_MIXOUT_L_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_MASK, + DA7219_MIXOUT_R_AMP_EN_MASK); + snd_soc_write(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); + snd_soc_write(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); + + /* Configure & start Tone Generator */ + snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK); + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); + regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + &tonegen_freq_hptest, sizeof(tonegen_freq_hptest)); + snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2, + DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK, + DA7219_SWG_SEL_SRAMP | + DA7219_TONE_GEN_GAIN_MINUS_15DB); + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Grab comparator reading */ + accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8); + if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK) + report |= SND_JACK_HEADPHONE; + else + report |= SND_JACK_LINEOUT; + + /* Stop tone generator */ + snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Restore original settings from cache */ + regcache_mark_dirty(da7219->regmap); + regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_DROUTING_ST_OUTFILT_1R); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_R_SELECT); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC, + DA7219_DIG_ROUTING_DAC); + regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5, + DA7219_DAC_FILTERS5); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4, + DA7219_DAC_FILTERS1); + regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER, + DA7219_TONE_GEN_ON_PER); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + DA7219_TONE_GEN_FREQ1_U); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1, + DA7219_TONE_GEN_CFG2); + + regcache_cache_bypass(da7219->regmap, false); + + /* Disable HPTest block */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK, 0); + + /* Drive Headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + mutex_unlock(&da7219->lock); + snd_soc_dapm_mutex_unlock(dapm); + + /* + * Only send report if jack hasn't been removed during process, + * otherwise it's invalid and we drop it. + */ + if (da7219_aad->jack_inserted) + snd_soc_jack_report(da7219_aad->jack, report, + SND_JACK_HEADSET | SND_JACK_LINEOUT); +} + + +/* + * IRQ + */ + +static irqreturn_t da7219_aad_irq_thread(int irq, void *data) +{ + struct da7219_aad_priv *da7219_aad = data; + struct snd_soc_codec *codec = da7219_aad->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 events[DA7219_AAD_IRQ_REG_MAX]; + u8 statusa; + int i, report = 0, mask = 0; + + /* Read current IRQ events */ + regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B]) + return IRQ_NONE; + + /* Read status register for jack insertion & type status */ + statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A); + + /* Clear events */ + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n", + events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B], + statusa); + + if (statusa & DA7219_JACK_INSERTION_STS_MASK) { + /* Jack Insertion */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_INSERTED_MASK) { + report |= SND_JACK_MECHANICAL; + mask |= SND_JACK_MECHANICAL; + da7219_aad->jack_inserted = true; + } + + /* Jack type detection */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_DETECT_COMPLETE_MASK) { + /* + * If 4-pole, then enable button detection, else perform + * HP impedance test to determine output type to report. + * + * We schedule work here as the tasks themselves can + * take time to complete, and in particular for hptest + * we want to be able to check if the jack was removed + * during the procedure as this will invalidate the + * result. By doing this as work, the IRQ thread can + * handle a removal, and we can check at the end of + * hptest if we have a valid result or not. + */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + report |= SND_JACK_HEADSET; + mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT; + schedule_work(&da7219_aad->btn_det_work); + } else { + schedule_work(&da7219_aad->hptest_work); + } + } + + /* Button support for 4-pole jack */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Press */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_PRESSED_MASK << i)) { + report |= SND_JACK_BTN_0 >> i; + mask |= SND_JACK_BTN_0 >> i; + } + } + snd_soc_jack_report(da7219_aad->jack, report, mask); + + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Release */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) { + report &= ~(SND_JACK_BTN_0 >> i); + mask |= SND_JACK_BTN_0 >> i; + } + } + } + } else { + /* Jack removal */ + if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) { + report = 0; + mask |= DA7219_AAD_REPORT_ALL_MASK; + da7219_aad->jack_inserted = false; + + /* Un-drive headphones/lineout */ + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, 0); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, 0); + + /* Ensure button detection disabled */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + /* Disable mic bias */ + snd_soc_dapm_disable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + /* Cancel any pending work */ + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); + } + } + + snd_soc_jack_report(da7219_aad->jack, report, mask); + + return IRQ_HANDLED; +} + +/* + * DT to pdata conversion + */ + +static enum da7219_aad_micbias_pulse_lvl + da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 2800: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V; + case 2900: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V; + default: + dev_warn(codec->dev, "Invalid micbias pulse level"); + return DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + } +} + +static enum da7219_aad_btn_cfg + da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 2: + return DA7219_AAD_BTN_CFG_2MS; + case 5: + return DA7219_AAD_BTN_CFG_5MS; + case 10: + return DA7219_AAD_BTN_CFG_10MS; + case 50: + return DA7219_AAD_BTN_CFG_50MS; + case 100: + return DA7219_AAD_BTN_CFG_100MS; + case 200: + return DA7219_AAD_BTN_CFG_200MS; + case 500: + return DA7219_AAD_BTN_CFG_500MS; + default: + dev_warn(codec->dev, "Invalid button config"); + return DA7219_AAD_BTN_CFG_10MS; + } +} + +static enum da7219_aad_mic_det_thr + da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 200: + return DA7219_AAD_MIC_DET_THR_200_OHMS; + case 500: + return DA7219_AAD_MIC_DET_THR_500_OHMS; + case 750: + return DA7219_AAD_MIC_DET_THR_750_OHMS; + case 1000: + return DA7219_AAD_MIC_DET_THR_1000_OHMS; + default: + dev_warn(codec->dev, "Invalid mic detect threshold"); + return DA7219_AAD_MIC_DET_THR_500_OHMS; + } +} + +static enum da7219_aad_jack_ins_deb + da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 5: + return DA7219_AAD_JACK_INS_DEB_5MS; + case 10: + return DA7219_AAD_JACK_INS_DEB_10MS; + case 20: + return DA7219_AAD_JACK_INS_DEB_20MS; + case 50: + return DA7219_AAD_JACK_INS_DEB_50MS; + case 100: + return DA7219_AAD_JACK_INS_DEB_100MS; + case 200: + return DA7219_AAD_JACK_INS_DEB_200MS; + case 500: + return DA7219_AAD_JACK_INS_DEB_500MS; + case 1000: + return DA7219_AAD_JACK_INS_DEB_1S; + default: + dev_warn(codec->dev, "Invalid jack insert debounce"); + return DA7219_AAD_JACK_INS_DEB_20MS; + } +} + +static enum da7219_aad_jack_det_rate + da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "32ms_64ms")) { + return DA7219_AAD_JACK_DET_RATE_32_64MS; + } else if (!strcmp(str, "64ms_128ms")) { + return DA7219_AAD_JACK_DET_RATE_64_128MS; + } else if (!strcmp(str, "128ms_256ms")) { + return DA7219_AAD_JACK_DET_RATE_128_256MS; + } else if (!strcmp(str, "256ms_512ms")) { + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } else { + dev_warn(codec->dev, "Invalid jack detect rate"); + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } +} + +static enum da7219_aad_jack_rem_deb + da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_JACK_REM_DEB_1MS; + case 5: + return DA7219_AAD_JACK_REM_DEB_5MS; + case 10: + return DA7219_AAD_JACK_REM_DEB_10MS; + case 20: + return DA7219_AAD_JACK_REM_DEB_20MS; + default: + dev_warn(codec->dev, "Invalid jack removal debounce"); + return DA7219_AAD_JACK_REM_DEB_1MS; + } +} + +static enum da7219_aad_btn_avg + da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_BTN_AVG_1; + case 2: + return DA7219_AAD_BTN_AVG_2; + case 4: + return DA7219_AAD_BTN_AVG_4; + case 8: + return DA7219_AAD_BTN_AVG_8; + default: + dev_warn(codec->dev, "Invalid button average value"); + return DA7219_AAD_BTN_AVG_2; + } +} + +static enum da7219_aad_adc_1bit_rpt + da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_ADC_1BIT_RPT_1; + case 2: + return DA7219_AAD_ADC_1BIT_RPT_2; + case 4: + return DA7219_AAD_ADC_1BIT_RPT_4; + case 8: + return DA7219_AAD_ADC_1BIT_RPT_8; + default: + dev_warn(codec->dev, "Invalid ADC 1-bit repeat value"); + return DA7219_AAD_ADC_1BIT_RPT_1; + } +} + +static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad"); + struct da7219_aad_pdata *aad_pdata; + const char *of_str; + u32 of_val32; + + if (!aad_np) + return NULL; + + aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL); + if (!aad_pdata) + goto out; + + aad_pdata->irq = irq_of_parse_and_map(np, 0); + + if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl", + &of_val32) >= 0) + aad_pdata->micbias_pulse_lvl = + da7219_aad_of_micbias_pulse_lvl(codec, of_val32); + else + aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + + if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time", + &of_val32) >= 0) + aad_pdata->micbias_pulse_time = of_val32; + + if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0) + aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32); + else + aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; + + if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0) + aad_pdata->mic_det_thr = + da7219_aad_of_mic_det_thr(codec, of_val32); + else + aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS; + + if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0) + aad_pdata->jack_ins_deb = + da7219_aad_of_jack_ins_deb(codec, of_val32); + else + aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + + if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str)) + aad_pdata->jack_det_rate = + da7219_aad_of_jack_det_rate(codec, of_str); + else + aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; + + if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0) + aad_pdata->jack_rem_deb = + da7219_aad_of_jack_rem_deb(codec, of_val32); + else + aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; + + if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0) + aad_pdata->a_d_btn_thr = (u8) of_val32; + else + aad_pdata->a_d_btn_thr = 0xA; + + if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0) + aad_pdata->d_b_btn_thr = (u8) of_val32; + else + aad_pdata->d_b_btn_thr = 0x16; + + if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0) + aad_pdata->b_c_btn_thr = (u8) of_val32; + else + aad_pdata->b_c_btn_thr = 0x21; + + if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0) + aad_pdata->c_mic_btn_thr = (u8) of_val32; + else + aad_pdata->c_mic_btn_thr = 0x3E; + + if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0) + aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32); + else + aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; + + if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0) + aad_pdata->adc_1bit_rpt = + da7219_aad_of_adc_1bit_rpt(codec, of_val32); + else + aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; + +out: + of_node_put(aad_np); + + return aad_pdata; +} + +static void da7219_aad_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct da7219_pdata *pdata = da7219->pdata; + + if ((pdata) && (pdata->aad_pdata)) { + struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata; + u8 cfg, mask; + + da7219_aad->irq = aad_pdata->irq; + + switch (aad_pdata->micbias_pulse_lvl) { + case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V: + case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V: + da7219_aad->micbias_pulse_lvl = + (aad_pdata->micbias_pulse_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + default: + break; + } + + da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time; + + switch (aad_pdata->btn_cfg) { + case DA7219_AAD_BTN_CFG_2MS: + case DA7219_AAD_BTN_CFG_5MS: + case DA7219_AAD_BTN_CFG_10MS: + case DA7219_AAD_BTN_CFG_50MS: + case DA7219_AAD_BTN_CFG_100MS: + case DA7219_AAD_BTN_CFG_200MS: + case DA7219_AAD_BTN_CFG_500MS: + da7219_aad->btn_cfg = (aad_pdata->btn_cfg << + DA7219_BUTTON_CONFIG_SHIFT); + } + + cfg = 0; + mask = 0; + switch (aad_pdata->mic_det_thr) { + case DA7219_AAD_MIC_DET_THR_200_OHMS: + case DA7219_AAD_MIC_DET_THR_500_OHMS: + case DA7219_AAD_MIC_DET_THR_750_OHMS: + case DA7219_AAD_MIC_DET_THR_1000_OHMS: + cfg |= (aad_pdata->mic_det_thr << + DA7219_MIC_DET_THRESH_SHIFT); + mask |= DA7219_MIC_DET_THRESH_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg); + + cfg = 0; + mask = 0; + switch (aad_pdata->jack_ins_deb) { + case DA7219_AAD_JACK_INS_DEB_5MS: + case DA7219_AAD_JACK_INS_DEB_10MS: + case DA7219_AAD_JACK_INS_DEB_20MS: + case DA7219_AAD_JACK_INS_DEB_50MS: + case DA7219_AAD_JACK_INS_DEB_100MS: + case DA7219_AAD_JACK_INS_DEB_200MS: + case DA7219_AAD_JACK_INS_DEB_500MS: + case DA7219_AAD_JACK_INS_DEB_1S: + cfg |= (aad_pdata->jack_ins_deb << + DA7219_JACKDET_DEBOUNCE_SHIFT); + mask |= DA7219_JACKDET_DEBOUNCE_MASK; + } + switch (aad_pdata->jack_det_rate) { + case DA7219_AAD_JACK_DET_RATE_32_64MS: + case DA7219_AAD_JACK_DET_RATE_64_128MS: + case DA7219_AAD_JACK_DET_RATE_128_256MS: + case DA7219_AAD_JACK_DET_RATE_256_512MS: + cfg |= (aad_pdata->jack_det_rate << + DA7219_JACK_DETECT_RATE_SHIFT); + mask |= DA7219_JACK_DETECT_RATE_MASK; + } + switch (aad_pdata->jack_rem_deb) { + case DA7219_AAD_JACK_REM_DEB_1MS: + case DA7219_AAD_JACK_REM_DEB_5MS: + case DA7219_AAD_JACK_REM_DEB_10MS: + case DA7219_AAD_JACK_REM_DEB_20MS: + cfg |= (aad_pdata->jack_rem_deb << + DA7219_JACKDET_REM_DEB_SHIFT); + mask |= DA7219_JACKDET_REM_DEB_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg); + + snd_soc_write(codec, DA7219_ACCDET_CONFIG_3, + aad_pdata->a_d_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_4, + aad_pdata->d_b_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_5, + aad_pdata->b_c_btn_thr); + snd_soc_write(codec, DA7219_ACCDET_CONFIG_6, + aad_pdata->c_mic_btn_thr); + + cfg = 0; + mask = 0; + switch (aad_pdata->btn_avg) { + case DA7219_AAD_BTN_AVG_1: + case DA7219_AAD_BTN_AVG_2: + case DA7219_AAD_BTN_AVG_4: + case DA7219_AAD_BTN_AVG_8: + cfg |= (aad_pdata->btn_avg << + DA7219_BUTTON_AVERAGE_SHIFT); + mask |= DA7219_BUTTON_AVERAGE_MASK; + } + switch (aad_pdata->adc_1bit_rpt) { + case DA7219_AAD_ADC_1BIT_RPT_1: + case DA7219_AAD_ADC_1BIT_RPT_2: + case DA7219_AAD_ADC_1BIT_RPT_4: + case DA7219_AAD_ADC_1BIT_RPT_8: + cfg |= (aad_pdata->adc_1bit_rpt << + DA7219_ADC_1_BIT_REPEAT_SHIFT); + mask |= DA7219_ADC_1_BIT_REPEAT_MASK; + } + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg); + } +} + + +/* + * Init/Exit + */ + +int da7219_aad_init(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + int ret; + + da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL); + if (!da7219_aad) + return -ENOMEM; + + da7219->aad = da7219_aad; + da7219_aad->codec = codec; + + /* Handle any DT/platform data */ + if ((codec->dev->of_node) && (da7219->pdata)) + da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec); + + da7219_aad_handle_pdata(codec); + + /* Disable button detection */ + snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work); + INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work); + + ret = request_threaded_irq(da7219_aad->irq, NULL, + da7219_aad_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "da7219-aad", da7219_aad); + if (ret) { + dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); + return ret; + } + + /* Unmask AAD IRQs */ + memset(mask, 0, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + &mask, DA7219_AAD_IRQ_REG_MAX); + + return 0; +} +EXPORT_SYMBOL_GPL(da7219_aad_init); + +void da7219_aad_exit(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_aad_priv *da7219_aad = da7219->aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + + /* Mask off AAD IRQs */ + memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + mask, DA7219_AAD_IRQ_REG_MAX); + + free_irq(da7219_aad->irq, da7219_aad); + + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); +} +EXPORT_SYMBOL_GPL(da7219_aad_exit); + +MODULE_DESCRIPTION("ASoC DA7219 AAD Driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h new file mode 100644 index 000000000000..4fccf677cd06 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.h @@ -0,0 +1,212 @@ +/* + * da7219-aad.h - DA7322 ASoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_AAD_H +#define __DA7219_AAD_H + +#include +#include +#include +#include + +/* + * Registers + */ + +#define DA7219_ACCDET_STATUS_A 0xC0 +#define DA7219_ACCDET_STATUS_B 0xC1 +#define DA7219_ACCDET_IRQ_EVENT_A 0xC2 +#define DA7219_ACCDET_IRQ_EVENT_B 0xC3 +#define DA7219_ACCDET_IRQ_MASK_A 0xC4 +#define DA7219_ACCDET_IRQ_MASK_B 0xC5 +#define DA7219_ACCDET_CONFIG_1 0xC6 +#define DA7219_ACCDET_CONFIG_2 0xC7 +#define DA7219_ACCDET_CONFIG_3 0xC8 +#define DA7219_ACCDET_CONFIG_4 0xC9 +#define DA7219_ACCDET_CONFIG_5 0xCA +#define DA7219_ACCDET_CONFIG_6 0xCB +#define DA7219_ACCDET_CONFIG_7 0xCC +#define DA7219_ACCDET_CONFIG_8 0xCD + + +/* + * Bit Fields + */ + +/* DA7219_ACCDET_STATUS_A = 0xC0 */ +#define DA7219_JACK_INSERTION_STS_SHIFT 0 +#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0) +#define DA7219_JACK_TYPE_STS_SHIFT 1 +#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1) +#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2 +#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2) +#define DA7219_MICBIAS_UP_STS_SHIFT 3 +#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3) + +/* DA7219_ACCDET_STATUS_B = 0xC1 */ +#define DA7219_BUTTON_TYPE_STS_SHIFT 0 +#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0) + +/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */ +#define DA7219_E_JACK_INSERTED_SHIFT 0 +#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_E_JACK_REMOVED_SHIFT 1 +#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */ +#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */ +#define DA7219_M_JACK_INSERTED_SHIFT 0 +#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_M_JACK_REMOVED_SHIFT 1 +#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */ +#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_1 = 0xC6 */ +#define DA7219_ACCDET_EN_SHIFT 0 +#define DA7219_ACCDET_EN_MASK (0x1 << 0) +#define DA7219_BUTTON_CONFIG_SHIFT 1 +#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1) +#define DA7219_MIC_DET_THRESH_SHIFT 4 +#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4) +#define DA7219_JACK_TYPE_DET_EN_SHIFT 6 +#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6) +#define DA7219_PIN_ORDER_DET_EN_SHIFT 7 +#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_2 = 0xC7 */ +#define DA7219_ACCDET_PAUSE_SHIFT 0 +#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0) +#define DA7219_JACKDET_DEBOUNCE_SHIFT 1 +#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1) +#define DA7219_JACK_DETECT_RATE_SHIFT 4 +#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4) +#define DA7219_JACKDET_REM_DEB_SHIFT 6 +#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6) + +/* DA7219_ACCDET_CONFIG_3 = 0xC8 */ +#define DA7219_A_D_BUTTON_THRESH_SHIFT 0 +#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_4 = 0xC9 */ +#define DA7219_D_B_BUTTON_THRESH_SHIFT 0 +#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_5 = 0xCA */ +#define DA7219_B_C_BUTTON_THRESH_SHIFT 0 +#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_6 = 0xCB */ +#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0 +#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_7 = 0xCC */ +#define DA7219_BUTTON_AVERAGE_SHIFT 0 +#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0) +#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2 +#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2) +#define DA7219_PIN_ORDER_FORCE_SHIFT 4 +#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4) +#define DA7219_JACK_TYPE_FORCE_SHIFT 5 +#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5) + +/* DA7219_ACCDET_CONFIG_8 = 0xCD */ +#define DA7219_HPTEST_EN_SHIFT 0 +#define DA7219_HPTEST_EN_MASK (0x1 << 0) +#define DA7219_HPTEST_RES_SEL_SHIFT 1 +#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1) +#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1) +#define DA7219_HPTEST_COMP_SHIFT 4 +#define DA7219_HPTEST_COMP_MASK (0x1 << 4) + + +#define DA7219_AAD_MAX_BUTTONS 4 +#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \ + SND_JACK_HEADSET | SND_JACK_LINEOUT | \ + SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +#define DA7219_AAD_MICBIAS_CHK_DELAY 10 +#define DA7219_AAD_MICBIAS_CHK_RETRIES 5 + +#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28 +#define DA7219_AAD_HPTEST_PERIOD 65 + +enum da7219_aad_event_regs { + DA7219_AAD_IRQ_REG_A = 0, + DA7219_AAD_IRQ_REG_B, + DA7219_AAD_IRQ_REG_MAX, +}; + +/* Private data */ +struct da7219_aad_priv { + struct snd_soc_codec *codec; + int irq; + + u8 micbias_pulse_lvl; + u32 micbias_pulse_time; + + u8 btn_cfg; + + struct work_struct btn_det_work; + struct work_struct hptest_work; + + struct snd_soc_jack *jack; + bool jack_inserted; +}; + +/* AAD control */ +void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack); + +/* Init/Exit */ +int da7219_aad_init(struct snd_soc_codec *codec); +void da7219_aad_exit(struct snd_soc_codec *codec); + +#endif /* __DA7219_AAD_H */ diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c new file mode 100644 index 000000000000..76f8fc2c00fe --- /dev/null +++ b/sound/soc/codecs/da7219.c @@ -0,0 +1,1940 @@ +/* + * da7219.c - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0); +static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0); + +static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv, + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -77.25dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0) +); + +static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0); + +/* Input Enums */ +static const char * const da7219_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs" +}; + +static const struct soc_enum da7219_alc_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT, + DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt); + +static const char * const da7219_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs" +}; + +static const struct soc_enum da7219_alc_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT, + DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt); + +static const char * const da7219_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7219_alc_hold_time = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT, + DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt); + +static const char * const da7219_alc_env_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7219_alc_env_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const struct soc_enum da7219_alc_env_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const char * const da7219_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs" +}; + +static const struct soc_enum da7219_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTICLIP_STEP_SHIFT, + DA7219_ALC_ANTICLIP_STEP_MAX, + da7219_alc_anticlip_step_txt); + +/* Input/Output Enums */ +static const char * const da7219_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16" +}; + +static const struct soc_enum da7219_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT, + DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt); + +static const char * const da7219_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice" +}; + +static const unsigned int da7219_hpf_mode_val[] = { + DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN, +}; + +static const struct soc_enum da7219_adc_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const struct soc_enum da7219_dac_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const char * const da7219_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz" +}; + +static const struct soc_enum da7219_adc_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const struct soc_enum da7219_dac_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const char * const da7219_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da7219_adc_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const struct soc_enum da7219_dac_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const char * const da7219_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7219_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT, + DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt); + +static const char * const da7219_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7219_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT, + DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7219_dac_softmute_rate_txt[] = { + "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples", + "32 Samples", "64 Samples" +}; + +static const struct soc_enum da7219_dac_softmute_rate = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT, + DA7219_DAC_SOFTMUTE_RATE_MAX, + da7219_dac_softmute_rate_txt); + +static const char * const da7219_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7219_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_SETUP_TIME_SHIFT, + DA7219_DAC_NG_SETUP_TIME_MAX, + da7219_dac_ng_setup_time_txt); + +static const char * const da7219_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPUP_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampup_txt); + +static const char * const da7219_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPDN_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampdown_txt); + + +static const char * const da7219_cp_track_mode_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7219_cp_track_mode_val[] = { + DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL, + DA7219_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7219_cp_track_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT, + DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX, + da7219_cp_track_mode_txt, + da7219_cp_track_mode_val); + + +/* + * Control Functions + */ + +/* Locked Kcontrol calls */ +static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->lock); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->lock); + + return ret; +} + +/* ALC */ +static void da7219_alc_calib(struct snd_soc_codec *codec) +{ + u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl; + + /* Save current state of mic control register */ + mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL); + + /* Save current state of input mixer control register */ + mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL); + + /* Save current state of input ADC control register */ + adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK, + DA7219_MIC_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_MASK, + DA7219_MIC_1_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_MASK | + DA7219_MIXIN_L_AMP_MUTE_EN_MASK, + DA7219_MIXIN_L_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK, + DA7219_ADC_L_EN_MASK); + + /* Perform auto calibration */ + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_AUTO_CALIB_EN_MASK, + DA7219_ALC_AUTO_CALIB_EN_MASK); + do { + calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1); + } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) { + dev_warn(codec->dev, + "ALC auto calibration failed with overflow\n"); + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, 0); + } else { + /* Enable DC offset cancellation, hybrid mode */ + snd_soc_update_bits(codec, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK); + } + + /* Restore input filter control register to original state */ + snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl); +} + +static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7219->alc_en)) + da7219_alc_calib(codec); + + return ret; +} + +static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + + /* Force ALC offset calibration if enabling ALC */ + if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) { + da7219_alc_calib(codec); + da7219->alc_en = false; + } else { + da7219->alc_en = false; + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + mutex_lock(&da7219->lock); + ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->lock); + + if (ret) + return ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val = cpu_to_le16(ucontrol->value.integer.value[0]); + + mutex_lock(&da7219->lock); + ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->lock); + + return ret; +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7219_snd_controls[] = { + /* Mics */ + SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN, + DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX, + DA7219_NO_INVERT, da7219_mic_gain_tlv), + SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Mixer Input */ + SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN, + DA7219_MIXIN_L_AMP_GAIN_SHIFT, + DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_mixin_gain_put, + da7219_mixin_gain_tlv), + SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ADC */ + SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN, + DA7219_ADC_L_DIGITAL_GAIN_SHIFT, + DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_adc_dig_gain_tlv), + SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ALC */ + SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7219_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7219_alc_hold_time), + SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate), + SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate), + SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE, + DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN, + DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX, + DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MIN_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MAX_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step), + SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_alc_sw_put), + + /* Input High-Pass Filters */ + SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode), + SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner), + SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner), + + /* Sidetone Filter */ + SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN, + DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX, + DA7219_NO_INVERT, da7219_sidetone_gain_tlv), + SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Tone Generator */ + SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2, + DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put, da7219_tonegen_gain_tlv), + SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1, + DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L, + DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L, + DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER, + DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER, + DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT), + + /* Gain ramping */ + SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate), + + /* DAC High-Pass Filter */ + SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner), + SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner), + + /* DAC 5-Band Equaliser */ + SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Softmute */ + SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate), + SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Noise Gate */ + SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate), + SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH, + DA7219_DAC_NG_OFF_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH, + DA7219_DAC_NG_ON_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* DACs */ + SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT, + DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_dac_dig_gain_tlv), + SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put), + SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* CP */ + SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode), + SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1, + DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX, + DA7219_NO_INVERT), + + /* Headphones */ + SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT, + DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_hp_gain_tlv), + SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL, + DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + + +/* + * DAPM Mux Controls + */ + +static const char * const da7219_out_sel_txt[] = { + "ADC", "Tone Generator", "DAIL", "DAIR" +}; + +static const struct soc_enum da7219_out_dail_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dail_sel_mux = + SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel); + +static const struct soc_enum da7219_out_dair_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dair_sel_mux = + SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel); + +static const struct soc_enum da7219_out_dacl_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacl_sel_mux = + SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel); + +static const struct soc_enum da7219_out_dacr_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacr_sel_mux = + SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel); + + +/* + * DAPM Mixer Controls + */ + +static const struct snd_kcontrol_new da7219_mixin_controls[] = { + SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT, + DA7219_MIXIN_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_l_controls[] = { + SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_r_controls[] = { + SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +#define DA7219_DMIX_ST_CTRLS(reg) \ + SOC_DAPM_SINGLE("Out FilterL Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Out FilterR Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Sidetone Switch", reg, \ + DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \ + +static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R), +}; + + +/* + * DAPM Events + */ + +static int da7219_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 pll_ctrl, pll_status; + int i = 0; + bool srm_lock = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (da7219->master) + /* Enable DAI clks for master mode */ + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, + DA7219_DAI_CLK_EN_MASK); + + /* PC synchronised to DAI */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, 0); + + /* Slave mode, if SRM not enabled no need for status checks */ + pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL); + if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM) + return 0; + + /* Check SRM has locked */ + do { + pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS); + if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); + + if (!srm_lock) + dev_warn(codec->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Disable DAI clks if in master mode */ + if (da7219->master) + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* + * DAPM Widgets + */ + +static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { + /* Input Supplies */ + SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC"), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Filters */ + SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT, + DA7219_NO_INVERT), + + /* Tone Generator */ + SND_SOC_DAPM_SIGGEN("TONE"), + SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1, + DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Sidetone Input */ + SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT), + + /* Input Mixer Supply */ + SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Mixer */ + SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0, + da7219_mixin_controls, + ARRAY_SIZE(da7219_mixin_controls)), + + /* Input Muxes */ + SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dail_sel_mux), + SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dair_sel_mux), + + /* DAI Supply */ + SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT, + DA7219_NO_INVERT, da7219_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DAI */ + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Muxes */ + SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacl_sel_mux), + SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacr_sel_mux), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_mixout_l_controls, + ARRAY_SIZE(da7219_mixout_l_controls)), + SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7219_mixout_r_controls, + ARRAY_SIZE(da7219_mixout_r_controls)), + + /* Sidetone Mixers */ + SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_st_out_filtl_mix_controls, + ARRAY_SIZE(da7219_st_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, + 0, da7219_st_out_filtr_mix_controls, + ARRAY_SIZE(da7219_st_out_filtr_mix_controls)), + + /* DACs */ + SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT, + DA7219_NO_INVERT), + SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT, + DA7219_NO_INVERT), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Output Supplies */ + SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT, + DA7219_NO_INVERT, NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + + +/* + * DAPM Mux Routes + */ + +#define DA7219_OUT_DAI_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIOUT"}, \ + {name, "DAIR", "DAIOUT"} + +#define DA7219_OUT_DAC_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIIN"}, \ + {name, "DAIR", "DAIIN"} + +/* + * DAPM Mixer Routes + */ + +#define DA7219_DMIX_ST_ROUTES(name) \ + {name, "Out FilterL Switch", "Mixer Out FilterL"}, \ + {name, "Out FilterR Switch", "Mixer Out FilterR"}, \ + {name, "Sidetone Switch", "Sidetone Filter"} + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7219_audio_map[] = { + /* Input paths */ + {"MIC", NULL, "Mic Bias"}, + {"Mic PGA", NULL, "MIC"}, + {"Mixin PGA", NULL, "Mic PGA"}, + {"ADC", NULL, "Mixin PGA"}, + + {"Sidetone Filter", NULL, "ADC"}, + {"Mixer In", NULL, "Mixer In Supply"}, + {"Mixer In", "Mic Switch", "ADC"}, + + {"Tone Generator", NULL, "TONE"}, + + DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"), + DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"), + + {"DAIOUT", NULL, "Out DAIL Mux"}, + {"DAIOUT", NULL, "Out DAIR Mux"}, + {"DAIOUT", NULL, "DAI"}, + + /* Output paths */ + {"DAIIN", NULL, "DAI"}, + + DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"), + DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"), + + {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"}, + {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"}, + + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"), + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"), + + {"DACL", NULL, "ST Mixer Out FilterL"}, + {"DACR", NULL, "ST Mixer Out FilterR"}, + + {"Mixout Left PGA", NULL, "DACL"}, + {"Mixout Right PGA", NULL, "DACR"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + + {"HPL", NULL, "Headphone Left PGA"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"HPL", NULL, "Charge Pump"}, + {"HPR", NULL, "Charge Pump"}, +}; + + +/* + * DAI operations + */ + +static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) + return 0; + + if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + switch (clk_id) { + case DA7219_CLKSRC_MCLK_SQR: + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, + DA7219_PLL_MCLK_SQR_EN_MASK); + break; + case DA7219_CLKSRC_MCLK: + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, 0); + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + da7219->clk_src = clk_id; + + if (da7219->mclk) { + freq = clk_round_rate(da7219->mclk, freq); + ret = clk_set_rate(da7219->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7219->mclk_rate = freq; + + return 0; +} + +static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7219->mclk_rate == 32768) { + indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; + indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; + } else if (da7219->mclk_rate < 2000000) { + dev_err(codec->dev, "PLL input clock %d below valid range\n", + da7219->mclk_rate); + return -EINVAL; + } else if (da7219->mclk_rate <= 5000000) { + indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; + indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; + } else if (da7219->mclk_rate <= 10000000) { + indiv_bits = DA7219_PLL_INDIV_5_10_MHZ; + indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL; + } else if (da7219->mclk_rate <= 20000000) { + indiv_bits = DA7219_PLL_INDIV_10_20_MHZ; + indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL; + } else if (da7219->mclk_rate <= 40000000) { + indiv_bits = DA7219_PLL_INDIV_20_40_MHZ; + indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7219->mclk_rate <= 54000000) { + indiv_bits = DA7219_PLL_INDIV_40_54_MHZ; + indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL; + } else { + dev_err(codec->dev, "PLL input clock %d above valid range\n", + da7219->mclk_rate); + return -EINVAL; + } + freq_ref = (da7219->mclk_rate / indiv); + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7219_SYSCLK_MCLK: + pll_ctrl |= DA7219_PLL_MODE_BYPASS; + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | + DA7219_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7219_SYSCLK_PLL: + pll_ctrl |= DA7219_PLL_MODE_NORMAL; + break; + case DA7219_SYSCLK_PLL_SRM: + pll_ctrl |= DA7219_PLL_MODE_SRM; + break; + case DA7219_SYSCLK_PLL_32KHZ: + pll_ctrl |= DA7219_PLL_MODE_32KHZ; + break; + default: + dev_err(codec->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7219_BYTE_MASK; + + /* Write PLL config & dividers */ + snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top); + snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer); + snd_soc_update_bits(codec, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK, + pll_ctrl); + + return 0; +} + +static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7219->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7219->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7219_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV | + DA7219_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7219_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + dai_ctrl |= DA7219_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default 64 BCLKs per WCLK is supported */ + dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64; + + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK | + DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, + dai_clk_mode); + snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + u8 dai_bclks_per_wclk; + u16 offset; + u32 frame_size; + + /* No channels enabled so disable TDM, revert to 64-bit frames */ + if (!tx_mask) { + snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, 0); + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + DA7219_DAI_BCLKS_PER_WCLK_64); + return 0; + } + + /* Check we have valid slots */ + if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(codec->dev, "Invalid number of slots, max = %d\n", + DA7219_DAI_TDM_MAX_SLOTS); + return -EINVAL; + } + + /* Check we have a valid offset given */ + if (rx_mask > DA7219_DAI_OFFSET_MAX) { + dev_err(codec->dev, "Invalid slot offset, max = %d\n", + DA7219_DAI_OFFSET_MAX); + return -EINVAL; + } + + /* Calculate & validate frame size based on slot info provided. */ + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(codec->dev, "Invalid frame size %d\n", frame_size); + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + + offset = cpu_to_le16(rx_mask); + regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, + &offset, sizeof(offset)); + + snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, + (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + DA7219_DAI_TDM_MODE_EN_MASK); + + return 0; +} + +static int da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dai_ctrl = 0, fs; + unsigned int channels; + + switch (params_width(params)) { + case 16: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; + break; + case 20: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + channels = params_channels(params); + if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) { + dev_err(codec->dev, + "Invalid number of channels, only 1 to %d supported\n", + DA7219_DAI_CH_NUM_MAX); + return -EINVAL; + } + dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT; + + switch (params_rate(params)) { + case 8000: + fs = DA7219_SR_8000; + break; + case 11025: + fs = DA7219_SR_11025; + break; + case 12000: + fs = DA7219_SR_12000; + break; + case 16000: + fs = DA7219_SR_16000; + break; + case 22050: + fs = DA7219_SR_22050; + break; + case 24000: + fs = DA7219_SR_24000; + break; + case 32000: + fs = DA7219_SR_32000; + break; + case 44100: + fs = DA7219_SR_44100; + break; + case 48000: + fs = DA7219_SR_48000; + break; + case 88200: + fs = DA7219_SR_88200; + break; + case 96000: + fs = DA7219_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7219_DAI_CTRL, + DA7219_DAI_WORD_LENGTH_MASK | + DA7219_DAI_CH_NUM_MASK, + dai_ctrl); + snd_soc_write(codec, DA7219_SR, fs); + + return 0; +} + +static const struct snd_soc_dai_ops da7219_dai_ops = { + .hw_params = da7219_hw_params, + .set_sysclk = da7219_set_dai_sysclk, + .set_pll = da7219_set_dai_pll, + .set_fmt = da7219_set_dai_fmt, + .set_tdm_slot = da7219_set_dai_tdm_slot, +}; + +#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver da7219_dai = { + .name = "da7219-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7219_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7219_FORMATS, + }, + .ops = &da7219_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, +}; + + +/* + * DT + */ + +static const struct of_device_id da7219_of_match[] = { + { .compatible = "dlg,da7219", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7219_of_match); + +static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec, + u32 val) +{ + switch (val) { + case 1050: + return DA7219_LDO_LVL_SEL_1_05V; + case 1100: + return DA7219_LDO_LVL_SEL_1_10V; + case 1200: + return DA7219_LDO_LVL_SEL_1_20V; + case 1400: + return DA7219_LDO_LVL_SEL_1_40V; + default: + dev_warn(codec->dev, "Invalid LDO level"); + return DA7219_LDO_LVL_SEL_1_05V; + } +} + +static enum da7219_micbias_voltage + da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1800: + return DA7219_MICBIAS_1_8V; + case 2000: + return DA7219_MICBIAS_2_0V; + case 2200: + return DA7219_MICBIAS_2_2V; + case 2400: + return DA7219_MICBIAS_2_4V; + case 2600: + return DA7219_MICBIAS_2_6V; + default: + dev_warn(codec->dev, "Invalid micbias level"); + return DA7219_MICBIAS_2_2V; + } +} + +static enum da7219_mic_amp_in_sel + da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "diff")) { + return DA7219_MIC_AMP_IN_SEL_DIFF; + } else if (!strcmp(str, "se_p")) { + return DA7219_MIC_AMP_IN_SEL_SE_P; + } else if (!strcmp(str, "se_n")) { + return DA7219_MIC_AMP_IN_SEL_SE_N; + } else { + dev_warn(codec->dev, "Invalid mic input type selection"); + return DA7219_MIC_AMP_IN_SEL_DIFF; + } +} + +static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct da7219_pdata *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0) + pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32); + + if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0) + pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32); + else + pdata->micbias_lvl = DA7219_MICBIAS_2_2V; + + if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str)) + pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str); + else + pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF; + + return pdata; +} + + +/* + * Codec driver functions + */ + +static int da7219_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + clk_prepare_enable(da7219->mclk); + + /* Master bias */ + snd_soc_update_bits(codec, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, + DA7219_BIAS_EN_MASK); + + /* Enable Internal Digital LDO */ + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_EN_MASK, + DA7219_LDO_EN_MASK); + } + break; + case SND_SOC_BIAS_OFF: + /* Only disable if jack detection not active */ + if (!da7219->aad->jack) { + /* Bypass Internal Digital LDO */ + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_EN_MASK, 0); + + /* Master bias */ + snd_soc_update_bits(codec, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, 0); + } + + /* MCLK */ + clk_disable_unprepare(da7219->mclk); + break; + } + + return 0; +} + +static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = { + [DA7219_SUPPLY_VDD] = "VDD", + [DA7219_SUPPLY_VDDMIC] = "VDDMIC", + [DA7219_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7219_handle_supplies(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct regulator *vddio; + u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; + int i, ret; + + /* Get required supplies */ + for (i = 0; i < DA7219_NUM_SUPPLIES; ++i) + da7219->supplies[i].supply = da7219_supply_names[i]; + + ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES, + da7219->supplies); + if (ret) + return ret; + + /* Determine VDDIO voltage provided */ + vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; + ret = regulator_get_voltage(vddio); + if (ret < 1200000) + dev_warn(codec->dev, "Invalid VDDIO voltage\n"); + else if (ret < 2800000) + io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; + + /* Enable main supplies */ + ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); + + /* Ensure device in active mode */ + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK); + + /* Update IO voltage level range */ + snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl); + + return ret; +} + +static void da7219_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + struct da7219_pdata *pdata = da7219->pdata; + + if (pdata) { + u8 micbias_lvl = 0; + + /* Internal LDO */ + switch (pdata->ldo_lvl_sel) { + case DA7219_LDO_LVL_SEL_1_05V: + case DA7219_LDO_LVL_SEL_1_10V: + case DA7219_LDO_LVL_SEL_1_20V: + case DA7219_LDO_LVL_SEL_1_40V: + snd_soc_update_bits(codec, DA7219_LDO_CTRL, + DA7219_LDO_LEVEL_SELECT_MASK, + (pdata->ldo_lvl_sel << + DA7219_LDO_LEVEL_SELECT_SHIFT)); + break; + } + + /* Mic Bias voltages */ + switch (pdata->micbias_lvl) { + case DA7219_MICBIAS_1_8V: + case DA7219_MICBIAS_2_0V: + case DA7219_MICBIAS_2_2V: + case DA7219_MICBIAS_2_4V: + case DA7219_MICBIAS_2_6V: + micbias_lvl |= (pdata->micbias_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + } + + snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl); + + /* Mic */ + switch (pdata->mic_amp_in_sel) { + case DA7219_MIC_AMP_IN_SEL_DIFF: + case DA7219_MIC_AMP_IN_SEL_SE_P: + case DA7219_MIC_AMP_IN_SEL_SE_N: + snd_soc_write(codec, DA7219_MIC_1_SELECT, + pdata->mic_amp_in_sel); + break; + } + } +} + +static int da7219_probe(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_init(&da7219->lock); + + /* Regulator configuration */ + ret = da7219_handle_supplies(codec); + if (ret) + return ret; + + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7219->pdata = da7219_of_to_pdata(codec); + else + da7219->pdata = dev_get_platdata(codec->dev); + + da7219_handle_pdata(codec); + + /* Check if MCLK provided */ + da7219->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7219->mclk)) { + if (PTR_ERR(da7219->mclk) != -ENOENT) + return PTR_ERR(da7219->mclk); + else + da7219->mclk = NULL; + } + + /* Default PC counter to free-running */ + snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Default gain ramping */ + snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK, + DA7219_ADC_L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK, + DA7219_DAC_L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK, + DA7219_DAC_R_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_RAMP_EN_MASK, + DA7219_HP_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_RAMP_EN_MASK, + DA7219_HP_R_AMP_RAMP_EN_MASK); + + /* Default infinite tone gen, start/stop by Kcontrol */ + snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); + + /* Initialise AAD block */ + return da7219_aad_init(codec); +} + +static int da7219_remove(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + da7219_aad_exit(codec); + + /* Supplies */ + return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); +} + +#ifdef CONFIG_PM +static int da7219_suspend(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + + /* Put device into standby mode if jack detection disabled */ + if (!da7219->aad->jack) + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0); + + return 0; +} + +static int da7219_resume(struct snd_soc_codec *codec) +{ + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + + /* Put device into active mode if previously pushed to standby */ + if (!da7219->aad->jack) + snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK); + + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define da7219_suspend NULL +#define da7219_resume NULL +#endif + +static struct snd_soc_codec_driver soc_codec_dev_da7219 = { + .probe = da7219_probe, + .remove = da7219_remove, + .suspend = da7219_suspend, + .resume = da7219_resume, + .set_bias_level = da7219_set_bias_level, + + .controls = da7219_snd_controls, + .num_controls = ARRAY_SIZE(da7219_snd_controls), + + .dapm_widgets = da7219_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets), + .dapm_routes = da7219_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7219_audio_map), +}; + + +/* + * Regmap configs + */ + +static struct reg_default da7219_reg_defaults[] = { + { DA7219_MIC_1_SELECT, 0x00 }, + { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7219_SR_24_48, 0x00 }, + { DA7219_SR, 0x0A }, + { DA7219_CIF_I2C_ADDR_CFG, 0x02 }, + { DA7219_PLL_CTRL, 0x10 }, + { DA7219_PLL_FRAC_TOP, 0x00 }, + { DA7219_PLL_FRAC_BOT, 0x00 }, + { DA7219_PLL_INTEGER, 0x20 }, + { DA7219_DIG_ROUTING_DAI, 0x10 }, + { DA7219_DAI_CLK_MODE, 0x01 }, + { DA7219_DAI_CTRL, 0x28 }, + { DA7219_DAI_TDM_CTRL, 0x40 }, + { DA7219_DIG_ROUTING_DAC, 0x32 }, + { DA7219_DAI_OFFSET_LOWER, 0x00 }, + { DA7219_DAI_OFFSET_UPPER, 0x00 }, + { DA7219_REFERENCES, 0x00 }, + { DA7219_MIXIN_L_SELECT, 0x00 }, + { DA7219_MIXIN_L_GAIN, 0x03 }, + { DA7219_ADC_L_GAIN, 0x6F }, + { DA7219_ADC_FILTERS1, 0x80 }, + { DA7219_MIC_1_GAIN, 0x01 }, + { DA7219_SIDETONE_CTRL, 0x40 }, + { DA7219_SIDETONE_GAIN, 0x0E }, + { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7219_DAC_FILTERS5, 0x00 }, + { DA7219_DAC_FILTERS2, 0x88 }, + { DA7219_DAC_FILTERS3, 0x88 }, + { DA7219_DAC_FILTERS4, 0x08 }, + { DA7219_DAC_FILTERS1, 0x80 }, + { DA7219_DAC_L_GAIN, 0x6F }, + { DA7219_DAC_R_GAIN, 0x6F }, + { DA7219_CP_CTRL, 0x20 }, + { DA7219_HP_L_GAIN, 0x39 }, + { DA7219_HP_R_GAIN, 0x39 }, + { DA7219_MIXOUT_L_SELECT, 0x00 }, + { DA7219_MIXOUT_R_SELECT, 0x00 }, + { DA7219_MICBIAS_CTRL, 0x03 }, + { DA7219_MIC_1_CTRL, 0x40 }, + { DA7219_MIXIN_L_CTRL, 0x40 }, + { DA7219_ADC_L_CTRL, 0x40 }, + { DA7219_DAC_L_CTRL, 0x40 }, + { DA7219_DAC_R_CTRL, 0x40 }, + { DA7219_HP_L_CTRL, 0x40 }, + { DA7219_HP_R_CTRL, 0x40 }, + { DA7219_MIXOUT_L_CTRL, 0x10 }, + { DA7219_MIXOUT_R_CTRL, 0x10 }, + { DA7219_CHIP_ID1, 0x23 }, + { DA7219_CHIP_ID2, 0x93 }, + { DA7219_CHIP_REVISION, 0x00 }, + { DA7219_LDO_CTRL, 0x00 }, + { DA7219_IO_CTRL, 0x00 }, + { DA7219_GAIN_RAMP_CTRL, 0x00 }, + { DA7219_PC_COUNT, 0x02 }, + { DA7219_CP_VOL_THRESHOLD1, 0x0E }, + { DA7219_DIG_CTRL, 0x00 }, + { DA7219_ALC_CTRL2, 0x00 }, + { DA7219_ALC_CTRL3, 0x00 }, + { DA7219_ALC_NOISE, 0x3F }, + { DA7219_ALC_TARGET_MIN, 0x3F }, + { DA7219_ALC_TARGET_MAX, 0x00 }, + { DA7219_ALC_GAIN_LIMITS, 0xFF }, + { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7219_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7219_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7219_DAC_NG_SETUP_TIME, 0x00 }, + { DA7219_DAC_NG_OFF_THRESH, 0x00 }, + { DA7219_DAC_NG_ON_THRESH, 0x00 }, + { DA7219_DAC_NG_CTRL, 0x00 }, + { DA7219_TONE_GEN_CFG1, 0x00 }, + { DA7219_TONE_GEN_CFG2, 0x00 }, + { DA7219_TONE_GEN_CYCLES, 0x00 }, + { DA7219_TONE_GEN_FREQ1_L, 0x55 }, + { DA7219_TONE_GEN_FREQ1_U, 0x15 }, + { DA7219_TONE_GEN_FREQ2_L, 0x00 }, + { DA7219_TONE_GEN_FREQ2_U, 0x40 }, + { DA7219_TONE_GEN_ON_PER, 0x02 }, + { DA7219_TONE_GEN_OFF_PER, 0x01 }, + { DA7219_ACCDET_IRQ_MASK_A, 0x00 }, + { DA7219_ACCDET_IRQ_MASK_B, 0x00 }, + { DA7219_ACCDET_CONFIG_1, 0xD6 }, + { DA7219_ACCDET_CONFIG_2, 0x34 }, + { DA7219_ACCDET_CONFIG_3, 0x0A }, + { DA7219_ACCDET_CONFIG_4, 0x16 }, + { DA7219_ACCDET_CONFIG_5, 0x21 }, + { DA7219_ACCDET_CONFIG_6, 0x3E }, + { DA7219_ACCDET_CONFIG_7, 0x01 }, + { DA7219_SYSTEM_ACTIVE, 0x00 }, +}; + +static bool da7219_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7219_MIC_1_GAIN_STATUS: + case DA7219_MIXIN_L_GAIN_STATUS: + case DA7219_ADC_L_GAIN_STATUS: + case DA7219_DAC_L_GAIN_STATUS: + case DA7219_DAC_R_GAIN_STATUS: + case DA7219_HP_L_GAIN_STATUS: + case DA7219_HP_R_GAIN_STATUS: + case DA7219_CIF_CTRL: + case DA7219_PLL_SRM_STS: + case DA7219_ALC_CTRL1: + case DA7219_SYSTEM_MODES_INPUT: + case DA7219_SYSTEM_MODES_OUTPUT: + case DA7219_ALC_OFFSET_AUTO_M_L: + case DA7219_ALC_OFFSET_AUTO_U_L: + case DA7219_TONE_GEN_CFG1: + case DA7219_ACCDET_STATUS_A: + case DA7219_ACCDET_STATUS_B: + case DA7219_ACCDET_IRQ_EVENT_A: + case DA7219_ACCDET_IRQ_EVENT_B: + case DA7219_ACCDET_CONFIG_8: + case DA7219_SYSTEM_STATUS: + return 1; + default: + return 0; + } +} + +static const struct regmap_config da7219_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7219_SYSTEM_ACTIVE, + .reg_defaults = da7219_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults), + .volatile_reg = da7219_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + + +/* + * I2C layer + */ + +static int da7219_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da7219_priv *da7219; + int ret; + + da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv), + GFP_KERNEL); + if (!da7219) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7219); + + da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config); + if (IS_ERR(da7219->regmap)) { + ret = PTR_ERR(da7219->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219, + &da7219_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n", + ret); + } + return ret; +} + +static int da7219_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id da7219_i2c_id[] = { + { "da7219", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); + +static struct i2c_driver da7219_i2c_driver = { + .driver = { + .name = "da7219", + .of_match_table = da7219_of_match, + }, + .probe = da7219_i2c_probe, + .remove = da7219_i2c_remove, + .id_table = da7219_i2c_id, +}; + +module_i2c_driver(da7219_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7219 Codec Driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h new file mode 100644 index 000000000000..b514268c6c56 --- /dev/null +++ b/sound/soc/codecs/da7219.h @@ -0,0 +1,820 @@ +/* + * da7219.h - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __DA7219_H +#define __DA7219_H + +#include +#include +#include + +/* + * Registers + */ + +#define DA7219_MIC_1_GAIN_STATUS 0x6 +#define DA7219_MIXIN_L_GAIN_STATUS 0x8 +#define DA7219_ADC_L_GAIN_STATUS 0xA +#define DA7219_DAC_L_GAIN_STATUS 0xC +#define DA7219_DAC_R_GAIN_STATUS 0xD +#define DA7219_HP_L_GAIN_STATUS 0xE +#define DA7219_HP_R_GAIN_STATUS 0xF +#define DA7219_MIC_1_SELECT 0x10 +#define DA7219_CIF_TIMEOUT_CTRL 0x12 +#define DA7219_CIF_CTRL 0x13 +#define DA7219_SR_24_48 0x16 +#define DA7219_SR 0x17 +#define DA7219_CIF_I2C_ADDR_CFG 0x1B +#define DA7219_PLL_CTRL 0x20 +#define DA7219_PLL_FRAC_TOP 0x22 +#define DA7219_PLL_FRAC_BOT 0x23 +#define DA7219_PLL_INTEGER 0x24 +#define DA7219_PLL_SRM_STS 0x25 +#define DA7219_DIG_ROUTING_DAI 0x2A +#define DA7219_DAI_CLK_MODE 0x2B +#define DA7219_DAI_CTRL 0x2C +#define DA7219_DAI_TDM_CTRL 0x2D +#define DA7219_DIG_ROUTING_DAC 0x2E +#define DA7219_ALC_CTRL1 0x2F +#define DA7219_DAI_OFFSET_LOWER 0x30 +#define DA7219_DAI_OFFSET_UPPER 0x31 +#define DA7219_REFERENCES 0x32 +#define DA7219_MIXIN_L_SELECT 0x33 +#define DA7219_MIXIN_L_GAIN 0x34 +#define DA7219_ADC_L_GAIN 0x36 +#define DA7219_ADC_FILTERS1 0x38 +#define DA7219_MIC_1_GAIN 0x39 +#define DA7219_SIDETONE_CTRL 0x3A +#define DA7219_SIDETONE_GAIN 0x3B +#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C +#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D +#define DA7219_DAC_FILTERS5 0x40 +#define DA7219_DAC_FILTERS2 0x41 +#define DA7219_DAC_FILTERS3 0x42 +#define DA7219_DAC_FILTERS4 0x43 +#define DA7219_DAC_FILTERS1 0x44 +#define DA7219_DAC_L_GAIN 0x45 +#define DA7219_DAC_R_GAIN 0x46 +#define DA7219_CP_CTRL 0x47 +#define DA7219_HP_L_GAIN 0x48 +#define DA7219_HP_R_GAIN 0x49 +#define DA7219_MIXOUT_L_SELECT 0x4B +#define DA7219_MIXOUT_R_SELECT 0x4C +#define DA7219_SYSTEM_MODES_INPUT 0x50 +#define DA7219_SYSTEM_MODES_OUTPUT 0x51 +#define DA7219_MICBIAS_CTRL 0x62 +#define DA7219_MIC_1_CTRL 0x63 +#define DA7219_MIXIN_L_CTRL 0x65 +#define DA7219_ADC_L_CTRL 0x67 +#define DA7219_DAC_L_CTRL 0x69 +#define DA7219_DAC_R_CTRL 0x6A +#define DA7219_HP_L_CTRL 0x6B +#define DA7219_HP_R_CTRL 0x6C +#define DA7219_MIXOUT_L_CTRL 0x6E +#define DA7219_MIXOUT_R_CTRL 0x6F +#define DA7219_CHIP_ID1 0x81 +#define DA7219_CHIP_ID2 0x82 +#define DA7219_CHIP_REVISION 0x83 +#define DA7219_LDO_CTRL 0x90 +#define DA7219_IO_CTRL 0x91 +#define DA7219_GAIN_RAMP_CTRL 0x92 +#define DA7219_PC_COUNT 0x94 +#define DA7219_CP_VOL_THRESHOLD1 0x95 +#define DA7219_CP_DELAY 0x96 +#define DA7219_DIG_CTRL 0x99 +#define DA7219_ALC_CTRL2 0x9A +#define DA7219_ALC_CTRL3 0x9B +#define DA7219_ALC_NOISE 0x9C +#define DA7219_ALC_TARGET_MIN 0x9D +#define DA7219_ALC_TARGET_MAX 0x9E +#define DA7219_ALC_GAIN_LIMITS 0x9F +#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA7219_ALC_ANTICLIP_CTRL 0xA1 +#define DA7219_ALC_ANTICLIP_LEVEL 0xA2 +#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3 +#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4 +#define DA7219_DAC_NG_SETUP_TIME 0xAF +#define DA7219_DAC_NG_OFF_THRESH 0xB0 +#define DA7219_DAC_NG_ON_THRESH 0xB1 +#define DA7219_DAC_NG_CTRL 0xB2 +#define DA7219_TONE_GEN_CFG1 0xB4 +#define DA7219_TONE_GEN_CFG2 0xB5 +#define DA7219_TONE_GEN_CYCLES 0xB6 +#define DA7219_TONE_GEN_FREQ1_L 0xB7 +#define DA7219_TONE_GEN_FREQ1_U 0xB8 +#define DA7219_TONE_GEN_FREQ2_L 0xB9 +#define DA7219_TONE_GEN_FREQ2_U 0xBA +#define DA7219_TONE_GEN_ON_PER 0xBB +#define DA7219_TONE_GEN_OFF_PER 0xBC +#define DA7219_SYSTEM_STATUS 0xE0 +#define DA7219_SYSTEM_ACTIVE 0xFD + + +/* + * Bit Fields + */ + +#define DA7219_SWITCH_EN_MAX 0x1 + +/* DA7219_MIC_1_GAIN_STATUS = 0x6 */ +#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0) +#define DA7219_MIC_1_AMP_GAIN_MAX 0x7 + +/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */ +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0) + +/* DA7219_ADC_L_GAIN_STATUS = 0xA */ +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_L_GAIN_STATUS = 0xC */ +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_R_GAIN_STATUS = 0xD */ +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_HP_L_GAIN_STATUS = 0xE */ +#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_HP_R_GAIN_STATUS = 0xF */ +#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_MIC_1_SELECT = 0x10 */ +#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0 +#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */ +#define DA7219_I2C_TIMEOUT_EN_SHIFT 0 +#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7219_CIF_CTRL = 0x13 */ +#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0 +#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0) +#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7 +#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7) + +/* DA7219_SR_24_48 = 0x16 */ +#define DA7219_SR_24_48_SHIFT 0 +#define DA7219_SR_24_48_MASK (0x1 << 0) + +/* DA7219_SR = 0x17 */ +#define DA7219_SR_SHIFT 0 +#define DA7219_SR_MASK (0xF << 0) +#define DA7219_SR_8000 (0x01 << 0) +#define DA7219_SR_11025 (0x02 << 0) +#define DA7219_SR_12000 (0x03 << 0) +#define DA7219_SR_16000 (0x05 << 0) +#define DA7219_SR_22050 (0x06 << 0) +#define DA7219_SR_24000 (0x07 << 0) +#define DA7219_SR_32000 (0x09 << 0) +#define DA7219_SR_44100 (0x0A << 0) +#define DA7219_SR_48000 (0x0B << 0) +#define DA7219_SR_88200 (0x0E << 0) +#define DA7219_SR_96000 (0x0F << 0) + +/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */ +#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0 +#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0) + +/* DA7219_PLL_CTRL = 0x20 */ +#define DA7219_PLL_INDIV_SHIFT 2 +#define DA7219_PLL_INDIV_MASK (0x7 << 2) +#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2) +#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2) +#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2) +#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2) +#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2) +#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 +#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) +#define DA7219_PLL_MODE_SHIFT 6 +#define DA7219_PLL_MODE_MASK (0x3 << 6) +#define DA7219_PLL_MODE_BYPASS (0x0 << 6) +#define DA7219_PLL_MODE_NORMAL (0x1 << 6) +#define DA7219_PLL_MODE_SRM (0x2 << 6) +#define DA7219_PLL_MODE_32KHZ (0x3 << 6) + +/* DA7219_PLL_FRAC_TOP = 0x22 */ +#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0) + +/* DA7219_PLL_FRAC_BOT = 0x23 */ +#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0) + +/* DA7219_PLL_INTEGER = 0x24 */ +#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0 +#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0) + +/* DA7219_PLL_SRM_STS = 0x25 */ +#define DA7219_PLL_SRM_STATE_SHIFT 0 +#define DA7219_PLL_SRM_STATE_MASK (0xF << 0) +#define DA7219_PLL_SRM_STATUS_SHIFT 4 +#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4) +#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7) + +/* DA7219_DIG_ROUTING_DAI = 0x2A */ +#define DA7219_DAI_L_SRC_SHIFT 0 +#define DA7219_DAI_L_SRC_MASK (0x3 << 0) +#define DA7219_DAI_R_SRC_SHIFT 4 +#define DA7219_DAI_R_SRC_MASK (0x3 << 4) +#define DA7219_OUT_SRC_MAX 4 + +/* DA7219_DAI_CLK_MODE = 0x2B */ +#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0 +#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7219_DAI_CLK_POL_SHIFT 2 +#define DA7219_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7219_DAI_CLK_POL_INV (0x1 << 2) +#define DA7219_DAI_WCLK_POL_SHIFT 3 +#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7219_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4 +#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4) +#define DA7219_DAI_CLK_EN_SHIFT 7 +#define DA7219_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7219_DAI_CTRL = 0x2C */ +#define DA7219_DAI_FORMAT_SHIFT 0 +#define DA7219_DAI_FORMAT_MASK (0x3 << 0) +#define DA7219_DAI_FORMAT_I2S (0x0 << 0) +#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7219_DAI_FORMAT_DSP (0x3 << 0) +#define DA7219_DAI_WORD_LENGTH_SHIFT 2 +#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7219_DAI_CH_NUM_SHIFT 4 +#define DA7219_DAI_CH_NUM_MASK (0x3 << 4) +#define DA7219_DAI_CH_NUM_MAX 2 +#define DA7219_DAI_EN_SHIFT 7 +#define DA7219_DAI_EN_MASK (0x1 << 7) + +/* DA7219_DAI_TDM_CTRL = 0x2D */ +#define DA7219_DAI_TDM_CH_EN_SHIFT 0 +#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0) +#define DA7219_DAI_OE_SHIFT 6 +#define DA7219_DAI_OE_MASK (0x1 << 6) +#define DA7219_DAI_TDM_MODE_EN_SHIFT 7 +#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7) +#define DA7219_DAI_TDM_MAX_SLOTS 2 + +/* DA7219_DIG_ROUTING_DAC = 0x2E */ +#define DA7219_DAC_L_SRC_SHIFT 0 +#define DA7219_DAC_L_SRC_MASK (0x3 << 0) +#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0) +#define DA7219_DAC_L_MONO_SHIFT 3 +#define DA7219_DAC_L_MONO_MASK (0x1 << 3) +#define DA7219_DAC_R_SRC_SHIFT 4 +#define DA7219_DAC_R_SRC_MASK (0x3 << 4) +#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4) +#define DA7219_DAC_R_MONO_SHIFT 7 +#define DA7219_DAC_R_MONO_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL1 = 0x2F */ +#define DA7219_ALC_OFFSET_EN_SHIFT 0 +#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0) +#define DA7219_ALC_SYNC_MODE_SHIFT 1 +#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1) +#define DA7219_ALC_EN_SHIFT 3 +#define DA7219_ALC_EN_MASK (0x1 << 3) +#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4 +#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4) +#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5 +#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5) + +/* DA7219_DAI_OFFSET_LOWER = 0x30 */ +#define DA7219_DAI_OFFSET_LOWER_SHIFT 0 +#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0) + +/* DA7219_DAI_OFFSET_UPPER = 0x31 */ +#define DA7219_DAI_OFFSET_UPPER_SHIFT 0 +#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0) +#define DA7219_DAI_OFFSET_MAX 0x2FF + +/* DA7219_REFERENCES = 0x32 */ +#define DA7219_BIAS_EN_SHIFT 3 +#define DA7219_BIAS_EN_MASK (0x1 << 3) +#define DA7219_VMID_FAST_CHARGE_SHIFT 4 +#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4) + +/* DA7219_MIXIN_L_SELECT = 0x33 */ +#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXIN_L_GAIN = 0x34 */ +#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0) +#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF + +/* DA7219_ADC_L_GAIN = 0x36 */ +#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F + +/* DA7219_ADC_FILTERS1 = 0x38 */ +#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_VOICE_HPF_CORNER_MAX 8 +#define DA7219_ADC_VOICE_EN_SHIFT 3 +#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_AUDIO_HPF_CORNER_MAX 4 +#define DA7219_ADC_HPF_EN_SHIFT 7 +#define DA7219_ADC_HPF_EN_MASK (0x1 << 7) +#define DA7219_HPF_MODE_SHIFT 0 +#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7)) +#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7)) +#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MAX 3 + +/* DA7219_MIC_1_GAIN = 0x39 */ +#define DA7219_MIC_1_AMP_GAIN_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0) + +/* DA7219_SIDETONE_CTRL = 0x3A */ +#define DA7219_SIDETONE_MUTE_EN_SHIFT 6 +#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6) +#define DA7219_SIDETONE_EN_SHIFT 7 +#define DA7219_SIDETONE_EN_MASK (0x1 << 7) + +/* DA7219_SIDETONE_GAIN = 0x3B */ +#define DA7219_SIDETONE_GAIN_SHIFT 0 +#define DA7219_SIDETONE_GAIN_MASK (0xF << 0) +#define DA7219_SIDETONE_GAIN_MAX 0xE + +/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */ +#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0 +#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1 +#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2 +#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1) + +/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */ +#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0) + +/* DA7219_DAC_FILTERS5 = 0x40 */ +#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4 +#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4) +#define DA7219_DAC_SOFTMUTE_RATE_MAX 7 +#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7 +#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS2 = 0x41 */ +#define DA7219_DAC_EQ_BAND1_SHIFT 0 +#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND2_SHIFT 4 +#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4) +#define DA7219_DAC_EQ_BAND_MAX 0xF + +/* DA7219_DAC_FILTERS3 = 0x42 */ +#define DA7219_DAC_EQ_BAND3_SHIFT 0 +#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND4_SHIFT 4 +#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4) + +/* DA7219_DAC_FILTERS4 = 0x43 */ +#define DA7219_DAC_EQ_BAND5_SHIFT 0 +#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0) +#define DA7219_DAC_EQ_EN_SHIFT 7 +#define DA7219_DAC_EQ_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS1 = 0x44 */ +#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_DAC_VOICE_EN_SHIFT 3 +#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_DAC_HPF_EN_SHIFT 7 +#define DA7219_DAC_HPF_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_GAIN = 0x45 */ +#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F +#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0) + +/* DA7219_DAC_R_GAIN = 0x46 */ +#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7219_CP_CTRL = 0x47 */ +#define DA7219_CP_MCHANGE_SHIFT 4 +#define DA7219_CP_MCHANGE_MASK (0x3 << 4) +#define DA7219_CP_MCHANGE_REL_MASK 0x3 +#define DA7219_CP_MCHANGE_MAX 3 +#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1 +#define DA7219_CP_MCHANGE_DAC_VOL 0x2 +#define DA7219_CP_MCHANGE_SIG_MAG 0x3 +#define DA7219_CP_EN_SHIFT 7 +#define DA7219_CP_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_GAIN = 0x48 */ +#define DA7219_HP_L_AMP_GAIN_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0) +#define DA7219_HP_AMP_GAIN_MAX 0x3F +#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0) + +/* DA7219_HP_R_GAIN = 0x49 */ +#define DA7219_HP_R_AMP_GAIN_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0) + +/* DA7219_MIXOUT_L_SELECT = 0x4B */ +#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXOUT_R_SELECT = 0x4C */ +#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_SYSTEM_MODES_INPUT = 0x50 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_ADC_MODE_SHIFT 1 +#define DA7219_ADC_MODE_MASK (0x7F << 1) + +/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_DAC_MODE_SHIFT 1 +#define DA7219_DAC_MODE_MASK (0x7F << 1) + +/* DA7219_MICBIAS_CTRL = 0x62 */ +#define DA7219_MICBIAS1_LEVEL_SHIFT 0 +#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0) +#define DA7219_MICBIAS1_EN_SHIFT 3 +#define DA7219_MICBIAS1_EN_MASK (0x1 << 3) + +/* DA7219_MIC_1_CTRL = 0x63 */ +#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIC_1_AMP_EN_SHIFT 7 +#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXIN_L_CTRL = 0x65 */ +#define DA7219_MIXIN_L_MIX_EN_SHIFT 3 +#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3) +#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIXIN_L_AMP_EN_SHIFT 7 +#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_ADC_L_CTRL = 0x67 */ +#define DA7219_ADC_L_BIAS_SHIFT 0 +#define DA7219_ADC_L_BIAS_MASK (0x3 << 0) +#define DA7219_ADC_L_RAMP_EN_SHIFT 5 +#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_ADC_L_MUTE_EN_SHIFT 6 +#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_ADC_L_EN_SHIFT 7 +#define DA7219_ADC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_CTRL = 0x69 */ +#define DA7219_DAC_L_RAMP_EN_SHIFT 5 +#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_L_MUTE_EN_SHIFT 6 +#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_L_EN_SHIFT 7 +#define DA7219_DAC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_R_CTRL = 0x6A */ +#define DA7219_DAC_R_RAMP_EN_SHIFT 5 +#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_R_MUTE_EN_SHIFT 6 +#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_R_EN_SHIFT 7 +#define DA7219_DAC_R_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_CTRL = 0x6B */ +#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_L_AMP_OE_SHIFT 3 +#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_L_AMP_EN_SHIFT 7 +#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_HP_R_CTRL = 0x6C */ +#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_R_AMP_OE_SHIFT 3 +#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_R_AMP_EN_SHIFT 7 +#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_L_CTRL = 0x6E */ +#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_R_CTRL = 0x6F */ +#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_CHIP_ID1 = 0x81 */ +#define DA7219_CHIP_ID1_SHIFT 0 +#define DA7219_CHIP_ID1_MASK (0xFF << 0) + +/* DA7219_CHIP_ID2 = 0x82 */ +#define DA7219_CHIP_ID2_SHIFT 0 +#define DA7219_CHIP_ID2_MASK (0xFF << 0) + +/* DA7219_CHIP_REVISION = 0x83 */ +#define DA7219_CHIP_MINOR_SHIFT 0 +#define DA7219_CHIP_MINOR_MASK (0xF << 0) +#define DA7219_CHIP_MAJOR_SHIFT 4 +#define DA7219_CHIP_MAJOR_MASK (0xF << 4) + +/* DA7219_LDO_CTRL = 0x90 */ +#define DA7219_LDO_LEVEL_SELECT_SHIFT 4 +#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4) +#define DA7219_LDO_EN_SHIFT 7 +#define DA7219_LDO_EN_MASK (0x1 << 7) + +/* DA7219_IO_CTRL = 0x91 */ +#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0 +#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) +#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0 +#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1 + +/* DA7219_GAIN_RAMP_CTRL = 0x92 */ +#define DA7219_GAIN_RAMP_RATE_SHIFT 0 +#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7219_GAIN_RAMP_RATE_MAX 4 + +/* DA7219_PC_COUNT = 0x94 */ +#define DA7219_PC_FREERUN_SHIFT 0 +#define DA7219_PC_FREERUN_MASK (0x1 << 0) +#define DA7219_PC_RESYNC_AUTO_SHIFT 1 +#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1) + +/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */ +#define DA7219_CP_THRESH_VDD2_SHIFT 0 +#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0) +#define DA7219_CP_THRESH_VDD2_MAX 0x3F + +/* DA7219_DIG_CTRL = 0x99 */ +#define DA7219_DAC_L_INV_SHIFT 3 +#define DA7219_DAC_L_INV_MASK (0x1 << 3) +#define DA7219_DAC_R_INV_SHIFT 7 +#define DA7219_DAC_R_INV_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL2 = 0x9A */ +#define DA7219_ALC_ATTACK_SHIFT 0 +#define DA7219_ALC_ATTACK_MASK (0xF << 0) +#define DA7219_ALC_ATTACK_MAX 13 +#define DA7219_ALC_RELEASE_SHIFT 4 +#define DA7219_ALC_RELEASE_MASK (0xF << 4) +#define DA7219_ALC_RELEASE_MAX 11 + +/* DA7219_ALC_CTRL3 = 0x9B */ +#define DA7219_ALC_HOLD_SHIFT 0 +#define DA7219_ALC_HOLD_MASK (0xF << 0) +#define DA7219_ALC_HOLD_MAX 16 +#define DA7219_ALC_INTEG_ATTACK_SHIFT 4 +#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4) +#define DA7219_ALC_INTEG_RELEASE_SHIFT 6 +#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6) +#define DA7219_ALC_INTEG_MAX 4 + +/* DA7219_ALC_NOISE = 0x9C */ +#define DA7219_ALC_NOISE_SHIFT 0 +#define DA7219_ALC_NOISE_MASK (0x3F << 0) +#define DA7219_ALC_THRESHOLD_MAX 0x3F + +/* DA7219_ALC_TARGET_MIN = 0x9D */ +#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0) + +/* DA7219_ALC_TARGET_MAX = 0x9E */ +#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0) + +/* DA7219_ALC_GAIN_LIMITS = 0x9F */ +#define DA7219_ALC_ATTEN_MAX_SHIFT 0 +#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0) +#define DA7219_ALC_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4) +#define DA7219_ALC_ATTEN_GAIN_MAX 0xF + +/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */ +#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0) +#define DA7219_ALC_ANA_GAIN_MIN 0x1 +#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4) +#define DA7219_ALC_ANA_GAIN_MAX 0x7 + +/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */ +#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0 +#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0) +#define DA7219_ALC_ANTICLIP_STEP_MAX 4 +#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7 +#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7) + +/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */ +#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0 +#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0) + +/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */ +#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0) + +/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */ +#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0) + +/* DA7219_DAC_NG_SETUP_TIME = 0xAF */ +#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0) +#define DA7219_DAC_NG_SETUP_TIME_MAX 4 +#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2) +#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3) +#define DA7219_DAC_NG_RAMP_RATE_MAX 2 + +/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */ +#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0) +#define DA7219_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7219_DAC_NG_ON_THRESH = 0xB1 */ +#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0) + +/* DA7219_DAC_NG_CTRL = 0xB2 */ +#define DA7219_DAC_NG_EN_SHIFT 7 +#define DA7219_DAC_NG_EN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG1 = 0xB4 */ +#define DA7219_DTMF_REG_SHIFT 0 +#define DA7219_DTMF_REG_MASK (0xF << 0) +#define DA7219_DTMF_REG_MAX 16 +#define DA7219_DTMF_EN_SHIFT 4 +#define DA7219_DTMF_EN_MASK (0x1 << 4) +#define DA7219_START_STOPN_SHIFT 7 +#define DA7219_START_STOPN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG2 = 0xB5 */ +#define DA7219_SWG_SEL_SHIFT 0 +#define DA7219_SWG_SEL_MASK (0x3 << 0) +#define DA7219_SWG_SEL_MAX 4 +#define DA7219_SWG_SEL_SRAMP (0x3 << 0) +#define DA7219_TONE_GEN_GAIN_SHIFT 4 +#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4) +#define DA7219_TONE_GEN_GAIN_MAX 0xF +#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4) +#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4) + +/* DA7219_TONE_GEN_CYCLES = 0xB6 */ +#define DA7219_BEEP_CYCLES_SHIFT 0 +#define DA7219_BEEP_CYCLES_MASK (0x7 << 0) + +/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */ +#define DA7219_FREQ1_L_SHIFT 0 +#define DA7219_FREQ1_L_MASK (0xFF << 0) +#define DA7219_FREQ_MAX 0xFFFF + +/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */ +#define DA7219_FREQ1_U_SHIFT 0 +#define DA7219_FREQ1_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */ +#define DA7219_FREQ2_L_SHIFT 0 +#define DA7219_FREQ2_L_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_U = 0xBA */ +#define DA7219_FREQ2_U_SHIFT 0 +#define DA7219_FREQ2_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_ON_PER = 0xBB */ +#define DA7219_BEEP_ON_PER_SHIFT 0 +#define DA7219_BEEP_ON_PER_MASK (0x3F << 0) +#define DA7219_BEEP_ON_OFF_MAX 0x3F + +/* DA7219_TONE_GEN_OFF_PER = 0xBC */ +#define DA7219_BEEP_OFF_PER_SHIFT 0 +#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0) + +/* DA7219_SYSTEM_STATUS = 0xE0 */ +#define DA7219_SC1_BUSY_SHIFT 0 +#define DA7219_SC1_BUSY_MASK (0x1 << 0) +#define DA7219_SC2_BUSY_SHIFT 1 +#define DA7219_SC2_BUSY_MASK (0x1 << 1) + +/* DA7219_SYSTEM_ACTIVE = 0xFD */ +#define DA7219_SYSTEM_ACTIVE_SHIFT 0 +#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0) + + +/* + * General defines & data + */ + +/* Register inversion */ +#define DA7219_NO_INVERT 0 +#define DA7219_INVERT 1 + +/* Byte related defines */ +#define DA7219_BYTE_SHIFT 8 +#define DA7219_BYTE_MASK 0xFF + +/* PLL Output Frequencies */ +#define DA7219_PLL_FREQ_OUT_90316 90316800 +#define DA7219_PLL_FREQ_OUT_98304 98304000 + +/* PLL Frequency Dividers */ +#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1 +#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2 +#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4 +#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8 +#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16 + +/* SRM */ +#define DA7219_SRM_CHECK_RETRIES 8 + +enum da7219_clk_src { + DA7219_CLKSRC_MCLK = 0, + DA7219_CLKSRC_MCLK_SQR, +}; + +enum da7219_sys_clk { + DA7219_SYSCLK_MCLK = 0, + DA7219_SYSCLK_PLL, + DA7219_SYSCLK_PLL_SRM, + DA7219_SYSCLK_PLL_32KHZ +}; + +/* Regulators */ +enum da7219_supplies { + DA7219_SUPPLY_VDD = 0, + DA7219_SUPPLY_VDDMIC, + DA7219_SUPPLY_VDDIO, + DA7219_NUM_SUPPLIES, +}; + +struct da7219_aad_priv; + +/* Private data */ +struct da7219_priv { + struct da7219_aad_priv *aad; + struct da7219_pdata *pdata; + + struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES]; + struct regmap *regmap; + struct mutex lock; + + struct clk *mclk; + unsigned int mclk_rate; + int clk_src; + + bool master; + bool alc_en; +}; + +#endif /* __DA7219_H */ -- cgit v1.2.3-59-g8ed1b From fbe039bb0815e6113f82021aa8c0e36a1941f511 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 28 Sep 2015 13:32:37 -0700 Subject: ASoC: rt5645: Allow 4 channel recording on AIF1 The codec supports 4 channel recording with TDM on AIF1. This patch modifies the DAI capability to allow it. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 1f7045bc23a7..66b7332e8654 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3113,7 +3113,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = { .capture = { .stream_name = "AIF1 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = RT5645_STEREO_RATES, .formats = RT5645_FORMATS, }, -- cgit v1.2.3-59-g8ed1b From 425a570e1bfaeadaf6558df1e14e1735d5a76c10 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 4 Oct 2015 09:17:03 +0900 Subject: ALSA: bebob: support Firewire I/O card of Mackie Onyx 1220/1620/1640 Current ALSA BeBoB drivers has an entry for this model, while the value of vendor ID seems to be wrong according to an user's report. The vendor had released no updated firmware, thus we can judge that this model had not changed the content of its config ROM. It's reasonable to fix the ID according to the report. $ ./linux-firewire-utils/src/crpp < /sys/bus/firewire/devices/fw1/config_rom ROM header and bus information block ----------------------------------------------------------------- 400 0425720f bus_info_length 4, crc_length 37, crc 29199 404 31333934 bus_name "1394" 408 f0646122 irmc 1, cmc 1, isc 1, bmc 1, pmc 0, cyc_clk_acc 100, max_rec 6 (128), max_rom 1, gen 2, spd 2 (S400) 40c 00000ff2 company_id 00000f | 410 00004697 device_id f200004697 | EUI-64 00000ff200004697 root directory ----------------------------------------------------------------- 414 000859be directory_length 8, crc 22974 418 04000082 hardware version 41c 0c0083c0 node capabilities per IEEE 1394 420 03000ff2 vendor 424 8100000a --> descriptor leaf at 44c 428 17010065 model 42c 8100000d --> descriptor leaf at 460 430 13000910 version 434 d1000001 --> unit directory at 438 unit directory at 438 ----------------------------------------------------------------- 438 0004ccec directory_length 4, crc 52460 43c 1200a02d specifier id: 1394 TA 440 13010001 version: AV/C 444 17010065 model 448 8100000d --> descriptor leaf at 47c descriptor leaf at 44c ----------------------------------------------------------------- 44c 0004152a leaf_length 4, crc 5418 450 00000000 textual descriptor 454 00000000 minimal ASCII 458 4d61636b "Mack" 45c 69650000 "ie" descriptor leaf at 460 ----------------------------------------------------------------- 460 000612b5 leaf_length 6, crc 4789 464 00000000 textual descriptor 468 00000000 minimal ASCII 46c 4f6e7978 "Onyx" 470 20466972 " Fir" 474 65776972 "ewir" 478 65000000 "e" descriptor leaf at 47c ----------------------------------------------------------------- 47c 000612b5 leaf_length 6, crc 4789 480 00000000 textual descriptor 484 00000000 minimal ASCII 488 4f6e7978 "Onyx" 48c 20466972 " Fir" 490 65776972 "ewir" 494 65000000 "e" $ cat /proc/asound/card3/firewire/firmware Manufacturer: bridgeCo Protocol Ver: 1 Build Ver: 0 GUID: 0x00000FF200004697 Model ID: 0x82 Model Rev: 1 Firmware Date: 20040430 Firmware Time: 131527 Firmware ID: 0x10065 Firmware Ver: 2320 Base Addr: 0x20080000 Max Size: 1572864 Loader Date: 20040430 Loader Time: 112036 Reported-by: Andrzej Gansiniec Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 27a04ac8ffee..0ed5e5ff9346 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -41,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS); #define VEN_EDIROL 0x000040ab #define VEN_PRESONUS 0x00000a92 #define VEN_BRIDGECO 0x000007f5 -#define VEN_MACKIE 0x0000000f +#define VEN_MACKIE1 0x0000000f +#define VEN_MACKIE2 0x00000ff2 #define VEN_STANTON 0x00001260 #define VEN_TASCAM 0x0000022e #define VEN_BEHRINGER 0x00001564 @@ -360,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = { /* BridgeCo, Audio5 */ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal), /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */ - SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal), + SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal), /* Mackie, d.2 (Firewire Option) */ - SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal), + SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal), /* Stanton, ScratchAmp */ SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal), /* Tascam, IF-FW DM */ -- cgit v1.2.3-59-g8ed1b From a2c026cfec3fb84375785dd2d6ec80bd60c5120e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 5 Oct 2015 19:34:16 +0800 Subject: ASoC: rt5645: Prevent the weird sound of the headphone while rebooting The patch adds the codec reset setting in the shutdown function to prevent the weird sound of the headphone happened by rebooting. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 66b7332e8654..cd1a4ec980ac 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3514,6 +3514,8 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) RT5645_CBJ_MN_JD); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN, 0); + msleep(20); + regmap_write(rt5645->regmap, RT5645_RESET, 0); } static struct i2c_driver rt5645_i2c_driver = { -- cgit v1.2.3-59-g8ed1b From 4e929134eb8271abc9c52c371e056debfea6898b Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Mon, 5 Oct 2015 15:10:04 +0100 Subject: ASoC: da7219: Improve error handling for regulator supplies Currently if bulk_enable() of supplies fails, the code still goes on to try and put the device into active state, and set expected IO voltage of the device. This doesn't really make sense so code now returns on bulk_enable() failure, with an error message. Also, to help with debug, failure to get supplies also provides an error message. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 76f8fc2c00fe..c86a83305fb7 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1554,8 +1554,10 @@ static int da7219_handle_supplies(struct snd_soc_codec *codec) ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES, da7219->supplies); - if (ret) + if (ret) { + dev_err(codec->dev, "Failed to get supplies"); return ret; + } /* Determine VDDIO voltage provided */ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; @@ -1567,6 +1569,10 @@ static int da7219_handle_supplies(struct snd_soc_codec *codec) /* Enable main supplies */ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies"); + return ret; + } /* Ensure device in active mode */ snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK); @@ -1574,7 +1580,7 @@ static int da7219_handle_supplies(struct snd_soc_codec *codec) /* Update IO voltage level range */ snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl); - return ret; + return 0; } static void da7219_handle_pdata(struct snd_soc_codec *codec) -- cgit v1.2.3-59-g8ed1b From 88b19968a247117d3cbf0d405d004c7fc0e7a42c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 5 Oct 2015 15:09:48 +0100 Subject: ALSA: hdac: Fix incorrect update of stream id mapping Bits in LOSIDV need to be set to map the stream id for specific link. Fixing this by setting the required bits in the register. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/hda/ext/hdac_ext_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 33ba77dd32f2..4bcebc8cde26 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -227,7 +227,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup); void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, int stream) { - snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0); + snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id); -- cgit v1.2.3-59-g8ed1b From a04267fd87b62cd03d2a2ebb05a5b38d272e8d11 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Mon, 5 Oct 2015 15:09:49 +0100 Subject: ALSA: hdac: Fix to check if stream not in use in release if the stream is decoupled and both link and host are used, while releasing the stream, need to check if link and host stream are not in use. This patch adds fix to check if the host/link stream is in used before coupling it back when releasing the stream. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/hda/ext/hdac_ext_stream.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 4bcebc8cde26..cb89ec7c8147 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -385,14 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) break; case HDAC_EXT_STREAM_TYPE_HOST: - if (stream->decoupled) { + if (stream->decoupled && !stream->link_locked) snd_hdac_ext_stream_decouple(ebus, stream, false); - snd_hdac_stream_release(&stream->hstream); - } + snd_hdac_stream_release(&stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: - if (stream->decoupled) + if (stream->decoupled && !stream->hstream.opened) snd_hdac_ext_stream_decouple(ebus, stream, false); spin_lock_irq(&bus->reg_lock); stream->link_locked = 0; -- cgit v1.2.3-59-g8ed1b From 1d957d862ac782eaf5803d4d4cf167708e4dc147 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 25 Sep 2015 17:48:22 -0400 Subject: ASoC: dwc: support dw i2s in slave mode dw i2s controller can work in slave mode, codec being master. dw i2s is made to support master/slave operation, by reading dwc register. Signed-off-by: Maruthi Bayyavarapu Signed-off-by: Alex Deucher Signed-off-by: Mark Brown --- include/sound/designware_i2s.h | 2 + sound/soc/dwc/designware_i2s.c | 92 +++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 37 deletions(-) (limited to 'sound') diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 3a8fca9409a7..8966ba7c9629 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -38,6 +38,8 @@ struct i2s_clk_config_data { struct i2s_platform_data { #define DWC_I2S_PLAY (1 << 0) #define DWC_I2S_RECORD (1 << 1) + #define DW_I2S_SLAVE (1 << 2) + #define DW_I2S_MASTER (1 << 3) unsigned int cap; int channel; u32 snd_fmts; diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index a3e97b46b64e..3a52f82b5523 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -273,23 +273,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); - if (dev->i2s_clk_cfg) { - ret = dev->i2s_clk_cfg(config); - if (ret < 0) { - dev_err(dev->dev, "runtime audio clk config fail\n"); - return ret; - } - } else { - u32 bitclk = config->sample_rate * config->data_width * 2; - - ret = clk_set_rate(dev->clk, bitclk); - if (ret) { - dev_err(dev->dev, "Can't set I2S clock rate: %d\n", - ret); - return ret; + if (dev->capability & DW_I2S_MASTER) { + if (dev->i2s_clk_cfg) { + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + } else { + u32 bitclk = config->sample_rate * + config->data_width * 2; + + ret = clk_set_rate(dev->clk, bitclk); + if (ret) { + dev_err(dev->dev, "Can't set I2S clock rate: %d\n", + ret); + return ret; + } } } - return 0; } @@ -357,7 +359,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - clk_disable(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable(dev->clk); return 0; } @@ -365,7 +368,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - clk_enable(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_enable(dev->clk); return 0; } @@ -443,6 +447,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dw_i2s_dai->capture.rates = rates; } + if (COMP1_MODE_EN(comp1)) { + dev_dbg(dev->dev, "designware: i2s master mode supported\n"); + dev->capability |= DW_I2S_MASTER; + } else { + dev_dbg(dev->dev, "designware: i2s slave mode supported\n"); + dev->capability |= DW_I2S_SLAVE; + } + return 0; } @@ -529,6 +541,7 @@ static int dw_i2s_probe(struct platform_device *pdev) struct resource *res; int ret; struct snd_soc_dai_driver *dw_i2s_dai; + const char *clk_id; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { @@ -550,32 +563,35 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; + if (pdata) { + dev->capability = pdata->cap; + clk_id = NULL; ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); - if (ret < 0) - return ret; + } else { + clk_id = "i2sclk"; + ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + } + if (ret < 0) + return ret; - dev->capability = pdata->cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - if (!dev->i2s_clk_cfg) { - dev_err(&pdev->dev, "no clock configure method\n"); - return -ENODEV; + if (dev->capability & DW_I2S_MASTER) { + if (pdata) { + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + if (!dev->i2s_clk_cfg) { + dev_err(&pdev->dev, "no clock configure method\n"); + return -ENODEV; + } } + dev->clk = devm_clk_get(&pdev->dev, clk_id); - dev->clk = devm_clk_get(&pdev->dev, NULL); - } else { - ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; - - dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); } - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); - - ret = clk_prepare_enable(dev->clk); - if (ret < 0) - return ret; dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, @@ -597,7 +613,8 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: - clk_disable_unprepare(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable_unprepare(dev->clk); return ret; } @@ -605,7 +622,8 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(dev->clk); + if (dev->capability & DW_I2S_MASTER) + clk_disable_unprepare(dev->clk); return 0; } -- cgit v1.2.3-59-g8ed1b From 43ac946922b337507c4131c45bf339ddcd7e7402 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 17:18:48 +0800 Subject: ASoC: imx-spdif: add snd_soc_pm_ops for spdif machine driver Add snd_soc_pm_ops in machine driver to make the trigger suspend/resume be called in suspend/resume. Signed-off-by: Zidan Wang Signed-off-by: Mark Brown --- sound/soc/fsl/imx-spdif.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 33da26a12457..a407e833c612 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); static struct platform_driver imx_spdif_driver = { .driver = { .name = "imx-spdif", + .pm = &snd_soc_pm_ops, .of_match_table = imx_spdif_dt_ids, }, .probe = imx_spdif_audio_probe, -- cgit v1.2.3-59-g8ed1b From 1fde5e83a17acbcfcce27f68be46a6da4344efbd Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:10 +0800 Subject: ASoC: fsl_sai: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, SAI needs to save all the values of registers before the system suspend and restore them after the system resume. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a18fd92c4a85..5c737f167c5d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_RCSR: case FSL_SAI_TFR: case FSL_SAI_RFR: case FSL_SAI_TDR: @@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = { .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, + .cache_type = REGCACHE_FLAT, }; static int fsl_sai_probe(struct platform_device *pdev) @@ -802,10 +805,40 @@ static const struct of_device_id fsl_sai_ids[] = { { /* sentinel */ } }; +#if CONFIG_PM_SLEEP +static int fsl_sai_suspend(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, true); + regcache_mark_dirty(sai->regmap); + + return 0; +} + +static int fsl_sai_resume(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + + regcache_cache_only(sai->regmap, false); + regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR); + regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR); + msleep(1); + regmap_write(sai->regmap, FSL_SAI_TCSR, 0); + regmap_write(sai->regmap, FSL_SAI_RCSR, 0); + return regcache_sync(sai->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_sai_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume) +}; + static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, .driver = { .name = "fsl-sai", + .pm = &fsl_sai_pm_ops, .of_match_table = fsl_sai_ids, }, }; -- cgit v1.2.3-59-g8ed1b From f9f4fa61aab9417e40898cf6706fffa94005dc44 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:11 +0800 Subject: ASoC: fsl_spdif: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, SPDIF needs to save all the values of registers before the system suspend and restore them after the system resume. The SRPC register should be volatile, LOCK bit is set by the hardware. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index ab729f2426fe..3d59bb6719f2 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -108,6 +108,8 @@ struct fsl_spdif_priv { struct clk *sysclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; + /* regcache for SRPC */ + u32 regcache_srpc; }; /* DPLL locked and lock loss interrupt handler */ @@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) struct regmap *regmap = spdif_priv->regmap; u32 val, cycle = 1000; + regcache_cache_bypass(regmap, true); + regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET); /* @@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) regmap_read(regmap, REG_SPDIF_SCR, &val); } while ((val & SCR_SOFT_RESET) && cycle--); + regcache_cache_bypass(regmap, false); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + if (cycle) return 0; else @@ -997,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = { }; /* FSL SPDIF REGMAP */ +static const struct reg_default fsl_spdif_reg_defaults[] = { + {0x0, 0x00000400}, + {0x4, 0x00000000}, + {0xc, 0x00000000}, + {0x34, 0x00000000}, + {0x38, 0x00000000}, + {0x50, 0x00020f00}, +}; static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) { @@ -1022,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) } } +static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SRPC: + case REG_SPDIF_SIS: + case REG_SPDIF_SRL: + case REG_SPDIF_SRR: + case REG_SPDIF_SRCSH: + case REG_SPDIF_SRCSL: + case REG_SPDIF_SRU: + case REG_SPDIF_SRQ: + case REG_SPDIF_STL: + case REG_SPDIF_STR: + case REG_SPDIF_SRFM: + return true; + default: + return false; + } +} + static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -1047,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = { .val_bits = 32, .max_register = REG_SPDIF_STC, + .reg_defaults = fsl_spdif_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults), .readable_reg = fsl_spdif_readable_reg, + .volatile_reg = fsl_spdif_volatile_reg, .writeable_reg = fsl_spdif_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, @@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM_SLEEP +static int fsl_spdif_suspend(struct device *dev) +{ + struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + + regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, + &spdif_priv->regcache_srpc); + + regcache_cache_only(spdif_priv->regmap, true); + regcache_mark_dirty(spdif_priv->regmap); + + return 0; +} + +static int fsl_spdif_resume(struct device *dev) +{ + struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + + regcache_cache_only(spdif_priv->regmap, false); + + regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, + SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, + spdif_priv->regcache_srpc); + + return regcache_sync(spdif_priv->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_spdif_pm = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume) +}; + static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, { .compatible = "fsl,vf610-spdif", }, @@ -1282,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = { .driver = { .name = "fsl-spdif-dai", .of_match_table = fsl_spdif_dt_ids, + .pm = &fsl_spdif_pm, }, .probe = fsl_spdif_probe, }; -- cgit v1.2.3-59-g8ed1b From 05cf237972fe65eb537ea4f10e5627ceeb8f89b6 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:12 +0800 Subject: ASoC: fsl_ssi: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, SSI needs to save all the values of registers before the system suspend and restore them after the system resume. The register SFCSR is volatile, but some bits in it need to be recovered after suspend/resume. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8ec6fb208ea0..7c495d3d188a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val { struct fsl_ssi_reg_val rx; struct fsl_ssi_reg_val tx; }; + +static const struct reg_default fsl_ssi_reg_defaults[] = { + {0x10, 0x00000000}, + {0x18, 0x00003003}, + {0x1c, 0x00000200}, + {0x20, 0x00000200}, + {0x24, 0x00040000}, + {0x28, 0x00040000}, + {0x38, 0x00000000}, + {0x48, 0x00000000}, + {0x4c, 0x00000000}, + {0x54, 0x00000000}, + {0x58, 0x00000000}, +}; + +static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_SACCEN: + case CCSR_SSI_SACCDIS: + return false; + default: + return true; + } +} + +static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_STX0: + case CCSR_SSI_STX1: + case CCSR_SSI_SRX0: + case CCSR_SSI_SRX1: + case CCSR_SSI_SISR: + case CCSR_SSI_SFCSR: + case CCSR_SSI_SACADD: + case CCSR_SSI_SACDAT: + case CCSR_SSI_SATAG: + case CCSR_SSI_SACCST: + return true; + default: + return false; + } +} + +static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CCSR_SSI_SRX0: + case CCSR_SSI_SRX1: + case CCSR_SSI_SACCST: + return false; + default: + return true; + } +} + static const struct regmap_config fsl_ssi_regconfig = { .max_register = CCSR_SSI_SACCDIS, .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, + .reg_defaults = fsl_ssi_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults), + .readable_reg = fsl_ssi_readable_reg, + .volatile_reg = fsl_ssi_volatile_reg, + .writeable_reg = fsl_ssi_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; struct fsl_ssi_soc_data { @@ -176,6 +239,9 @@ struct fsl_ssi_private { unsigned int baudclk_streams; unsigned int bitclk_freq; + /*regcache for SFCSR*/ + u32 regcache_sfcsr; + /* DMA params */ struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -1513,10 +1579,46 @@ static int fsl_ssi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int fsl_ssi_suspend(struct device *dev) +{ + struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); + struct regmap *regs = ssi_private->regs; + + regmap_read(regs, CCSR_SSI_SFCSR, + &ssi_private->regcache_sfcsr); + + regcache_cache_only(regs, true); + regcache_mark_dirty(regs); + + return 0; +} + +static int fsl_ssi_resume(struct device *dev) +{ + struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); + struct regmap *regs = ssi_private->regs; + + regcache_cache_only(regs, false); + + regmap_update_bits(regs, CCSR_SSI_SFCSR, + CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK | + CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK, + ssi_private->regcache_sfcsr); + + return regcache_sync(regs); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_ssi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume) +}; + static struct platform_driver fsl_ssi_driver = { .driver = { .name = "fsl-ssi-dai", .of_match_table = fsl_ssi_ids, + .pm = &fsl_ssi_pm, }, .probe = fsl_ssi_probe, .remove = fsl_ssi_remove, -- cgit v1.2.3-59-g8ed1b From c64c60763b4e3c72a3520c8d51be858cd67bacb5 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 11:09:13 +0800 Subject: ASoC: fsl_esai: Add driver suspend and resume to support MEGA Fast For i.MX6 SoloX, there is a mode of the SoC to shutdown all power source of modules during system suspend and resume procedure. Thus, ESAI needs to save all the values of registers before the system suspend and restore them after the system resume. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 837979ea5c92..aab675a9d3f1 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = { .name = "fsl-esai", }; +static const struct reg_default fsl_esai_reg_defaults[] = { + {0x8, 0x00000000}, + {0x10, 0x00000000}, + {0x18, 0x00000000}, + {0x98, 0x00000000}, + {0xd0, 0x00000000}, + {0xd4, 0x00000000}, + {0xd8, 0x00000000}, + {0xdc, 0x00000000}, + {0xe0, 0x00000000}, + {0xe4, 0x0000ffff}, + {0xe8, 0x0000ffff}, + {0xec, 0x0000ffff}, + {0xf0, 0x0000ffff}, + {0xf8, 0x00000000}, + {0xfc, 0x00000000}, +}; + static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) } } +static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ETDR: + case REG_ESAI_ERDR: + case REG_ESAI_ESR: + case REG_ESAI_TFSR: + case REG_ESAI_RFSR: + case REG_ESAI_TX0: + case REG_ESAI_TX1: + case REG_ESAI_TX2: + case REG_ESAI_TX3: + case REG_ESAI_TX4: + case REG_ESAI_TX5: + case REG_ESAI_RX0: + case REG_ESAI_RX1: + case REG_ESAI_RX2: + case REG_ESAI_RX3: + case REG_ESAI_SAISR: + return true; + default: + return false; + } +} + static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = { .val_bits = 32, .max_register = REG_ESAI_PCRC, + .reg_defaults = fsl_esai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults), .readable_reg = fsl_esai_readable_reg, + .volatile_reg = fsl_esai_volatile_reg, .writeable_reg = fsl_esai_writeable_reg, + .cache_type = REGCACHE_RBTREE, }; static int fsl_esai_probe(struct platform_device *pdev) @@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); +#if CONFIG_PM_SLEEP +static int fsl_esai_suspend(struct device *dev) +{ + struct fsl_esai *esai = dev_get_drvdata(dev); + + regcache_cache_only(esai->regmap, true); + regcache_mark_dirty(esai->regmap); + + return 0; +} + +static int fsl_esai_resume(struct device *dev) +{ + struct fsl_esai *esai = dev_get_drvdata(dev); + int ret; + + regcache_cache_only(esai->regmap, false); + + /* FIFO reset for safety */ + regmap_update_bits(esai->regmap, REG_ESAI_TFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + regmap_update_bits(esai->regmap, REG_ESAI_RFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + + ret = regcache_sync(esai->regmap); + if (ret) + return ret; + + /* FIFO reset done */ + regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); + regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_esai_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume) +}; + static struct platform_driver fsl_esai_driver = { .probe = fsl_esai_probe, .driver = { .name = "fsl-esai-dai", + .pm = &fsl_esai_pm_ops, .of_match_table = fsl_esai_dt_ids, }, }; -- cgit v1.2.3-59-g8ed1b From e5224f58e3efd74972f7dcf46264c3ba9aa807ba Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 29 Sep 2015 16:41:43 +0200 Subject: ASoC: ad193x: add support to ad1934 The AD1934 codec has no ADC feature. Hence it register mapping is slightly different from the register mapping of other members of the AD193x family. Some ASoC controls and widgets are related to the DAC feature so are not relevant in the case of an AD1934 codec. Signed-off-by: Cyrille Pitchen Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x-i2c.c | 8 +-- sound/soc/codecs/ad193x-spi.c | 16 +++++- sound/soc/codecs/ad193x.c | 122 ++++++++++++++++++++++++++++++++---------- sound/soc/codecs/ad193x.h | 9 +++- 4 files changed, 121 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index df3a1a415825..171313664bc8 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -15,8 +15,8 @@ #include "ad193x.h" static const struct i2c_device_id ad193x_id[] = { - { "ad1936", 0 }, - { "ad1937", 0 }, + { "ad1936", AD193X }, + { "ad1937", AD193X }, { } }; MODULE_DEVICE_TABLE(i2c, ad193x_id); @@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client, config.val_bits = 8; config.reg_bits = 8; - return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config)); + return ad193x_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + (enum ad193x_type)id->driver_data); } static int ad193x_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index 390cef9b9dc2..364e82112e8c 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -16,6 +16,7 @@ static int ad193x_spi_probe(struct spi_device *spi) { + const struct spi_device_id *id = spi_get_device_id(spi); struct regmap_config config; config = ad193x_regmap_config; @@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi) config.read_flag_mask = 0x09; config.write_flag_mask = 0x08; - return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); + return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config), + (enum ad193x_type)id->driver_data); } static int ad193x_spi_remove(struct spi_device *spi) @@ -33,6 +35,17 @@ static int ad193x_spi_remove(struct spi_device *spi) return 0; } +static const struct spi_device_id ad193x_spi_id[] = { + { "ad193x", AD193X }, + { "ad1933", AD1933 }, + { "ad1934", AD1934 }, + { "ad1936", AD193X }, + { "ad1937", AD193X }, + { "ad1938", AD193X }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad193x_spi_id); + static struct spi_driver ad193x_spi_driver = { .driver = { .name = "ad193x", @@ -40,6 +53,7 @@ static struct spi_driver ad193x_spi_driver = { }, .probe = ad193x_spi_probe, .remove = ad193x_spi_remove, + .id_table = ad193x_spi_id, }; module_spi_driver(ad193x_spi_driver); diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 17c953595660..76d7966bd72c 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -23,6 +23,7 @@ /* codec private data */ struct ad193x_priv { struct regmap *regmap; + enum ad193x_type type; int sysclk; }; @@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL, AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv), - /* ADC switch control */ - SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, - AD193X_ADCR1_MUTE, 1, 1), - SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, - AD193X_ADCR2_MUTE, 1, 1), - /* DAC switch control */ SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE, AD193X_DACR1_MUTE, 1, 1), @@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = { SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE, AD193X_DACR4_MUTE, 1, 1), + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), +}; + +static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = { + /* ADC switch control */ + SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, + AD193X_ADCR1_MUTE, 1, 1), + SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, + AD193X_ADCR2_MUTE, 1, 1), + /* ADC high-pass filter */ SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0, AD193X_ADC_HIGHPASS_FILTER, 1, 0), - - /* DAC de-emphasis */ - SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), }; static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0), SND_SOC_DAPM_VMID("VMID"), SND_SOC_DAPM_OUTPUT("DAC1OUT"), SND_SOC_DAPM_OUTPUT("DAC2OUT"), SND_SOC_DAPM_OUTPUT("DAC3OUT"), SND_SOC_DAPM_OUTPUT("DAC4OUT"), +}; + +static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_INPUT("ADC1IN"), SND_SOC_DAPM_INPUT("ADC2IN"), }; @@ -91,18 +97,33 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, { "DAC Output", NULL, "DAC" }, { "DAC Output", NULL, "VMID" }, - { "ADC", NULL, "SYSCLK" }, - { "DAC", NULL, "ADC_PWR" }, - { "ADC", NULL, "ADC_PWR" }, { "DAC1OUT", NULL, "DAC Output" }, { "DAC2OUT", NULL, "DAC Output" }, { "DAC3OUT", NULL, "DAC Output" }, { "DAC4OUT", NULL, "DAC Output" }, + { "SYSCLK", NULL, "PLL_PWR" }, +}; + +static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { + { "ADC", NULL, "SYSCLK" }, + { "ADC", NULL, "ADC_PWR" }, { "ADC", NULL, "ADC1IN" }, { "ADC", NULL, "ADC2IN" }, - { "SYSCLK", NULL, "PLL_PWR" }, }; +static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x) +{ + switch (ad193x->type) { + case AD1933: + case AD1934: + return false; + default: + break; + } + + return true; +} + /* * DAI ops entries */ @@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, - AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_CHAN_MASK, + channels << AD193X_ADC_CHAN_SHFT); return 0; } @@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, adc_serfmt |= AD193X_ADC_SERFMT_AUX; break; default: - return -EINVAL; + if (ad193x_has_adc(ad193x)) + return -EINVAL; + break; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, - AD193X_ADC_SERFMT_MASK, adc_serfmt); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, - AD193X_ADC_FMT_MASK, adc_fmt); + if (ad193x_has_adc(ad193x)) { + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_SERFMT_MASK, adc_serfmt); + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_FMT_MASK, adc_fmt); + } regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_FMT_MASK, dac_fmt); @@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, AD193X_DAC_WORD_LEN_MASK, word_len << AD193X_DAC_WORD_LEN_SHFT); - regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, - AD193X_ADC_WORD_LEN_MASK, word_len); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_WORD_LEN_MASK, word_len); return 0; } @@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = { static int ad193x_codec_probe(struct snd_soc_codec *codec) { struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int num, ret; /* default setting for ad193x */ @@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec) regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A); /* dac in tdm mode */ regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40); - /* high-pass filter enable */ - regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); - /* sata delay=1, adc aux mode */ - regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); + + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* high-pass filter enable */ + regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); + /* sata delay=1, adc aux mode */ + regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); + } + /* pll input: mclki/xi */ regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04); + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* add adc controls */ + num = ARRAY_SIZE(ad193x_adc_snd_controls); + ret = snd_soc_add_codec_controls(codec, + ad193x_adc_snd_controls, + num); + if (ret) + return ret; + + /* add adc widgets */ + num = ARRAY_SIZE(ad193x_adc_widgets); + ret = snd_soc_dapm_new_controls(dapm, + ad193x_adc_widgets, + num); + if (ret) + return ret; + + /* add adc routes */ + num = ARRAY_SIZE(ad193x_adc_audio_paths); + ret = snd_soc_dapm_add_routes(dapm, + ad193x_adc_audio_paths, + num); + if (ret) + return ret; + } + return 0; } @@ -367,7 +429,8 @@ const struct regmap_config ad193x_regmap_config = { }; EXPORT_SYMBOL_GPL(ad193x_regmap_config); -int ad193x_probe(struct device *dev, struct regmap *regmap) +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type) { struct ad193x_priv *ad193x; @@ -379,6 +442,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap) return -ENOMEM; ad193x->regmap = regmap; + ad193x->type = type; dev_set_drvdata(dev, ad193x); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index ab9a998f15be..8b1e65f928d2 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -13,8 +13,15 @@ struct device; +enum ad193x_type { + AD193X, + AD1933, + AD1934, +}; + extern const struct regmap_config ad193x_regmap_config; -int ad193x_probe(struct device *dev, struct regmap *regmap); +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type); #define AD193X_PLL_CLK_CTRL0 0x00 #define AD193X_PLL_POWERDOWN 0x01 -- cgit v1.2.3-59-g8ed1b From 698d0b59f3d91cc44a76dd2994a002d263c3606b Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Mon, 5 Oct 2015 21:03:39 +0200 Subject: ASoC: rockchip: namespace rockchip i2s module name Change the rockchip i2s object name (and thus module name) from the rather generic snd-soc-i2s to the more specific snd-soc-rockchip-i2s Signed-off-by: Sjoerd Simons Signed-off-by: Mark Brown --- sound/soc/rockchip/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 1bc1dc3c729a..e9ba55842879 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,7 +1,7 @@ # ROCKCHIP Platform Support -snd-soc-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-i2s-objs := rockchip_i2s.o -obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o -- cgit v1.2.3-59-g8ed1b From d46183efe3b57d06461c9bea35e1a0262391fe77 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 6 Oct 2015 17:26:36 +0800 Subject: ASoC: ad193x-spi: Fixup ad193x_spi_id table AD1939 is missed from the table, so add it. AD1936 and AD1937 are controlled by I2C interface, so remove them. Fixes: e5224f58e3ef ("ASoC: ad193x: add support to ad1934") Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x-spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index 364e82112e8c..b55055a6a265 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -39,9 +39,8 @@ static const struct spi_device_id ad193x_spi_id[] = { { "ad193x", AD193X }, { "ad1933", AD1933 }, { "ad1934", AD1934 }, - { "ad1936", AD193X }, - { "ad1937", AD193X }, { "ad1938", AD193X }, + { "ad1939", AD193X }, { } }; MODULE_DEVICE_TABLE(spi, ad193x_spi_id); -- cgit v1.2.3-59-g8ed1b From dc6d84c69cf8296b1e8e2fd0b1e115b7787ef4e9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 5 Oct 2015 21:22:40 +0800 Subject: ASoC: rt286: Fix run time error while modifying const data Make a copy of memory for index_cache rather than directly use the rt286_index_def to avoid run time error. Fixes: c418a84a8c8f ("ASoC: Constify reg_default tables") Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt286.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index bd9365885f73..2088dfa0612d 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -38,7 +38,7 @@ #define RT288_VENDOR_ID 0x10ec0288 struct rt286_priv { - const struct reg_default *index_cache; + struct reg_default *index_cache; int index_cache_size; struct regmap *regmap; struct snd_soc_codec *codec; @@ -1161,7 +1161,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt286->index_cache = rt286_index_def; + rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def, + sizeof(rt286_index_def), GFP_KERNEL); + if (!rt286->index_cache) + return -ENOMEM; + rt286->index_cache_size = INDEX_CACHE_SIZE; rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); -- cgit v1.2.3-59-g8ed1b From b5e5a4549c8b88a880fa3866fa3803ea9396ba03 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 5 Oct 2015 14:53:50 +0300 Subject: ASoC: Intel: use dw_dmac autoconfiguration Instead of hardconding a platform data for dw_dmac let's use it's own autoconfiguration feature. Thus, remove hardcoded values. Acked-by: Liam Girdwood Cc: Mark Brown Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-firmware.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index ebcca6dc48d1..1636a1eeb002 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -26,7 +26,6 @@ #include /* supported DMA engine drivers */ -#include #include #include @@ -169,12 +168,6 @@ err: return ret; } -static struct dw_dma_platform_data dw_pdata = { - .is_private = 1, - .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, - .chan_priority = CHAN_PRIORITY_ASCENDING, -}; - static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, int irq) { @@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, return ERR_PTR(err); chip->dev = dev; - err = dw_dma_probe(chip, &dw_pdata); + + err = dw_dma_probe(chip, NULL); if (err) return ERR_PTR(err); -- cgit v1.2.3-59-g8ed1b From 34ca27f34f413b4a684fc7336911799da3ac84d5 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 2 Oct 2015 09:49:14 -0700 Subject: ASoC: nau8825: Add driver for headset chip Nuvoton 8825 Sponsored-by: Google Chromium project Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/nau8825.txt | 102 ++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/nau8825.c | 1107 ++++++++++++++++++++ sound/soc/codecs/nau8825.h | 323 ++++++ 5 files changed, 1538 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nau8825.txt create mode 100644 sound/soc/codecs/nau8825.c create mode 100644 sound/soc/codecs/nau8825.h (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/nau8825.txt b/Documentation/devicetree/bindings/sound/nau8825.txt new file mode 100644 index 000000000000..d3374231c871 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nau8825.txt @@ -0,0 +1,102 @@ +Nuvoton NAU8825 audio codec + +This device supports I2C only. + +Required properties: + - compatible : Must be "nuvoton,nau8825" + + - reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1). + +Optional properties: + - nuvoton,jkdet-enable: Enable jack detection via JKDET pin. + - nuvoton,jkdet-pull-enable: Enable JKDET pin pull. If set - pin pull enabled, + otherwise pin in high impedance state. + - nuvoton,jkdet-pull-up: Pull-up JKDET pin. If set then JKDET pin is pull up, otherwise pull down. + - nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low. + + - nuvoton,vref-impedance: VREF Impedance selection + 0 - Open + 1 - 25 kOhm + 2 - 125 kOhm + 3 - 2.5 kOhm + + - nuvoton,micbias-voltage: Micbias voltage level. + 0 - VDDA + 1 - VDDA + 2 - VDDA * 1.1 + 3 - VDDA * 1.2 + 4 - VDDA * 1.3 + 5 - VDDA * 1.4 + 6 - VDDA * 1.53 + 7 - VDDA * 1.53 + + - nuvoton,sar-threshold-num: Number of buttons supported + - nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as + SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R) + where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance. + Refer datasheet section 10.2 for more information about threshold calculation. + + - nuvoton,sar-hysteresis: Button impedance measurement hysteresis. + + - nuvoton,sar-voltage: Reference voltage for button impedance measurement. + 0 - VDDA + 1 - VDDA + 2 - VDDA * 1.1 + 3 - VDDA * 1.2 + 4 - VDDA * 1.3 + 5 - VDDA * 1.4 + 6 - VDDA * 1.53 + 7 - VDDA * 1.53 + + - nuvoton,sar-compare-time: SAR compare time + 0 - 500 ns + 1 - 1 us + 2 - 2 us + 3 - 4 us + + - nuvoton,sar-sampling-time: SAR sampling time + 0 - 2 us + 1 - 4 us + 2 - 8 us + 3 - 16 us + + - nuvoton,short-key-debounce: Button short key press debounce time. + 0 - 30 ms + 1 - 50 ms + 2 - 100 ms + 3 - 30 ms + + - nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms + - nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms + + - clocks: list of phandle and clock specifier pairs according to common clock bindings for the + clocks described in clock-names + - clock-names: should include "mclk" for the MCLK master clock + +Example: + + headset: nau8825@1a { + compatible = "nuvoton,nau8825"; + reg = <0x1a>; + interrupt-parent = <&gpio>; + interrupts = ; + nuvoton,jkdet-enable; + nuvoton,jkdet-pull-enable; + nuvoton,jkdet-pull-up; + nuvoton,jkdet-polarity = ; + nuvoton,vref-impedance = <2>; + nuvoton,micbias-voltage = <6>; + // Setup 4 buttons impedance according to Android specification + nuvoton,sar-threshold-num = <4>; + nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>; + nuvoton,sar-hysteresis = <1>; + nuvoton,sar-voltage = <0>; + nuvoton,sar-compare-time = <0>; + nuvoton,sar-sampling-time = <0>; + nuvoton,short-key-debounce = <2>; + nuvoton,jack-insert-debounce = <7>; + nuvoton,jack-eject-debounce = <7>; + + clock-names = "mclk"; + clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0142396bb42c..cc60ab92b21d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -79,6 +79,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C + select SND_SOC_NAU8825 if I2C select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 @@ -892,6 +893,9 @@ config SND_SOC_MC13783 config SND_SOC_ML26124 tristate +config SND_SOC_NAU8825 + tristate + config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7d7cc1b049c2..d7b0f41690b2 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -72,6 +72,7 @@ snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o +snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o @@ -263,6 +264,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o +obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c new file mode 100644 index 000000000000..f31a5008e879 --- /dev/null +++ b/sound/soc/codecs/nau8825.c @@ -0,0 +1,1107 @@ +/* + * Nuvoton NAU8825 audio codec driver + * + * Copyright 2015 Google Chromium project. + * Author: Anatol Pomozov + * Copyright 2015 Nuvoton Technology Corp. + * Co-author: Meng-Huang Kuo + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "nau8825.h" + +static const struct reg_default nau8825_reg_defaults[] = { + { NAU8825_REG_ENA_CTRL, 0x00ff }, + { NAU8825_REG_CLK_DIVIDER, 0x0050 }, + { NAU8825_REG_FLL1, 0x0 }, + { NAU8825_REG_FLL2, 0x3126 }, + { NAU8825_REG_FLL3, 0x0008 }, + { NAU8825_REG_FLL4, 0x0010 }, + { NAU8825_REG_FLL5, 0x0 }, + { NAU8825_REG_FLL6, 0x6000 }, + { NAU8825_REG_FLL_VCO_RSV, 0xf13c }, + { NAU8825_REG_HSD_CTRL, 0x000c }, + { NAU8825_REG_JACK_DET_CTRL, 0x0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0 }, + { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff }, + { NAU8825_REG_SAR_CTRL, 0x0015 }, + { NAU8825_REG_KEYDET_CTRL, 0x0110 }, + { NAU8825_REG_VDET_THRESHOLD_1, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_2, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_3, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_4, 0x0 }, + { NAU8825_REG_GPIO34_CTRL, 0x0 }, + { NAU8825_REG_GPIO12_CTRL, 0x0 }, + { NAU8825_REG_TDM_CTRL, 0x0 }, + { NAU8825_REG_I2S_PCM_CTRL1, 0x000b }, + { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 }, + { NAU8825_REG_LEFT_TIME_SLOT, 0x0 }, + { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 }, + { NAU8825_REG_BIQ_CTRL, 0x0 }, + { NAU8825_REG_BIQ_COF1, 0x0 }, + { NAU8825_REG_BIQ_COF2, 0x0 }, + { NAU8825_REG_BIQ_COF3, 0x0 }, + { NAU8825_REG_BIQ_COF4, 0x0 }, + { NAU8825_REG_BIQ_COF5, 0x0 }, + { NAU8825_REG_BIQ_COF6, 0x0 }, + { NAU8825_REG_BIQ_COF7, 0x0 }, + { NAU8825_REG_BIQ_COF8, 0x0 }, + { NAU8825_REG_BIQ_COF9, 0x0 }, + { NAU8825_REG_BIQ_COF10, 0x0 }, + { NAU8825_REG_ADC_RATE, 0x0010 }, + { NAU8825_REG_DAC_CTRL1, 0x0001 }, + { NAU8825_REG_DAC_CTRL2, 0x0 }, + { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 }, + { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf }, + { NAU8825_REG_MUTE_CTRL, 0x0 }, + { NAU8825_REG_HSVOL_CTRL, 0x0 }, + { NAU8825_REG_DACL_CTRL, 0x02cf }, + { NAU8825_REG_DACR_CTRL, 0x00cf }, + { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff }, + { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 }, + { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_IMM_MODE_CTRL, 0x0 }, + { NAU8825_REG_CLASSG_CTRL, 0x0 }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, + { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_BIAS_ADJ, 0x0 }, + { NAU8825_REG_TRIM_SETTINGS, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0 }, + { NAU8825_REG_ANALOG_ADC_1, 0x0011 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0020 }, + { NAU8825_REG_RDAC, 0x0008 }, + { NAU8825_REG_MIC_BIAS, 0x0006 }, + { NAU8825_REG_BOOST, 0x0 }, + { NAU8825_REG_FEPGA, 0x0 }, + { NAU8825_REG_POWER_UP_CONTROL, 0x0 }, + { NAU8825_REG_CHARGE_PUMP, 0x0 }, +}; + +static bool nau8825_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_ENA_CTRL: + case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } + +} + +static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL: + case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK: + case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET: + case NAU8825_REG_IRQ_STATUS: + case NAU8825_REG_INT_CLR_KEY_STATUS: + case NAU8825_REG_IMM_RMS_L: + case NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_I2C_DEVICE_ID: + case NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_CHARGE_PUMP_INPUT_READ: + case NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } +} + +static int nau8825_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Prevent startup click by letting charge pump to ramp up */ + msleep(10); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const char * const nau8825_adc_decimation[] = { + "32", "64", "128", "256" +}; + +static const struct soc_enum nau8825_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT, + ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation); + +static const char * const nau8825_dac_oversampl[] = { + "64", "256", "128", "", "32" +}; + +static const struct soc_enum nau8825_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT, + ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl); + +static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400); +static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); +static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400); + +static const struct snd_kcontrol_new nau8825_controls[] = { + SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 0, 0xff, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 12, 8, 0x0f, 0, sidetone_vol_tlv), + SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL, + 6, 0, 0x3f, 1, dac_vol_tlv), + SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL, + 8, 37, 0, fepga_gain_tlv), + SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL, + 0, 8, 0xff, 0, crosstalk_vol_tlv), + + SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum), +}; + +/* DAC Mux 0x33[9] and 0x34[9] */ +static const char * const nau8825_dac_src[] = { + "DACL", "DACR", +}; + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacl_enum, NAU8825_REG_DACL_CTRL, + NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src); + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacr_enum, NAU8825_REG_DACR_CTRL, + NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src); + +static const struct snd_kcontrol_new nau8825_dacl_mux = + SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum); + +static const struct snd_kcontrol_new nau8825_dacr_mux = + SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum); + + +static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2, + 15, 1), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), + + SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0), + + SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, + 0), + + /* ADC for button press detection */ + SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_ADC_EN_SFT, 0), + + SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0), + SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0), + SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR_SFT, 0), + SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACL_SFT, 0), + SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), + SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), + + SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, + 0), + + SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0, + nau8825_pump_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA("Output Driver R Stage 1", + NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver L Stage 1", + NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver R Stage 2", + NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver L Stage 2", + NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1, + NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1, + NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { + {"Frontend PGA", NULL, "MIC"}, + {"ADC", NULL, "Frontend PGA"}, + {"ADC", NULL, "ADC Clock"}, + {"ADC", NULL, "ADC Power"}, + {"AIFTX", NULL, "ADC"}, + + {"DDACL", NULL, "Playback"}, + {"DDACR", NULL, "Playback"}, + {"DDACL", NULL, "DDAC Clock"}, + {"DDACR", NULL, "DDAC Clock"}, + {"DACL Mux", "DACL", "DDACL"}, + {"DACL Mux", "DACR", "DDACR"}, + {"DACR Mux", "DACL", "DDACL"}, + {"DACR Mux", "DACR", "DDACR"}, + {"HP amp L", NULL, "DACL Mux"}, + {"HP amp R", NULL, "DACR Mux"}, + {"HP amp L", NULL, "HP amp power"}, + {"HP amp R", NULL, "HP amp power"}, + {"ADACL", NULL, "HP amp L"}, + {"ADACR", NULL, "HP amp R"}, + {"ADACL", NULL, "ADACL Clock"}, + {"ADACR", NULL, "ADACR Clock"}, + {"Output Driver L Stage 1", NULL, "ADACL"}, + {"Output Driver R Stage 1", NULL, "ADACR"}, + {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, + {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, + {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, + {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, + {"Output DACL", NULL, "Output Driver L Stage 3"}, + {"Output DACR", NULL, "Output Driver R Stage 3"}, + {"HPOL", NULL, "Output DACL"}, + {"HPOR", NULL, "Output DACR"}, + {"HPOL", NULL, "Charge Pump"}, + {"HPOR", NULL, "Charge Pump"}, +}; + +static int nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0; + + switch (params_width(params)) { + case 16: + val_len |= NAU8825_I2S_DL_16; + break; + case 20: + val_len |= NAU8825_I2S_DL_20; + break; + case 24: + val_len |= NAU8825_I2S_DL_24; + break; + case 32: + val_len |= NAU8825_I2S_DL_32; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK, val_len); + + return 0; +} + +static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8825_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8825_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8825_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8825_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8825_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + ctrl1_val |= NAU8825_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK | + NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK, + ctrl1_val); + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, ctrl2_val); + + return 0; +} + +static const struct snd_soc_dai_ops nau8825_dai_ops = { + .hw_params = nau8825_hw_params, + .set_fmt = nau8825_set_dai_fmt, +}; + +#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8825_dai = { + .name = "nau8825-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .ops = &nau8825_dai_ops, +}; + +/** + * nau8825_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8825_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap = nau8825->regmap; + + nau8825->jack = jack; + + /* Ground HP Outputs[1:0], needed for headset auto detection + * Enable Automatic Mic/Gnd switching reading on insert interrupt[6] + */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect); + + +static bool nau8825_is_jack_inserted(struct regmap *regmap) +{ + int status; + + regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status); + return !(status & NAU8825_GPIO2JD1); +} + +static void nau8825_restart_jack_detection(struct regmap *regmap) +{ + /* this will restart the entire jack detection process including MIC/GND + * switching and create interrupts. We have to go from 0 to 1 and back + * to 0 to restart. + */ + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, 0); +} + +static void nau8825_eject_jack(struct nau8825 *nau8825) +{ + struct snd_soc_dapm_context *dapm = nau8825->dapm; + struct regmap *regmap = nau8825->regmap; + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + /* ground HPL/HPR, MICGRND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf); + + snd_soc_dapm_sync(dapm); +} + +static int nau8825_button_decode(int value) +{ + int buttons = 0; + + /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */ + if (value & BIT(0)) + buttons |= SND_JACK_BTN_0; + if (value & BIT(1)) + buttons |= SND_JACK_BTN_1; + if (value & BIT(2)) + buttons |= SND_JACK_BTN_2; + if (value & BIT(3)) + buttons |= SND_JACK_BTN_3; + if (value & BIT(4)) + buttons |= SND_JACK_BTN_4; + if (value & BIT(5)) + buttons |= SND_JACK_BTN_5; + + return buttons; +} + +static int nau8825_jack_insert(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + struct snd_soc_dapm_context *dapm = nau8825->dapm; + int jack_status_reg, mic_detected; + int type = 0; + + regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg); + mic_detected = (jack_status_reg >> 10) & 3; + + switch (mic_detected) { + case 0: + /* no mic */ + type = SND_JACK_HEADPHONE; + break; + case 1: + dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 1 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKR2); + /* Attach SARADC to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKR2); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + case 2: + case 3: + dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 2 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKSLV); + /* Attach SARADC to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKSLV); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + } + + if (type & SND_JACK_HEADPHONE) { + /* Unground HPL/R */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0); + } + + return type; +} + +#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +static irqreturn_t nau8825_interrupt(int irq, void *data) +{ + struct nau8825 *nau8825 = (struct nau8825 *)data; + struct regmap *regmap = nau8825->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq); + + if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == + NAU8825_JACK_EJECTION_DETECTED) { + + nau8825_eject_jack(nau8825); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK; + } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) { + int key_status; + + regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, + &key_status); + + /* upper 8 bits of the register are for short pressed keys, + * lower 8 bits - for long pressed buttons + */ + nau8825->button_pressed = nau8825_button_decode( + key_status >> 8); + + event |= nau8825->button_pressed; + event_mask |= NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) { + event_mask = NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_RELEASE_IRQ; + } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { + if (nau8825_is_jack_inserted(regmap)) { + event |= nau8825_jack_insert(nau8825); + } else { + dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n"); + nau8825_eject_jack(nau8825); + } + + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_HEADSET_COMPLETION_IRQ; + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); + + if (event_mask) + snd_soc_jack_report(nau8825->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static void nau8825_setup_buttons(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_TRACKING_GAIN_MASK, + nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_COMPARE_TIME_MASK, + nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_SAMPLING_TIME_MASK, + nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT); + + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_LEVELS_NR_MASK, + (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_HYSTERESIS_MASK, + nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK, + nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT); + + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1, + (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2, + (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3, + (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4, + (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]); + + /* Enable short press and release interruptions */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN, + 0); +} + +static void nau8825_init_regs(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + /* Enable Bias/Vmid */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN); + + /* VMID Tieoff */ + regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID_SEL_MASK, + nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8825_REG_BOOST, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | + NAU8825_SHORT_SHUTDOWN_EN, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | + NAU8825_SHORT_SHUTDOWN_EN); + + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_OUTPUT_EN, + nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_EN, + nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_UP, + nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_POLARITY, + /* jkdet_polarity - 1 is for active-low */ + nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY); + + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_INSERT_DEBOUNCE_MASK, + nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_EJECT_DEBOUNCE_MASK, + nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT); + + /* Mask unneeded IRQs: 1 - disable, 0 - enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff); + + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage); + + if (nau8825->sar_threshold_num) + nau8825_setup_buttons(nau8825); + + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8825_REG_ADC_RATE, + NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); +} + +static const struct regmap_config nau8825_regmap_config = { + .val_bits = 16, + .reg_bits = 16, + + .max_register = NAU8825_REG_MAX, + .readable_reg = nau8825_readable_reg, + .writeable_reg = nau8825_writeable_reg, + .volatile_reg = nau8825_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8825_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults), +}; + +static int nau8825_codec_probe(struct snd_soc_codec *codec) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + nau8825->dapm = dapm; + + /* The interrupt clock is gated by x1[10:8], + * one of them needs to be enabled all the time for + * interrupts to happen. + */ + snd_soc_dapm_force_enable_pin(dapm, "DDACR"); + snd_soc_dapm_sync(dapm); + + /* Unmask interruptions. Handler uses dapm object so we can enable + * interruptions only after dapm is fully initialized. + */ + regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0); + nau8825_restart_jack_detection(nau8825->regmap); + + return 0; +} + +static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, + unsigned int freq) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + switch (clk_id) { + case NAU8825_CLK_MCLK: + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); + + /* We selected MCLK source but the clock itself managed externally */ + if (!nau8825->mclk) + break; + + if (!nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + if (nau8825->mclk_freq != freq) { + nau8825->mclk_freq = freq; + + freq = clk_round_rate(nau8825->mclk, freq); + ret = clk_set_rate(nau8825->mclk, freq); + if (ret) { + dev_err(nau8825->dev, "Unable to set mclk rate\n"); + return ret; + } + } + + break; + case NAU8825_CLK_INTERNAL: + regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, + NAU8825_DCO_EN); + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + default: + dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq, + clk_id); + return 0; +} + +static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + return nau8825_configure_sysclk(nau8825, clk_id, freq); +} + +static int nau8825_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + if (nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + ret = regcache_sync(nau8825->regmap); + if (ret) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + + break; + + case SND_SOC_BIAS_OFF: + if (nau8825->mclk_freq) + clk_disable_unprepare(nau8825->mclk); + + regcache_mark_dirty(nau8825->regmap); + break; + } + return 0; +} + +static struct snd_soc_codec_driver nau8825_codec_driver = { + .probe = nau8825_codec_probe, + .set_sysclk = nau8825_set_sysclk, + .set_bias_level = nau8825_set_bias_level, + .suspend_bias_off = true, + + .controls = nau8825_controls, + .num_controls = ARRAY_SIZE(nau8825_controls), + .dapm_widgets = nau8825_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), + .dapm_routes = nau8825_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), +}; + +static void nau8825_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8825_REG_RESET, 0x00); + regmap_write(regmap, NAU8825_REG_RESET, 0x00); +} + +static int nau8825_read_device_properties(struct device *dev, + struct nau8825 *nau8825) { + + nau8825->jkdet_enable = device_property_read_bool(dev, + "nuvoton,jkdet-enable"); + nau8825->jkdet_pull_enable = device_property_read_bool(dev, + "nuvoton,jkdet-pull-enable"); + nau8825->jkdet_pull_up = device_property_read_bool(dev, + "nuvoton,jkdet-pull-up"); + device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8825->jkdet_polarity); + device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8825->micbias_voltage); + device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8825->vref_impedance); + device_property_read_u32(dev, "nuvoton,sar-threshold-num", + &nau8825->sar_threshold_num); + device_property_read_u32_array(dev, "nuvoton,sar-threshold", + nau8825->sar_threshold, nau8825->sar_threshold_num); + device_property_read_u32(dev, "nuvoton,sar-hysteresis", + &nau8825->sar_hysteresis); + device_property_read_u32(dev, "nuvoton,sar-voltage", + &nau8825->sar_voltage); + device_property_read_u32(dev, "nuvoton,sar-compare-time", + &nau8825->sar_compare_time); + device_property_read_u32(dev, "nuvoton,sar-sampling-time", + &nau8825->sar_sampling_time); + device_property_read_u32(dev, "nuvoton,short-key-debounce", + &nau8825->key_debounce); + device_property_read_u32(dev, "nuvoton,jack-insert-debounce", + &nau8825->jack_insert_debounce); + device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8825->jack_eject_debounce); + + nau8825->mclk = devm_clk_get(dev, "mclk"); + if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + /* The MCLK is managed externally or not used at all */ + nau8825->mclk = NULL; + dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); + } else if (IS_ERR(nau8825->mclk)) { + return -EINVAL; + } + + return 0; +} + +static int nau8825_setup_irq(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + /* IRQ Output Enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN); + + /* Enable internal VCO needed for interruptions */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0); + + /* Enable DDACR needed for interrupts + * It is the same as force_enable_pin("DDACR") we do later + */ + regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR); + + /* Chip needs one FSCLK cycle in order to generate interrupts, + * as we cannot guarantee one will be provided by the system. Turning + * master mode on then off enables us to generate that FSCLK cycle + * with a minimum of contention on the clock bus. + */ + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); + + ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, + nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8825", nau8825); + + if (ret) { + dev_err(nau8825->dev, "Cannot request irq %d (%d)\n", + nau8825->irq, ret); + return ret; + } + + return 0; +} + +static int nau8825_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev); + int ret, value; + + if (!nau8825) { + nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL); + if (!nau8825) + return -ENOMEM; + ret = nau8825_read_device_properties(dev, nau8825); + if (ret) + return ret; + } + + i2c_set_clientdata(i2c, nau8825); + + nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config); + if (IS_ERR(nau8825->regmap)) + return PTR_ERR(nau8825->regmap); + nau8825->dev = dev; + nau8825->irq = i2c->irq; + + nau8825_reset_chip(nau8825->regmap); + ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU8825: %d\n", + ret); + return ret; + } + if ((value & NAU8825_SOFTWARE_ID_MASK) != + NAU8825_SOFTWARE_ID_NAU8825) { + dev_err(dev, "Not a NAU8825 chip\n"); + return -ENODEV; + } + + nau8825_init_regs(nau8825); + + if (i2c->irq) + nau8825_setup_irq(nau8825); + + return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver, + &nau8825_dai, 1); +} + +static int nau8825_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id nau8825_i2c_ids[] = { + { "nau8825", 0 }, + { } +}; + +#ifdef CONFIG_OF +static const struct of_device_id nau8825_of_ids[] = { + { .compatible = "nuvoton,nau8825", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8825_of_ids); +#endif + +static struct i2c_driver nau8825_driver = { + .driver = { + .name = "nau8825", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nau8825_of_ids), + }, + .probe = nau8825_i2c_probe, + .remove = nau8825_i2c_remove, + .id_table = nau8825_i2c_ids, +}; +module_i2c_driver(nau8825_driver); + +MODULE_DESCRIPTION("ASoC nau8825 driver"); +MODULE_AUTHOR("Anatol Pomozov "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h new file mode 100644 index 000000000000..8774923502b4 --- /dev/null +++ b/sound/soc/codecs/nau8825.h @@ -0,0 +1,323 @@ +/* + * NAU8825 ALSA SoC audio driver + * + * Copyright 2015 Google Inc. + * Author: Anatol Pomozov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __NAU8825_H__ +#define __NAU8825_H__ + +#define NAU8825_REG_RESET 0x00 +#define NAU8825_REG_ENA_CTRL 0x01 +#define NAU8825_REG_CLK_DIVIDER 0x03 +#define NAU8825_REG_FLL1 0x04 +#define NAU8825_REG_FLL2 0x05 +#define NAU8825_REG_FLL3 0x06 +#define NAU8825_REG_FLL4 0x07 +#define NAU8825_REG_FLL5 0x08 +#define NAU8825_REG_FLL6 0x09 +#define NAU8825_REG_FLL_VCO_RSV 0x0a +#define NAU8825_REG_HSD_CTRL 0x0c +#define NAU8825_REG_JACK_DET_CTRL 0x0d +#define NAU8825_REG_INTERRUPT_MASK 0x0f +#define NAU8825_REG_IRQ_STATUS 0x10 +#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11 +#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12 +#define NAU8825_REG_SAR_CTRL 0x13 +#define NAU8825_REG_KEYDET_CTRL 0x14 +#define NAU8825_REG_VDET_THRESHOLD_1 0x15 +#define NAU8825_REG_VDET_THRESHOLD_2 0x16 +#define NAU8825_REG_VDET_THRESHOLD_3 0x17 +#define NAU8825_REG_VDET_THRESHOLD_4 0x18 +#define NAU8825_REG_GPIO34_CTRL 0x19 +#define NAU8825_REG_GPIO12_CTRL 0x1a +#define NAU8825_REG_TDM_CTRL 0x1b +#define NAU8825_REG_I2S_PCM_CTRL1 0x1c +#define NAU8825_REG_I2S_PCM_CTRL2 0x1d +#define NAU8825_REG_LEFT_TIME_SLOT 0x1e +#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f +#define NAU8825_REG_BIQ_CTRL 0x20 +#define NAU8825_REG_BIQ_COF1 0x21 +#define NAU8825_REG_BIQ_COF2 0x22 +#define NAU8825_REG_BIQ_COF3 0x23 +#define NAU8825_REG_BIQ_COF4 0x24 +#define NAU8825_REG_BIQ_COF5 0x25 +#define NAU8825_REG_BIQ_COF6 0x26 +#define NAU8825_REG_BIQ_COF7 0x27 +#define NAU8825_REG_BIQ_COF8 0x28 +#define NAU8825_REG_BIQ_COF9 0x29 +#define NAU8825_REG_BIQ_COF10 0x2a +#define NAU8825_REG_ADC_RATE 0x2b +#define NAU8825_REG_DAC_CTRL1 0x2c +#define NAU8825_REG_DAC_CTRL2 0x2d +#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f +#define NAU8825_REG_ADC_DGAIN_CTRL 0x30 +#define NAU8825_REG_MUTE_CTRL 0x31 +#define NAU8825_REG_HSVOL_CTRL 0x32 +#define NAU8825_REG_DACL_CTRL 0x33 +#define NAU8825_REG_DACR_CTRL 0x34 +#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38 +#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39 +#define NAU8825_REG_ADC_DRC_SLOPES 0x3a +#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b +#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45 +#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46 +#define NAU8825_REG_DAC_DRC_SLOPES 0x47 +#define NAU8825_REG_DAC_DRC_ATKDCY 0x48 +#define NAU8825_REG_IMM_MODE_CTRL 0x4c +#define NAU8825_REG_IMM_RMS_L 0x4d +#define NAU8825_REG_IMM_RMS_R 0x4e +#define NAU8825_REG_CLASSG_CTRL 0x50 +#define NAU8825_REG_OPT_EFUSE_CTRL 0x51 +#define NAU8825_REG_MISC_CTRL 0x55 +#define NAU8825_REG_I2C_DEVICE_ID 0x58 +#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_BIAS_ADJ 0x66 +#define NAU8825_REG_TRIM_SETTINGS 0x68 +#define NAU8825_REG_ANALOG_CONTROL_1 0x69 +#define NAU8825_REG_ANALOG_CONTROL_2 0x6a +#define NAU8825_REG_ANALOG_ADC_1 0x71 +#define NAU8825_REG_ANALOG_ADC_2 0x72 +#define NAU8825_REG_RDAC 0x73 +#define NAU8825_REG_MIC_BIAS 0x74 +#define NAU8825_REG_BOOST 0x76 +#define NAU8825_REG_FEPGA 0x77 +#define NAU8825_REG_POWER_UP_CONTROL 0x7f +#define NAU8825_REG_CHARGE_PUMP 0x80 +#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81 +#define NAU8825_REG_GENERAL_STATUS 0x82 +#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS + +/* ENA_CTRL (0x1) */ +#define NAU8825_ENABLE_DACR_SFT 10 +#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT) +#define NAU8825_ENABLE_DACL_SFT 9 +#define NAU8825_ENABLE_ADC_SFT 8 +#define NAU8825_ENABLE_SAR_SFT 1 + +/* CLK_DIVIDER (0x3) */ +#define NAU8825_CLK_SRC_SFT 15 +#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) + +/* FLL6 (0x9) */ +#define NAU8825_DCO_EN (1 << 15) + +/* HSD_CTRL (0xc) */ +#define NAU8825_HSD_AUTO_MODE (1 << 6) +/* 0 - short to GND, 1 - open */ +#define NAU8825_SPKR_DWN1R (1 << 1) +#define NAU8825_SPKR_DWN1L (1 << 0) + +/* JACK_DET_CTRL (0xd) */ +#define NAU8825_JACK_DET_RESTART (1 << 9) +#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5 +#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT) +#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2 +#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT) +#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */ + +/* INTERRUPT_MASK (0xf) */ +#define NAU8825_IRQ_OUTPUT_EN (1 << 11) +#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5) +#define NAU8825_IRQ_EJECT_EN (1 << 2) + +/* IRQ_STATUS (0x10) */ +#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10) +#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9) +#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8) +#define NAU8825_KEY_IRQ_MASK (0x7 << 5) +#define NAU8825_KEY_RELEASE_IRQ (1 << 7) +#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6) +#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5) +#define NAU8825_MIC_DETECTION_IRQ (1 << 4) +#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2) +#define NAU8825_JACK_EJECTION_DETECTED (1 << 2) +#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0) +#define NAU8825_JACK_INSERTION_DETECTED (1 << 0) + +/* INTERRUPT_DIS_CTRL (0x12) */ +#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5) +#define NAU8825_IRQ_EJECT_DIS (1 << 2) + +/* SAR_CTRL (0x13) */ +#define NAU8825_SAR_ADC_EN_SFT 12 +#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT) +#define NAU8825_SAR_INPUT_MASK (1 << 11) +#define NAU8825_SAR_INPUT_JKSLV (1 << 11) +#define NAU8825_SAR_INPUT_JKR2 (0 << 11) +#define NAU8825_SAR_TRACKING_GAIN_SFT 8 +#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT) +#define NAU8825_SAR_COMPARE_TIME_SFT 2 +#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2) +#define NAU8825_SAR_SAMPLING_TIME_SFT 0 +#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0) + +/* KEYDET_CTRL (0x14) */ +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12 +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT) +#define NAU8825_KEYDET_LEVELS_NR_SFT 8 +#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8) +#define NAU8825_KEYDET_HYSTERESIS_SFT 0 +#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf + +/* GPIO12_CTRL (0x1a) */ +#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */ +#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */ +#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */ + +/* I2S_PCM_CTRL1 (0x1c) */ +#define NAU8825_I2S_BP_SFT 7 +#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_PCMB_SFT 6 +#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_DL_SFT 2 +#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DF_SFT 0 +#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT) + +/* I2S_PCM_CTRL2 (0x1d) */ +#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ +#define NAU8825_I2S_MS_SFT 3 +#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) + +/* ADC_RATE (0x2b) */ +#define NAU8825_ADC_SYNC_DOWN_SFT 0 +#define NAU8825_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8825_ADC_SYNC_DOWN_32 0 +#define NAU8825_ADC_SYNC_DOWN_64 1 +#define NAU8825_ADC_SYNC_DOWN_128 2 +#define NAU8825_ADC_SYNC_DOWN_256 3 + +/* DAC_CTRL1 (0x2c) */ +#define NAU8825_DAC_CLIP_OFF (1 << 7) +#define NAU8825_DAC_OVERSAMPLE_SFT 0 +#define NAU8825_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8825_DAC_OVERSAMPLE_64 0 +#define NAU8825_DAC_OVERSAMPLE_256 1 +#define NAU8825_DAC_OVERSAMPLE_128 2 +#define NAU8825_DAC_OVERSAMPLE_32 4 + +/* MUTE_CTRL (0x31) */ +#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9) +#define NAU8825_DAC_SOFT_MUTE (1 << 9) + +/* HSVOL_CTRL (0x32) */ +#define NAU8825_HP_MUTE (1 << 15) + +/* DACL_CTRL (0x33) */ +#define NAU8825_DACL_CH_SEL_SFT 9 + +/* DACR_CTRL (0x34) */ +#define NAU8825_DACR_CH_SEL_SFT 9 + +/* I2C_DEVICE_ID (0x58) */ +#define NAU8825_GPIO2JD1 (1 << 7) +#define NAU8825_SOFTWARE_ID_MASK 0x3 +#define NAU8825_SOFTWARE_ID_NAU8825 0x0 + +/* BIAS_ADJ (0x66) */ +#define NAU8825_BIAS_VMID (1 << 6) +#define NAU8825_BIAS_VMID_SEL_SFT 4 +#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_2 (0x6a) */ +#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12) +#define NAU8825_DAC_CAPACITOR_MSB (1 << 1) +#define NAU8825_DAC_CAPACITOR_LSB (1 << 0) + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8) +#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8) +#define NAU8825_ADC_VREFSEL_VMID (1 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) +#define NAU8825_POWERUP_ADCL (1 << 6) + +/* MIC_BIAS (0x74) */ +#define NAU8825_MICBIAS_JKSLV (1 << 14) +#define NAU8825_MICBIAS_JKR2 (1 << 12) +#define NAU8825_MICBIAS_POWERUP_SFT 8 +#define NAU8825_MICBIAS_VOLTAGE_SFT 0 +#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8825_PRECHARGE_DIS (1 << 13) +#define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_HP_BOOST_G_DIS (1 << 8) +#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) + +/* POWER_UP_CONTROL (0x7f) */ +#define NAU8825_POWERUP_INTEGR_R (1 << 5) +#define NAU8825_POWERUP_INTEGR_L (1 << 4) +#define NAU8825_POWERUP_DRV_IN_R (1 << 3) +#define NAU8825_POWERUP_DRV_IN_L (1 << 2) +#define NAU8825_POWERUP_HP_DRV_R (1 << 1) +#define NAU8825_POWERUP_HP_DRV_L (1 << 0) + +/* CHARGE_PUMP (0x80) */ +#define NAU8825_JAMNODCLOW (1 << 10) +#define NAU8825_POWER_DOWN_DACR (1 << 9) +#define NAU8825_POWER_DOWN_DACL (1 << 8) +#define NAU8825_CHANRGE_PUMP_EN (1 << 5) + + +/* System Clock Source */ +enum { + NAU8825_CLK_MCLK = 0, + NAU8825_CLK_INTERNAL, +}; + +struct nau8825 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct clk *mclk; + int irq; + int mclk_freq; /* 0 - mclk is disabled */ + int button_pressed; + int micbias_voltage; + int vref_impedance; + bool jkdet_enable; + bool jkdet_pull_enable; + bool jkdet_pull_up; + int jkdet_polarity; + int sar_threshold_num; + int sar_threshold[8]; + int sar_hysteresis; + int sar_voltage; + int sar_compare_time; + int sar_sampling_time; + int key_debounce; + int jack_insert_debounce; + int jack_eject_debounce; +}; + +int nau8825_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + + +#endif /* __NAU8825_H__ */ -- cgit v1.2.3-59-g8ed1b From cc91ef0fd4904fc130aecea8603585a9c0fe8384 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sat, 3 Oct 2015 03:37:59 +0800 Subject: ASoC: nau8825: fix platform_no_drv_owner.cocci warnings sound/soc/codecs/nau8825.c:1096:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Anatol Pomozov Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index f31a5008e879..c44a7a16b046 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1093,7 +1093,6 @@ MODULE_DEVICE_TABLE(of, nau8825_of_ids); static struct i2c_driver nau8825_driver = { .driver = { .name = "nau8825", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(nau8825_of_ids), }, .probe = nau8825_i2c_probe, -- cgit v1.2.3-59-g8ed1b From c570b82c5e6ac78be35b4e72594c0f1b1888fce1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 7 Oct 2015 11:59:58 +0100 Subject: ASoC: sun4i-codec: Remove the routing property Most of the boards have their headphone jack directly connected to the matching pins of the SoCs. Since most of the time we will have the same routing path, it makes no sense to put that in the DTS, since it will only be some useless duplication there. It also fixes the following warning messages that were seen so far, on boards where we were using the bindings in the documentation example. sun4i-codec 1c22c00.codec: ASoC: no sink widget found for Headphone Jack sun4i-codec 1c22c00.codec: ASoC: Failed to add route HP Left -> direct -> Headphone Jack sun4i-codec 1c22c00.codec: ASoC: no sink widget found for Headphone Jack sun4i-codec 1c22c00.codec: ASoC: Failed to add route HP Right -> direct -> Headphone Jack Reported-by: Priit Laes Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/sun4i-codec.txt | 6 ------ sound/soc/sunxi/sun4i-codec.c | 7 ------- 2 files changed, 13 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt index 680144b74ae9..c92966bd5488 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt @@ -13,10 +13,6 @@ Required properties: - clock-names: should contain followings: - "apb": the parent APB clock for this controller - "codec": the parent module clock -- routing : A list of the connections between audio components. Each - entry is a pair of strings, the first being the connection's sink, - the second being the connection's source. - Example: codec: codec@01c22c00 { @@ -28,6 +24,4 @@ codec: codec@01c22c00 { clock-names = "apb", "codec"; dmas = <&dma 0 19>, <&dma 0 19>; dma-names = "rx", "tx"; - routing = "Headphone Jack", "HP Right", - "Headphone Jack", "HP Left"; }; diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 47780552dcd0..bcbf4da168b6 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -571,7 +571,6 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) { struct snd_soc_card *card; - int ret; card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) @@ -584,12 +583,6 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) card->dev = dev; card->name = "sun4i-codec"; - ret = snd_soc_of_parse_audio_routing(card, "routing"); - if (ret) { - dev_err(dev, "Failed to create our audio routing\n"); - return NULL; - } - return card; }; -- cgit v1.2.3-59-g8ed1b From b7ebd78d1d142e4e47c3547b08faf51218384583 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 11:57:12 +0100 Subject: ASoC: da7219: Use of_match_ptr() when assigning match table Use of_match_ptr() to handle non-DT kernel scenario where match table should be NULL. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index c86a83305fb7..adcc079ef801 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1932,7 +1932,7 @@ MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); static struct i2c_driver da7219_i2c_driver = { .driver = { .name = "da7219", - .of_match_table = da7219_of_match, + .of_match_table = of_match_ptr(da7219_of_match), }, .probe = da7219_i2c_probe, .remove = da7219_i2c_remove, -- cgit v1.2.3-59-g8ed1b From ba856fbd602175d386ab5a4fc0fdd89b912546cb Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 11:57:14 +0100 Subject: ASoC: da7219: Improve error checking of mclk enable/disable Should only try to enable/disable the provided mclk, during bias level changes, if it's not NULL. Also return value of clk_prepare_enable() should be checked and dealt with accordingly. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index adcc079ef801..abba4b36caa0 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1494,6 +1494,7 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; switch (level) { case SND_SOC_BIAS_ON: @@ -1502,7 +1503,14 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* MCLK */ - clk_prepare_enable(da7219->mclk); + if (da7219->mclk) { + ret = clk_prepare_enable(da7219->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } /* Master bias */ snd_soc_update_bits(codec, DA7219_REFERENCES, @@ -1528,7 +1536,8 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, } /* MCLK */ - clk_disable_unprepare(da7219->mclk); + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); break; } -- cgit v1.2.3-59-g8ed1b From dcc448e619194098b24477a6d56af50c57f26f1d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 7 Oct 2015 12:42:22 +0200 Subject: ASoC: rsnd: Remove obsolete platform data support Since commit 3d7608e4c169af03 ("ARM: shmobile: bockw: remove legacy board file and config"), Renesas R-Car SoCs are only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to use platform data anymore, hence remove platform data configuration. Move to sound/soc/sh/rcar/, as it's no longer needed by platform code. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 117 ------------------------------------------- sound/soc/sh/rcar/core.c | 19 ++----- sound/soc/sh/rcar/rcar_snd.h | 117 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 3 +- sound/soc/sh/rcar/ssi.c | 3 -- 5 files changed, 124 insertions(+), 135 deletions(-) delete mode 100644 include/sound/rcar_snd.h create mode 100644 sound/soc/sh/rcar/rcar_snd.h (limited to 'sound') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h deleted file mode 100644 index d8e33d38da43..000000000000 --- a/include/sound/rcar_snd.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Renesas R-Car SRU/SCU/SSIU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef RCAR_SND_H -#define RCAR_SND_H - - -#define RSND_GEN1_SRU 0 -#define RSND_GEN1_ADG 1 -#define RSND_GEN1_SSI 2 - -#define RSND_GEN2_SCU 0 -#define RSND_GEN2_ADG 1 -#define RSND_GEN2_SSIU 2 -#define RSND_GEN2_SSI 3 - -#define RSND_BASE_MAX 4 - -/* - * flags - * - * 0xAB000000 - * - * A : clock sharing settings - * B : SSI direction - */ -#define RSND_SSI_CLK_PIN_SHARE (1 << 31) -#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ - -#define RSND_SSI(_dma_id, _irq, _flags) \ -{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } -#define RSND_SSI_UNUSED \ -{ .dma_id = -1, .irq = -1, .flags = 0 } - -struct rsnd_ssi_platform_info { - int dma_id; - int irq; - u32 flags; -}; - -#define RSND_SRC(rate, _dma_id) \ -{ .convert_rate = rate, .dma_id = _dma_id, } -#define RSND_SRC_UNUSED \ -{ .convert_rate = 0, .dma_id = -1, } - -struct rsnd_src_platform_info { - u32 convert_rate; /* sampling rate convert */ - int dma_id; /* for Gen2 SCU */ - int irq; -}; - -/* - * flags - */ -struct rsnd_ctu_platform_info { - u32 flags; -}; - -struct rsnd_mix_platform_info { - u32 flags; -}; - -struct rsnd_dvc_platform_info { - u32 flags; -}; - -struct rsnd_dai_path_info { - struct rsnd_ssi_platform_info *ssi; - struct rsnd_src_platform_info *src; - struct rsnd_ctu_platform_info *ctu; - struct rsnd_mix_platform_info *mix; - struct rsnd_dvc_platform_info *dvc; -}; - -struct rsnd_dai_platform_info { - struct rsnd_dai_path_info playback; - struct rsnd_dai_path_info capture; -}; - -/* - * flags - * - * 0x0000000A - * - * A : generation - */ -#define RSND_GEN_MASK (0xF << 0) -#define RSND_GEN1 (1 << 0) /* fixme */ -#define RSND_GEN2 (2 << 0) /* fixme */ - -struct rcar_snd_info { - u32 flags; - struct rsnd_ssi_platform_info *ssi_info; - int ssi_info_nr; - struct rsnd_src_platform_info *src_info; - int src_info_nr; - struct rsnd_ctu_platform_info *ctu_info; - int ctu_info_nr; - struct rsnd_mix_platform_info *mix_info; - int mix_info_nr; - struct rsnd_dvc_platform_info *dvc_info; - int dvc_info_nr; - struct rsnd_dai_platform_info *dai_info; - int dai_info_nr; - int (*start)(int id); - int (*stop)(int id); -}; - -#endif diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index eec294da81e3..6ef9a884ca7c 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1236,20 +1236,11 @@ static int rsnd_probe(struct platform_device *pdev) }; int ret, i; - info = NULL; - of_data = NULL; - if (of_id) { - info = devm_kzalloc(&pdev->dev, - sizeof(struct rcar_snd_info), GFP_KERNEL); - of_data = of_id->data; - } else { - info = pdev->dev.platform_data; - } - - if (!info) { - dev_err(dev, "driver needs R-Car sound information\n"); - return -ENODEV; - } + info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + of_data = of_id->data; /* * init priv data diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h new file mode 100644 index 000000000000..d8e33d38da43 --- /dev/null +++ b/sound/soc/sh/rcar/rcar_snd.h @@ -0,0 +1,117 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + + +#define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 + +#define RSND_GEN2_SCU 0 +#define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 + +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xAB000000 + * + * A : clock sharing settings + * B : SSI direction + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ + +#define RSND_SSI(_dma_id, _irq, _flags) \ +{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dma_id = -1, .irq = -1, .flags = 0 } + +struct rsnd_ssi_platform_info { + int dma_id; + int irq; + u32 flags; +}; + +#define RSND_SRC(rate, _dma_id) \ +{ .convert_rate = rate, .dma_id = _dma_id, } +#define RSND_SRC_UNUSED \ +{ .convert_rate = 0, .dma_id = -1, } + +struct rsnd_src_platform_info { + u32 convert_rate; /* sampling rate convert */ + int dma_id; /* for Gen2 SCU */ + int irq; +}; + +/* + * flags + */ +struct rsnd_ctu_platform_info { + u32 flags; +}; + +struct rsnd_mix_platform_info { + u32 flags; +}; + +struct rsnd_dvc_platform_info { + u32 flags; +}; + +struct rsnd_dai_path_info { + struct rsnd_ssi_platform_info *ssi; + struct rsnd_src_platform_info *src; + struct rsnd_ctu_platform_info *ctu; + struct rsnd_mix_platform_info *mix; + struct rsnd_dvc_platform_info *dvc; +}; + +struct rsnd_dai_platform_info { + struct rsnd_dai_path_info playback; + struct rsnd_dai_path_info capture; +}; + +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN_MASK (0xF << 0) +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + +struct rcar_snd_info { + u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; + struct rsnd_src_platform_info *src_info; + int src_info_nr; + struct rsnd_ctu_platform_info *ctu_info; + int ctu_info_nr; + struct rsnd_mix_platform_info *mix_info; + int mix_info_nr; + struct rsnd_dvc_platform_info *dvc_info; + int dvc_info_nr; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e4068d78616c..e9fef53968b4 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -21,10 +21,11 @@ #include #include #include -#include #include #include +#include "rcar_snd.h" + /* * pseudo register * diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5e05f9422073..842a35b1363a 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -700,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, struct device *dev = &pdev->dev; int nr, i; - if (!of_data) - return; - node = rsnd_ssi_of_node(priv); if (!node) return; -- cgit v1.2.3-59-g8ed1b From e90996a3ea224fbeb459b8052ecd366d7990e1f3 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 14:27:08 +0100 Subject: ASoC: da7213: Add DT support to codec driver This patch adds support for DT bindings in the codec driver. As part of this support, the mclk data can now be provided and used to control the mclk during codec operation. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- sound/soc/codecs/da7213.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a9c86efb3187..ab1486b04c30 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1387,10 +1387,118 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, return 0; } +/* DT */ +static const struct of_device_id da7213_of_match[] = { + { .compatible = "dlg,da7213", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7213_of_match); + +static enum da7213_micbias_voltage + da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1600: + return DA7213_MICBIAS_1_6V; + case 2200: + return DA7213_MICBIAS_2_2V; + case 2500: + return DA7213_MICBIAS_2_5V; + case 3000: + return DA7213_MICBIAS_3_0V; + default: + dev_warn(codec->dev, "Invalid micbias level\n"); + return DA7213_MICBIAS_2_2V; + } +} + +static enum da7213_dmic_data_sel + da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "lrise_rfall")) { + return DA7213_DMIC_DATA_LRISE_RFALL; + } else if (!strcmp(str, "lfall_rrise")) { + return DA7213_DMIC_DATA_LFALL_RRISE; + } else { + dev_warn(codec->dev, "Invalid DMIC data select type\n"); + return DA7213_DMIC_DATA_LRISE_RFALL; + } +} + +static enum da7213_dmic_samplephase + da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "on_clkedge")) { + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } else if (!strcmp(str, "between_clkedge")) { + return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE; + } else { + dev_warn(codec->dev, "Invalid DMIC sample phase\n"); + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } +} + +static enum da7213_dmic_clk_rate + da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1500000: + return DA7213_DMIC_CLK_1_5MHZ; + case 3000000: + return DA7213_DMIC_CLK_3_0MHZ; + default: + dev_warn(codec->dev, "Invalid DMIC clock rate\n"); + return DA7213_DMIC_CLK_1_5MHZ; + } +} + +static struct da7213_platform_data + *da7213_of_to_pdata(struct snd_soc_codec *codec) +{ + struct device_node *np = codec->dev->of_node; + struct da7213_platform_data *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_warn(codec->dev, "Failed to allocate memory for pdata\n"); + return NULL; + } + + if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0) + pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32); + else + pdata->micbias1_lvl = DA7213_MICBIAS_2_2V; + + if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0) + pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32); + else + pdata->micbias2_lvl = DA7213_MICBIAS_2_2V; + + if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str)) + pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str); + else + pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str)) + pdata->dmic_samplephase = + da7213_of_dmic_samplephase(codec, of_str); + else + pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0) + pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32); + else + pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ; + + return pdata; +} + + static int da7213_probe(struct snd_soc_codec *codec) { struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); - struct da7213_platform_data *pdata = da7213->pdata; /* Default to using ALC auto offset calibration mode. */ snd_soc_update_bits(codec, DA7213_ALC_CTRL1, @@ -1450,8 +1558,15 @@ static int da7213_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, DA7213_LINE_CTRL, DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE); + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7213->pdata = da7213_of_to_pdata(codec); + else + da7213->pdata = dev_get_platdata(codec->dev); + /* Set platform data values */ if (da7213->pdata) { + struct da7213_platform_data *pdata = da7213->pdata; u8 micbias_lvl = 0, dmic_cfg = 0; /* Set Mic Bias voltages */ @@ -1507,6 +1622,7 @@ static int da7213_probe(struct snd_soc_codec *codec) /* Set MCLK squaring */ da7213->mclk_squarer_en = pdata->mclk_squaring; } + return 0; } @@ -1537,7 +1653,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da7213_priv *da7213; - struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev); int ret; da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv), @@ -1545,9 +1660,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c, if (!da7213) return -ENOMEM; - if (pdata) - da7213->pdata = pdata; - i2c_set_clientdata(i2c, da7213); da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config); @@ -1582,6 +1694,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); static struct i2c_driver da7213_i2c_driver = { .driver = { .name = "da7213", + .of_match_table = of_match_ptr(da7213_of_match), }, .probe = da7213_i2c_probe, .remove = da7213_remove, -- cgit v1.2.3-59-g8ed1b From 6e7c444318699496e6e6f30c875cf67534aeccc6 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Wed, 7 Oct 2015 14:27:11 +0100 Subject: ASoC: da7213: Add support to handle mclk data provided to driver Driver now can make use of mclk data, if provided, to set, enable and disable the clock source. As part of this, the choice to enable clock squaring is dealt with as part of dai_sysclk() call rather than as platform data. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- include/sound/da7213.h | 3 --- sound/soc/codecs/da7213.c | 67 +++++++++++++++++++++++++++++++++++++++-------- sound/soc/codecs/da7213.h | 8 ++++-- 3 files changed, 62 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/include/sound/da7213.h b/include/sound/da7213.h index 673f5c39cbf2..e7eac8979995 100644 --- a/include/sound/da7213.h +++ b/include/sound/da7213.h @@ -44,9 +44,6 @@ struct da7213_platform_data { enum da7213_dmic_data_sel dmic_data_sel; enum da7213_dmic_samplephase dmic_samplephase; enum da7213_dmic_clk_rate dmic_clk_rate; - - /* MCLK squaring config */ - bool mclk_squaring; }; #endif /* _DA7213_PDATA_H */ diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index ab1486b04c30..7278f93460c1 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -12,6 +12,7 @@ * option) any later version. */ +#include #include #include #include @@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq)) + return 0; + + if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } switch (clk_id) { case DA7213_CLKSRC_MCLK: - if ((freq == 32768) || - ((freq >= 5000000) && (freq <= 54000000))) { - da7213->mclk_rate = freq; - return 0; - } else { - dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", - freq); - return -EINVAL; - } + da7213->mclk_squarer_en = false; + break; + case DA7213_CLKSRC_MCLK_SQR: + da7213->mclk_squarer_en = true; break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); return -EINVAL; } + + da7213->clk_src = clk_id; + + if (da7213->mclk) { + freq = clk_round_rate(da7213->mclk, freq); + ret = clk_set_rate(da7213->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7213->mclk_rate = freq; + + return 0; } /* Supported PLL input frequencies are 5MHz - 54MHz. */ @@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = { static int da7213_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + if (da7213->mclk) { + ret = clk_prepare_enable(da7213->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } + /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, @@ -1382,6 +1417,10 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, /* Disable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, 0); + + /* MCLK */ + if (da7213->mclk) + clk_disable_unprepare(da7213->mclk); break; } return 0; @@ -1618,9 +1657,15 @@ static int da7213_probe(struct snd_soc_codec *codec) DA7213_DMIC_DATA_SEL_MASK | DA7213_DMIC_SAMPLEPHASE_MASK | DA7213_DMIC_CLK_RATE_MASK, dmic_cfg); + } - /* Set MCLK squaring */ - da7213->mclk_squarer_en = pdata->mclk_squaring; + /* Check if MCLK provided */ + da7213->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7213->mclk)) { + if (PTR_ERR(da7213->mclk) != -ENOENT) + return PTR_ERR(da7213->mclk); + else + da7213->mclk = NULL; } return 0; diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 9cb9ddd01282..030fd691b076 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -13,6 +13,7 @@ #ifndef _DA7213_H #define _DA7213_H +#include #include #include @@ -504,14 +505,17 @@ #define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 #define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 -enum clk_src { - DA7213_CLKSRC_MCLK +enum da7213_clk_src { + DA7213_CLKSRC_MCLK = 0, + DA7213_CLKSRC_MCLK_SQR, }; /* Codec private data */ struct da7213_priv { struct regmap *regmap; + struct clk *mclk; unsigned int mclk_rate; + int clk_src; bool master; bool mclk_squarer_en; bool srm_en; -- cgit v1.2.3-59-g8ed1b From e4e2d2f45284f620eeb9eea25570838b2a42c968 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:52 +0100 Subject: ASoC: Intel: Skylake: Add pipe and modules handlers SKL driver needs to instantiate pipelines and modules in the DSP. The topology in the DSP is modelled as DAPM graph with a PGA representing a module instance and mixer representing a pipeline for a group of modules along with the mixer itself. Here we start adding building block for handling these. We add resource checks (memory/compute) for pipelines, find the modules in a pipeline, init modules in a pipe and lastly bind/unbind modules in a pipe These will be used by pipe event handlers in subsequent patches Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/Makefile | 3 +- sound/soc/intel/skylake/skl-topology.c | 187 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 10 ++ sound/soc/intel/skylake/skl.h | 11 ++ 4 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/skylake/skl-topology.c (limited to 'sound') diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 27db22178204..914b6dab9bea 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,4 +1,5 @@ -snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o +snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ +skl-topology.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c new file mode 100644 index 000000000000..b0ee1fe05274 --- /dev/null +++ b/sound/soc/intel/skylake/skl-topology.c @@ -0,0 +1,187 @@ +/* + * skl-topology.c - Implements Platform component ALSA controls/widget + * handlers. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#include +#include +#include +#include +#include +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" +#include "skl-topology.h" +#include "skl.h" +#include "skl-tplg-interface.h" + +/* + * SKL DSP driver modelling uses only few DAPM widgets so for rest we will + * ignore. This helpers checks if the SKL driver handles this widget type + */ +static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w) +{ + switch (w->id) { + case snd_soc_dapm_dai_link: + case snd_soc_dapm_dai_in: + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_switch: + return false; + default: + return true; + } +} + +/* + * Each pipelines needs memory to be allocated. Check if we have free memory + * from available pool. Then only add this to pool + * This is freed when pipe is deleted + * Note: DSP does actual memory management we only keep track for complete + * pool + */ +static bool skl_tplg_alloc_pipe_mem(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mem + mconfig->pipe->memory_pages > + skl->resource.max_mem) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, + mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d mem %d\n", + skl->resource.max_mem, skl->resource.mem); + return false; + } + + skl->resource.mem += mconfig->pipe->memory_pages; + return true; +} + +/* + * Pipeline needs needs DSP CPU resources for computation, this is + * quantified in MCPS (Million Clocks Per Second) required for module/pipe + * + * Each pipelines needs mcps to be allocated. Check if we have mcps for this + * pipe. This adds the mcps to driver counter + * This is removed on pipeline delete + */ +static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d > mem %d\n", + skl->resource.max_mcps, skl->resource.mcps); + return false; + } + + skl->resource.mcps += mconfig->mcps; + return true; +} + +/* + * Free the mcps when tearing down + */ +static void +skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mcps -= mconfig->mcps; +} + +/* + * Free the memory when tearing down + */ +static void +skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mem -= mconfig->pipe->memory_pages; +} + +/* + * A pipe can have multiple modules, each of them will be a DAPM widget as + * well. While managing a pipeline we need to get the list of all the + * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps + * to get the SKL type widgets in that pipeline + */ +static int skl_tplg_alloc_pipe_widget(struct device *dev, + struct snd_soc_dapm_widget *w, struct skl_pipe *pipe) +{ + struct skl_module_cfg *src_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct skl_pipe_module *p_module = NULL; + + p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if ((p->sink->priv == NULL) + && (!is_skl_dsp_widget_type(w))) + continue; + + if ((p->sink->priv != NULL) && p->connect + && is_skl_dsp_widget_type(p->sink)) { + + src_module = p->sink->priv; + if (pipe->ppl_id == src_module->pipe->ppl_id) + skl_tplg_alloc_pipe_widget(dev, + p->sink, pipe); + } + } + return 0; +} + +/* + * Inside a pipe instance, we can have various modules. These modules need + * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by + * skl_init_module() routine, so invoke that for all modules in a pipeline + */ +static int +skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mconfig = w->priv; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -ENOMEM; + + ret = skl_init_module(ctx, mconfig, NULL); + if (ret < 0) + return ret; + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8c7767baa94f..73d7916ee33e 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -263,6 +263,16 @@ struct skl_module_cfg { struct skl_specific_cfg formats_config; }; +struct skl_pipeline { + struct skl_pipe *pipe; + struct list_head node; +}; + +struct skl_dapm_path_list { + struct snd_soc_dapm_path *dapm_path; + struct list_head node; +}; + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index f7fdbb02947f..e980d7897642 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -48,6 +48,13 @@ #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +struct skl_dsp_resource { + u32 max_mcps; + u32 max_mem; + u32 mcps; + u32 mem; +}; + struct skl { struct hdac_ext_bus ebus; struct pci_dev *pci; @@ -57,6 +64,10 @@ struct skl { void __iomem *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ + + struct skl_dsp_resource resource; + struct list_head ppl_list; + struct list_head dapm_path_list; }; #define skl_to_ebus(s) (&(s)->ebus) -- cgit v1.2.3-59-g8ed1b From f7590d4f1565b0d609d25113c37b9bea8a13e9f7 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:53 +0100 Subject: ASoC: Intel: Skylake: Add module configuration helpers To configure a module, driver needs to send input and output PCM params for a module in DSP. The FE PCM params come from hw_params ie from user, for a BE they also come from hw_params but from BE-link fixups. So based on PCM params required driver has to find a converter module (src/updown/format) and then do the conversion and calculate PCM params in these pipelines In this patch we add the helper modules which allow driver to do these calculations. Signed-off-by: Hardik T Shah Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 140 +++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b0ee1fe05274..56baf9d88f28 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -27,6 +27,10 @@ #include "skl.h" #include "skl-tplg-interface.h" +#define SKL_CH_FIXUP_MASK (1 << 0) +#define SKL_RATE_FIXUP_MASK (1 << 1) +#define SKL_FMT_FIXUP_MASK (1 << 2) + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type @@ -119,6 +123,137 @@ skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) skl->resource.mem -= mconfig->pipe->memory_pages; } + +static void skl_dump_mconfig(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + dev_dbg(ctx->dev, "Dumping config\n"); + dev_dbg(ctx->dev, "Input Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->in_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "Output Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->out_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg); +} + +static void skl_tplg_update_params(struct skl_module_fmt *fmt, + struct skl_pipe_params *params, int fixup) +{ + if (fixup & SKL_RATE_FIXUP_MASK) + fmt->s_freq = params->s_freq; + if (fixup & SKL_CH_FIXUP_MASK) + fmt->channels = params->ch; + if (fixup & SKL_FMT_FIXUP_MASK) + fmt->valid_bit_depth = params->s_fmt; +} + +/* + * A pipeline may have modules which impact the pcm parameters, like SRC, + * channel converter, format converter. + * We need to calculate the output params by applying the 'fixup' + * Topology will tell driver which type of fixup is to be applied by + * supplying the fixup mask, so based on that we calculate the output + * + * Now In FE the pcm hw_params is source/target format. Same is applicable + * for BE with its hw_params invoked. + * here based on FE, BE pipeline and direction we calculate the input and + * outfix and then apply that for a module + */ +static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg, + struct skl_pipe_params *params, bool is_fe) +{ + int in_fixup, out_fixup; + struct skl_module_fmt *in_fmt, *out_fmt; + + in_fmt = &m_cfg->in_fmt; + out_fmt = &m_cfg->out_fmt; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (is_fe) { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } else { + if (is_fe) { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } + + skl_tplg_update_params(in_fmt, params, in_fixup); + skl_tplg_update_params(out_fmt, params, out_fixup); +} + +/* + * A module needs input and output buffers, which are dependent upon pcm + * params, so once we have calculate params, we need buffer calculation as + * well. + */ +static void skl_tplg_update_buffer_size(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + int multiplier = 1; + + if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) + multiplier = 5; + + mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) * + (mcfg->in_fmt.channels) * + (mcfg->in_fmt.bit_depth >> 3) * + multiplier; + + mcfg->obs = (mcfg->out_fmt.s_freq / 1000) * + (mcfg->out_fmt.channels) * + (mcfg->out_fmt.bit_depth >> 3) * + multiplier; +} + +static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + struct skl_module_cfg *m_cfg = w->priv; + struct skl_pipe_params *params = m_cfg->pipe->p_params; + int p_conn_type = m_cfg->pipe->conn_type; + bool is_fe; + + if (!m_cfg->params_fixup) + return; + + dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); + + if (p_conn_type == SKL_PIPE_CONN_TYPE_FE) + is_fe = true; + else + is_fe = false; + + skl_tplg_update_params_fixup(m_cfg, params, is_fe); + skl_tplg_update_buffer_size(ctx, m_cfg); + + dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); +} + /* * A pipe can have multiple modules, each of them will be a DAPM widget as * well. While managing a pipeline we need to get the list of all the @@ -178,6 +313,11 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) return -ENOMEM; + /* + * apply fix/conversion to module params based on + * FE/BE params + */ + skl_tplg_update_module_params(w, ctx); ret = skl_init_module(ctx, mconfig, NULL); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From d93f8e550f44ed1f54285f77f40f7962a3f25267 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:54 +0100 Subject: ASoC: Intel: Skylake: add DSP platform widget event handlers The Skylake driver topology model tries to model the firmware rule for pipeline and module creation. The creation rule is: - Create Pipe - Add modules to Pipe - Connect the modules (bind) - Start the pipes Similarly destroy rule is: - Stop the pipe - Disconnect it (unbind) - Delete the pipe In driver we use Mixer, as there will always be ONE mixer in a pipeline to model a pipe. The modules in pipe are modelled as PGA widgets. The DAPM sequencing rules (mixer and then PGA) are used to create the sequence DSP expects as depicted above, and then widget handlers for PMU and PMD events help in that. This patch adds widget event handlers for PRE/POST PMU and PRE/POST PMD event for mixer and pga modules. These event handlers invoke pipeline creation, destroy, module creation, module bind, unbind and pipeline bind unbind Event handler sequencing is implement to target the DSP FW sequence expectations to enable path from source to sink pipe for Playback/Capture. Signed-off-by: Jeeja KP Signed-off-by: Hardik T Shah Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 448 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 7 + 2 files changed, 455 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 56baf9d88f28..6d1d29aa933a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -325,3 +325,451 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) return 0; } + +/* + * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we + * need create the pipeline. So we do following: + * - check the resources + * - Create the pipeline + * - Initialize the modules in pipeline + * - finally bind all modules together + */ +static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret; + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_pipe *s_pipe = mconfig->pipe; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -EBUSY; + + if (!skl_tplg_alloc_pipe_mem(skl, mconfig)) + return -ENOMEM; + + /* + * Create a list of modules for pipe. + * This list contains modules from source to sink + */ + ret = skl_create_pipeline(ctx, mconfig->pipe); + if (ret < 0) + return ret; + + /* + * we create a w_list of all widgets in that pipe. This list is not + * freed on PMD event as widgets within a pipe are static. This + * saves us cycles to get widgets in pipe every time. + * + * So if we have already initialized all the widgets of a pipeline + * we skip, so check for list_empty and create the list if empty + */ + if (list_empty(&s_pipe->w_list)) { + ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe); + if (ret < 0) + return ret; + } + + /* Init all pipe modules from source to sink */ + ret = skl_tplg_init_pipe_modules(skl, s_pipe); + if (ret < 0) + return ret; + + /* Bind modules from source to sink */ + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_bind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + return 0; +} + +/* + * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA + * we need to do following: + * - Bind to sink pipeline + * Since the sink pipes can be running and we don't get mixer event on + * connect for already running mixer, we need to find the sink pipes + * here and bind to them. This way dynamic connect works. + * - Start sink pipeline, if not running + * - Then run current pipe + */ +static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct skl_dapm_path_list *path_list; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + source = w; + src_mconfig = source->priv; + + /* + * find which sink it is connected to, bind with the sink, + * if sink is not started, start sink pipe first, then start + * this pipe + */ + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); + dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->sink->priv != NULL) && + is_skl_dsp_widget_type(p->sink)) { + + sink = p->sink; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + + /* Bind source to sink, mixin is always source */ + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + /* Start sinks pipe first */ + if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + } + + path_list = kzalloc( + sizeof(struct skl_dapm_path_list), + GFP_KERNEL); + if (path_list == NULL) + return -ENOMEM; + + /* Add connected path to one global list */ + path_list->dapm_path = p; + list_add_tail(&path_list->node, &skl->dapm_path_list); + break; + } + } + + /* Start source pipe last after starting all sinks */ + ret = skl_run_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + return 0; +} + +/* + * in the Post-PMU event of mixer we need to do following: + * - Check if this pipe is running + * - if not, then + * - bind this pipeline to its source pipeline + * if source pipe is already running, this means it is a dynamic + * connection and we need to bind only to that pipe + * - start this pipeline + */ +static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret = 0; + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int src_pipe_started = 0; + + sink = w; + sink_mconfig = sink->priv; + + /* + * If source pipe is already started, that means source is driving + * one more sink before this sink got connected, Since source is + * started, bind this sink to source and start this pipe. + */ + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "sink widget=%s\n", w->name); + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->source->priv != NULL) && + is_skl_dsp_widget_type(p->source)) { + source = p->source; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + src_pipe_started = 1; + + /* + * check pipe state, then no need to bind or start + * the pipe + */ + if (src_mconfig->pipe->state != SKL_PIPE_STARTED) + src_pipe_started = 0; + } + } + + if (src_pipe_started) { + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + } + + return ret; +} + +/* + * in the Pre-PMD event of mixer we need to do following: + * - Stop the pipe + * - find the source connections and remove that from dapm_path_list + * - unbind with source pipelines if still connected + */ +static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_list; + struct skl_sst *ctx = skl->skl_sst; + + sink = w; + sink_mconfig = sink->priv; + + /* Stop the pipe */ + ret = skl_stop_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + + /* + * This list, dapm_path_list handling here does not need any locks + * as we are under dapm lock while handling widget events. + * List can be manipulated safely only under dapm widgets handler + * routines + */ + list_for_each_entry_safe(path_list, tmp_list, + &skl->dapm_path_list, node) { + if (path_list->dapm_path->sink == sink) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + source = path_list->dapm_path->source; + src_mconfig = source->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * If path_found == 1, that means pmd for source pipe has + * not occurred, source is connected to some other sink. + * so its responsibility of sink to unbind itself from source. + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * in the Post-PMD event of mixer we need to do following: + * - Free the mcps used + * - Free the mem used + * - Unbind the modules within the pipeline + * - Delete the pipeline (modules are not required to be explicitly + * deleted, pipeline delete is enough here + */ +static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + struct skl_pipe *s_pipe = mconfig->pipe; + int ret = 0; + + skl_tplg_free_pipe_mcps(skl, mconfig); + + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_unbind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + ret = skl_delete_pipe(ctx, mconfig->pipe); + skl_tplg_free_pipe_mem(skl, mconfig); + + return ret; +} + +/* + * in the Post-PMD event of PGA we need to do following: + * - Free the mcps used + * - Stop the pipeline + * - In source pipe is connected, unbind with source pipelines + */ +static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_path_list; + struct skl_sst *ctx = skl->skl_sst; + + source = w; + src_mconfig = source->priv; + + skl_tplg_free_pipe_mcps(skl, src_mconfig); + /* Stop the pipe since this is a mixin module */ + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) { + if (path_list->dapm_path->source == source) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + sink = path_list->dapm_path->sink; + sink_mconfig = sink->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * This is a connector and if path is found that means + * unbind between source and sink has not happened yet + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If + * mixer is not required then it is treated as static mixer aka vmixer with + * a hard path to source module + * So we don't need to check if source is started or not as hard path puts + * dependency on each other + */ +static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If a + * second one is required that is created as another pipe entity. + * The mixer is responsible for pipe management and represent a pipeline + * instance + */ +static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_mixer_dapm_post_pmu_event(w, skl); + + case SND_SOC_DAPM_PRE_PMD: + return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assumed rest of the modules in pipeline are PGA. But we + * are interested in last PGA (leaf PGA) in a pipeline to disconnect with + * the sink when it is running (two FE to one BE or one FE to two BE) + * scenarios + */ +static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_pga_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_pga_dapm_post_pmd_event(w, skl); + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 73d7916ee33e..b3a22a6f8d27 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -273,6 +273,13 @@ struct skl_dapm_path_list { struct list_head node; }; +static inline struct skl *get_skl_ctx(struct device *dev) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + + return ebus_to_skl(ebus); +} + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); -- cgit v1.2.3-59-g8ed1b From cfb0a87383c6c2827f108ecee3471ef981876b38 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:55 +0100 Subject: ASoC: Intel: Skylake: Add FE and BE hw_params handling For FE and BE, the PCM parameters come from FE and BE hw_params values passed. For a FE we convert the FE params to DSP expected module format and pass to DSP. For a BE we need to find the gateway settings (i2s/PDM) to be applied. These are queried from NHLT table and applied. Further for BE based on direction the settings are applied as either source or destination parameters. These helpers here allow the format to be calculated and queried as per firmware format. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 238 +++++++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 12 ++ 2 files changed, 250 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 6d1d29aa933a..827f21db0eb2 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -773,3 +773,241 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } + +/* + * The FE params are passed by hw_params of the DAI. + * On hw_params, the params are stored in Gateway module of the FE and we + * need to calculate the format in DSP module configuration, that + * conversion is done here + */ +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct skl_module_fmt *format = NULL; + + memcpy(pipe->p_params, params, sizeof(*params)); + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) + format = &mconfig->in_fmt; + else + format = &mconfig->out_fmt; + + /* set the hw_params */ + format->s_freq = params->s_freq; + format->channels = params->ch; + format->valid_bit_depth = skl_get_bit_depth(params->s_fmt); + + /* + * 16 bit is 16 bit container whereas 24 bit is in 32 bit + * container so update bit depth accordingly + */ + switch (format->valid_bit_depth) { + case SKL_DEPTH_16BIT: + format->bit_depth = format->valid_bit_depth; + break; + + case SKL_DEPTH_24BIT: + format->bit_depth = SKL_DEPTH_32BIT; + break; + + default: + dev_err(dev, "Invalid bit depth %x for pipe\n", + format->valid_bit_depth); + return -EINVAL; + } + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mconfig->ibs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } else { + mconfig->obs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } + + return 0; +} + +/* + * Query the module config for the FE DAI + * This is used to find the hw_params set for that DAI and apply to FE + * pipeline + */ +struct skl_module_cfg * +skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && p->sink->power && + is_skl_dsp_widget_type(p->sink)) + continue; + + if (p->sink->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->sink->name); + return p->sink->priv; + } + } + } else { + w = dai->capture_widget; + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && p->source->power && + is_skl_dsp_widget_type(p->source)) + continue; + + if (p->source->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->source->name); + return p->source->priv; + } + } + } + + return NULL; +} + +static u8 skl_tplg_be_link_type(int dev_type) +{ + int ret; + + switch (dev_type) { + case SKL_DEVICE_BT: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_DMIC: + ret = NHLT_LINK_DMIC; + break; + + case SKL_DEVICE_I2S: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_HDALINK: + ret = NHLT_LINK_HDA; + break; + + default: + ret = NHLT_LINK_INVALID; + break; + } + + return ret; +} + +/* + * Fill the BE gateway parameters + * The BE gateway expects a blob of parameters which are kept in the ACPI + * NHLT blob, so query the blob for interface type (i2s/pdm) and instance. + * The port can have multiple settings so pick based on the PCM + * parameters + */ +static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct nhlt_specific_cfg *cfg; + struct skl *skl = get_skl_ctx(dai->dev); + int link_type = skl_tplg_be_link_type(mconfig->dev_type); + + memcpy(pipe->p_params, params, sizeof(*params)); + + /* update the blob based on virtual bus_id*/ + cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + params->s_fmt, params->ch, + params->s_freq, params->stream); + if (cfg) { + mconfig->formats_config.caps_size = cfg->size; + memcpy(mconfig->formats_config.caps, &cfg->caps, cfg->size); + } else { + dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", + mconfig->vbus_id, link_type, + params->stream); + dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n", + params->ch, params->s_freq, params->s_fmt); + return -EINVAL; + } + + return 0; +} + +static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->source) && + p->source->priv) { + + if (!p->source->power) + return skl_tplg_be_fill_pipe_params( + dai, p->source->priv, + params); + else + return -EBUSY; + } else { + return skl_tplg_be_set_src_pipe_params( + dai, p->source, params); + } + } + + return -EIO; +} + +static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p = NULL; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->sink) && + p->sink->priv) { + + if (!p->sink->power) + return skl_tplg_be_fill_pipe_params( + dai, p->sink->priv, params); + else + return -EBUSY; + + } else { + return skl_tplg_be_set_sink_pipe_params( + dai, p->sink, params); + } + } + + return -EIO; +} + +/* + * BE hw_params can be a source parameters (capture) or sink parameters + * (playback). Based on sink and source we need to either find the source + * list or the sink list and set the pipeline parameters + */ +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_widget *w; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + + return skl_tplg_be_set_src_pipe_params(dai, w, params); + + } else { + w = dai->capture_widget; + + return skl_tplg_be_set_sink_pipe_params(dai, w, params); + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b3a22a6f8d27..12db94d238f2 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -280,6 +280,18 @@ static inline struct skl *get_skl_ctx(struct device *dev) return ebus_to_skl(ebus); } +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params); +void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, + struct skl_pipe_params *params, int stream); +int skl_tplg_init(struct snd_soc_platform *platform, + struct hdac_ext_bus *ebus); +struct skl_module_cfg *skl_tplg_fe_get_cpr_module( + struct snd_soc_dai *dai, int stream); +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, struct skl_pipe_params *params); + + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); -- cgit v1.2.3-59-g8ed1b From 3af36706ff6c4ea8695e92b1ba80e183f1919684 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:56 +0100 Subject: ASoC: Intel: Skylake: Add topology core init and handlers The SKL driver does not code DSP topology in driver. It uses the newly added ASoC topology core to parse the topology information (controls, widgets and map) from topology binary. Each topology element passed private data which contains information that driver used to identify the module instance within firmware and send IPCs for that module to DSP firmware along with parameters. This patch adds init routine to invoke topology load and callback for topology creation. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-topology.c | 218 +++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 - sound/soc/intel/skylake/skl-tplg-interface.h | 80 ++++++++++ 4 files changed, 299 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 05fde5e6e257..664df1f44530 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -138,4 +138,5 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE + select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 827f21db0eb2..648bbbfdd472 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1011,3 +1011,221 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } + +static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { + {SKL_MIXER_EVENT, skl_tplg_mixer_event}, + {SKL_VMIXER_EVENT, skl_tplg_vmixer_event}, + {SKL_PGA_EVENT, skl_tplg_pga_event}, +}; + +/* + * The topology binary passes the pin info for a module so initialize the pin + * info passed into module instance + */ +static void skl_fill_module_pin_info(struct device *dev, + struct skl_module_pin *m_pin, + int max_pin) +{ + int i; + + for (i = 0; i < max_pin; i++) { + m_pin[i].id.module_id = 0; + m_pin[i].id.instance_id = 0; + m_pin[i].in_use = false; + m_pin[i].is_dynamic = true; + m_pin[i].pin_index = i; + } +} + +/* + * Add pipeline from topology binary into driver pipeline list + * + * If already added we return that instance + * Otherwise we create a new instance and add into driver list + */ +static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, + struct skl *skl, struct skl_dfw_pipe *dfw_pipe) +{ + struct skl_pipeline *ppl; + struct skl_pipe *pipe; + struct skl_pipe_params *params; + + list_for_each_entry(ppl, &skl->ppl_list, node) { + if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) + return ppl->pipe; + } + + ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return NULL; + + pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return NULL; + + params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); + if (!params) + return NULL; + + pipe->ppl_id = dfw_pipe->pipe_id; + pipe->memory_pages = dfw_pipe->memory_pages; + pipe->pipe_priority = dfw_pipe->pipe_priority; + pipe->conn_type = dfw_pipe->conn_type; + pipe->state = SKL_PIPE_INVALID; + pipe->p_params = params; + INIT_LIST_HEAD(&pipe->w_list); + + ppl->pipe = pipe; + list_add(&ppl->node, &skl->ppl_list); + + return ppl->pipe; +} + +/* + * Topology core widget load callback + * + * This is used to save the private data for each widget which gives + * information to the driver about module and pipeline parameters which DSP + * FW expects like ids, resource values, formats etc + */ +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + int ret; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl_module_cfg *mconfig; + struct skl_pipe *pipe; + struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data; + + if (!tplg_w->priv.size) + goto bind_event; + + mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL); + + if (!mconfig) + return -ENOMEM; + + w->priv = mconfig; + mconfig->id.module_id = dfw_config->module_id; + mconfig->id.instance_id = dfw_config->instance_id; + mconfig->mcps = dfw_config->max_mcps; + mconfig->ibs = dfw_config->ibs; + mconfig->obs = dfw_config->obs; + mconfig->core_id = dfw_config->core_id; + mconfig->max_in_queue = dfw_config->max_in_queue; + mconfig->max_out_queue = dfw_config->max_out_queue; + mconfig->is_loadable = dfw_config->is_loadable; + mconfig->in_fmt.channels = dfw_config->in_fmt.channels; + mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; + mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; + mconfig->in_fmt.valid_bit_depth = dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; + mconfig->out_fmt.channels = dfw_config->out_fmt.channels; + mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; + mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; + mconfig->out_fmt.valid_bit_depth = dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; + mconfig->params_fixup = dfw_config->params_fixup; + mconfig->converter = dfw_config->converter; + mconfig->m_type = dfw_config->module_type; + mconfig->vbus_id = dfw_config->vbus_id; + + pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); + if (pipe) + mconfig->pipe = pipe; + + mconfig->dev_type = dfw_config->dev_type; + mconfig->hw_conn_type = dfw_config->hw_conn_type; + mconfig->time_slot = dfw_config->time_slot; + mconfig->formats_config.caps_size = dfw_config->caps.caps_size; + + mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) + return -ENOMEM; + + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); + if (!mconfig->m_out_pin) + return -ENOMEM; + + skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin, + mconfig->max_in_queue); + skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin, + mconfig->max_out_queue); + + if (mconfig->formats_config.caps_size == 0) + goto bind_event; + + mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, + mconfig->formats_config.caps_size, GFP_KERNEL); + + if (mconfig->formats_config.caps == NULL) + return -ENOMEM; + + memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, + dfw_config->caps.caps_size); + +bind_event: + if (tplg_w->event_type == 0) { + dev_info(bus->dev, "ASoC: No event handler required\n"); + return 0; + } + + ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, + ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type); + + if (ret) { + dev_err(bus->dev, "%s: No matching event handlers found for %d\n", + __func__, tplg_w->event_type); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_tplg_ops skl_tplg_ops = { + .widget_load = skl_tplg_widget_load, +}; + +/* This will be read from topology manifest, currently defined here */ +#define SKL_MAX_MCPS 30000000 +#define SKL_FW_MAX_MEM 1000000 + +/* + * SKL topology init routine + */ +int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) +{ + int ret; + const struct firmware *fw; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "config firmware %s request failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } + + /* + * The complete tplg for SKL is loaded as index 0, we don't use + * any other index + */ + ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + if (ret < 0) { + dev_err(bus->dev, "tplg component load failed%d\n", ret); + return -EINVAL; + } + + skl->resource.max_mcps = SKL_MAX_MCPS; + skl->resource.max_mem = SKL_FW_MAX_MEM; + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 12db94d238f2..0c83dc3ea3e2 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -291,7 +291,6 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module( int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params); - int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index a50689825bca..d14f7fe64aa8 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -19,6 +19,29 @@ #ifndef __HDA_TPLG_INTERFACE_H__ #define __HDA_TPLG_INTERFACE_H__ +/* + * Default types range from 0~12. type can range from 0 to 0xff + * SST types start at higher to avoid any overlapping in future + */ +#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100 +#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103 + +#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +#define MAX_IN_QUEUE 8 +#define MAX_OUT_QUEUE 8 + +/* Event types goes here */ +/* Reserve event type 0 for no event handlers */ +enum skl_event_types { + SKL_EVENT_NONE = 0, + SKL_MIXER_EVENT, + SKL_MUX_EVENT, + SKL_VMIXER_EVENT, + SKL_PGA_EVENT +}; + /** * enum skl_ch_cfg - channel configuration * @@ -85,4 +108,61 @@ enum skl_dev_type { SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_NONE }; + +struct skl_dfw_module_pin { + u16 module_id; + u16 instance_id; + u8 pin_id; + bool is_dynamic; +} __packed; + +struct skl_dfw_module_fmt { + u32 channels; + u32 freq; + u32 bit_depth; + u32 valid_bit_depth; + u32 ch_cfg; +} __packed; + +struct skl_dfw_module_caps { + u32 caps_size; + u32 caps[HDA_SST_CFG_MAX]; +}; + +struct skl_dfw_pipe { + u8 pipe_id; + u8 pipe_priority; + u16 conn_type; + u32 memory_pages; +} __packed; + +struct skl_dfw_module { + u16 module_id; + u16 instance_id; + u32 max_mcps; + u8 core_id; + u8 max_in_queue; + u8 max_out_queue; + u8 is_loadable; + u8 conn_type; + u8 dev_type; + u8 hw_conn_type; + u8 time_slot; + u32 obs; + u32 ibs; + u32 params_fixup; + u32 converter; + u32 module_type; + u32 vbus_id; + struct skl_dfw_pipe pipe; + struct skl_dfw_module_fmt in_fmt; + struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_caps caps; +} __packed; + +struct skl_dfw_algo_data { + u32 max; + char *params; +} __packed; + #endif -- cgit v1.2.3-59-g8ed1b From b663a8c5c9c043c47e5e0eea9f9c77ea88fbe67a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:57 +0100 Subject: ASoC: Intel: Skylake: Initialize and load DSP controls Initialize and creates DSP controls if processing pipe capability is supported by HW. Updates the dma_id, hw_params to module param to be used when DSP module has to be configured. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 141 ++++++++++++++++++++++++++------- sound/soc/intel/skylake/skl-topology.c | 39 +++++---- 2 files changed, 136 insertions(+), 44 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bea26730873c..a8f53da11ae3 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -24,6 +24,7 @@ #include #include #include "skl.h" +#include "skl-topology.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct skl_pipe_params p_params = {0}; + struct skl_module_cfg *m_cfg; int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.host_dma_id = dma_id; + p_params.stream = substream->stream; + + m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream); + if (m_cfg) + skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params); + return 0; } @@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, return skl_substream_free_pages(ebus_to_hbus(ebus), substream); } +static int skl_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct skl_pipe_params p_params = {0}; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + skl_tplg_be_update_params(dai, &p_params); + + return 0; +} + +static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_sst *ctx = skl->skl_sst; + struct skl_module_cfg *mconfig; + + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (!mconfig) + return -EIO; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + return skl_run_pipe(ctx, mconfig->pipe); + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + return skl_stop_pipe(ctx, mconfig->pipe); + + default: + return 0; + } +} + static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - int dma_id; + struct skl_pipe_params p_params = {0}; - pr_debug("%s\n", __func__); link_dev = snd_hdac_ext_stream_assign(ebus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) @@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); - dma_id = hdac_stream(link_dev)->stream_tag - 1; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; + + skl_tplg_be_update_params(dai, &p_params); return 0; } @@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, unsigned int format_val = 0; struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_pcm_hw_params *params; - struct snd_interval *channels, *rate; struct hdac_ext_link *link; - dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); if (link_dev->link_prepared) { dev_dbg(dai->dev, "already stream is prepared - returning\n"); return 0; } - params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL); - if (params == NULL) - return -ENOMEM; - - channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = substream->runtime->channels; - rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - rate->min = rate->max = substream->runtime->rate; - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - substream->runtime->format); - dma_params = (struct skl_dma_params *) snd_soc_dai_get_dma_data(codec_dai, substream); @@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } -static int skl_hda_be_startup(struct snd_pcm_substream *substream, +static int skl_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { return pm_runtime_get_sync(dai->dev); } -static void skl_hda_be_shutdown(struct snd_pcm_substream *substream, +static void skl_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { pm_runtime_mark_last_busy(dai->dev); @@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = { .prepare = skl_pcm_prepare, .hw_params = skl_pcm_hw_params, .hw_free = skl_pcm_hw_free, + .trigger = skl_pcm_trigger, }; static struct snd_soc_dai_ops skl_dmic_dai_ops = { - .startup = skl_hda_be_startup, - .shutdown = skl_hda_be_shutdown, + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, +}; + +static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_ops skl_link_dai_ops = { - .startup = skl_hda_be_startup, + .startup = skl_be_startup, .prepare = skl_link_pcm_prepare, .hw_params = skl_link_hw_params, .hw_free = skl_link_hw_free, .trigger = skl_link_pcm_trigger, - .shutdown = skl_hda_be_shutdown, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_driver skl_platform_dai[] = { @@ -487,6 +539,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, /* BE CPU Dais */ +{ + .name = "SSP0 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, { .name = "iDisp Pin", .ops = &skl_link_dai_ops, @@ -544,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream) return 0; } -static int skl_pcm_trigger(struct snd_pcm_substream *substream, +static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -618,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_dsp_trigger(struct snd_pcm_substream *substream, +static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -675,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = get_bus_ctx(substream); if (ebus->ppcap) - return skl_dsp_trigger(substream, cmd); + return skl_decoupled_trigger(substream, cmd); else - return skl_pcm_trigger(substream, cmd); + return skl_coupled_trigger(substream, cmd); } /* calculate runtime delay from LPIB */ @@ -844,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; } +static int skl_platform_soc_probe(struct snd_soc_platform *platform) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); + + if (ebus->ppcap) + return skl_tplg_init(platform, ebus); + + return 0; +} static struct snd_soc_platform_driver skl_platform_drv = { + .probe = skl_platform_soc_probe, .ops = &skl_platform_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, @@ -857,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = { int skl_platform_register(struct device *dev) { int ret; + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct skl *skl = ebus_to_skl(ebus); + + INIT_LIST_HEAD(&skl->ppl_list); + INIT_LIST_HEAD(&skl->dapm_path_list); ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 648bbbfdd472..b1bffa52cbcd 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1089,8 +1089,8 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, * FW expects like ids, resource values, formats etc */ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, - struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) { int ret; struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); @@ -1098,7 +1098,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_module_cfg *mconfig; struct skl_pipe *pipe; - struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data; + struct skl_dfw_module *dfw_config = + (struct skl_dfw_module *)tplg_w->priv.data; if (!tplg_w->priv.size) goto bind_event; @@ -1121,12 +1122,14 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->in_fmt.channels = dfw_config->in_fmt.channels; mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; - mconfig->in_fmt.valid_bit_depth = dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.valid_bit_depth = + dfw_config->in_fmt.valid_bit_depth; mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; mconfig->out_fmt.channels = dfw_config->out_fmt.channels; mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; - mconfig->out_fmt.valid_bit_depth = dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.valid_bit_depth = + dfw_config->out_fmt.valid_bit_depth; mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; mconfig->params_fixup = dfw_config->params_fixup; mconfig->converter = dfw_config->converter; @@ -1142,15 +1145,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); + mconfig->m_in_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); + mconfig->m_out_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; @@ -1163,13 +1168,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, goto bind_event; mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, - mconfig->formats_config.caps_size, GFP_KERNEL); + mconfig->formats_config.caps_size, GFP_KERNEL); if (mconfig->formats_config.caps == NULL) return -ENOMEM; memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, - dfw_config->caps.caps_size); + dfw_config->caps.caps_size); bind_event: if (tplg_w->event_type == 0) { @@ -1178,7 +1183,8 @@ bind_event: } ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, - ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type); + ARRAY_SIZE(skl_tplg_widget_ops), + tplg_w->event_type); if (ret) { dev_err(bus->dev, "%s: No matching event handlers found for %d\n", @@ -1209,7 +1215,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); if (ret < 0) { - dev_err(bus->dev, "config firmware %s request failed with %d\n", + dev_err(bus->dev, "tplg fw %s load failed with %d\n", "dfw_sst.bin", ret); return ret; } @@ -1218,7 +1224,8 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) * The complete tplg for SKL is loaded as index 0, we don't use * any other index */ - ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + ret = snd_soc_tplg_component_load(&platform->component, + &skl_tplg_ops, fw, 0); if (ret < 0) { dev_err(bus->dev, "tplg component load failed%d\n", ret); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 2a29b200c6f17827ee712e719ad29366f79205ee Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:58 +0100 Subject: ASoC: Intel: Skylake: Add DSP support and enable it If processing pipe capability is supported, add DSP support. Adds initialization/free/suspend/resume DSP functionality. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 348d094e81d6..2f1890e703c6 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -166,11 +166,16 @@ static int skl_runtime_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + int ret; dev_dbg(bus->dev, "in %s\n", __func__); /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + ret = skl_suspend_dsp(skl); + if (ret < 0) + return ret; snd_hdac_bus_stop_chip(bus); snd_hdac_bus_enter_link_reset(bus); @@ -183,7 +188,7 @@ static int skl_runtime_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *hda = ebus_to_skl(ebus); + struct skl *skl = ebus_to_skl(ebus); int status; dev_dbg(bus->dev, "in %s\n", __func__); @@ -191,12 +196,12 @@ static int skl_runtime_resume(struct device *dev) /* Read STATESTS before controller reset */ status = snd_hdac_chip_readw(bus, STATESTS); - skl_init_pci(hda); + skl_init_pci(skl); snd_hdac_bus_init_chip(bus, true); /* disable controller Wake Up event */ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); - return 0; + return skl_resume_dsp(skl); } #endif /* CONFIG_PM */ @@ -457,17 +462,19 @@ static int skl_probe(struct pci_dev *pci, /* check if dsp is there */ if (ebus->ppcap) { - /* TODO register with dsp IPC */ - dev_dbg(bus->dev, "Register dsp\n"); + err = skl_init_dsp(skl); + if (err < 0) { + dev_dbg(bus->dev, "error failed to register dsp\n"); + goto out_free; + } } - if (ebus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); /* create device for soc dmic */ err = skl_dmic_device_register(skl); if (err < 0) - goto out_free; + goto out_dsp_free; /* register platform dai and controls */ err = skl_platform_register(bus->dev); @@ -491,6 +498,8 @@ out_unregister: skl_platform_unregister(bus->dev); out_dmic_free: skl_dmic_device_unregister(skl); +out_dsp_free: + skl_free_dsp(skl); out_free: skl->init_failed = 1; skl_free(ebus); @@ -507,6 +516,7 @@ static void skl_remove(struct pci_dev *pci) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); skl_platform_unregister(&pci->dev); + skl_free_dsp(skl); skl_dmic_device_unregister(skl); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); -- cgit v1.2.3-59-g8ed1b From 87b2bdf02278e5623cc66e5189792fde01f829a3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:59 +0100 Subject: ASoC: Intel: Skylake: Initialize NHLT table Load and Initialize Non HDA Link Table in Skylake driver to get platform configuration. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2f1890e703c6..ca135b8ab5c0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -458,6 +458,11 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->nhlt = skl_nhlt_init(bus->dev); + + if (skl->nhlt == NULL) + goto out_free; + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ -- cgit v1.2.3-59-g8ed1b From 3373f716830f36c0042f5845201caf1d9290f95b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 16:39:38 +0100 Subject: ASoC: Intel: Skylake: Modify the log level dev_info is too noisy for tplg wiget loading, so move it to debug level Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b1bffa52cbcd..a5d8420ce69a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1178,7 +1178,7 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, bind_event: if (tplg_w->event_type == 0) { - dev_info(bus->dev, "ASoC: No event handler required\n"); + dev_dbg(bus->dev, "ASoC: No event handler required\n"); return 0; } -- cgit v1.2.3-59-g8ed1b From b3681308cc7162fb08249589b5bb1b2d42e00f4b Mon Sep 17 00:00:00 2001 From: "Fang, Yang A" Date: Wed, 7 Oct 2015 14:33:57 -0700 Subject: ASoC: nau8825: add acpi match ID This patch adds the acpi match ID for nau8825 codec Signed-off-by: Fang, Yang A Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index c44a7a16b046..5c1badf47ac9 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1090,10 +1091,19 @@ static const struct of_device_id nau8825_of_ids[] = { MODULE_DEVICE_TABLE(of, nau8825_of_ids); #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8825_acpi_match[] = { + { "10508825", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match); +#endif + static struct i2c_driver nau8825_driver = { .driver = { .name = "nau8825", .of_match_table = of_match_ptr(nau8825_of_ids), + .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, -- cgit v1.2.3-59-g8ed1b From f874b80e1571118fcf4554878633556f06f998e6 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Thu, 8 Oct 2015 15:31:13 +0200 Subject: ASoC: rockchip: Add rockchip SPDIF transceiver driver Add a driver for the SPDIF transceiver available on RK3066, RK3188 and RK3288. Heavily based on the rockchip i2s driver. Signed-off-by: Sjoerd Simons Signed-off-by: Mark Brown --- sound/soc/rockchip/Kconfig | 8 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_spdif.c | 409 ++++++++++++++++++++++++++++++++++++ sound/soc/rockchip/rockchip_spdif.h | 63 ++++++ 4 files changed, 482 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_spdif.c create mode 100644 sound/soc/rockchip/rockchip_spdif.h (limited to 'sound') diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 570905709d3a..f1e0c703e0d2 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -15,6 +15,14 @@ config SND_SOC_ROCKCHIP_I2S Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. +config SND_SOC_ROCKCHIP_SPDIF + tristate "Rockchip SPDIF Device Driver" + depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF driver for + Rockchip SPDIF transceiver device. + config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index e9ba55842879..c0bf560125f3 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,7 +1,9 @@ # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-spdif-objs := rockchip_spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c new file mode 100644 index 000000000000..9d5c470cee82 --- /dev/null +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -0,0 +1,409 @@ +/* sound/soc/rockchip/rk_spdif.c + * + * ALSA SoC Audio Layer - Rockchip I2S Controller driver + * + * Copyright (c) 2014 Rockchip Electronics Co. Ltd. + * Author: Jianqun + * Copyright (c) 2015 Collabora Ltd. + * Author: Sjoerd Simons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_spdif.h" + +enum rk_spdif_type { + RK_SPDIF_RK3066, + RK_SPDIF_RK3188, + RK_SPDIF_RK3288, +}; + +#define RK3288_GRF_SOC_CON2 0x24c + +struct rk_spdif_dev { + struct device *dev; + + struct clk *mclk; + struct clk *hclk; + + struct snd_dmaengine_dai_dma_data playback_dma_data; + + struct regmap *regmap; +}; + +static const struct of_device_id rk_spdif_match[] = { + { .compatible = "rockchip,rk3066-spdif", + .data = (void *) RK_SPDIF_RK3066 }, + { .compatible = "rockchip,rk3188-spdif", + .data = (void *) RK_SPDIF_RK3188 }, + { .compatible = "rockchip,rk3288-spdif", + .data = (void *) RK_SPDIF_RK3288 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk_spdif_match); + +static int rk_spdif_runtime_suspend(struct device *dev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + + return 0; +} + +static int rk_spdif_runtime_resume(struct device *dev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->mclk); + if (ret) { + dev_err(spdif->dev, "mclk clock enable failed %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(spdif->hclk); + if (ret) { + dev_err(spdif->dev, "hclk clock enable failed %d\n", ret); + return ret; + } + + return 0; +} + +static int rk_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE; + int srate, mclk; + int ret; + + srate = params_rate(params); + switch (srate) { + case 32000: + case 48000: + case 96000: + mclk = 96000 * 128; /* 12288000 hz */ + break; + case 44100: + mclk = 44100 * 256; /* 11289600 hz */ + break; + case 192000: + mclk = 192000 * 128; /* 24576000 hz */ + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val |= SPDIF_CFGR_VDW_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= SPDIF_CFGR_VDW_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= SPDIF_CFGR_VDW_24; + break; + default: + return -EINVAL; + } + + /* Set clock and calculate divider */ + ret = clk_set_rate(spdif->mclk, mclk); + if (ret != 0) { + dev_err(spdif->dev, "Failed to set module clock rate: %d\n", + ret); + return ret; + } + + val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256)); + ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, + SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | + SDPIF_CFGR_VDW_MASK, + val); + + return ret; +} + +static int rk_spdif_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, + SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_ENABLE); + + if (ret != 0) + return ret; + + ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, + SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_START); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, + SPDIF_DMACR_TDE_ENABLE, + SPDIF_DMACR_TDE_DISABLE); + + if (ret != 0) + return ret; + + ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, + SPDIF_XFER_TXS_START, + SPDIF_XFER_TXS_STOP); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rk_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &spdif->playback_dma_data; + + return 0; +} + +static const struct snd_soc_dai_ops rk_spdif_dai_ops = { + .hw_params = rk_spdif_hw_params, + .trigger = rk_spdif_trigger, +}; + +static struct snd_soc_dai_driver rk_spdif_dai = { + .probe = rk_spdif_dai_probe, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &rk_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver rk_spdif_component = { + .name = "rockchip-spdif", +}; + +static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_CFGR: + case SPDIF_DMACR: + case SPDIF_INTCR: + case SPDIF_XFER: + case SPDIF_SMPDR: + return true; + default: + return false; + } +} + +static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_CFGR: + case SPDIF_SDBLR: + case SPDIF_INTCR: + case SPDIF_INTSR: + case SPDIF_XFER: + return true; + default: + return false; + } +} + +static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIF_INTSR: + case SPDIF_SDBLR: + return true; + default: + return false; + } +} + +static const struct regmap_config rk_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SPDIF_SMPDR, + .writeable_reg = rk_spdif_wr_reg, + .readable_reg = rk_spdif_rd_reg, + .volatile_reg = rk_spdif_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int rk_spdif_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct rk_spdif_dev *spdif; + const struct of_device_id *match; + struct resource *res; + void __iomem *regs; + int ret; + + match = of_match_node(rk_spdif_match, np); + if ((int) match->data == RK_SPDIF_RK3288) { + struct regmap *grf; + + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, + "rockchip_spdif missing 'rockchip,grf' \n"); + return PTR_ERR(grf); + } + + /* Select the 8 channel SPDIF solution on RK3288 as + * the 2 channel one does not appear to work + */ + regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16); + } + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + spdif->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(spdif->hclk)) { + dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n"); + return PTR_ERR(spdif->hclk); + } + ret = clk_prepare_enable(spdif->hclk); + if (ret) { + dev_err(spdif->dev, "hclock enable failed %d\n", ret); + return ret; + } + + spdif->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(spdif->mclk)) { + dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n"); + return PTR_ERR(spdif->mclk); + } + + ret = clk_prepare_enable(spdif->mclk); + if (ret) { + dev_err(spdif->dev, "clock enable failed %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs, + &rk_spdif_regmap_config); + if (IS_ERR(spdif->regmap)) { + dev_err(&pdev->dev, + "Failed to initialise managed register map\n"); + return PTR_ERR(spdif->regmap); + } + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + spdif->playback_dma_data.maxburst = 4; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_request_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &rk_spdif_component, + &rk_spdif_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI\n"); + goto err_pm_runtime; + } + + ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM\n"); + goto err_pcm_register; + } + + return 0; + +err_pcm_register: + snd_dmaengine_pcm_unregister(&pdev->dev); +err_pm_runtime: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int rk_spdif_remove(struct platform_device *pdev) +{ + struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + rk_spdif_runtime_suspend(&pdev->dev); + + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + snd_dmaengine_pcm_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops rk_spdif_pm_ops = { + SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, + NULL) +}; + +static struct platform_driver rk_spdif_driver = { + .probe = rk_spdif_probe, + .remove = rk_spdif_remove, + .driver = { + .name = "rockchip-spdif", + .of_match_table = of_match_ptr(rk_spdif_match), + .pm = &rk_spdif_pm_ops, + }, +}; +module_platform_driver(rk_spdif_driver); + +MODULE_ALIAS("platform:rockchip-spdif"); +MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface"); +MODULE_AUTHOR("Sjoerd Simons "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h new file mode 100644 index 000000000000..07f86a21046a --- /dev/null +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -0,0 +1,63 @@ +/* + * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver + * + * Copyright (c) 2015 Collabora Ltd. + * Author: Sjoerd Simons + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ROCKCHIP_SPDIF_H +#define _ROCKCHIP_SPDIF_H + +/* + * CFGR + * transfer configuration register +*/ +#define SPDIF_CFGR_CLK_DIV_SHIFT (16) +#define SPDIF_CFGR_CLK_DIV_MASK (0xff << SPDIF_CFGR_CLK_DIV_SHIFT) +#define SPDIF_CFGR_CLK_DIV(x) (x << SPDIF_CFGR_CLK_DIV_SHIFT) + +#define SPDIF_CFGR_HALFWORD_SHIFT 2 +#define SPDIF_CFGR_HALFWORD_DISABLE (0 << SPDIF_CFGR_HALFWORD_SHIFT) +#define SPDIF_CFGR_HALFWORD_ENABLE (1 << SPDIF_CFGR_HALFWORD_SHIFT) + +#define SPDIF_CFGR_VDW_SHIFT 0 +#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT) +#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT) + +#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00) +#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01) +#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10) + +/* + * DMACR + * DMA control register +*/ +#define SPDIF_DMACR_TDE_SHIFT 5 +#define SPDIF_DMACR_TDE_DISABLE (0 << SPDIF_DMACR_TDE_SHIFT) +#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT) + +#define SPDIF_DMACR_TDL_SHIFT 0 +#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT) +#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT) + +/* + * XFER + * Transfer control register +*/ +#define SPDIF_XFER_TXS_SHIFT 0 +#define SPDIF_XFER_TXS_STOP (0 << SPDIF_XFER_TXS_SHIFT) +#define SPDIF_XFER_TXS_START (1 << SPDIF_XFER_TXS_SHIFT) + +#define SPDIF_CFGR (0x0000) +#define SPDIF_SDBLR (0x0004) +#define SPDIF_DMACR (0x0008) +#define SPDIF_INTCR (0x000c) +#define SPDIF_INTSR (0x0010) +#define SPDIF_XFER (0x0018) +#define SPDIF_SMPDR (0x0020) + +#endif /* _ROCKCHIP_SPDIF_H */ -- cgit v1.2.3-59-g8ed1b From dc542fb4179a104fc73760a2da0fd78f68f70d6d Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 7 Oct 2015 17:52:12 +0530 Subject: ASoC: rt5645: fix build warning We were getting build warning about "Section mismatch". dmi_platform_intel_broadwell is being referenced from the probe function rt5645_i2c_probe(), but dmi_platform_intel_broadwell was marked with __initdata. Signed-off-by: Sudip Mukherjee Reviewed-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index cd1a4ec980ac..d9993d42cc4e 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3241,7 +3241,7 @@ static int buddy_quirk_cb(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = { +static struct dmi_system_id dmi_platform_intel_broadwell[] = { { .ident = "Chrome Buddy", .callback = buddy_quirk_cb, -- cgit v1.2.3-59-g8ed1b From 1b5e6167c27e1d3be33155baf9660768ac74aae0 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 8 Oct 2015 09:48:05 +0100 Subject: ALSA: hdac: Copy codec helpers to core The current codec helpers are local to hda code and needs to be moved to core so that other users can use it. The helpers to read/write the codec and to check the power state of widgets is copied Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 6 ++++ sound/hda/hdac_device.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) (limited to 'sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 49bc836fcd84..26e956f4b7c6 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -147,6 +147,12 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, unsigned int format); +int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm); +int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm); +bool snd_hdac_check_power_state(struct hdac_device *hdac, + hda_nid_t nid, unsigned int target_state); /** * snd_hdac_read_parm - read a codec parameter * @codec: the codec object diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index db96042a497f..b3b0ad289df1 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -952,3 +952,84 @@ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, return true; } EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format); + +static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm) +{ + unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm); + unsigned int res; + + if (snd_hdac_exec_verb(hdac, cmd, flags, &res)) + return -1; + + return res; +} + +static int codec_write(struct hdac_device *hdac, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm) +{ + unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm); + + return snd_hdac_exec_verb(hdac, cmd, flags, NULL); +} + +/** + * snd_hdac_codec_read - send a command and get the response + * @hdac: the HDAC device + * @nid: NID to send the command + * @flags: optional bit flags + * @verb: the verb to send + * @parm: the parameter for the verb + * + * Send a single command and read the corresponding response. + * + * Returns the obtained response value, or -1 for an error. + */ +int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm) +{ + return codec_read(hdac, nid, flags, verb, parm); +} +EXPORT_SYMBOL_GPL(snd_hdac_codec_read); + +/** + * snd_hdac_codec_write - send a single command without waiting for response + * @hdac: the HDAC device + * @nid: NID to send the command + * @flags: optional bit flags + * @verb: the verb to send + * @parm: the parameter for the verb + * + * Send a single command without waiting for response. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm) +{ + return codec_write(hdac, nid, flags, verb, parm); +} +EXPORT_SYMBOL_GPL(snd_hdac_codec_write); + +/* + * snd_hdac_check_power_state: check whether the actual power state matches + * with the target state + * + * @hdac: the HDAC device + * @nid: NID to send the command + * @target_state: target state to check for + * + * Return true if state matches, false if not + */ +bool snd_hdac_check_power_state(struct hdac_device *hdac, + hda_nid_t nid, unsigned int target_state) +{ + unsigned int state = codec_read(hdac, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + + if (state & AC_PWRST_ERROR) + return true; + state = (state >> 4) & 0x0f; + return (state == target_state); +} +EXPORT_SYMBOL_GPL(snd_hdac_check_power_state); -- cgit v1.2.3-59-g8ed1b From 70b4891cc889d359d3b1f3b8ec6f10c5800ecc70 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 8 Oct 2015 09:48:06 +0100 Subject: ALSA: hda: make use of core codec fns Now that we have introduced the core fns we should make hda use these helpers Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 44 -------------------------------------------- sound/pci/hda/hda_codec.h | 18 ++++++++++++++---- sound/pci/hda/hda_local.h | 7 +------ 3 files changed, 15 insertions(+), 54 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 37f43a1b34ef..2eeaf5ea20f9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -90,50 +90,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, return err; } -/** - * snd_hda_codec_read - send a command and get the response - * @codec: the HDA codec - * @nid: NID to send the command - * @flags: optional bit flags - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command and read the corresponding response. - * - * Returns the obtained response value, or -1 for an error. - */ -unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, - int flags, - unsigned int verb, unsigned int parm) -{ - unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); - unsigned int res; - if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res)) - return -1; - return res; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_read); - -/** - * snd_hda_codec_write - send a single command without waiting for response - * @codec: the HDA codec - * @nid: NID to send the command - * @flags: optional bit flags - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command without waiting for response. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, - unsigned int verb, unsigned int parm) -{ - unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); - return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_write); - /** * snd_hda_sequence_write - sequence writes * @codec: the HDA codec diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2970413f18a0..95991e463abb 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -309,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec); /* * low level functions */ -unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, +static inline unsigned int +snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int flags, - unsigned int verb, unsigned int parm); -int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, - unsigned int verb, unsigned int parm); + unsigned int verb, unsigned int parm) +{ + return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm); +} + +static inline int +snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, + unsigned int verb, unsigned int parm) +{ + return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm); +} + #define snd_hda_param_read(codec, nid, param) \ snd_hdac_read_parm(&(codec)->core, nid, param) #define snd_hda_get_sub_nodes(codec, nid, start_nid) \ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4a21c2199e02..d0e066e4c985 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -681,12 +681,7 @@ static inline bool snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int target_state) { - unsigned int state = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - if (state & AC_PWRST_ERROR) - return true; - state = (state >> 4) & 0x0f; - return (state == target_state); + return snd_hdac_check_power_state(&codec->core, nid, target_state); } unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, -- cgit v1.2.3-59-g8ed1b From 694470273de29c2d3f3792856d4e748969294789 Mon Sep 17 00:00:00 2001 From: Kosuke Tatsukawa Date: Fri, 9 Oct 2015 00:35:54 +0000 Subject: ALSA: seq_oss: fix waitqueue_active without memory barrier in snd-seq-oss snd_seq_oss_readq_put_event() seems to be missing a memory barrier which might cause the waker to not notice the waiter and miss sending a wake_up as in the following figure. snd_seq_oss_readq_put_event snd_seq_oss_readq_wait ------------------------------------------------------------------------ /* wait_event_interruptible_timeout */ /* __wait_event_interruptible_timeout */ /* ___wait_event */ for (;;) { prepare_to_wait_event(&wq, &__wait, state); spin_lock_irqsave(&q->lock, flags); if (waitqueue_active(&q->midi_sleep)) /* The CPU might reorder the test for the waitqueue up here, before prior writes complete */ if ((q->qlen>0 || q->head==q->tail) ... __ret = schedule_timeout(__ret) if (q->qlen >= q->maxlen - 1) { memcpy(&q->q[q->tail], ev, sizeof(*ev)); q->tail = (q->tail + 1) % q->maxlen; q->qlen++; ------------------------------------------------------------------------ There are two other place in sound/core/seq/oss/ which have similar code. The attached patch removes the call to waitqueue_active() leaving just wake_up() behind. This fixes the problem because the call to spin_lock_irqsave() in wake_up() will be an ACQUIRE operation. I found this issue when I was looking through the linux source code for places calling waitqueue_active() before wake_up*(), but without preceding memory barriers, after sending a patch to fix a similar issue in drivers/tty/n_tty.c (Details about the original issue can be found here: https://lkml.org/lkml/2015/9/28/849). Signed-off-by: Kosuke Tatsukawa Signed-off-by: Takashi Iwai --- sound/core/seq/oss/seq_oss_readq.c | 6 ++---- sound/core/seq/oss/seq_oss_writeq.c | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index ccd893566f1d..046cb586fb2f 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q) q->head = q->tail = 0; } /* if someone sleeping, wake'em up */ - if (waitqueue_active(&q->midi_sleep)) - wake_up(&q->midi_sleep); + wake_up(&q->midi_sleep); q->input_time = (unsigned long)-1; } @@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) q->qlen++; /* wake up sleeper */ - if (waitqueue_active(&q->midi_sleep)) - wake_up(&q->midi_sleep); + wake_up(&q->midi_sleep); spin_unlock_irqrestore(&q->lock, flags); diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c index d50338bbc21f..1f6788a18444 100644 --- a/sound/core/seq/oss/seq_oss_writeq.c +++ b/sound/core/seq/oss/seq_oss_writeq.c @@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time) spin_lock_irqsave(&q->sync_lock, flags); q->sync_time = time; q->sync_event_put = 0; - if (waitqueue_active(&q->sync_sleep)) { - wake_up(&q->sync_sleep); - } + wake_up(&q->sync_sleep); spin_unlock_irqrestore(&q->sync_lock, flags); } -- cgit v1.2.3-59-g8ed1b From 585d7cba5e1fcd8703a120042f35695165986b9b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 9 Oct 2015 08:10:25 +0900 Subject: ALSA: firewire-lib: add helper functions for asynchronous transactions to transfer MIDI messages Some models receive MIDI messages via IEEE 1394 asynchronous transactions. In this case, MIDI messages are transferred in fixed-length payload. It's nice that firewire-lib module has common helper functions. This commit implements this idea. Each driver adds 'struct snd_fw_async_midi_port' in its instance structure. In probing, it should call snd_fw_async_midi_port_init() to initialize the structure with some parameters such as target address, the length of payload in a transaction and a pointer for callback function to fill the payload buffer. At 'struct snd_rawmidi_ops.trigger()' callback, it should call 'snd_fw_async_midi_port_run()' to start transactions. Each driver should ensure that the lifetime of MIDI substream continues till calling 'snd_fw_async_midi_port_finish()'. The helper functions support retries to transferring MIDI messages when transmission errors occur. When transactions are successful, the helper functions call 'snd_rawmidi_transmit_ack()' internally to consume MIDI bytes in the buffer. Therefore, Each driver is expected to use 'snd_rawmidi_transmit_peek()' to tell the number of bytes to transfer to return value of 'fill' callback. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/lib.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ sound/firewire/lib.h | 50 +++++++++++++++++++++++++ 2 files changed, 153 insertions(+) (limited to 'sound') diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 7409edba9f06..03ada3f1047a 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "lib.h" #define ERROR_RETRY_DELAY_MS 20 @@ -66,6 +67,108 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, } EXPORT_SYMBOL(snd_fw_transaction); +static void async_midi_port_callback(struct fw_card *card, int rcode, + void *data, size_t length, + void *callback_data) +{ + struct snd_fw_async_midi_port *port = callback_data; + struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream); + + if (rcode == RCODE_COMPLETE && substream != NULL) + snd_rawmidi_transmit_ack(substream, port->consume_bytes); +} + +static void midi_port_work(struct work_struct *work) +{ + struct snd_fw_async_midi_port *port = + container_of(work, struct snd_fw_async_midi_port, work); + struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream); + int generation; + int type; + + /* Nothing to do. */ + if (substream == NULL || snd_rawmidi_transmit_empty(substream)) + return; + + /* + * Fill the buffer. The callee must use snd_rawmidi_transmit_peek(). + * Later, snd_rawmidi_transmit_ack() is called. + */ + memset(port->buf, 0, port->len); + port->consume_bytes = port->fill(substream, port->buf); + if (port->consume_bytes <= 0) { + /* Do it in next chance, immediately. */ + if (port->consume_bytes == 0) + schedule_work(&port->work); + return; + } + + /* Calculate type of transaction. */ + if (port->len == 4) + type = TCODE_WRITE_QUADLET_REQUEST; + else + type = TCODE_WRITE_BLOCK_REQUEST; + + /* Start this transaction. */ + /* + * In Linux FireWire core, when generation is updated with memory + * barrier, node id has already been updated. In this module, After + * this smp_rmb(), load/store instructions to memory are completed. + * Thus, both of generation and node id are available with recent + * values. This is a light-serialization solution to handle bus reset + * events on IEEE 1394 bus. + */ + generation = port->parent->generation; + smp_rmb(); + + fw_send_request(port->parent->card, &port->transaction, type, + port->parent->node_id, generation, + port->parent->max_speed, port->addr, + port->buf, port->len, async_midi_port_callback, + port); +} + +/** + * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure + * @port: the asynchronous MIDI port to initialize + * @unit: the target of the asynchronous transaction + * @addr: the address to which transactions are transferred + * @len: the length of transaction + * @fill: the callback function to fill given buffer, and returns the + * number of consumed bytes for MIDI message. + * + */ +int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, + struct fw_unit *unit, u64 addr, unsigned int len, + snd_fw_async_midi_port_fill fill) +{ + port->len = DIV_ROUND_UP(len, 4) * 4; + port->buf = kzalloc(port->len, GFP_KERNEL); + if (port->buf == NULL) + return -ENOMEM; + + port->parent = fw_parent_device(unit); + port->addr = addr; + port->fill = fill; + + INIT_WORK(&port->work, midi_port_work); + + return 0; +} +EXPORT_SYMBOL(snd_fw_async_midi_port_init); + +/** + * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure + * @port: the asynchronous MIDI port structure + */ +void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port) +{ + snd_fw_async_midi_port_finish(port); + cancel_work_sync(&port->work); + kfree(port->buf); +} +EXPORT_SYMBOL(snd_fw_async_midi_port_destroy); + MODULE_DESCRIPTION("FireWire audio helper functions"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index 02cfabc9c3c4..37a7fe4235f2 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -3,6 +3,8 @@ #include #include +#include +#include struct fw_unit; @@ -20,4 +22,52 @@ static inline bool rcode_is_permanent_error(int rcode) return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; } +struct snd_fw_async_midi_port; +typedef int (*snd_fw_async_midi_port_fill)( + struct snd_rawmidi_substream *substream, + u8 *buf); + +struct snd_fw_async_midi_port { + struct fw_device *parent; + struct work_struct work; + + u64 addr; + struct fw_transaction transaction; + + u8 *buf; + unsigned int len; + + struct snd_rawmidi_substream *substream; + snd_fw_async_midi_port_fill fill; + unsigned int consume_bytes; +}; + +int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, + struct fw_unit *unit, u64 addr, unsigned int len, + snd_fw_async_midi_port_fill fill); +void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port); + +/** + * snd_fw_async_midi_port_run - run transactions for the async MIDI port + * @port: the asynchronous MIDI port + * @substream: the MIDI substream + */ +static inline void +snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port, + struct snd_rawmidi_substream *substream) +{ + port->substream = substream; + schedule_work(&port->work); +} + +/** + * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port + * @port: the asynchronous MIDI port + */ +static inline void +snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port) +{ + port->substream = NULL; +} + #endif -- cgit v1.2.3-59-g8ed1b From d3ef9cb93aec59eb1d90d01ae0642fd517c25381 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 9 Oct 2015 08:10:26 +0900 Subject: ALSA: firewire-lib: add a restriction for a transaction at once Currently, when waiting for a response, callers can start another transaction by scheduling another work. This is not good for error processing of transaction, especially the first response is too late. This commit serialize request/response transactions, by adding one boolean member to represent idling state. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/lib.c | 9 +++++++++ sound/firewire/lib.h | 1 + 2 files changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 03ada3f1047a..ddc3e88ee0d1 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -76,6 +76,8 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, if (rcode == RCODE_COMPLETE && substream != NULL) snd_rawmidi_transmit_ack(substream, port->consume_bytes); + + port->idling = true; } static void midi_port_work(struct work_struct *work) @@ -86,6 +88,10 @@ static void midi_port_work(struct work_struct *work) int generation; int type; + /* Under transacting. */ + if (!port->idling) + return; + /* Nothing to do. */ if (substream == NULL || snd_rawmidi_transmit_empty(substream)) return; @@ -110,6 +116,8 @@ static void midi_port_work(struct work_struct *work) type = TCODE_WRITE_BLOCK_REQUEST; /* Start this transaction. */ + port->idling = false; + /* * In Linux FireWire core, when generation is updated with memory * barrier, node id has already been updated. In this module, After @@ -150,6 +158,7 @@ int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, port->parent = fw_parent_device(unit); port->addr = addr; port->fill = fill; + port->idling = true; INIT_WORK(&port->work, midi_port_work); diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index 37a7fe4235f2..0af06f44e8c2 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -30,6 +30,7 @@ typedef int (*snd_fw_async_midi_port_fill)( struct snd_fw_async_midi_port { struct fw_device *parent; struct work_struct work; + bool idling; u64 addr; struct fw_transaction transaction; -- cgit v1.2.3-59-g8ed1b From e8a40d9bcb2392ea502dc629d5d6c0556fdd3b0c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 9 Oct 2015 08:10:27 +0900 Subject: ALSA: firewire-lib: schedule work again when MIDI substream has rest of MIDI messages Currently, when two MIDI trigger callbacks can be called immediately, transactions for the second MIDI messages can be postpone till next trigger callback. This is not good for real-time message transmission. This commit schedules work again at response handling callback if the MIDI substream still includes untransferred MIDI messages. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/lib.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index ddc3e88ee0d1..3e9afd7d402c 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -78,6 +78,9 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, snd_rawmidi_transmit_ack(substream, port->consume_bytes); port->idling = true; + + if (!snd_rawmidi_transmit_empty(substream)) + schedule_work(&port->work); } static void midi_port_work(struct work_struct *work) -- cgit v1.2.3-59-g8ed1b From ea848b7b62ab6d3078955b87833c3b336b71bc9f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 9 Oct 2015 08:10:28 +0900 Subject: ALSA: firewire-lib: add throttle for MIDI data rate Typically, the target devices have internal buffer to adjust output of received MIDI messages for MIDI serial bus, while the capacity of the buffer is limited. IEEE 1394 transactions can transfer more MIDI messages than MIDI serial bus can. This can cause buffer over flow in device side. This commit adds throttle to limit MIDI data rate by counting intervals between two MIDI messages. Usual MIDI messages consists of two or three bytes. This requires 1.302 to 1.953 mili-seconds interval between these messages. This commit uses kernel monotonic time service to calculate the time of next transaction. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/lib.c | 18 +++++++++++++++++- sound/firewire/lib.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 3e9afd7d402c..9a98c7cd8744 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -76,6 +76,9 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, if (rcode == RCODE_COMPLETE && substream != NULL) snd_rawmidi_transmit_ack(substream, port->consume_bytes); + else if (!rcode_is_permanent_error(rcode)) + /* To start next transaction immediately for recovery. */ + port->next_ktime = ktime_set(0, 0); port->idling = true; @@ -99,6 +102,12 @@ static void midi_port_work(struct work_struct *work) if (substream == NULL || snd_rawmidi_transmit_empty(substream)) return; + /* Do it in next chance. */ + if (ktime_after(port->next_ktime, ktime_get())) { + schedule_work(&port->work); + return; + } + /* * Fill the buffer. The callee must use snd_rawmidi_transmit_peek(). * Later, snd_rawmidi_transmit_ack() is called. @@ -107,8 +116,10 @@ static void midi_port_work(struct work_struct *work) port->consume_bytes = port->fill(substream, port->buf); if (port->consume_bytes <= 0) { /* Do it in next chance, immediately. */ - if (port->consume_bytes == 0) + if (port->consume_bytes == 0) { + port->next_ktime = ktime_set(0, 0); schedule_work(&port->work); + } return; } @@ -118,6 +129,10 @@ static void midi_port_work(struct work_struct *work) else type = TCODE_WRITE_BLOCK_REQUEST; + /* Set interval to next transaction. */ + port->next_ktime = ktime_add_ns(ktime_get(), + port->consume_bytes * 8 * NSEC_PER_SEC / 31250); + /* Start this transaction. */ port->idling = false; @@ -162,6 +177,7 @@ int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, port->addr = addr; port->fill = fill; port->idling = true; + port->next_ktime = ktime_set(0, 0); INIT_WORK(&port->work, midi_port_work); diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index 0af06f44e8c2..59e086587212 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -31,6 +31,7 @@ struct snd_fw_async_midi_port { struct fw_device *parent; struct work_struct work; bool idling; + ktime_t next_ktime; u64 addr; struct fw_transaction transaction; -- cgit v1.2.3-59-g8ed1b From bde3e2880ff92f9956914afdd19648ccb20f487a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 9 Oct 2015 08:10:29 +0900 Subject: ALSA: firewire-lib: avoid endless loop to transfer MIDI messages at fatal error Currently, when asynchronous transactions finish in error state and retries, work scheduling and work running also continues. This should be canceled at fatal error because it can cause endless loop. This commit enables to cancel transferring MIDI messages when transactions encounter fatal errors. This is achieved by setting error state. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/lib.c | 11 +++++++++-- sound/firewire/lib.h | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 9a98c7cd8744..edf1c8bd25a6 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -79,6 +79,9 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, else if (!rcode_is_permanent_error(rcode)) /* To start next transaction immediately for recovery. */ port->next_ktime = ktime_set(0, 0); + else + /* Don't continue processing. */ + port->error = true; port->idling = true; @@ -94,8 +97,8 @@ static void midi_port_work(struct work_struct *work) int generation; int type; - /* Under transacting. */ - if (!port->idling) + /* Under transacting or error state. */ + if (!port->idling || port->error) return; /* Nothing to do. */ @@ -119,6 +122,9 @@ static void midi_port_work(struct work_struct *work) if (port->consume_bytes == 0) { port->next_ktime = ktime_set(0, 0); schedule_work(&port->work); + } else { + /* Fatal error. */ + port->error = true; } return; } @@ -178,6 +184,7 @@ int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, port->fill = fill; port->idling = true; port->next_ktime = ktime_set(0, 0); + port->error = false; INIT_WORK(&port->work, midi_port_work); diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index 59e086587212..f3f6f84c48d6 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -32,6 +32,7 @@ struct snd_fw_async_midi_port { struct work_struct work; bool idling; ktime_t next_ktime; + bool error; u64 addr; struct fw_transaction transaction; @@ -58,8 +59,10 @@ static inline void snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port, struct snd_rawmidi_substream *substream) { - port->substream = substream; - schedule_work(&port->work); + if (!port->error) { + port->substream = substream; + schedule_work(&port->work); + } } /** @@ -70,6 +73,7 @@ static inline void snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port) { port->substream = NULL; + port->error = false; } #endif -- cgit v1.2.3-59-g8ed1b From 6ea8ba33e65d3d284de7e7373939352e2c728f10 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:48 +0100 Subject: ASoC: Intel: Skylake: Check CPA bit in DSP core power down Skylake driver will set the SPA bit to 0 to turn off the DSP core. Driver will poll the Current Power Active (CPA) bit to match the Set Power Active (SPA) bit value. When CPA bit matches the value of SPA bit, the achieved power state has reached. In case of DSP power down, register that was polled is SPA instead of CPA. This patch corrects the register to be polled in case of DSP power down. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 94875b008b0b..194bd0036454 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) /* poll with timeout to check if operation successful */ return sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_SPA_MASK, + SKL_ADSPCS_CPA_MASK, 0, SKL_DSP_PD_TO, "Power down"); -- cgit v1.2.3-59-g8ed1b From def656fe22abb4fbf174a982dcef1d40274ddb11 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:49 +0100 Subject: ASoC: Intel: Skylake: Verify the status bit before handling interrupt Like we have in legacy mode HDA driver, we need to check the status bit and handle interrupt only when it is not zero or all bits set. We typically see the status as all 1's when controller resumes from suspend, So add the check here as well and don't handle for these cases. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 194bd0036454..1bfb7f63b572 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); ctx->intr_status = val; + if (val == 0xffffffff) { + spin_unlock(&ctx->spinlock); + return IRQ_NONE; + } + if (val & SKL_ADSPIS_IPC) { skl_ipc_int_disable(ctx); result = IRQ_WAKE_THREAD; -- cgit v1.2.3-59-g8ed1b From 84c9e2836aa7c87b19a24de091c7e7cf16124645 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:50 +0100 Subject: ASoC: Intel: Skylake: Correct the runtime handler behaviour On runtime pm resume, we need to download the firmware, also on suspend we need to ensure all the interrupts from controller and DSP are disabled. Also since we download the firmware on resume, we don't need to do so on init, so remove that bit Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 12 ++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 1 + sound/soc/intel/skylake/skl-sst.c | 56 +++++++++++++++++++---------------- 4 files changed, 44 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index cbd568eac033..2151652d37b7 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -314,6 +314,7 @@ struct sst_dsp { int sst_state; struct skl_cl_dev cl_dev; u32 intr_status; + const struct firmware *fw; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 937a0a3a63a0..3345ea0d4414 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx) SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY); } +void skl_ipc_op_int_disable(struct sst_dsp *ctx) +{ + /* disable IPC DONE interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, 0); + + /* Disable IPC BUSY interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_BUSY, 0); + +} + bool skl_ipc_int_status(struct sst_dsp *ctx) { return sst_dsp_shim_read_unlocked(ctx, diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 9f5f67202858..f1a154e45dc3 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); +void skl_ipc_op_int_disable(struct sst_dsp *ctx); void skl_ipc_int_disable(struct sst_dsp *dsp); bool skl_ipc_int_status(struct sst_dsp *dsp); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index c18ea51b7484..3b83dc99f1d4 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx, static int skl_load_base_firmware(struct sst_dsp *ctx) { int ret = 0, i; - const struct firmware *fw = NULL; struct skl_sst *skl = ctx->thread_context; u32 reg; - ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev); + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + if (ctx->fw == NULL) { + ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + skl_dsp_disable_core(ctx); + return -EIO; + } + } + + ret = skl_dsp_boot(ctx); if (ret < 0) { - dev_err(ctx->dev, "Request firmware failed %d\n", ret); - skl_dsp_disable_core(ctx); - return -EIO; + dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); + goto skl_load_base_firmware_failed; + } + + ret = skl_cldma_prepare(ctx); + if (ret < 0) { + dev_err(ctx->dev, "CL dma prepare failed : %d", ret); + goto skl_load_base_firmware_failed; } /* enable Interrupt */ @@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto skl_load_base_firmware_failed; } - ret = skl_transfer_firmware(ctx, fw->data, fw->size); + ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); if (ret < 0) { dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); goto skl_load_base_firmware_failed; @@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); } - release_firmware(fw); - return 0; skl_load_base_firmware_failed: skl_dsp_disable_core(ctx); - release_firmware(fw); + release_firmware(ctx->fw); + ctx->fw = NULL; return ret; } @@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx) } skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + /* disable Interrupt */ + ctx->cl_dev.ops.cl_cleanup_controller(ctx); + skl_cldma_int_disable(ctx); + skl_ipc_op_int_disable(ctx); + skl_ipc_int_disable(ctx); + return ret; } @@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; - skl->boot_complete = false; - init_waitqueue_head(&skl->boot_wait); - - ret = skl_dsp_boot(sst); - if (ret < 0) { - dev_err(skl->dev, "Boot dsp core failed ret: %d", ret); - goto free_ipc; - } - - ret = skl_cldma_prepare(sst); - if (ret < 0) { - dev_err(dev, "CL dma prepare failed : %d", ret); - goto free_ipc; - } - - ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); @@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return 0; -free_ipc: skl_ipc_free(&skl->ipc); return ret; } -- cgit v1.2.3-59-g8ed1b From 01bb84b5001e4a6221582b163b34e90b3095f451 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:51 +0100 Subject: ASoC: Intel: Skylake: power down all link in suspend This ensures that the link is not requesting any clock and the PLL can turn off. The link is powered when controller is brought out of reset. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ca135b8ab5c0..5319529aedf7 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -173,6 +173,9 @@ static int skl_runtime_suspend(struct device *dev) /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + + snd_hdac_ext_bus_link_power_down_all(ebus); + ret = skl_suspend_dsp(skl); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From 6b9866c893fc6a51e74df65fd9d9f851a3c2872e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Oct 2015 08:10:55 +0200 Subject: ALSA: bebob: constify various snd_bebob structures The structures of type snd_bebob_clock_spec, snd_bebob_rate_spec, snd_bebob_meter_spec, and snd_bebob_spec are never modified after they are initialized. Make them all const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Tested-by: Takashi Sakamoto Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob.c | 2 +- sound/firewire/bebob/bebob.h | 32 ++++++++++++++++---------------- sound/firewire/bebob/bebob_focusrite.c | 26 +++++++++++++------------- sound/firewire/bebob/bebob_maudio.c | 32 ++++++++++++++++---------------- sound/firewire/bebob/bebob_pcm.c | 2 +- sound/firewire/bebob/bebob_proc.c | 6 +++--- sound/firewire/bebob/bebob_stream.c | 6 +++--- sound/firewire/bebob/bebob_terratec.c | 10 +++++----- sound/firewire/bebob/bebob_yamaha.c | 6 +++--- 9 files changed, 61 insertions(+), 61 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 0ed5e5ff9346..091290d1f3ea 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -335,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit) snd_card_free_when_closed(bebob->card); } -static struct snd_bebob_rate_spec normal_rate_spec = { +static const struct snd_bebob_rate_spec normal_rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate }; diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index d3c9d8de289b..4d8fcc78e747 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -70,9 +70,9 @@ struct snd_bebob_meter_spec { int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size); }; struct snd_bebob_spec { - struct snd_bebob_clock_spec *clock; - struct snd_bebob_rate_spec *rate; - struct snd_bebob_meter_spec *meter; + const struct snd_bebob_clock_spec *clock; + const struct snd_bebob_rate_spec *rate; + const struct snd_bebob_meter_spec *meter; }; struct snd_bebob { @@ -235,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); /* model specific operations */ -extern struct snd_bebob_spec phase88_rack_spec; -extern struct snd_bebob_spec phase24_series_spec; -extern struct snd_bebob_spec yamaha_go_spec; -extern struct snd_bebob_spec saffirepro_26_spec; -extern struct snd_bebob_spec saffirepro_10_spec; -extern struct snd_bebob_spec saffire_le_spec; -extern struct snd_bebob_spec saffire_spec; -extern struct snd_bebob_spec maudio_fw410_spec; -extern struct snd_bebob_spec maudio_audiophile_spec; -extern struct snd_bebob_spec maudio_solo_spec; -extern struct snd_bebob_spec maudio_ozonic_spec; -extern struct snd_bebob_spec maudio_nrv10_spec; -extern struct snd_bebob_spec maudio_special_spec; +extern const struct snd_bebob_spec phase88_rack_spec; +extern const struct snd_bebob_spec phase24_series_spec; +extern const struct snd_bebob_spec yamaha_go_spec; +extern const struct snd_bebob_spec saffirepro_26_spec; +extern const struct snd_bebob_spec saffirepro_10_spec; +extern const struct snd_bebob_spec saffire_le_spec; +extern const struct snd_bebob_spec saffire_spec; +extern const struct snd_bebob_spec maudio_fw410_spec; +extern const struct snd_bebob_spec maudio_audiophile_spec; +extern const struct snd_bebob_spec maudio_solo_spec; +extern const struct snd_bebob_spec maudio_ozonic_spec; +extern const struct snd_bebob_spec maudio_nrv10_spec; +extern const struct snd_bebob_spec maudio_special_spec; int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814); int snd_bebob_maudio_load_firmware(struct fw_unit *unit); diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c index a1a39494ea6c..f11090057949 100644 --- a/sound/firewire/bebob/bebob_focusrite.c +++ b/sound/firewire/bebob/bebob_focusrite.c @@ -200,7 +200,7 @@ end: return err; } -struct snd_bebob_spec saffire_le_spec; +const struct snd_bebob_spec saffire_le_spec; static enum snd_bebob_clock_type saffire_both_clk_src_types[] = { SND_BEBOB_CLOCK_TYPE_INTERNAL, SND_BEBOB_CLOCK_TYPE_EXTERNAL, @@ -229,7 +229,7 @@ static const char *const saffire_meter_labels[] = { static int saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size) { - struct snd_bebob_meter_spec *spec = bebob->spec->meter; + const struct snd_bebob_meter_spec *spec = bebob->spec->meter; unsigned int channels; u64 offset; int err; @@ -260,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size) return err; } -static struct snd_bebob_rate_spec saffirepro_both_rate_spec = { +static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = { .get = &saffirepro_both_clk_freq_get, .set = &saffirepro_both_clk_freq_set, }; /* Saffire Pro 26 I/O */ -static struct snd_bebob_clock_spec saffirepro_26_clk_spec = { +static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = { .num = ARRAY_SIZE(saffirepro_26_clk_src_types), .types = saffirepro_26_clk_src_types, .get = &saffirepro_both_clk_src_get, }; -struct snd_bebob_spec saffirepro_26_spec = { +const struct snd_bebob_spec saffirepro_26_spec = { .clock = &saffirepro_26_clk_spec, .rate = &saffirepro_both_rate_spec, .meter = NULL }; /* Saffire Pro 10 I/O */ -static struct snd_bebob_clock_spec saffirepro_10_clk_spec = { +static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = { .num = ARRAY_SIZE(saffirepro_10_clk_src_types), .types = saffirepro_10_clk_src_types, .get = &saffirepro_both_clk_src_get, }; -struct snd_bebob_spec saffirepro_10_spec = { +const struct snd_bebob_spec saffirepro_10_spec = { .clock = &saffirepro_10_clk_spec, .rate = &saffirepro_both_rate_spec, .meter = NULL }; -static struct snd_bebob_rate_spec saffire_both_rate_spec = { +static const struct snd_bebob_rate_spec saffire_both_rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate, }; -static struct snd_bebob_clock_spec saffire_both_clk_spec = { +static const struct snd_bebob_clock_spec saffire_both_clk_spec = { .num = ARRAY_SIZE(saffire_both_clk_src_types), .types = saffire_both_clk_src_types, .get = &saffire_both_clk_src_get, }; /* Saffire LE */ -static struct snd_bebob_meter_spec saffire_le_meter_spec = { +static const struct snd_bebob_meter_spec saffire_le_meter_spec = { .num = ARRAY_SIZE(saffire_le_meter_labels), .labels = saffire_le_meter_labels, .get = &saffire_meter_get, }; -struct snd_bebob_spec saffire_le_spec = { +const struct snd_bebob_spec saffire_le_spec = { .clock = &saffire_both_clk_spec, .rate = &saffire_both_rate_spec, .meter = &saffire_le_meter_spec }; /* Saffire */ -static struct snd_bebob_meter_spec saffire_meter_spec = { +static const struct snd_bebob_meter_spec saffire_meter_spec = { .num = ARRAY_SIZE(saffire_meter_labels), .labels = saffire_meter_labels, .get = &saffire_meter_get, }; -struct snd_bebob_spec saffire_spec = { +const struct snd_bebob_spec saffire_spec = { .clock = &saffire_both_clk_spec, .rate = &saffire_both_rate_spec, .meter = &saffire_meter_spec diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 057495d54ab0..7b86a6b99f07 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -687,7 +687,7 @@ static const char *const nrv10_meter_labels[] = { static int normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size) { - struct snd_bebob_meter_spec *spec = bebob->spec->meter; + const struct snd_bebob_meter_spec *spec = bebob->spec->meter; unsigned int c, channels; int err; @@ -712,85 +712,85 @@ end: } /* for special customized devices */ -static struct snd_bebob_rate_spec special_rate_spec = { +static const struct snd_bebob_rate_spec special_rate_spec = { .get = &special_get_rate, .set = &special_set_rate, }; -static struct snd_bebob_clock_spec special_clk_spec = { +static const struct snd_bebob_clock_spec special_clk_spec = { .num = ARRAY_SIZE(special_clk_types), .types = special_clk_types, .get = &special_clk_get, }; -static struct snd_bebob_meter_spec special_meter_spec = { +static const struct snd_bebob_meter_spec special_meter_spec = { .num = ARRAY_SIZE(special_meter_labels), .labels = special_meter_labels, .get = &special_meter_get }; -struct snd_bebob_spec maudio_special_spec = { +const struct snd_bebob_spec maudio_special_spec = { .clock = &special_clk_spec, .rate = &special_rate_spec, .meter = &special_meter_spec }; /* Firewire 410 specification */ -static struct snd_bebob_rate_spec usual_rate_spec = { +static const struct snd_bebob_rate_spec usual_rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate, }; -static struct snd_bebob_meter_spec fw410_meter_spec = { +static const struct snd_bebob_meter_spec fw410_meter_spec = { .num = ARRAY_SIZE(fw410_meter_labels), .labels = fw410_meter_labels, .get = &normal_meter_get }; -struct snd_bebob_spec maudio_fw410_spec = { +const struct snd_bebob_spec maudio_fw410_spec = { .clock = NULL, .rate = &usual_rate_spec, .meter = &fw410_meter_spec }; /* Firewire Audiophile specification */ -static struct snd_bebob_meter_spec audiophile_meter_spec = { +static const struct snd_bebob_meter_spec audiophile_meter_spec = { .num = ARRAY_SIZE(audiophile_meter_labels), .labels = audiophile_meter_labels, .get = &normal_meter_get }; -struct snd_bebob_spec maudio_audiophile_spec = { +const struct snd_bebob_spec maudio_audiophile_spec = { .clock = NULL, .rate = &usual_rate_spec, .meter = &audiophile_meter_spec }; /* Firewire Solo specification */ -static struct snd_bebob_meter_spec solo_meter_spec = { +static const struct snd_bebob_meter_spec solo_meter_spec = { .num = ARRAY_SIZE(solo_meter_labels), .labels = solo_meter_labels, .get = &normal_meter_get }; -struct snd_bebob_spec maudio_solo_spec = { +const struct snd_bebob_spec maudio_solo_spec = { .clock = NULL, .rate = &usual_rate_spec, .meter = &solo_meter_spec }; /* Ozonic specification */ -static struct snd_bebob_meter_spec ozonic_meter_spec = { +static const struct snd_bebob_meter_spec ozonic_meter_spec = { .num = ARRAY_SIZE(ozonic_meter_labels), .labels = ozonic_meter_labels, .get = &normal_meter_get }; -struct snd_bebob_spec maudio_ozonic_spec = { +const struct snd_bebob_spec maudio_ozonic_spec = { .clock = NULL, .rate = &usual_rate_spec, .meter = &ozonic_meter_spec }; /* NRV10 specification */ -static struct snd_bebob_meter_spec nrv10_meter_spec = { +static const struct snd_bebob_meter_spec nrv10_meter_spec = { .num = ARRAY_SIZE(nrv10_meter_labels), .labels = nrv10_meter_labels, .get = &normal_meter_get }; -struct snd_bebob_spec maudio_nrv10_spec = { +const struct snd_bebob_spec maudio_nrv10_spec = { .clock = NULL, .rate = &usual_rate_spec, .meter = &nrv10_meter_spec diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 2fdc1f10a2c7..ef224d6f5c24 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -155,7 +155,7 @@ static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - struct snd_bebob_rate_spec *spec = bebob->spec->rate; + const struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; enum snd_bebob_clock_type src; int err; diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c index 301cc6a93945..ec24f96794f5 100644 --- a/sound/firewire/bebob/bebob_proc.c +++ b/sound/firewire/bebob/bebob_proc.c @@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_bebob *bebob = entry->private_data; - struct snd_bebob_meter_spec *spec = bebob->spec->meter; + const struct snd_bebob_meter_spec *spec = bebob->spec->meter; u32 *buf; unsigned int i, c, channels, size; @@ -138,8 +138,8 @@ proc_read_clock(struct snd_info_entry *entry, "SYT-Match", }; struct snd_bebob *bebob = entry->private_data; - struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; - struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; + const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; + const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; enum snd_bebob_clock_type src; unsigned int rate; diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index a2baa478d4ba..926e5dcbb66a 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -119,7 +119,7 @@ end: int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, enum snd_bebob_clock_type *src) { - struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; + const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7]; unsigned int id; enum avc_bridgeco_plug_type type; @@ -580,7 +580,7 @@ end: int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) { - struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; + const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; struct amdtp_stream *master, *slave; enum cip_flags sync_mode; unsigned int curr_rate; @@ -967,7 +967,7 @@ end: int snd_bebob_stream_discover(struct snd_bebob *bebob) { - struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; + const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES]; enum avc_bridgeco_plug_type type; unsigned int i; diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c index 9242e33d2cf1..c38358b82ada 100644 --- a/sound/firewire/bebob/bebob_terratec.c +++ b/sound/firewire/bebob/bebob_terratec.c @@ -55,30 +55,30 @@ phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id) return 0; } -static struct snd_bebob_rate_spec phase_series_rate_spec = { +static const struct snd_bebob_rate_spec phase_series_rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate, }; /* PHASE 88 Rack FW */ -static struct snd_bebob_clock_spec phase88_rack_clk = { +static const struct snd_bebob_clock_spec phase88_rack_clk = { .num = ARRAY_SIZE(phase88_rack_clk_src_types), .types = phase88_rack_clk_src_types, .get = &phase88_rack_clk_src_get, }; -struct snd_bebob_spec phase88_rack_spec = { +const struct snd_bebob_spec phase88_rack_spec = { .clock = &phase88_rack_clk, .rate = &phase_series_rate_spec, .meter = NULL }; /* 'PHASE 24 FW' and 'PHASE X24 FW' */ -static struct snd_bebob_clock_spec phase24_series_clk = { +static const struct snd_bebob_clock_spec phase24_series_clk = { .num = ARRAY_SIZE(phase24_series_clk_src_types), .types = phase24_series_clk_src_types, .get = &phase24_series_clk_src_get, }; -struct snd_bebob_spec phase24_series_spec = { +const struct snd_bebob_spec phase24_series_spec = { .clock = &phase24_series_clk, .rate = &phase_series_rate_spec, .meter = NULL diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c index 58101702410b..90d4404f77ce 100644 --- a/sound/firewire/bebob/bebob_yamaha.c +++ b/sound/firewire/bebob/bebob_yamaha.c @@ -46,16 +46,16 @@ clk_src_get(struct snd_bebob *bebob, unsigned int *id) return 0; } -static struct snd_bebob_clock_spec clock_spec = { +static const struct snd_bebob_clock_spec clock_spec = { .num = ARRAY_SIZE(clk_src_types), .types = clk_src_types, .get = &clk_src_get, }; -static struct snd_bebob_rate_spec rate_spec = { +static const struct snd_bebob_rate_spec rate_spec = { .get = &snd_bebob_stream_get_rate, .set = &snd_bebob_stream_set_rate, }; -struct snd_bebob_spec yamaha_go_spec = { +const struct snd_bebob_spec yamaha_go_spec = { .clock = &clock_spec, .rate = &rate_spec, .meter = NULL -- cgit v1.2.3-59-g8ed1b From 2a7e1713cd652e7d2fdb54057fb6b2508cab15dc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 22:33:50 +0900 Subject: ALSA: firewire-lib: continue packet processing at detecting wrong CIP headers In firewire-lib, isochronous packet streaming is stopped when detecting wrong value for FMT field of CIP headers. Although this is appropriate to IEC 61883-1 and 6, some BeBoB based devices with vendors' customization use invalid value to FMT field of CIP headers in the beginning of streaming. $ journalctl snd-bebob fw1.0: Detect unexpected protocol: 01000000 8000ffff I got this log with M-Audio FireWire 1814. In this line, the value of FMT field is 0x00, while it should be 0x10 in usual AMDTP. Except for the beginning, these devices continue to transfer packets with valid value for FMT field, except for the beginning. Therefore, in this case, firewire-lib should continue to process packets. The former implementation of firewire-lib performs it. This commit loosens the handling of wrong value, to continue packet processing in the case. Fixes: 414ba022a528 ('ALSA: firewire-lib: add support arbitrary value for fmt/fdf fields in CIP header') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-stream.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index fa10b58a0957..ed2902609a4c 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -468,10 +468,12 @@ static int handle_in_packet(struct amdtp_stream *s, /* Check valid protocol or not. */ fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; if (fmt != s->fmt) { - dev_err(&s->unit->device, - "Detect unexpected protocol: %08x %08x\n", - cip_header[0], cip_header[1]); - return -EIO; + dev_info_ratelimited(&s->unit->device, + "Detect unexpected protocol: %08x %08x\n", + cip_header[0], cip_header[1]); + *data_blocks = 0; + pcm_frames = 0; + goto end; } /* Calculate data blocks */ -- cgit v1.2.3-59-g8ed1b From ac77423609aa499265a0ab75c497661bc7157f37 Mon Sep 17 00:00:00 2001 From: "Keith A. Milner" Date: Sun, 11 Oct 2015 15:19:48 +0100 Subject: ALSA: usb-audio: Allow any MIDI endpoint to drive use of interrupt transfer on newer Roland devices This patch enables interrupt transfer mode for MIDI ports on newer Boss/Roland devices such as the GT-100/001 which support interrupt transfer on both IN and OUT MIDI endpoints. Previously this wasn't being enabled for these devices as the code was specifically looking for the scenario where the IN endpoint supported interrupt transfer and the OUT endpoint was bulk transfer. Newer devices support interrupt transfer for both endpoints. This has been tested on Boss devices GT-001, BR-80 and JS-8 and Roland VS-20. It would benefit from some regresison testing with other devices if possible. Signed-off-by: Keith A. Milner Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 417ebb11cf48..7661616f3636 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1903,11 +1903,14 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi) hostif = &intf->altsetting[1]; intfd = get_iface_desc(hostif); + /* If either or both of the endpoints support interrupt transfer, + * then use the alternate setting + */ if (intfd->bNumEndpoints != 2 || - (get_endpoint(hostif, 0)->bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK || - (get_endpoint(hostif, 1)->bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + !((get_endpoint(hostif, 0)->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT || + (get_endpoint(hostif, 1)->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) return; dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n", -- cgit v1.2.3-59-g8ed1b From 17385a386ccae1b5a9a135c43493652b4dd82f61 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 12:30:14 +0900 Subject: ALSA: firewire-digi00x: use in-kernel representation for the type of 8 bits Original code for 'DoubleOhThree' encoding was written with '__u8' type, while the type is usually used to export something to userspace. This commit replaces the type with 'u8'. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/amdtp-dot.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index e6731d33c480..c12ce4d0821a 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -21,8 +21,8 @@ * Zammit in 2012, with reverse-engineering for Digi 003 Rack. */ struct dot_state { - __u8 carry; - __u8 idx; + u8 carry; + u8 idx; unsigned int off; }; @@ -47,25 +47,25 @@ struct amdtp_dot { #define BYTE_PER_SAMPLE (4) #define MAGIC_DOT_BYTE (2) #define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE) -static const __u8 dot_scrt(const __u8 idx, const unsigned int off) +static const u8 dot_scrt(const u8 idx, const unsigned int off) { /* * the length of the added pattern only depends on the lower nibble * of the last non-zero data */ - static const __u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14, - 12, 10, 8, 6, 4, 2, 0}; + static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14, + 12, 10, 8, 6, 4, 2, 0}; /* * the lower nibble of the salt. Interleaved sequence. * this is walked backwards according to len[] */ - static const __u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4, - 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf}; + static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4, + 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf}; /* circular list for the salt's hi nibble. */ - static const __u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4, - 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa}; + static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4, + 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa}; /* * start offset for upper nibble mapping. @@ -73,12 +73,12 @@ static const __u8 dot_scrt(const __u8 idx, const unsigned int off) * hir[] is not used and - coincidentally - the salt's hi nibble is * 0x09 regardless of the offset. */ - static const __u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4, - 3, 0x00, 14, 13, 8, 9, 10, 2}; + static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4, + 3, 0x00, 14, 13, 8, 9, 10, 2}; - const __u8 ln = idx & 0xf; - const __u8 hn = (idx >> 4) & 0xf; - const __u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15]; + const u8 ln = idx & 0xf; + const u8 hn = (idx >> 4) & 0xf; + const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15]; if (len[ln] < off) return 0x00; @@ -88,7 +88,7 @@ static const __u8 dot_scrt(const __u8 idx, const unsigned int off) static void dot_encode_step(struct dot_state *state, __be32 *const buffer) { - __u8 * const data = (__u8 *) buffer; + u8 * const data = (u8 *) buffer; if (data[MAGIC_DOT_BYTE] != 0x00) { state->off = 0; -- cgit v1.2.3-59-g8ed1b From 9dc5d31cdceb5f1ee65edc8a41a5bc7c92d85dd5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 12:30:15 +0900 Subject: ALSA: firewire-digi00x: handle MIDI messages in isochronous packets In Digi 002/003 protocol, MIDI messages are transferred in the last data channel of data blocks. Although this data channel has a label of 0x80, it's not fully MIDI conformant data channel especially because the Counter field always zero independently of included MIDI bytes. The 4th byte of the data channel in LSB tells the number of included MIDI bytes. This byte also includes the number of MIDI port. Therefore, the data format in this data channel is: * 1st: 0x80 as label * 2nd: MIDI bytes * 3rd: 0 or MIDI bytes * 4th: the number of MIDI byte and the number of MIDI port This commit adds support of MIDI messages in data block processing layer. Like AM824 data format, this data channel has a capability to transfer more MIDI messages than the capability of phisical MIDI bus. Therefore, a throttle for data rate is required to prevent devices' internal buffer to overflow. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/amdtp-dot.c | 120 ++++++++++++++++++++++++++++++-- sound/firewire/digi00x/digi00x-stream.c | 4 +- sound/firewire/digi00x/digi00x.h | 9 ++- 3 files changed, 125 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index c12ce4d0821a..b02a5e8cad44 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -16,6 +16,18 @@ /* 'Clock-based rate control mode' is just supported. */ #define AMDTP_FDF_AM824 0x00 +/* + * Nominally 3125 bytes/second, but the MIDI port's clock might be + * 1% too slow, and the bus clock 100 ppm too fast. + */ +#define MIDI_BYTES_PER_SECOND 3093 + +/* + * Several devices look only at the first eight data blocks. + * In any case, this is more than enough for the MIDI data rate. + */ +#define MAX_MIDI_RX_BLOCKS 8 + /* * The double-oh-three algorithm was discovered by Robin Gareus and Damien * Zammit in 2012, with reverse-engineering for Digi 003 Rack. @@ -31,6 +43,10 @@ struct amdtp_dot { struct dot_state state; unsigned int midi_ports; + /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */ + struct snd_rawmidi_substream *midi[2]; + int midi_fifo_used[2]; + int midi_fifo_limit; void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -99,7 +115,7 @@ static void dot_encode_step(struct dot_state *state, __be32 *const buffer) } int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int pcm_channels, unsigned int midi_ports) + unsigned int pcm_channels) { struct amdtp_dot *p = s->protocol; int err; @@ -118,7 +134,19 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, s->fdf = AMDTP_FDF_AM824 | s->sfc; p->pcm_channels = pcm_channels; - p->midi_ports = midi_ports; + + if (s->direction == AMDTP_IN_STREAM) + p->midi_ports = DOT_MIDI_IN_PORTS; + else + p->midi_ports = DOT_MIDI_OUT_PORTS; + + /* + * We do not know the actual MIDI FIFO size of most devices. Just + * assume two bytes, i.e., one byte can be received over the bus while + * the previous one is transmitted over MIDI. + * (The value here is adjusted for midi_ratelimit_per_packet().) + */ + p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; return 0; } @@ -216,6 +244,81 @@ static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, } } +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_dot *p = s->protocol; + int used; + + used = p->midi_fifo_used[port]; + if (used == 0) + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + p->midi_fifo_used[port] = used; + + return used < p->midi_fifo_limit; +} + +static inline void midi_use_bytes(struct amdtp_stream *s, + unsigned int port, unsigned int count) +{ + struct amdtp_dot *p = s->protocol; + + p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count; +} + +static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < data_blocks; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[0]; + + len = 0; + if (port < p->midi_ports && + midi_ratelimit_per_packet(s, port) && + p->midi[port] != NULL) + len = snd_rawmidi_transmit(p->midi[port], b + 1, 2); + + if (len > 0) { + b[3] = (0x10 << port) | len; + midi_use_bytes(s, port, len); + } else { + b[1] = 0; + b[2] = 0; + b[3] = 0; + } + b[0] = 0x80; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int f, port, len; + u8 *b; + + for (f = 0; f < data_blocks; f++) { + b = (u8 *)&buffer[0]; + port = b[3] >> 4; + len = b[3] & 0x0f; + + if (port < p->midi_ports && p->midi[port] && len > 0) + snd_rawmidi_receive(p->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } +} + int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime) { @@ -256,6 +359,15 @@ void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) } } +void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi) +{ + struct amdtp_dot *p = s->protocol; + + if (port < p->midi_ports) + ACCESS_ONCE(p->midi[port]) = midi; +} + static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks, @@ -273,7 +385,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, pcm_frames = 0; } - /* A place holder for MIDI processing. */ + read_midi_messages(s, buffer, data_blocks); return pcm_frames; } @@ -296,7 +408,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, pcm_frames = 0; } - /* A place holder for MIDI processing. */ + write_midi_messages(s, buffer, data_blocks); return pcm_frames; } diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 8aac31be3132..e9be162fc895 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -199,7 +199,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) /* Keep resources for out-stream. */ err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate, - snd_dg00x_stream_pcm_channels[i], 0); + snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; err = fw_iso_resources_allocate(&dg00x->rx_resources, @@ -210,7 +210,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) /* Keep resources for in-stream. */ err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate, - snd_dg00x_stream_pcm_channels[i], 0); + snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; err = fw_iso_resources_allocate(&dg00x->tx_resources, diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index b960c868b59b..88df67be5477 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "../lib.h" #include "../iso-resources.h" @@ -103,15 +104,19 @@ enum snd_dg00x_optical_mode { SND_DG00X_OPT_IFACE_MODE_COUNT, }; +#define DOT_MIDI_IN_PORTS 1 +#define DOT_MIDI_OUT_PORTS 2 + int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir); int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); + unsigned int pcm_channels); void amdtp_dot_reset(struct amdtp_stream *s); int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); +void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi); int snd_dg00x_transaction_register(struct snd_dg00x *dg00x); int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x); -- cgit v1.2.3-59-g8ed1b From 9fbfd38b204a3f04272183c0258a3ded03a5aae8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 12:30:16 +0900 Subject: ALSA: firewire-digi00x: add support for MIDI ports corresponding to isochronous packet streaming This commit adds MIDI functionality to capture/playback MIDI messages from/to physical MIDI ports. These messages are transferred in isochronous packets. When no substreams request AMDTP streams to run, this driver starts the streams at current sampling rate. When other substreams start at different sampling rate, the streams are stopped temporarily, then start again at requested sampling rate. This operation can generate missing MIDI bytes, thus it's preferable to start PCM substreams at favorite sampling rate in advance. Digi 002/003 console also has a set of MIDI port for physical controls. These ports are added in later commits. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/digi00x-midi.c | 131 ++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x-stream.c | 2 + sound/firewire/digi00x/digi00x.c | 4 + sound/firewire/digi00x/digi00x.h | 2 + 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/digi00x/digi00x-midi.c (limited to 'sound') diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 28e3d137ef57..1123e68c8b28 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,4 +1,4 @@ snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ digi00x-pcm.o digi00x-hwdep.o \ - digi00x-transaction.o digi00x.o + digi00x-transaction.o digi00x-midi.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c new file mode 100644 index 000000000000..4e258ac8438b --- /dev/null +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -0,0 +1,131 @@ +/* + * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "digi00x.h" + +static int midi_open(struct snd_rawmidi_substream *substream) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + int err; + + err = snd_dg00x_stream_lock_try(dg00x); + if (err < 0) + return err; + + mutex_lock(&dg00x->mutex); + dg00x->substreams_counter++; + err = snd_dg00x_stream_start_duplex(dg00x, 0); + mutex_unlock(&dg00x->mutex); + if (err < 0) + snd_dg00x_stream_lock_release(dg00x); + + return err; +} + +static int midi_close(struct snd_rawmidi_substream *substream) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + + mutex_lock(&dg00x->mutex); + dg00x->substreams_counter--; + snd_dg00x_stream_stop_duplex(dg00x); + mutex_unlock(&dg00x->mutex); + + snd_dg00x_stream_lock_release(dg00x); + return 0; +} + +static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dg00x *dg00x = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + + if (up) + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number, substrm); + else + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dg00x->lock, flags); +} + +static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dg00x *dg00x = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + + if (up) + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number, substrm); + else + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dg00x->lock, flags); +} + +static struct snd_rawmidi_ops midi_capture_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_capture_trigger, +}; + +static struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_playback_trigger, +}; + +static void set_midi_substream_names(struct snd_dg00x *dg00x, + struct snd_rawmidi_str *str) +{ + struct snd_rawmidi_substream *subs; + + list_for_each_entry(subs, &str->substreams, list) { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + dg00x->card->shortname, subs->number + 1); + } +} + +int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) +{ + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *str; + int err; + + err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, + DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", dg00x->card->shortname); + rmidi->private_data = dg00x; + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &midi_capture_ops); + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + set_midi_substream_names(dg00x, str); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &midi_playback_ops); + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + set_midi_substream_names(dg00x, str); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index e9be162fc895..4d3b4ebbdd49 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -285,6 +285,8 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate) err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); if (err < 0) goto error; + if (rate == 0) + rate = curr_rate; if (curr_rate != rate || amdtp_streaming_error(&dg00x->tx_stream) || amdtp_streaming_error(&dg00x->rx_stream)) { diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 34937a26c198..bbe3be7fea9b 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -90,6 +90,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_dg00x_create_midi_devices(dg00x); + if (err < 0) + goto error; + err = snd_dg00x_create_hwdep_device(dg00x); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 88df67be5477..89494d037da3 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -147,5 +147,7 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x); int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x); +int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x); + int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x); #endif -- cgit v1.2.3-59-g8ed1b From 3646a54acda65afdd76737d40c0994b991742e01 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 12:30:17 +0900 Subject: ALSA: firewire-digi00x: add support of asynchronous transaction for incoming MIDI messages from physical controls Digi 00x series has two types of model; rack and console. The console models have physical controls. The model can transmit control messages. These control messages are transferred by asynchronous transactions to registered address. This commit supports the asynchronous transaction. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x-transaction.c | 41 ++++++++++++++++++++++++++-- sound/firewire/digi00x/digi00x.h | 3 ++ 2 files changed, 41 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c index 49372901a1e1..8bf5ed5e5f3e 100644 --- a/sound/firewire/digi00x/digi00x-transaction.c +++ b/sound/firewire/digi00x/digi00x-transaction.c @@ -9,6 +9,28 @@ #include #include "digi00x.h" +static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf, + unsigned int length) +{ + struct snd_rawmidi_substream *substream; + unsigned int i; + unsigned int len; + u8 *b; + + substream = ACCESS_ONCE(dg00x->in_control); + if (substream == NULL) + return; + + length /= 4; + + for (i = 0; i < length; i++) { + b = (u8 *)&buf[i]; + len = b[3] & 0xf; + if (len > 0) + snd_rawmidi_receive(dg00x->in_control, b + 1, len); + } +} + static void handle_unknown_message(struct snd_dg00x *dg00x, unsigned long long offset, __be32 *buf) { @@ -31,6 +53,8 @@ static void handle_message(struct fw_card *card, struct fw_request *request, if (offset == dg00x->async_handler.offset) handle_unknown_message(dg00x, offset, buf); + else if (offset == dg00x->async_handler.offset + 4) + handle_midi_control(dg00x, buf, length); fw_send_response(card, request, RCODE_COMPLETE); } @@ -39,14 +63,25 @@ int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x) { struct fw_device *device = fw_parent_device(dg00x->unit); __be32 data[2]; + int err; /* Unknown. 4bytes. */ data[0] = cpu_to_be32((device->card->node_id << 16) | (dg00x->async_handler.offset >> 32)); data[1] = cpu_to_be32(dg00x->async_handler.offset); + err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, + &data, sizeof(data), 0); + if (err < 0) + return err; + + /* Asynchronous transactions for MIDI control message. */ + data[0] = cpu_to_be32((device->card->node_id << 16) | + (dg00x->async_handler.offset >> 32)); + data[1] = cpu_to_be32(dg00x->async_handler.offset + 4); return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, - DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, - &data, sizeof(data), 0); + DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR, + &data, sizeof(data), 0); } int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) @@ -57,7 +92,7 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) }; int err; - dg00x->async_handler.length = 4; + dg00x->async_handler.length = 12; dg00x->async_handler.address_callback = handle_message; dg00x->async_handler.callback_data = dg00x; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 89494d037da3..630f6aabb909 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -53,6 +53,9 @@ struct snd_dg00x { /* For asynchronous messages. */ struct fw_address_handler async_handler; u32 msg; + + /* For asynchronous MIDI controls. */ + struct snd_rawmidi_substream *in_control; }; #define DG00X_ADDR_BASE 0xffffe0000000ull -- cgit v1.2.3-59-g8ed1b From b47f525f760f29c4689a7d9bf768c28fcaac2281 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 12:30:18 +0900 Subject: ALSA: firewire-digi00x: add support of asynchronous transaction for outgoing MIDI messages to physical controls In previous commit, asynchronous transaction for incoming MIDI messages from physical controls is supported. The physical controls may be controlled by receiving MIDI messages at a certain address. This commit supports asynchronous transaction for this purpose. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x-transaction.c | 29 ++++++++++++++++++++++++---- sound/firewire/digi00x/digi00x.h | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c index 8bf5ed5e5f3e..554324d8c602 100644 --- a/sound/firewire/digi00x/digi00x-transaction.c +++ b/sound/firewire/digi00x/digi00x-transaction.c @@ -9,6 +9,18 @@ #include #include "digi00x.h" +static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf) +{ + int bytes; + + buf[0] = 0x80; + bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2); + if (bytes >= 0) + buf[3] = 0xc0 | bytes; + + return bytes; +} + static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf, unsigned int length) { @@ -102,15 +114,24 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) return err; err = snd_dg00x_transaction_reregister(dg00x); - if (err < 0) { - fw_core_remove_address_handler(&dg00x->async_handler); - dg00x->async_handler.address_callback = NULL; - } + if (err < 0) + goto error; + + err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit, + DG00X_ADDR_BASE + DG00X_OFFSET_MMC, + 4, fill_midi_message); + if (err < 0) + goto error; return err; +error: + fw_core_remove_address_handler(&dg00x->async_handler); + dg00x->async_handler.address_callback = NULL; + return err; } void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) { + snd_fw_async_midi_port_destroy(&dg00x->out_control); fw_core_remove_address_handler(&dg00x->async_handler); } diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 630f6aabb909..907e73993677 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -56,6 +56,7 @@ struct snd_dg00x { /* For asynchronous MIDI controls. */ struct snd_rawmidi_substream *in_control; + struct snd_fw_async_midi_port out_control; }; #define DG00X_ADDR_BASE 0xffffe0000000ull -- cgit v1.2.3-59-g8ed1b From e8bd577ae6df59160cd6fd82b8d3562bbbdd3c1d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 11 Oct 2015 12:30:19 +0900 Subject: ALSA: firewire-digi00x: add support for MIDI ports for physical controls In former commits, asynchronous transactions are supported for physical controls. This commit adds a pair of MIDI ports for them. This driver already adds diferrent number of ALSA MIDI ports for physical MIDI ports, and the number of in/out ports are different. As seeing as 'amidi' program in alsa-utils package, a pair of in/out MIDI ports is expected with the same name. Therefore, this commit adds a pair of new ports to the first. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x-midi.c | 61 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 4e258ac8438b..9aa8b4623108 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -13,6 +13,10 @@ static int midi_open(struct snd_rawmidi_substream *substream) struct snd_dg00x *dg00x = substream->rmidi->private_data; int err; + /* This port is for asynchronous transaction. */ + if (substream->number == 0) + return 0; + err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) return err; @@ -31,6 +35,10 @@ static int midi_close(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data; + /* This port is for asynchronous transaction. */ + if (substream->number == 0) + return 0; + mutex_lock(&dg00x->mutex); dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); @@ -47,12 +55,20 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dg00x->lock, flags); - if (up) - amdtp_dot_midi_trigger(&dg00x->tx_stream, - substrm->number, substrm); - else - amdtp_dot_midi_trigger(&dg00x->tx_stream, - substrm->number, NULL); + /* This port is for asynchronous transaction. */ + if (substrm->number == 0) { + if (up) + dg00x->in_control = substrm; + else + dg00x->in_control = NULL; + } else { + if (up) + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number - 1, substrm); + else + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number - 1, NULL); + } spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -64,12 +80,19 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dg00x->lock, flags); - if (up) - amdtp_dot_midi_trigger(&dg00x->rx_stream, - substrm->number, substrm); - else - amdtp_dot_midi_trigger(&dg00x->rx_stream, - substrm->number, NULL); + /* This port is for asynchronous transaction. */ + if (substrm->number == 0) { + if (up) + snd_fw_async_midi_port_run(&dg00x->out_control, + substrm); + } else { + if (up) + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number - 1, substrm); + else + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number - 1, NULL); + } spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -92,9 +115,15 @@ static void set_midi_substream_names(struct snd_dg00x *dg00x, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - dg00x->card->shortname, subs->number + 1); + if (subs->number > 0) + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + dg00x->card->shortname, subs->number); + else + /* This port is for asynchronous transaction. */ + snprintf(subs->name, sizeof(subs->name), + "%s control", + dg00x->card->shortname); } } @@ -105,7 +134,7 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) int err; err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, - DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi); + DOT_MIDI_OUT_PORTS + 1, DOT_MIDI_IN_PORTS + 1, &rmidi); if (err < 0) return err; -- cgit v1.2.3-59-g8ed1b From 107cc0129a685e88d09af88b8a371caec5c51ff0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Oct 2015 19:10:21 +0900 Subject: ALSA: firewire-tascam: add support for incoming MIDI messages by asynchronous transaction TASCAM FireWire series use asynchronous transaction to transfer MIDI messages. The transaction is sent to a registered address. This commit supports the incoming MIDI messages. The messages in the transaction include some quirks: * Two quadlets are used for one MIDI message and one timestamp. * Usually, the first byte of the first quadlet includes MIDI port and MSB 4 bit of MIDI status. For system exclusive message, the first byte includes MIDI port and 0x04, or 0x07 in the end of the message. * The rest of the first quadlet includes MIDI bytes up to 3. * Several set of MIDI messages and timestamp can be transferred in one block transaction, up to 8 sets. I note that TASCAM FireWire series ignores ID bytes of system exclusive message. When receiving system exclusive messages with ID bytes on physical MIDI bus, the series transfers the messages without ID bytes on IEEE 1394 bus, and vice versa. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/Makefile | 3 +- sound/firewire/tascam/tascam-transaction.c | 186 +++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.c | 7 ++ sound/firewire/tascam/tascam.h | 18 +++ 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/tascam/tascam-transaction.c (limited to 'sound') diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index 6beefc2ae8b0..a1f9fe793c81 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,3 +1,4 @@ snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \ - tascam-pcm.o tascam-hwdep.o tascam.o + tascam-pcm.o tascam-hwdep.o tascam-transaction.o \ + tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c new file mode 100644 index 000000000000..853438d93950 --- /dev/null +++ b/sound/firewire/tascam/tascam-transaction.c @@ -0,0 +1,186 @@ +/* + * tascam-transaction.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "tascam.h" + +/* + * When return minus value, given argument is not MIDI status. + * When return 0, given argument is a beginning of system exclusive. + * When return the others, given argument is MIDI data. + */ +static inline int calculate_message_bytes(u8 status) +{ + switch (status) { + case 0xf6: /* Tune request. */ + case 0xf8: /* Timing clock. */ + case 0xfa: /* Start. */ + case 0xfb: /* Continue. */ + case 0xfc: /* Stop. */ + case 0xfe: /* Active sensing. */ + case 0xff: /* System reset. */ + return 1; + case 0xf1: /* MIDI time code quarter frame. */ + case 0xf3: /* Song select. */ + return 2; + case 0xf2: /* Song position pointer. */ + return 3; + case 0xf0: /* Exclusive. */ + return 0; + case 0xf7: /* End of exclusive. */ + break; + case 0xf4: /* Undefined. */ + case 0xf5: /* Undefined. */ + case 0xf9: /* Undefined. */ + case 0xfd: /* Undefined. */ + break; + default: + switch (status & 0xf0) { + case 0x80: /* Note on. */ + case 0x90: /* Note off. */ + case 0xa0: /* Polyphonic key pressure. */ + case 0xb0: /* Control change and Mode change. */ + case 0xe0: /* Pitch bend change. */ + return 3; + case 0xc0: /* Program change. */ + case 0xd0: /* Channel pressure. */ + return 2; + default: + break; + } + break; + } + + return -EINVAL; +} + +static void handle_midi_tx(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct snd_tscm *tscm = callback_data; + u32 *buf = (u32 *)data; + unsigned int messages; + unsigned int i; + unsigned int port; + struct snd_rawmidi_substream *substream; + u8 *b; + int bytes; + + if (offset != tscm->async_handler.offset) + goto end; + + messages = length / 8; + for (i = 0; i < messages; i++) { + b = (u8 *)(buf + i * 2); + + port = b[0] >> 4; + /* TODO: support virtual MIDI ports. */ + if (port > tscm->spec->midi_capture_ports) + goto end; + + /* Assume the message length. */ + bytes = calculate_message_bytes(b[1]); + /* On MIDI data or exclusives. */ + if (bytes <= 0) { + /* Seek the end of exclusives. */ + for (bytes = 1; bytes < 4; bytes++) { + if (b[bytes] == 0xf7) + break; + } + if (bytes == 4) + bytes = 3; + } + + substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]); + if (substream != NULL) + snd_rawmidi_receive(substream, b + 1, bytes); + } +end: + fw_send_response(card, request, RCODE_COMPLETE); +} + +int snd_tscm_transaction_register(struct snd_tscm *tscm) +{ + static const struct fw_address_region resp_register_region = { + .start = 0xffffe0000000ull, + .end = 0xffffe000ffffull, + }; + int err; + + /* + * Usually, two quadlets are transferred by one transaction. The first + * quadlet has MIDI messages, the rest includes timestamp. + * Sometimes, 8 set of the data is transferred by a block transaction. + */ + tscm->async_handler.length = 8 * 8; + tscm->async_handler.address_callback = handle_midi_tx; + tscm->async_handler.callback_data = tscm; + + err = fw_core_add_address_handler(&tscm->async_handler, + &resp_register_region); + if (err < 0) + return err; + + err = snd_tscm_transaction_reregister(tscm); + if (err < 0) + fw_core_remove_address_handler(&tscm->async_handler); + + return err; +} + +/* At bus reset, these registers are cleared. */ +int snd_tscm_transaction_reregister(struct snd_tscm *tscm) +{ + struct fw_device *device = fw_parent_device(tscm->unit); + __be32 reg; + int err; + + /* Register messaging address. Block transaction is not allowed. */ + reg = cpu_to_be32((device->card->node_id << 16) | + (tscm->async_handler.offset >> 32)); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + reg = cpu_to_be32(tscm->async_handler.offset); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* Turn on messaging. */ + reg = cpu_to_be32(0x00000001); + return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, + ®, sizeof(reg), 0); +} + +void snd_tscm_transaction_unregister(struct snd_tscm *tscm) +{ + __be32 reg; + + /* Turn off messaging. */ + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, + ®, sizeof(reg), 0); + + /* Unregister the address. */ + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI, + ®, sizeof(reg), 0); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO, + ®, sizeof(reg), 0); + + fw_core_remove_address_handler(&tscm->async_handler); +} diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index ee2f498dcce4..de9e8df25569 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -82,6 +82,7 @@ static void tscm_card_free(struct snd_card *card) { struct snd_tscm *tscm = card->private_data; + snd_tscm_transaction_unregister(tscm); snd_tscm_stream_destroy_duplex(tscm); fw_unit_put(tscm->unit); @@ -127,6 +128,10 @@ static int snd_tscm_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_tscm_transaction_register(tscm); + if (err < 0) + goto error; + err = snd_tscm_create_hwdep_device(tscm); if (err < 0) goto error; @@ -147,6 +152,8 @@ static void snd_tscm_update(struct fw_unit *unit) { struct snd_tscm *tscm = dev_get_drvdata(&unit->device); + snd_tscm_transaction_reregister(tscm); + mutex_lock(&tscm->mutex); snd_tscm_stream_update_duplex(tscm); mutex_unlock(&tscm->mutex); diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 75a3b9a81fea..b0e602bb5c8f 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "../lib.h" #include "../amdtp-stream.h" @@ -41,6 +42,9 @@ struct snd_tscm_spec { bool is_controller; }; +#define TSCM_MIDI_IN_PORT_MAX 4 +#define TSCM_MIDI_OUT_PORT_MAX 4 + struct snd_tscm { struct snd_card *card; struct fw_unit *unit; @@ -59,6 +63,10 @@ struct snd_tscm { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; + + /* For MIDI message incoming transactions. */ + struct fw_address_handler async_handler; + struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX]; }; #define TSCM_ADDR_BASE 0xffff00000000ull @@ -81,6 +89,12 @@ struct snd_tscm { #define TSCM_OFFSET_CLOCK_STATUS 0x0228 #define TSCM_OFFSET_SET_OPTION 0x022c +#define TSCM_OFFSET_MIDI_TX_ON 0x0300 +#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304 +#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308 + +#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000 + enum snd_tscm_clock { SND_TSCM_CLOCK_INTERNAL = 0, SND_TSCM_CLOCK_WORD = 1, @@ -108,6 +122,10 @@ void snd_tscm_stream_lock_changed(struct snd_tscm *tscm); int snd_tscm_stream_lock_try(struct snd_tscm *tscm); void snd_tscm_stream_lock_release(struct snd_tscm *tscm); +int snd_tscm_transaction_register(struct snd_tscm *tscm); +int snd_tscm_transaction_reregister(struct snd_tscm *tscm); +void snd_tscm_transaction_unregister(struct snd_tscm *tscm); + void snd_tscm_proc_init(struct snd_tscm *tscm); int snd_tscm_create_pcm_devices(struct snd_tscm *tscm); -- cgit v1.2.3-59-g8ed1b From 3beab0f844fadefe16b6383f6ff7b76147db686b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Oct 2015 19:10:22 +0900 Subject: ALSA: firewire-tascam: add support for outgoing MIDI messages by asynchronous transaction TASCAM FireWire series use asynchronous transaction to receive MIDI messages. The transaction should be sent to a certain address. This commit supports the outgoing MIDI messages. The messages in the transaction includes some quirks: * One MIDI message is transferred in one quadlet transaction, except for system exclusives. * MIDI running status is not allowed, thus transactions always include status byte. * The basic data format is the same as transferring MIDI messages supported in previous commit. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 95 +++++++++++++++++++++++++++++- sound/firewire/tascam/tascam.h | 8 +++ 2 files changed, 102 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 853438d93950..6b74fb5e5fd7 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -58,6 +58,83 @@ static inline int calculate_message_bytes(u8 status) return -EINVAL; } +static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) +{ + struct snd_tscm *tscm = substream->rmidi->private_data; + unsigned int port = substream->number; + unsigned int len; + unsigned int i; + u8 status; + int consume; + + buf[0] = buf[1] = buf[2] = buf[3] = 0x00; + + len = snd_rawmidi_transmit_peek(substream, buf + 1, 3); + if (len == 0) + return 0; + + /* On exclusive message. */ + if (tscm->on_sysex[port]) { + /* Seek the end of exclusives. */ + for (i = 1; i < 4 || i < len; ++i) { + if (buf[i] == 0xf7) { + tscm->on_sysex[port] = false; + break; + } + } + + /* At the end of exclusive message, use label 0x07. */ + if (!tscm->on_sysex[port]) { + len = i; + buf[0] = (port << 4) | 0x07; + /* During exclusive message, use label 0x04. */ + } else if (len == 3) { + buf[0] = (port << 4) | 0x04; + /* We need to fill whole 3 bytes. Go to next change. */ + } else { + len = 0; + } + } else { + /* The beginning of exclusives. */ + if (buf[1] == 0xf0) { + /* Transfer it in next chance in another condition. */ + tscm->on_sysex[port] = true; + return 0; + } else { + /* On running-status. */ + if ((buf[1] & 0x80) != 0x80) + status = tscm->running_status[port]; + else + status = buf[1]; + + /* Calculate consume bytes. */ + consume = calculate_message_bytes(status); + if (consume <= 0) + return 0; + + /* On running-status. */ + if ((buf[1] & 0x80) != 0x80) { + buf[3] = buf[2]; + buf[2] = buf[1]; + buf[1] = tscm->running_status[port]; + consume--; + } else { + tscm->running_status[port] = buf[1]; + } + + /* Confirm length. */ + if (len < consume) + return 0; + if (len > consume) + len = consume; + } + + buf[0] = (port << 4) | (buf[1] >> 4); + } + + return len; +} + static void handle_midi_tx(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, unsigned long long offset, @@ -111,6 +188,7 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm) .start = 0xffffe0000000ull, .end = 0xffffe000ffffull, }; + unsigned int i; int err; /* @@ -129,8 +207,20 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm) err = snd_tscm_transaction_reregister(tscm); if (err < 0) - fw_core_remove_address_handler(&tscm->async_handler); + goto error; + + for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) { + err = snd_fw_async_midi_port_init( + &tscm->out_ports[i], tscm->unit, + TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD, + 4, fill_message); + if (err < 0) + goto error; + } + return err; +error: + fw_core_remove_address_handler(&tscm->async_handler); return err; } @@ -167,6 +257,7 @@ int snd_tscm_transaction_reregister(struct snd_tscm *tscm) void snd_tscm_transaction_unregister(struct snd_tscm *tscm) { __be32 reg; + unsigned int i; /* Turn off messaging. */ reg = cpu_to_be32(0x00000000); @@ -183,4 +274,6 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) ®, sizeof(reg), 0); fw_core_remove_address_handler(&tscm->async_handler); + for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) + snd_fw_async_midi_port_destroy(&tscm->out_ports[i]); } diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index b0e602bb5c8f..c2f0c74ab558 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -67,6 +67,14 @@ struct snd_tscm { /* For MIDI message incoming transactions. */ struct fw_address_handler async_handler; struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX]; + + /* For MIDI message outgoing transactions. */ + struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX]; + u8 running_status[TSCM_MIDI_OUT_PORT_MAX]; + bool on_sysex[TSCM_MIDI_OUT_PORT_MAX]; + + /* For control messages. */ + struct snd_firewire_tascam_status *status; }; #define TSCM_ADDR_BASE 0xffff00000000ull -- cgit v1.2.3-59-g8ed1b From 0db18e7eec40a4331214185b37b0440856856775 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Oct 2015 19:10:23 +0900 Subject: ALSA: firewire-tascam: add support for MIDI functionality In former commits, this driver got functionalities to transfer/receive MIDI messages to/from TASCAM FireWire series. This commit adds some ALSA MIDI ports to enable userspace applications to use the functionalities. I note that this commit doesn't support virtual MIDI ports which console models support. A physical controls can be assigned to a certain MIDI ports including physical and virtual. But the way is not clear. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/Makefile | 2 +- sound/firewire/tascam/tascam-midi.c | 135 ++++++++++++++++++++++++++++++++++++ sound/firewire/tascam/tascam.c | 4 ++ sound/firewire/tascam/tascam.h | 2 + 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/tascam/tascam-midi.c (limited to 'sound') diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index a1f9fe793c81..0fc955d5bd15 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,4 +1,4 @@ snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \ - tascam.o + tascam-midi.o tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c new file mode 100644 index 000000000000..41f842079d9d --- /dev/null +++ b/sound/firewire/tascam/tascam-midi.c @@ -0,0 +1,135 @@ +/* + * tascam-midi.c - a part of driver for TASCAM FireWire series + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "tascam.h" + +static int midi_capture_open(struct snd_rawmidi_substream *substream) +{ + /* Do nothing. */ + return 0; +} + +static int midi_playback_open(struct snd_rawmidi_substream *substream) +{ + struct snd_tscm *tscm = substream->rmidi->private_data; + + /* Initialize internal status. */ + tscm->running_status[substream->number] = 0; + tscm->on_sysex[substream->number] = 0; + return 0; +} + +static int midi_capture_close(struct snd_rawmidi_substream *substream) +{ + /* Do nothing. */ + return 0; +} + +static int midi_playback_close(struct snd_rawmidi_substream *substream) +{ + struct snd_tscm *tscm = substream->rmidi->private_data; + + snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]); + + return 0; +} + +static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_tscm *tscm = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&tscm->lock, flags); + + if (up) + tscm->tx_midi_substreams[substrm->number] = substrm; + else + tscm->tx_midi_substreams[substrm->number] = NULL; + + spin_unlock_irqrestore(&tscm->lock, flags); +} + +static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_tscm *tscm = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&tscm->lock, flags); + + if (up) + snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number], + substrm); + + spin_unlock_irqrestore(&tscm->lock, flags); +} + +static struct snd_rawmidi_ops midi_capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, +}; + +static struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, +}; + +int snd_tscm_create_midi_devices(struct snd_tscm *tscm) +{ + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *stream; + struct snd_rawmidi_substream *subs; + int err; + + err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0, + tscm->spec->midi_playback_ports, + tscm->spec->midi_capture_ports, + &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", tscm->card->shortname); + rmidi->private_data = tscm; + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &midi_capture_ops); + stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + + /* Set port names for MIDI input. */ + list_for_each_entry(subs, &stream->substreams, list) { + /* TODO: support virtual MIDI ports. */ + if (subs->number < tscm->spec->midi_capture_ports) { + /* Hardware MIDI ports. */ + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + tscm->card->shortname, subs->number + 1); + } + } + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &midi_playback_ops); + stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + + /* Set port names for MIDI ourput. */ + list_for_each_entry(subs, &stream->substreams, list) { + if (subs->number < tscm->spec->midi_playback_ports) { + /* Hardware MIDI ports only. */ + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + tscm->card->shortname, subs->number + 1); + } + } + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index de9e8df25569..dc07e3edbf5a 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -132,6 +132,10 @@ static int snd_tscm_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_tscm_create_midi_devices(tscm); + if (err < 0) + goto error; + err = snd_tscm_create_hwdep_device(tscm); if (err < 0) goto error; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index c2f0c74ab558..b86bb7f4f01d 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -138,6 +138,8 @@ void snd_tscm_proc_init(struct snd_tscm *tscm); int snd_tscm_create_pcm_devices(struct snd_tscm *tscm); +int snd_tscm_create_midi_devices(struct snd_tscm *tscm); + int snd_tscm_create_hwdep_device(struct snd_tscm *tscm); #endif -- cgit v1.2.3-59-g8ed1b From e65e2cb99e44704f63b76e4395092b0533bef88b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Oct 2015 19:10:24 +0900 Subject: ALSA: firewire-tascam: Turn on/off FireWire LED TASCAM FireWire series has some LEDs on its surface. These LEDs can be turned on/off by receiving asynchronous transactions to a certain address. One of the LEDs is labels as 'FireWire'. It's better to light it up when this driver starts to work. Besides, the LED for 'FireWire' is turned off at bus reset. This commit implements this idea. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 14 ++++++++++++++ sound/firewire/tascam/tascam.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 6b74fb5e5fd7..1c9a88be55c8 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -252,6 +252,14 @@ int snd_tscm_transaction_reregister(struct snd_tscm *tscm) return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* Turn on FireWire LED. */ + reg = cpu_to_be32(0x0001008e); + return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER, + ®, sizeof(reg), 0); } void snd_tscm_transaction_unregister(struct snd_tscm *tscm) @@ -259,6 +267,12 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) __be32 reg; unsigned int i; + /* Turn off FireWire LED. */ + reg = cpu_to_be32(0x0000008e); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER, + ®, sizeof(reg), 0); + /* Turn off messaging. */ reg = cpu_to_be32(0x00000000); snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index b86bb7f4f01d..2d028d2bd3bd 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -101,6 +101,8 @@ struct snd_tscm { #define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304 #define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308 +#define TSCM_OFFSET_LED_POWER 0x0404 + #define TSCM_OFFSET_MIDI_RX_QUAD 0x4000 enum snd_tscm_clock { -- cgit v1.2.3-59-g8ed1b From 53b3ffee788559fe26d32f21b223bf4bad959477 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 12 Oct 2015 19:10:25 +0900 Subject: ALSA: firewire-tascam: change device probing processing Currently, this driver picks up model name with be32_to_cpu() macro to align characters. This is wrong operation because the result is different depending on CPU endiannness. Additionally, vendor released several versions of firmware for this series. It's not better to assign model-dependent information to device entry according to the version field. This commit fixes these bugs. The name of model is picked up correctly and used to identify model-dependent information. Cc: Stefan Richter Fixes: c0949b278515 ('ALSA: firewire-tascam: add skeleton for TASCAM FireWire series') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam.c | 78 +++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 46 deletions(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index dc07e3edbf5a..c6747a45795b 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -23,16 +23,6 @@ static struct snd_tscm_spec model_specs[] = { .midi_playback_ports = 4, .is_controller = true, }, - { - .name = "FW-1804", - .has_adat = true, - .has_spdif = true, - .pcm_capture_analog_channels = 8, - .pcm_playback_analog_channels = 2, - .midi_capture_ports = 2, - .midi_playback_ports = 4, - .is_controller = false, - }, { .name = "FW-1082", .has_adat = false, @@ -43,34 +33,46 @@ static struct snd_tscm_spec model_specs[] = { .midi_playback_ports = 2, .is_controller = true, }, + /* FW-1804 may be supported. */ }; -static int check_name(struct snd_tscm *tscm) +static int identify_model(struct snd_tscm *tscm) { struct fw_device *fw_dev = fw_parent_device(tscm->unit); - char vendor[8]; + const u32 *config_rom = fw_dev->config_rom; char model[8]; - __u32 data; - - /* Retrieve model name. */ - data = be32_to_cpu(fw_dev->config_rom[28]); - memcpy(model, &data, 4); - data = be32_to_cpu(fw_dev->config_rom[29]); - memcpy(model + 4, &data, 4); - model[7] = '\0'; - - /* Retrieve vendor name. */ - data = be32_to_cpu(fw_dev->config_rom[23]); - memcpy(vendor, &data, 4); - data = be32_to_cpu(fw_dev->config_rom[24]); - memcpy(vendor + 4, &data, 4); - vendor[7] = '\0'; + unsigned int i; + u8 c; + + if (fw_dev->config_rom_length < 30) { + dev_err(&tscm->unit->device, + "Configuration ROM is too short.\n"); + return -ENODEV; + } + + /* Pick up model name from certain addresses. */ + for (i = 0; i < 8; i++) { + c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4)); + if (c == '\0') + break; + model[i] = c; + } + model[i] = '\0'; + + for (i = 0; i < ARRAY_SIZE(model_specs); i++) { + if (strcmp(model, model_specs[i].name) == 0) { + tscm->spec = &model_specs[i]; + break; + } + } + if (tscm->spec == NULL) + return -ENODEV; strcpy(tscm->card->driver, "FW-TASCAM"); strcpy(tscm->card->shortname, model); strcpy(tscm->card->mixername, model); snprintf(tscm->card->longname, sizeof(tscm->card->longname), - "%s %s, GUID %08x%08x at %s, S%d", vendor, model, + "TASCAM %s, GUID %08x%08x at %s, S%d", model, cpu_to_be32(fw_dev->config_rom[3]), cpu_to_be32(fw_dev->config_rom[4]), dev_name(&tscm->unit->device), 100 << fw_dev->max_speed); @@ -108,13 +110,12 @@ static int snd_tscm_probe(struct fw_unit *unit, tscm = card->private_data; tscm->card = card; tscm->unit = fw_unit_get(unit); - tscm->spec = (const struct snd_tscm_spec *)entry->driver_data; mutex_init(&tscm->mutex); spin_lock_init(&tscm->lock); init_waitqueue_head(&tscm->hwdep_wait); - err = check_name(tscm); + err = identify_model(tscm); if (err < 0) goto error; @@ -172,27 +173,12 @@ static void snd_tscm_remove(struct fw_unit *unit) } static const struct ieee1394_device_id snd_tscm_id_table[] = { - /* FW-1082 */ - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = 0x00022e, - .specifier_id = 0x00022e, - .version = 0x800003, - .driver_data = (kernel_ulong_t)&model_specs[2], - }, - /* FW-1884 */ { .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, + IEEE1394_MATCH_SPECIFIER_ID, .vendor_id = 0x00022e, .specifier_id = 0x00022e, - .version = 0x800000, - .driver_data = (kernel_ulong_t)&model_specs[0], }, - /* FW-1804 mey be supported if IDs are clear. */ /* FE-08 requires reverse-engineering because it just has faders. */ {} }; -- cgit v1.2.3-59-g8ed1b From 4272975a3444f0abde1354285c5720d7819b41ed Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sat, 10 Oct 2015 14:17:58 +0900 Subject: ASoC: sh: Fit typo in Kconfig s/SUR/SRU/g Signed-off-by: Masanari Iida Signed-off-by: Mark Brown --- sound/soc/sh/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 07114b0b0dc1..de668b27d123 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -40,7 +40,7 @@ config SND_SOC_RCAR select SND_SIMPLE_CARD select REGMAP_MMIO help - This option enables R-Car SUR/SCU/SSIU/SSI sound support + This option enables R-Car SRU/SCU/SSIU/SSI sound support config SND_SOC_RSRC_CARD tristate "Renesas Sampling Rate Convert Sound Card" -- cgit v1.2.3-59-g8ed1b From a5fe58fd2836987387a6ee8854c529db7f5be650 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 12 Oct 2015 21:34:59 +0800 Subject: ASoC: rt298: set register non-volatile by default It is not necessary to set registers volatile. So, return false for default case of rt298_volatile_register. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 3c2f0f8d6266..ff126a7cee76 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -129,7 +129,7 @@ static bool rt298_volatile_register(struct device *dev, unsigned int reg) case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): return true; default: - return true; + return false; } -- cgit v1.2.3-59-g8ed1b From ab30965d9bfcd04931f9b70d00faa2ea614835a8 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Sun, 11 Oct 2015 20:54:51 +0200 Subject: ALSA: usb-audio: Fix max packet size calculation for USB audio Rounding must take place before multiplication with the frame size, since each packet contains a whole number of frames. We must also properly consider the data interval, as a larger data interval will result in larger packets, which, depending on the sampling frequency, can result in packet sizes that are less than integral multiples of the packet size for a lower data interval. Detailed explanation and rationale: The code before this commit had the following expression on line 613 to calculate the maximum isochronous packet size: maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3)) >> (16 - ep->datainterval); Here, ep->freqmax is the maximum assumed sample frequency, calculated from the nominal sample frequency plus 25%. It is ultimately derived from ep->freqn, which is in the units of frames per packet, from get_usb_full_speed_rate() or usb_high_speed_rate(), as applicable, in Q16.16 format. The expression essentially adds the Q16.16 equivalent of 0.999... (i.e. the largest number less than one) to the sample rate, in order to get a rate whose integer part is rounded up from the fractional value. The multiplication with (frame_bits >> 3) yields the number of bytes in a packet, and the (16 >> ep->datainterval) then converts it from Q16.16 back to an integer, taking into consideration the bDataInterval field of the endpoint descriptor (which describes how often isochronous packets are transmitted relative to the (micro)frame rate (125us or 1ms, for USB high speed and full speed, respectively)). For this discussion we will initially assume a bDataInterval of 0, so the second line of the expression just converts the Q16.16 value to an integer. In order to illustrate the problem, we will set frame_bits 64, which corresponds to a frame size of 8 bytes. The problem here is twofold. First, the rounding operation consists of the addition of 0x0.ffff and subsequent conversion to integer, but as the expression stands, the conversion to integer is done after multiplication with the frame size, rather than before. This results in the resulting maxsize becoming too large. Let's take an example. We have a sample rate of 96 kHz, so our ep->freqn is 0xc0000 (see usb_high_speed_rate()). Add 25% (line 612) and we get 0xf0000. The calculated maxsize is then ((0xf0000 + 0x0ffff) * 8) >> 16 = 127 . However, if we do the number of bytes calculation in a less obscure way it's more apparent what the true corresponding packet size is: we get ceil(96000 * 1.25 / 8000) * 8 = 120, where 1.25 is the 25% from line 612, and the 8000 is the number of isochronous packets per second on a high speed USB connection (125 us microframe interval). This is fixed by performing the complete rounding operation prior to multiplication with the frame rate. The second problem is that when considering the ep->datainterval, this must be done before rounding, in order to take the advantage of the fact that if the number of bytes per packet is not an integer, the resulting rounded-up integer is not necessarily a factor of two when the data interval is increased by the same factor. For instance, assuming a freqency of 41 kHz, the resulting bytes-per-packet value for USB high speed is 41 kHz / 8000 = 5.125, or 0x52000 in Q16.16 format. With a data interval of 1 (ep->datainterval = 0), this means that 6 frames per packet are needed, whereas with a data interval of 2 we need 10.25, i.e. 11 frames needed. Rephrasing the maxsize expression to: maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) * (frame_bits >> 3); for the above 96 kHz example we instead get ((0xf0000 + 0xffff) >> 16) * 8 = 120 which is the correct value. We can also do the calculation with a non-integer sample rate which is when rounding comes into effect: say we have 44.1 kHz (resulting ep->freqn = 0x58333, and resulting ep->freqmax 0x58333 * 1.25 = 0x6e3ff (rounded down)): Original maxsize = ((0x6e3ff + 0xffff) * 8) << 16 = 63 (63.124.. rounded down) True maxsize = ceil(44100 * 1.25 / 8000) * 8 = 7 * 8 = 56 New maxsize = ((0x6e3ff + 0xffff) >> 16) * 8 = 7 * 8 = 56 This is also corroborated by the wMaxPacketSize check on line 616. Assume that wMaxPacketSize = 104, with ep->maxpacksize then having the same value. As 104 < 127, we get maxsize = 104. ep->freqmax is then recalculated to (104 / 8) << 16 = 0xd0000 . Putting that rate into the original maxsize calculation yields a maxsize of ((0xd0000 + 0xffff) * 8) >> 16 = 111 (with decimals 111.99988). Clearly, we should get back the 104 here, which we would with the new expression: ((0xd0000 + 0xffff) >> 16) * 8 = 104 . (The error has not been a problem because it only results in maxsize being a bit too big which just wastes a couple of bytes, either as a result of the first maxsize calculation, or because the resulting calculation will hit the wMaxPacketSize value before the packet is too big, resulting in fixing the size to wMaxPacketSize even though the packet is actually not too long.) Tested with an Edirol UA-5 both at 44.1 kHz and 96 kHz. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index e6f71894ecdc..a77d9c812dc6 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -610,8 +610,23 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, /* assume max. frequency is 25% higher than nominal */ ep->freqmax = ep->freqn + (ep->freqn >> 2); - maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3)) - >> (16 - ep->datainterval); + /* Round up freqmax to nearest integer in order to calculate maximum + * packet size, which must represent a whole number of frames. + * This is accomplished by adding 0x0.ffff before converting the + * Q16.16 format into integer. + * In order to accurately calculate the maximum packet size when + * the data interval is more than 1 (i.e. ep->datainterval > 0), + * multiply by the data interval prior to rounding. For instance, + * a freqmax of 41 kHz will result in a max packet size of 6 (5.125) + * frames with a data interval of 1, but 11 (10.25) frames with a + * data interval of 2. + * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the + * maximum datainterval value of 3, at USB full speed, higher for + * USB high speed, noting that ep->freqmax is in units of + * frames per packet in Q16.16 format.) + */ + maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) * + (frame_bits >> 3); /* but wMaxPacketSize might reduce this */ if (ep->maxpacksize && ep->maxpacksize < maxsize) { /* whatever fits into a max. size packet */ -- cgit v1.2.3-59-g8ed1b From 4362495734d155e10174ace9066827780edaed0d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 14 Oct 2015 08:56:07 +0000 Subject: ASoC: rsnd: Announce for removing Gen1 SRU support Gen1 SRU support was created for preparation of Gen2 SRC support, but no-one is using this feature (sampling rate convert) on Gen1. BockW had used SRU before, but it was pass through mode. This means it is same as SSI. And BockW "platform base" code was removed from upstream code. It is now supported via DT, but it doesn't use SRU. More detail, r8a7778.dtsi has "rcar_sound,src" entry, but no-one is using this feature today. SRU probing has no relation to this removing. This means there is no effect for DT compatibility, no issues on upstream kernel. Gen2 SRC was created from Gen1 SRU, these are similar but not same IP. Keeping Gen1 SRU in current driver is a little bit difficult, and no-one is using it today. Gen1 sound is still supported via SSI. Gen1 SRU support will be removed in the next kernel version. This patch announces it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index ca7a20f03c9b..1d379e825a9d 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -1036,8 +1036,10 @@ int rsnd_src_probe(struct platform_device *pdev, int i, nr, ret; ops = NULL; - if (rsnd_is_gen1(priv)) + if (rsnd_is_gen1(priv)) { ops = &rsnd_src_gen1_ops; + dev_warn(dev, "Gen1 support will be removed soon\n"); + } if (rsnd_is_gen2(priv)) ops = &rsnd_src_gen2_ops; if (!ops) { -- cgit v1.2.3-59-g8ed1b From 5a1f8c42259a424cfd262185da872764dcc673ad Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Oct 2015 10:01:42 +0300 Subject: ALSA: oss: underflow in snd_mixer_oss_proc_write() We cap the upper bound of "idx" but not the negative side. Let's make it unsigned to fix this. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/core/oss/mixer_oss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index a99f7200ff3f..7a8c79dd9734 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, struct snd_mixer_oss *mixer = entry->private_data; char line[128], str[32], idxstr[16]; const char *cptr; - int ch, idx; + unsigned int idx; + int ch; struct snd_mixer_oss_assign_table *tbl; struct slot *slot; -- cgit v1.2.3-59-g8ed1b From 3e19fec33a5493f8a627a96ad3494d6c6dc2a624 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Oct 2015 11:17:09 +0200 Subject: ALSA: hda - Enable widget power saving for Cirrus codecs Cirrus codecs have also fine power controls on each widget, thus it gets benefit from the recent widget power-saving feature. As we haven't seen any obvious regressions with tests on some MacBooks, let's try to enable it. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 85813de26da8..01c464a468f6 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) return NULL; codec->spec = spec; spec->vendor_nid = vendor_nid; + codec->power_save_node = 1; snd_hda_gen_spec_init(&spec->gen); return spec; -- cgit v1.2.3-59-g8ed1b From ded255be2276d365a91af2de7c7f8e2c233d4fa2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Oct 2015 17:59:43 +0200 Subject: ALSA: hda - consolidate chip rename functions A few multiple codec drivers do renaming the chip_name string but all these are open-coded and some of them have even no error check. Let's make common helpers to do it properly. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 1 + sound/hda/hdac_device.c | 22 ++++++++++++++++++++++ sound/pci/hda/hda_bind.c | 35 +++++++++++++++++++++++------------ sound/pci/hda/hda_codec.h | 2 ++ sound/pci/hda/hda_sysfs.c | 3 +-- sound/pci/hda/patch_realtek.c | 12 +----------- sound/pci/hda/patch_via.c | 18 ++++-------------- 7 files changed, 54 insertions(+), 39 deletions(-) (limited to 'sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 26e956f4b7c6..49df61c7afdc 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -117,6 +117,7 @@ int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, void snd_hdac_device_exit(struct hdac_device *dev); int snd_hdac_device_register(struct hdac_device *codec); void snd_hdac_device_unregister(struct hdac_device *codec); +int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name); int snd_hdac_refresh_widgets(struct hdac_device *codec); int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index b3b0ad289df1..4b06b26cee06 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -163,6 +163,28 @@ void snd_hdac_device_unregister(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_device_unregister); +/** + * snd_hdac_device_set_chip_name - set/update the codec name + * @codec: the HDAC device + * @name: name string to set + * + * Returns 0 if the name is set or updated, or a negative error code. + */ +int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name) +{ + char *newname; + + if (!name) + return 0; + newname = kstrdup(name, GFP_KERNEL); + if (!newname) + return -ENOMEM; + kfree(codec->chip_name); + codec->chip_name = newname; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name); + /** * snd_hdac_make_cmd - compose a 32bit command word to be sent to the * HD-audio controller diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index d5ac25cc7fee..ef6b8f836a87 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -45,15 +45,31 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) codec->patch_ops.unsol_event(codec, ev); } -/* reset the codec name from the preset */ -static int codec_refresh_name(struct hda_codec *codec, const char *name) +/** + * snd_hda_codec_set_name - set the codec name + * @codec: the HDA codec + * @name: name string to set + */ +int snd_hda_codec_set_name(struct hda_codec *codec, const char *name) { - if (name) { - kfree(codec->core.chip_name); - codec->core.chip_name = kstrdup(name, GFP_KERNEL); + int err; + + if (!name) + return 0; + err = snd_hdac_device_set_chip_name(&codec->core, name); + if (err < 0) + return err; + + /* update the mixer name */ + if (!*codec->card->mixername) { + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), "%s %s", + codec->core.vendor_name, codec->core.chip_name); } - return codec->core.chip_name ? 0 : -ENOMEM; + + return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_set_name); static int hda_codec_driver_probe(struct device *dev) { @@ -64,7 +80,7 @@ static int hda_codec_driver_probe(struct device *dev) if (WARN_ON(!codec->preset)) return -EINVAL; - err = codec_refresh_name(codec, codec->preset->name); + err = snd_hda_codec_set_name(codec, codec->preset->name); if (err < 0) goto error; err = snd_hdac_regmap_init(&codec->core); @@ -251,11 +267,6 @@ int snd_hda_codec_configure(struct hda_codec *codec) } } - /* audio codec should override the mixer name */ - if (codec->core.afg || !*codec->card->mixername) - snprintf(codec->card->mixername, - sizeof(codec->card->mixername), "%s %s", - codec->core.vendor_name, codec->core.chip_name); return 0; error: diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 95991e463abb..b6d937784afa 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -463,6 +463,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus); void snd_hda_bus_reset(struct hda_bus *bus); void snd_hda_bus_reset_codecs(struct hda_bus *bus); +int snd_hda_codec_set_name(struct hda_codec *codec, const char *name); + /* * power management */ diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index a6e3d9b511ab..64e0d1d81ca5 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus, static void parse_chip_name_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - kfree((*codecp)->core.chip_name); - (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL); + snd_hda_codec_set_name(*codecp, buf); } #define DEFINE_PARSE_ID_MODE(name) \ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 16b8dcba5c12..e1ffb0202ebc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -822,17 +822,7 @@ static const struct hda_codec_ops alc_patch_ops = { }; -/* replace the codec chip_name with the given string */ -static int alc_codec_rename(struct hda_codec *codec, const char *name) -{ - kfree(codec->core.chip_name); - codec->core.chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->core.chip_name) { - alc_free(codec); - return -ENOMEM; - } - return 0; -} +#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) /* * Rename codecs appropriately from COEF value or subvendor id diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index da5366405eda..d714a57e9460 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -785,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec) override_mic_boost(codec, 0x1e, 0, 3, 40); /* correct names for VT1708BCE */ - if (get_codec_type(codec) == VT1708BCE) { - kfree(codec->core.chip_name); - codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL); - snprintf(codec->card->mixername, - sizeof(codec->card->mixername), - "%s %s", codec->core.vendor_name, codec->core.chip_name); - } + if (get_codec_type(codec) == VT1708BCE) + snd_hda_codec_set_name(codec, "VT1708BCE"); /* correct names for VT1705 */ - if (codec->core.vendor_id == 0x11064397) { - kfree(codec->core.chip_name); - codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL); - snprintf(codec->card->mixername, - sizeof(codec->card->mixername), - "%s %s", codec->core.vendor_name, codec->core.chip_name); - } + if (codec->core.vendor_id == 0x11064397) + snd_hda_codec_set_name(codec, "VT1705"); /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); -- cgit v1.2.3-59-g8ed1b From 7fbe824a0f0e6a73de5b589a6690294f7cb9dae3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Oct 2015 14:06:14 +0200 Subject: ALSA: hda - Update mixer name for the lower codec address In most cases, we prefer the onboard codec as the primary device, thus it's better to set it as the mixer name. Currently, however, the mixer name is updated per the device instantiation order, and user gets often HDMI/DP or other seen as a mixer chip name. Also, if a codec name is renamed by the driver, the old chip name might be left still as the mixer name. This patch addresses these issues by remembering the chip address that was referred as the mixer name. When a codec with the same or lower address gives its name, renew the mixer name accordingly, as it's either the update of the codec name or we get likely the more appropriate chip as the reference. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_bind.c | 4 +++- sound/pci/hda/hda_codec.c | 1 + sound/pci/hda/hda_codec.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index ef6b8f836a87..021bcce59447 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -61,10 +61,12 @@ int snd_hda_codec_set_name(struct hda_codec *codec, const char *name) return err; /* update the mixer name */ - if (!*codec->card->mixername) { + if (!*codec->card->mixername || + codec->mixer_assigned >= codec->core.addr) { snprintf(codec->card->mixername, sizeof(codec->card->mixername), "%s %s", codec->core.vendor_name, codec->core.chip_name); + codec->mixer_assigned = codec->core.addr; } return 0; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2eeaf5ea20f9..0e55c6a6cc7e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -851,6 +851,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; codec->fixup_id = HDA_FIXUP_ID_NOT_SET; + codec->mixer_assigned = -1; #ifdef CONFIG_PM codec->power_jiffies = jiffies; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index b6d937784afa..891864ac60f5 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -257,6 +257,7 @@ struct hda_codec { unsigned long power_off_acct; unsigned long power_jiffies; #endif + unsigned int mixer_assigned; /* filter the requested power state per nid */ unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid, -- cgit v1.2.3-59-g8ed1b From dab9981756dbc3c50c194811f176f0d658c171af Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Fri, 16 Oct 2015 13:38:33 +0200 Subject: ALSA: USB-audio: Add support for Novation Nocturn MIDIcontrol surface The Nocturn needs the MIDI_RAW_BYTES quirk, like other Novation devices. Tested that the Nocturn shows up in aconnect, and that it can be used as a control surface (using the xtor synthesizer patch editor). Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index e4756651a52c..99de06100395 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2663,6 +2663,15 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_NOVATION } }, +{ + USB_DEVICE(0x1235, 0x000a), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Novation", */ + /* .product_name = "Nocturn", */ + .ifnum = 0, + .type = QUIRK_MIDI_RAW_BYTES + } +}, { USB_DEVICE(0x1235, 0x000e), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { -- cgit v1.2.3-59-g8ed1b From 90bbaf66ee7b946952f1e82a0069639dea5fd893 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Fri, 16 Oct 2015 17:57:46 +0800 Subject: ALSA: timer: add config item to export PCM timer disabling for expert PCM timer is not always used. For embedded device, we need an interface to disable it when it is not needed, to shrink the kernel size and memory footprint, here add CONFIG_SND_PCM_TIMER for it. When both CONFIG_SND_PCM_TIMER and CONFIG_SND_TIMER is unselected, about 25KB saving bonus we can get. Please be noted that when disabled, those stubs who using pcm timer (e.g. dmix, dsnoop & co) may work incorrectlly. Suggested-by: Takashi Iwai Signed-off-by: Jie Yang Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 8 +++++++- sound/core/Kconfig | 13 ++++++++++++- sound/core/Makefile | 3 ++- sound/core/pcm_lib.c | 2 ++ sound/core/pcm_native.c | 36 ++++++++++++++++-------------------- 5 files changed, 39 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index a4fcc9456194..2882dddfc91c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1111,10 +1111,16 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea * Timer interface */ +#ifdef CONFIG_SND_PCM_TIMER void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); void snd_pcm_timer_init(struct snd_pcm_substream *substream); void snd_pcm_timer_done(struct snd_pcm_substream *substream); - +#else +static inline void +snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {} +#endif /** * snd_pcm_gettime - Fill the timespec depending on the timestamp mode * @runtime: PCM runtime instance diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 6c96feeaf01e..e3e949126a56 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -4,7 +4,7 @@ config SND_TIMER config SND_PCM tristate - select SND_TIMER + select SND_TIMER if SND_PCM_TIMER config SND_PCM_ELD bool @@ -93,6 +93,17 @@ config SND_PCM_OSS_PLUGINS support conversion of channels, formats and rates. It will behave like most of new OSS/Free drivers in 2.4/2.6 kernels. +config SND_PCM_TIMER + bool "PCM timer interface" if EXPERT + default y + help + If you disable this option, pcm timer will be inavailable, so + those stubs used pcm timer (e.g. dmix, dsnoop & co) may work + incorrectlly. + + For some embedded device, we may disable it to reduce memory + footprint, about 20KB on x86_64 platform. + config SND_SEQUENCER_OSS bool "OSS Sequencer API" depends on SND_SEQUENCER diff --git a/sound/core/Makefile b/sound/core/Makefile index 3354f91e003a..48ab4b8f8279 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -13,8 +13,9 @@ snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o snd-$(CONFIG_SND_VMASTER) += vmaster.o snd-$(CONFIG_SND_JACK) += ctljack.o jack.o -snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ +snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \ pcm_memory.o memalloc.o +snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7d45645f10ba..6dc4277937b8 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1883,8 +1883,10 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) snd_pcm_update_hw_ptr0(substream, 1) < 0) goto _end; +#ifdef CONFIG_SND_PCM_TIMER if (substream->timer_running) snd_timer_interrupt(substream->timer, 1); +#endif _end: snd_pcm_stream_unlock_irqrestore(substream, flags); if (runtime->transfer_ack_end) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 139887011ba2..a8b27cdc2844 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -486,6 +486,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state) snd_pcm_stream_unlock_irq(substream); } +static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, + int event) +{ +#ifdef CONFIG_SND_PCM_TIMER + if (substream->timer) + snd_timer_notify(substream->timer, event, + &substream->runtime->trigger_tstamp); +#endif +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -1043,9 +1053,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - if (substream->timer) - snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, - &runtime->trigger_tstamp); + snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART); } static struct action_ops snd_pcm_action_start = { @@ -1093,9 +1101,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) if (runtime->status->state != state) { snd_pcm_trigger_tstamp(substream); runtime->status->state = state; - if (substream->timer) - snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, - &runtime->trigger_tstamp); + snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP); } wake_up(&runtime->sleep); wake_up(&runtime->tsleep); @@ -1209,18 +1215,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) snd_pcm_trigger_tstamp(substream); if (push) { runtime->status->state = SNDRV_PCM_STATE_PAUSED; - if (substream->timer) - snd_timer_notify(substream->timer, - SNDRV_TIMER_EVENT_MPAUSE, - &runtime->trigger_tstamp); + snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE); wake_up(&runtime->sleep); wake_up(&runtime->tsleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; - if (substream->timer) - snd_timer_notify(substream->timer, - SNDRV_TIMER_EVENT_MCONTINUE, - &runtime->trigger_tstamp); + snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE); } } @@ -1268,9 +1268,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) snd_pcm_trigger_tstamp(substream); runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; - if (substream->timer) - snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND, - &runtime->trigger_tstamp); + snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND); wake_up(&runtime->sleep); wake_up(&runtime->tsleep); } @@ -1374,9 +1372,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state) struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); runtime->status->state = runtime->status->suspended_state; - if (substream->timer) - snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, - &runtime->trigger_tstamp); + snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME); } static struct action_ops snd_pcm_action_resume = { -- cgit v1.2.3-59-g8ed1b From 8a98b4223d0eab88bba5eb215b275da4ff6ed99c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 15 Oct 2015 03:25:28 +0000 Subject: ASoC: rsnd: Gen1 probe is not error Probing from Gen1 is not error. This patch fixup it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 6 ++---- sound/soc/sh/rcar/dvc.c | 6 ++---- sound/soc/sh/rcar/mix.c | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index a3e7c716e1f7..f1541f464f30 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -118,10 +118,8 @@ int rsnd_ctu_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CTU is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_ctu(pdev, of_data, priv); diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 8d8eee6350c9..e36c0ac3374b 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -333,10 +333,8 @@ int rsnd_dvc_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "CMD is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_dvc(pdev, of_data, priv); diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 8544403ffb26..ac2687d9ee55 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -151,10 +151,8 @@ int rsnd_mix_probe(struct platform_device *pdev, int i, nr, ret; /* This driver doesn't support Gen1 at this point */ - if (rsnd_is_gen1(priv)) { - dev_warn(dev, "MIX is not supported on Gen1\n"); - return -EINVAL; - } + if (rsnd_is_gen1(priv)) + return 0; rsnd_of_parse_mix(pdev, of_data, priv); -- cgit v1.2.3-59-g8ed1b From 3943b9efb381531b99bf9c545736f9e1e2715b9c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 5 Oct 2015 21:23:48 +0800 Subject: ASoC: rt298: Make rt298_index_def const The index_cache is per instance run time state but rt298_index_def is not. Make rt298_index_def const and make a copy of memory for index_cache rather than directly use the rt298_index_def. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index ff126a7cee76..d039c995c051 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -49,7 +49,7 @@ struct rt298_priv { int is_hp_in; }; -static struct reg_default rt298_index_def[] = { +static const struct reg_default rt298_index_def[] = { { 0x01, 0xaaaa }, { 0x02, 0x8aaa }, { 0x03, 0x0002 }, @@ -1165,7 +1165,11 @@ static int rt298_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt298->index_cache = rt298_index_def; + rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def, + sizeof(rt298_index_def), GFP_KERNEL); + if (!rt298->index_cache) + return -ENOMEM; + rt298->index_cache_size = INDEX_CACHE_SIZE; rt298->i2c = i2c; i2c_set_clientdata(i2c, rt298); -- cgit v1.2.3-59-g8ed1b From c962d03be31f12fd8eea435fa59e5289ce0cc284 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 13 Oct 2015 15:07:25 +0800 Subject: ASoC: rt5645: Recheck the jack detect status after resuming from S3 The patch rechecks the jack detect status after resuming from S3. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index d9993d42cc4e..b3eff733d778 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2745,6 +2745,10 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_FV1 | RT5645_PWR_FV2, RT5645_PWR_FV1 | RT5645_PWR_FV2); + if (rt5645->en_button_func && + snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(0)); break; case SND_SOC_BIAS_OFF: -- cgit v1.2.3-59-g8ed1b From 69ec98d7e5661a1c98ec51c26d6e91af3bbe0e72 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Oct 2015 21:16:30 +0300 Subject: ALSA: firewire-tascam: fix an LED bug We recently tried to add some new code to support turning the LED on and off but the code in snd_tscm_transaction_reregister() is unreachable. Fixes: e65e2cb99e44 ('ALSA: firewire-tascam: Turn on/off FireWire LED') Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 1c9a88be55c8..45c3ce3d6020 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -249,7 +249,7 @@ int snd_tscm_transaction_reregister(struct snd_tscm *tscm) /* Turn on messaging. */ reg = cpu_to_be32(0x00000001); - return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, ®, sizeof(reg), 0); if (err < 0) -- cgit v1.2.3-59-g8ed1b From 724097059a8e5df1aa162d153b8f90b0ffbb82dc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Oct 2015 21:17:11 +0300 Subject: ALSA: firewire-tascam: off by one in handle_midi_tx() My static checker complains because tscm->spec->midi_capture_ports is either 2 or 4 but the tscm->tx_midi_substreams[] array has 4 elements so this is possibly off by one. I have looked at the code and I think it should be >= instead of > as well. Fixes: 107cc0129a68 ('ALSA: firewire-tascam: add support for incoming MIDI messages by asynchronous transaction') Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 45c3ce3d6020..d4f64ae182e7 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -158,7 +158,7 @@ static void handle_midi_tx(struct fw_card *card, struct fw_request *request, port = b[0] >> 4; /* TODO: support virtual MIDI ports. */ - if (port > tscm->spec->midi_capture_ports) + if (port >= tscm->spec->midi_capture_ports) goto end; /* Assume the message length. */ -- cgit v1.2.3-59-g8ed1b From 2f0eaad91005b00e774a118595a573a9e0fd30fc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 17 Oct 2015 18:25:38 +0200 Subject: ALSA: hda - Fix bogus codec address check for mixer name assignment The recent commit [7fbe824a0f0e: ALSA: hda - Update mixer name for the lower codec address] tried to improve the mixer chip name assignment in the order of codec address. However, this fix was utterly bogus; it checks the field set in each codec, thus this value is reset at each codec creation, of course. For really handling this priority, the assignment has to be remembered in the common place, namely in hda_bus, instead of hda_codec. Fixes: 7fbe824a0f0e ('ALSA: hda - Update mixer name for the lower codec address') Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_bind.c | 4 ++-- sound/pci/hda/hda_codec.c | 1 - sound/pci/hda/hda_codec.h | 2 +- sound/pci/hda/hda_controller.c | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 021bcce59447..57800a498f7b 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -62,11 +62,11 @@ int snd_hda_codec_set_name(struct hda_codec *codec, const char *name) /* update the mixer name */ if (!*codec->card->mixername || - codec->mixer_assigned >= codec->core.addr) { + codec->bus->mixer_assigned >= codec->core.addr) { snprintf(codec->card->mixername, sizeof(codec->card->mixername), "%s %s", codec->core.vendor_name, codec->core.chip_name); - codec->mixer_assigned = codec->core.addr; + codec->bus->mixer_assigned = codec->core.addr; } return 0; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0e55c6a6cc7e..2eeaf5ea20f9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -851,7 +851,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - codec->mixer_assigned = -1; #ifdef CONFIG_PM codec->power_jiffies = jiffies; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 891864ac60f5..864ea6802491 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -69,6 +69,7 @@ struct hda_bus { unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ int primary_dig_out_type; /* primary digital out PCM type */ + unsigned int mixer_assigned; /* codec addr for mixer name */ }; /* from hdac_bus to hda_bus */ @@ -257,7 +258,6 @@ struct hda_codec { unsigned long power_off_acct; unsigned long power_jiffies; #endif - unsigned int mixer_assigned; /* filter the requested power state per nid */ unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 944455997fdc..d6b93a20361b 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1045,6 +1045,7 @@ int azx_bus_init(struct azx *chip, const char *model, mutex_init(&bus->prepare_mutex); bus->pci = chip->pci; bus->modelname = model; + bus->mixer_assigned = -1; bus->core.snoop = azx_snoop(chip); if (chip->get_position[0] != azx_get_pos_lpib || chip->get_position[1] != azx_get_pos_lpib) -- cgit v1.2.3-59-g8ed1b From 658a69bb7568238fd9a65da2426736435d7e8b01 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 17 Oct 2015 18:31:31 +0200 Subject: ALSA: hda - Remove leftover snd_hda_bus() prototype It was forgotten to be removed. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 864ea6802491..89cc87e2393d 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -298,10 +298,6 @@ struct hda_codec { /* * constructors */ -int snd_hda_bus_new(struct snd_card *card, - const struct hdac_bus_ops *ops, - const struct hdac_io_ops *io_ops, - struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_configure(struct hda_codec *codec); -- cgit v1.2.3-59-g8ed1b From df4833886f91eea0d20e6e97066adab308625ef8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 13:46:47 +0900 Subject: ALSA: fireworks/bebob/oxfw/dice: enable to make as built-in When committed to upstream, these four modules had wrong entries for Makefile. This forces them to be loadable modules even if they're set as built-in. This commit fixes this bug. Fixes: b5b04336015e('ALSA: fireworks: Add skelton for Fireworks based devices') Fixes: fd6f4b0dc167('ALSA: bebob: Add skelton for BeBoB based devices') Fixes: 1a4e39c2e5ca('ALSA: oxfw: Move to its own directory') Fixes: 14ff6a094815('ALSA: dice: Move file to its own directory') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/Makefile | 2 +- sound/firewire/dice/Makefile | 2 +- sound/firewire/fireworks/Makefile | 2 +- sound/firewire/oxfw/Makefile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 6cf470c80d1f..af7ed6643266 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,4 +1,4 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \ bebob_focusrite.o bebob_maudio.o bebob.o -obj-m += snd-bebob.o +obj-$(CONFIG_SND_BEBOB) += snd-bebob.o diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 9ef228ef7baf..55b4be9b0034 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,3 +1,3 @@ snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ dice-pcm.o dice-hwdep.o dice.o -obj-m += snd-dice.o +obj-$(CONFIG_SND_DICE) += snd-dice.o diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile index 0c7440826db8..15ef7f75a8ef 100644 --- a/sound/firewire/fireworks/Makefile +++ b/sound/firewire/fireworks/Makefile @@ -1,4 +1,4 @@ snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \ fireworks_stream.o fireworks_proc.o fireworks_midi.o \ fireworks_pcm.o fireworks_hwdep.o fireworks.o -obj-m += snd-fireworks.o +obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index a926850864f6..06ff50f4e6c0 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile @@ -1,3 +1,3 @@ snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \ oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o -obj-m += snd-oxfw.o +obj-$(CONFIG_SND_OXFW) += snd-oxfw.o -- cgit v1.2.3-59-g8ed1b From ad4401e53d1a5db557f287fdb0820ef0c7dfffd6 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 13:46:48 +0900 Subject: ALSA: oxfw: remove a meaningless entry from firewire Makefile A former commit moves oxfw-related codes to a sub-directory, while it forgot to remove an entry from Makefile in parent directory. Fixes: 1a4e39c2e5ca('ALSA: oxfw: Move to its own directory') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Makefile | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 6ae50f50db62..f5fb62551c60 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,6 +1,5 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp-stream.o amdtp-am824.o -snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o -- cgit v1.2.3-59-g8ed1b From 3e93d42a04eea4e621f87bfc51b0ab868e131cb9 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 22:39:49 +0900 Subject: ALSA: dice: correct variable types for __be32 data Some local variables in some functions are typed as unsigned int, while __be32 value is assigned to them. This causes sparse warnings. dice-stream.c:50:17: warning: incorrect type in assignment (different base types) dice-stream.c:50:17: expected unsigned int [unsigned] channel dice-stream.c:50:17: got restricted __be32 [usertype] dice-stream.c:74:17: warning: incorrect type in assignment (different base types) dice-stream.c:74:17: expected unsigned int [unsigned] channel dice-stream.c:74:17: got restricted __be32 [usertype] This commit fixes this bug. Fixes: 288a8d0cb04f('ALSA: dice: Change the way to start stream') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 2108f7f1a764..a6a39f7ef58d 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, static void release_resources(struct snd_dice *dice, struct fw_iso_resources *resources) { - unsigned int channel; + __be32 channel; /* Reset channel number */ channel = cpu_to_be32((u32)-1); if (resources == &dice->tx_resources) snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, 4); + &channel, sizeof(channel)); else snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, 4); + &channel, sizeof(channel)); fw_iso_resources_free(resources); } @@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice, struct fw_iso_resources *resources, unsigned int max_payload_bytes) { - unsigned int channel; + __be32 channel; int err; err = fw_iso_resources_allocate(resources, max_payload_bytes, @@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice, channel = cpu_to_be32(resources->channel); if (resources == &dice->tx_resources) err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, 4); + &channel, sizeof(channel)); else err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, 4); + &channel, sizeof(channel)); if (err < 0) release_resources(dice, resources); end: -- cgit v1.2.3-59-g8ed1b From cbc6f28067aa0aa1193c2bf3546430b2c4dae22c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 22:39:50 +0900 Subject: ALSA: dice: assign converted data to the same type of variable In former commit, u32 data was assigned to __be32 variable instead of an int variable. This is not enough solution because it still causes sparse warnings. dice.c:80:23: warning: incorrect type in assignment (different base types) dice.c:80:23: expected restricted __be32 [usertype] value dice.c:80:23: got unsigned int dice.c:81:21: warning: restricted __be32 degrades to integer dice.c:81:46: warning: restricted __be32 degrades to integer This commit fixes this bug. Fixes: 7c2d4c0cf5ba('ALSA: dice: Split transaction functionality into a file') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 70a111d7f428..5d99436dfcae 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -29,7 +29,8 @@ static int dice_interface_check(struct fw_unit *unit) struct fw_csr_iterator it; int key, val, vendor = -1, model = -1, err; unsigned int category, i; - __be32 *pointers, value; + __be32 *pointers; + u32 value; __be32 version; pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32), -- cgit v1.2.3-59-g8ed1b From 463543ac2effaeb2b524f0a0a92f2413c23998ca Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 22:39:51 +0900 Subject: ALSA: fireworks: use u32 type for be32_to_cpup() macro In former commit, snd_efw_command_get_phys_meters() was added to handle metering data. The given buffer is used to save transaction result and to convert between endianness. But this causes sparse warnings. fireworks_command.c:269:25: warning: incorrect type in argument 1 (different base types) fireworks_command.c:269:25: expected unsigned int [usertype] *p fireworks_command.c:269:25: got restricted __be32 [usertype] * This commit fixes this bug. Fixes: bde8a8f23bbe('ALSA: fireworks: Add transaction and some commands') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks_command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c index 166f80584c2a..94bab0476a65 100644 --- a/sound/firewire/fireworks/fireworks_command.c +++ b/sound/firewire/fireworks/fireworks_command.c @@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw, struct snd_efw_phys_meters *meters, unsigned int len) { - __be32 *buf = (__be32 *)meters; + u32 *buf = (u32 *)meters; unsigned int i; int err; -- cgit v1.2.3-59-g8ed1b From fef586d58960bc867c1fa8126ee5d7364a5a89ac Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 22:39:52 +0900 Subject: ALSA: bebob: use correct type for __be32 data In former commit, metering is supported for BeBoB based models customized by M-Audio. The data in transaction is aligned to big-endianness, while in the driver code u16 typed variable is assigned to the data. This causes sparse warnings. bebob_maudio.c:651:31: warning: cast to restricted __be16 bebob_maudio.c:651:31: warning: cast to restricted __be16 bebob_maudio.c:651:31: warning: cast to restricted __be16 bebob_maudio.c:651:31: warning: cast to restricted __be16 This commit fixes this bug by using __be16 variable for the data. Fixes: 3149ac489ff8('ALSA: bebob: Add support for M-Audio special Firewire series') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob_maudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 7b86a6b99f07..07e5abdbceb5 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -628,7 +628,7 @@ static const char *const special_meter_labels[] = { static int special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size) { - u16 *buf; + __be16 *buf; unsigned int i, c, channels; int err; -- cgit v1.2.3-59-g8ed1b From bd04809bbe4c1f749650bb990c969112a5e10aef Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 22:39:53 +0900 Subject: ALSA: firewire-digi00x/firewire-tascam: remove wrong conversion for Config ROM The contents of Config ROM in firewire device structure are already aligned to CPU-endianness. Thus, no need to convert it again. This commit removes needless conversions Fixes: 9edf723fd858('ALSA: firewire-digi00x: add skeleton for Digi 002/003 family') Fixes: c0949b278515('ALSA: firewire-tascam: add skeleton for TASCAM FireWire series') Reported-by: Stefan Richter Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x.c | 3 +-- sound/firewire/tascam/tascam.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index bbe3be7fea9b..1f33b7a1fca4 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -34,8 +34,7 @@ static int name_card(struct snd_dg00x *dg00x) strcpy(dg00x->card->mixername, model); snprintf(dg00x->card->longname, sizeof(dg00x->card->longname), "Digidesign %s, GUID %08x%08x at %s, S%d", model, - cpu_to_be32(fw_dev->config_rom[3]), - cpu_to_be32(fw_dev->config_rom[4]), + fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed); return 0; diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index c6747a45795b..c2f42cd3f3b8 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -73,8 +73,7 @@ static int identify_model(struct snd_tscm *tscm) strcpy(tscm->card->mixername, model); snprintf(tscm->card->longname, sizeof(tscm->card->longname), "TASCAM %s, GUID %08x%08x at %s, S%d", model, - cpu_to_be32(fw_dev->config_rom[3]), - cpu_to_be32(fw_dev->config_rom[4]), + fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&tscm->unit->device), 100 << fw_dev->max_speed); return 0; -- cgit v1.2.3-59-g8ed1b From 56b1c72a75ec44a98aca8bbd71ac869a6f54e036 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 22:58:37 +0900 Subject: ALSA: firewire-lib: avoid NULL pointer dereference after closing MIDI port When asynchronous MIDI port is closed before callbacked, the callback function causes NULL pointer dereference to missing MIDI substream. This commit fixes this bug. Fixes: e8a40d9bcb23('ALSA: firewire-lib: schedule work again when MIDI substream has rest of MIDI messages') Reported-by: Dan Carpenter Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/lib.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index edf1c8bd25a6..f80aafa44c89 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -74,7 +74,11 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, struct snd_fw_async_midi_port *port = callback_data; struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream); - if (rcode == RCODE_COMPLETE && substream != NULL) + /* This port is closed. */ + if (substream == NULL) + return; + + if (rcode == RCODE_COMPLETE) snd_rawmidi_transmit_ack(substream, port->consume_bytes); else if (!rcode_is_permanent_error(rcode)) /* To start next transaction immediately for recovery. */ -- cgit v1.2.3-59-g8ed1b From 32056041019aa91c2555cc4c280f9fbca8a1be99 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 17:09:38 +0900 Subject: ALSA: oxfw: calculating MIDI ports in stream discover Current OXFW driver calculates the number of MIDI ports just before adding ALSA MIDI ports. It's convenient for some devices with quirks to move these codes before handling quirks. This commit implements this idea. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-midi.c | 24 +++--------------------- sound/firewire/oxfw/oxfw-stream.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 37a86cf69cbf..8665e1043d41 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw, int snd_oxfw_create_midi(struct snd_oxfw *oxfw) { - struct snd_oxfw_stream_formation formation; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; - u8 *format; - int i, err; - - /* If its stream has MIDI conformant data channel, add one MIDI port */ - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { - format = oxfw->tx_stream_formats[i]; - if (format != NULL) { - err = snd_oxfw_stream_parse_format(format, &formation); - if (err >= 0 && formation.midi > 0) - oxfw->midi_input_ports = 1; - } - - format = oxfw->rx_stream_formats[i]; - if (format != NULL) { - err = snd_oxfw_stream_parse_format(format, &formation); - if (err >= 0 && formation.midi > 0) - oxfw->midi_output_ports = 1; - } - } - if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0)) + int err; + + if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0) return 0; /* create midi ports */ diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 2c63058bd245..48798084de16 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -629,6 +629,9 @@ end: int snd_oxfw_stream_discover(struct snd_oxfw *oxfw) { u8 plugs[AVC_PLUG_INFO_BUF_BYTES]; + struct snd_oxfw_stream_formation formation; + u8 *format; + unsigned int i; int err; /* the number of plugs for isoc in/out, ext in/out */ @@ -648,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw) err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0); if (err < 0) goto end; + + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->tx_stream_formats[i]; + if (format == NULL) + continue; + err = snd_oxfw_stream_parse_format(format, &formation); + if (err < 0) + continue; + + /* Add one MIDI port. */ + if (formation.midi > 0) + oxfw->midi_input_ports = 1; + } + oxfw->has_output = true; } /* use iPCR[0] if exists */ - if (plugs[0] > 0) + if (plugs[0] > 0) { err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0); + if (err < 0) + goto end; + + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->rx_stream_formats[i]; + if (format == NULL) + continue; + err = snd_oxfw_stream_parse_format(format, &formation); + if (err < 0) + continue; + + /* Add one MIDI port. */ + if (formation.midi > 0) + oxfw->midi_output_ports = 1; + } + } end: return err; } -- cgit v1.2.3-59-g8ed1b From bb71da43469679bf53dc97433dc02895e4d58352 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 17:09:39 +0900 Subject: ALSA: oxfw: support more MIDI ports In IEC 61883-6, sequence multiplexing is applied to MIDI conformant data channel. As a result, eight MIDI data streams are included in the channel. Although ALSA AM824 data block processing layer implements this multiplexing, current OXFW driver doesn't utilize it due to wrong calculation of MIDI ports. This commit fixes this bug to add proper calculation. Although this commit allows to use 8 MIDI data streams, the number of available MIDI ports is limited by the number of ALSA MIDI ports added by the driver. Fixes: df075feefbd3('ALSA: firewire-lib: complete AM824 data block processing layer') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 48798084de16..7cb5743c073b 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -148,7 +148,7 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, } pcm_channels = formation.pcm; - midi_ports = DIV_ROUND_UP(formation.midi, 8); + midi_ports = formation.midi * 8; /* The stream should have one pcm channels at least */ if (pcm_channels == 0) { -- cgit v1.2.3-59-g8ed1b From 759a2f40c9fa3839c021f7cdc4ec19d10fd63b44 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 18 Oct 2015 17:09:40 +0900 Subject: ALSA: oxfw: add an entry for TASCAM FireOne TASCAM FireOne is based on OXFW971 and ALSA OXFW driver can support it. These are values of identical registers. $ ./firewire-request /dev/fw1 read 0xfffff0050000 result: 97100105 $ ./firewire-request /dev/fw1 read 0xfffff0090020 result: 39373100 This commit adds an entry for this model. This model has physical controls and its MIDI control messages are transferred to second MIDI data stream multiplexed in one MIDI conformant data channel. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound') diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index d606e3a9ce97..588b93f20c2e 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -18,6 +18,7 @@ #define VENDOR_GRIFFIN 0x001292 #define VENDOR_BEHRINGER 0x001564 #define VENDOR_LACIE 0x00d04b +#define VENDOR_TASCAM 0x00022e #define MODEL_SATELLITE 0x00200f @@ -154,6 +155,15 @@ static void detect_quirks(struct snd_oxfw *oxfw) */ if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE) oxfw->wrong_dbs = true; + + /* + * TASCAM FireOne has physical control and requires a pair of additional + * MIDI ports. + */ + if (vendor == VENDOR_TASCAM) { + oxfw->midi_input_ports++; + oxfw->midi_output_ports++; + } } static int oxfw_probe(struct fw_unit *unit, @@ -323,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = { .specifier_id = SPECIFIER_1394TA, .version = VERSION_AVC, }, + /* TASCAM, FireOne */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = VENDOR_TASCAM, + .model_id = 0x800007, + }, { } }; MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); -- cgit v1.2.3-59-g8ed1b From 07a40c2fc6f771a4abb2acc7e59434121e9ddf4e Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:49 +0200 Subject: ALSA: USB-audio: Break out copying to urb from prepare_playback_urb() Refactoring in preparation for adding Zoom R16/24 quirk. No functional change. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index cdac5179db3f..4292bad9f320 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1385,6 +1385,26 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs, } } +static void copy_to_urb(struct snd_usb_substream *subs, + struct urb *urb, int stride, unsigned int bytes) +{ + struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + + if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { + /* err, the transferred area goes over buffer boundary. */ + unsigned int bytes1 = + runtime->buffer_size * stride - subs->hwptr_done; + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done, bytes1); + memcpy(urb->transfer_buffer + bytes1, + runtime->dma_area, bytes - bytes1); + } else { + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done, bytes); + } + subs->hwptr_done += bytes; +} + static void prepare_playback_urb(struct snd_usb_substream *subs, struct urb *urb) { @@ -1462,20 +1482,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->hwptr_done += bytes; } else { /* usual PCM */ - if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { - /* err, the transferred area goes over buffer boundary. */ - unsigned int bytes1 = - runtime->buffer_size * stride - subs->hwptr_done; - memcpy(urb->transfer_buffer, - runtime->dma_area + subs->hwptr_done, bytes1); - memcpy(urb->transfer_buffer + bytes1, - runtime->dma_area, bytes - bytes1); - } else { - memcpy(urb->transfer_buffer, - runtime->dma_area + subs->hwptr_done, bytes); - } - - subs->hwptr_done += bytes; + copy_to_urb(subs, urb, stride, bytes); } if (subs->hwptr_done >= runtime->buffer_size * stride) -- cgit v1.2.3-59-g8ed1b From 4c4e4391b833e6297e0a6bc43db023a8c55d6835 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:50 +0200 Subject: ALSA: USB-audio: Also move out hwptr_done wrap from prepare_playback_urb() Refactoring in preparation for adding Zoom R16/24 quirk. No functional change. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 4292bad9f320..ee8dd9efb3ef 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1383,6 +1383,8 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs, subs->hwptr_done++; } } + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; } static void copy_to_urb(struct snd_usb_substream *subs, @@ -1403,6 +1405,8 @@ static void copy_to_urb(struct snd_usb_substream *subs, runtime->dma_area + subs->hwptr_done, bytes); } subs->hwptr_done += bytes; + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; } static void prepare_playback_urb(struct snd_usb_substream *subs, @@ -1480,14 +1484,13 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, } subs->hwptr_done += bytes; + if (subs->hwptr_done >= runtime->buffer_size * stride) + subs->hwptr_done -= runtime->buffer_size * stride; } else { /* usual PCM */ copy_to_urb(subs, urb, stride, bytes); } - if (subs->hwptr_done >= runtime->buffer_size * stride) - subs->hwptr_done -= runtime->buffer_size * stride; - /* update delay with exact number of samples queued */ runtime->delay = subs->last_delay; runtime->delay += frames; -- cgit v1.2.3-59-g8ed1b From 5cf310e976659caeaae350258940b73daaa0d478 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:51 +0200 Subject: ALSA: USB-audio: Break out creation of silent urbs from prepare_outbound_urb() Refactoring in preparation for adding Zoom R16/24 quirk. No functional change. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a77d9c812dc6..825a06ce83a9 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -183,13 +183,38 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep, ep->retire_data_urb(ep->data_subs, urb); } +static void prepare_silent_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx) +{ + struct urb *urb = ctx->urb; + unsigned int offs = 0; + int i; + + for (i = 0; i < ctx->packets; ++i) { + int counts; + + if (ctx->packet_size[i]) + counts = ctx->packet_size[i]; + else + counts = snd_usb_endpoint_next_packet_size(ep); + + urb->iso_frame_desc[i].offset = offs * ep->stride; + urb->iso_frame_desc[i].length = counts * ep->stride; + offs += counts; + } + + urb->number_of_packets = ctx->packets; + urb->transfer_buffer_length = offs * ep->stride; + memset(urb->transfer_buffer, ep->silence_value, + offs * ep->stride); +} + /* * Prepare a PLAYBACK urb for submission to the bus. */ static void prepare_outbound_urb(struct snd_usb_endpoint *ep, struct snd_urb_ctx *ctx) { - int i; struct urb *urb = ctx->urb; unsigned char *cp = urb->transfer_buffer; @@ -201,24 +226,7 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep, ep->prepare_data_urb(ep->data_subs, urb); } else { /* no data provider, so send silence */ - unsigned int offs = 0; - for (i = 0; i < ctx->packets; ++i) { - int counts; - - if (ctx->packet_size[i]) - counts = ctx->packet_size[i]; - else - counts = snd_usb_endpoint_next_packet_size(ep); - - urb->iso_frame_desc[i].offset = offs * ep->stride; - urb->iso_frame_desc[i].length = counts * ep->stride; - offs += counts; - } - - urb->number_of_packets = ctx->packets; - urb->transfer_buffer_length = offs * ep->stride; - memset(urb->transfer_buffer, ep->silence_value, - offs * ep->stride); + prepare_silent_urb(ep, ctx); } break; -- cgit v1.2.3-59-g8ed1b From b97a936910c8d668d25d60acbf62aea0d2ff587e Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:52 +0200 Subject: ALSA: USB-audio: Add offset parameter to copy_to_urb() Preparation for adding Zoom R16/24 quirk. No functional change. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ee8dd9efb3ef..e3c5bc0df69d 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1387,8 +1387,8 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs, subs->hwptr_done -= runtime->buffer_size * stride; } -static void copy_to_urb(struct snd_usb_substream *subs, - struct urb *urb, int stride, unsigned int bytes) +static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb, + int offset, int stride, unsigned int bytes) { struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; @@ -1396,12 +1396,12 @@ static void copy_to_urb(struct snd_usb_substream *subs, /* err, the transferred area goes over buffer boundary. */ unsigned int bytes1 = runtime->buffer_size * stride - subs->hwptr_done; - memcpy(urb->transfer_buffer, + memcpy(urb->transfer_buffer + offset, runtime->dma_area + subs->hwptr_done, bytes1); - memcpy(urb->transfer_buffer + bytes1, + memcpy(urb->transfer_buffer + offset + bytes1, runtime->dma_area, bytes - bytes1); } else { - memcpy(urb->transfer_buffer, + memcpy(urb->transfer_buffer + offset, runtime->dma_area + subs->hwptr_done, bytes); } subs->hwptr_done += bytes; @@ -1488,7 +1488,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->hwptr_done -= runtime->buffer_size * stride; } else { /* usual PCM */ - copy_to_urb(subs, urb, stride, bytes); + copy_to_urb(subs, urb, 0, stride, bytes); } /* update delay with exact number of samples queued */ -- cgit v1.2.3-59-g8ed1b From e05704467736231199503e5a21c587e7ec36b829 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:53 +0200 Subject: ALSA: USB-audio: Add quirk for Zoom R16/24 playback The Zoom R16/24 have a nonstandard playback format where each isochronous packet contains a length descriptor in the first four bytes. (Curiously, capture data does not contain this and requires no quirk.) The quirk involves adding the extra length descriptor whenever outgoing isochronous packets are generated, both in pcm.c (outgoing audio) and endpoint.c (silent data). In order to make the quirk as unintrusive as possible, for pcm.c:prepare_playback_urb(), the isochronous packet descriptors are initially set up in the same way no matter if the quirk is enabled or not. Once it is time to actually copy the data into the outgoing packet buffer (together with the added length descriptors) the isochronous descriptors are adjusted in order take the increased payload length into account. For endpoint.c:prepare_silent_urb() it makes more sense to modify the actual function, partly because the function is less complex to start with and partly because it is not as time-critical as prepare_playback_urb() (whose bulk is run with interrupts disabled), so the (minute) additional time spent in the non-quirk case is motivated by the simplicity of having a single function for all cases. The quirk is controlled by the new tx_length_quirk member in struct snd_usb_substream and struct snd_usb_audio, which is conveyed to pcm.c and endpoint.c from quirks.c in a similar manner to the txfr_quirk member in the same structs. In contrast to txfr_quirk however, the quirk is enabled directly in quirks.c:create_standard_audio_quirk() by checking the USB ID in that function. Another option would be to introduce a new QUIRK_AUDIO_ZOOM_INTERFACE or somesuch, which would have made the quirk very plain to see in the quirk table, but it was felt that the additional code needed to implement it this way would just make the implementation more complex with no real gain. Tested with a Zoom R16, both by doing capture and playback separately using arecord and aplay (8 channel capture and 2 channel playback, respectively), as well as capture and playback together using Ardour, as well as Audacity and Qtractor together with jackd. The R24 is reportedly compatible with the R16 when used as an audio interface. Both devices share the same USB ID and have the same number of inputs (8) and outputs (2). Therefore "R16/24" is mentioned throughout the patch. Regression tested using an Edirol UA-5 in both class compliant (16-bit) and "advanced" (24 bit, forces the use of quirks) modes. Signed-off-by: Ricard Wanderlof Tested-by: Panu Matilainen Signed-off-by: Takashi Iwai --- sound/usb/card.h | 1 + sound/usb/endpoint.c | 25 ++++++++++++++++++++----- sound/usb/pcm.c | 32 +++++++++++++++++++++++++++++++- sound/usb/quirks-table.h | 7 ++++--- sound/usb/quirks.c | 3 +++ sound/usb/stream.c | 1 + sound/usb/usbaudio.h | 1 + 7 files changed, 61 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index ef580b43f1e3..71778ca4b26a 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -122,6 +122,7 @@ struct snd_usb_substream { unsigned int buffer_periods; /* current periods per buffer */ unsigned int altset_idx; /* USB data format: index of alternate setting */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ + unsigned int tx_length_quirk:1; /* add length specifier to transfers */ unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 825a06ce83a9..0cc64bd4d0a4 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -188,9 +188,17 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, { struct urb *urb = ctx->urb; unsigned int offs = 0; + unsigned int extra = 0; + __le32 packet_length; int i; + /* For tx_length_quirk, put packet length at start of packet */ + if (ep->chip->tx_length_quirk) + extra = sizeof(packet_length); + for (i = 0; i < ctx->packets; ++i) { + unsigned int offset; + unsigned int length; int counts; if (ctx->packet_size[i]) @@ -198,15 +206,22 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, else counts = snd_usb_endpoint_next_packet_size(ep); - urb->iso_frame_desc[i].offset = offs * ep->stride; - urb->iso_frame_desc[i].length = counts * ep->stride; + length = counts * ep->stride; /* number of silent bytes */ + offset = offs * ep->stride + extra * i; + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = length + extra; + if (extra) { + packet_length = cpu_to_le32(length); + memcpy(urb->transfer_buffer + offset, + &packet_length, sizeof(packet_length)); + } + memset(urb->transfer_buffer + offset + extra, + ep->silence_value, length); offs += counts; } urb->number_of_packets = ctx->packets; - urb->transfer_buffer_length = offs * ep->stride; - memset(urb->transfer_buffer, ep->silence_value, - offs * ep->stride); + urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra; } /* diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e3c5bc0df69d..9245f52d43bd 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1409,6 +1409,32 @@ static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb, subs->hwptr_done -= runtime->buffer_size * stride; } +static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs, + struct urb *urb, int stride, + unsigned int bytes) +{ + __le32 packet_length; + int i; + + /* Put __le32 length descriptor at start of each packet. */ + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int length = urb->iso_frame_desc[i].length; + unsigned int offset = urb->iso_frame_desc[i].offset; + + packet_length = cpu_to_le32(length); + offset += i * sizeof(packet_length); + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length += sizeof(packet_length); + memcpy(urb->transfer_buffer + offset, + &packet_length, sizeof(packet_length)); + copy_to_urb(subs, urb, offset + sizeof(packet_length), + stride, length); + } + /* Adjust transfer size accordingly. */ + bytes += urb->number_of_packets * sizeof(packet_length); + return bytes; +} + static void prepare_playback_urb(struct snd_usb_substream *subs, struct urb *urb) { @@ -1488,7 +1514,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->hwptr_done -= runtime->buffer_size * stride; } else { /* usual PCM */ - copy_to_urb(subs, urb, 0, stride, bytes); + if (!subs->tx_length_quirk) + copy_to_urb(subs, urb, 0, stride, bytes); + else + bytes = copy_to_urb_quirk(subs, urb, stride, bytes); + /* bytes is now amount of outgoing data */ } /* update delay with exact number of samples queued */ diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 99de06100395..4d3848ce4cff 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3193,8 +3193,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), * ZOOM R16/24 in audio interface mode. * Mixer descriptors are garbage, further quirks will be needed * to make any of it functional, thus disabled for now. - * Playback stream appears to start and run fine but no sound - * is produced, so also disabled for now. + * Playback requires an extra four byte LE length indicator + * at the start of each isochronous packet. This quirk is + * enabled in create_standard_audio_quirk(). */ USB_DEVICE(0x1686, 0x00dd), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -3209,7 +3210,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), { /* Playback */ .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE, + .type = QUIRK_AUDIO_STANDARD_INTERFACE, }, { /* Capture */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 00ebc0ca008e..4897ea171194 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip, struct usb_interface_descriptor *altsd; int err; + if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */ + chip->tx_length_quirk = 1; + alts = &iface->altsetting[0]; altsd = get_iface_desc(alts); err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber); diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 970086015cde..8ee14f2365e7 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, subs->direction = stream; subs->dev = as->chip->dev; subs->txfr_quirk = as->chip->txfr_quirk; + subs->tx_length_quirk = as->chip->tx_length_quirk; subs->speed = snd_usb_get_speed(subs->dev); subs->pkt_offset_adj = 0; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 33a176437e2e..15a12715bd05 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -43,6 +43,7 @@ struct snd_usb_audio { atomic_t usage_count; wait_queue_head_t shutdown_wait; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ + unsigned int tx_length_quirk:1; /* Put length specifier in transfers */ int num_interfaces; int num_suspended_intf; -- cgit v1.2.3-59-g8ed1b From 759c90fe0129f23a4ff2a7c92e1bd30d41ac829c Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:54 +0200 Subject: ALSA: USB-audio: Adjust max packet size calculation for tx_length_quirk For the Zoom R16/24 (tx_length_quirk set), when calculating the maximum sample frequency, consideration must be made for the fact that four bytes of the packet contain a length descriptor and consequently must not be counted as part of the audio data. This is corroborated by the wMaxPacketSize for this device, which is 108 bytes according for the USB playback endpoint descriptor. The frame size is 8 bytes (2 channels of 4 bytes each), and the 108 bytes thus work out as 13 * 8 + 4, i.e. corresponding to 13 frames plus the additional 4 byte length descriptor. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 0cc64bd4d0a4..7b1cb365ffab 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -617,6 +617,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, unsigned int max_packs_per_period, urbs_per_period, urb_packs; unsigned int max_urbs, i; int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; + int tx_length_quirk = (ep->chip->tx_length_quirk && + usb_pipeout(ep->pipe)); if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { /* @@ -650,11 +652,17 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, */ maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) * (frame_bits >> 3); + if (tx_length_quirk) + maxsize += sizeof(__le32); /* Space for length descriptor */ /* but wMaxPacketSize might reduce this */ if (ep->maxpacksize && ep->maxpacksize < maxsize) { /* whatever fits into a max. size packet */ - maxsize = ep->maxpacksize; - ep->freqmax = (maxsize / (frame_bits >> 3)) + unsigned int data_maxsize = maxsize = ep->maxpacksize; + + if (tx_length_quirk) + /* Need to remove the length descriptor to calc freq */ + data_maxsize -= sizeof(__le32); + ep->freqmax = (data_maxsize / (frame_bits >> 3)) << (16 - ep->datainterval); } -- cgit v1.2.3-59-g8ed1b From 9fa5cf8c54940688ceb3a52eed7938b6b6585fc8 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Mon, 19 Oct 2015 08:52:55 +0200 Subject: ALSA: USB-audio: Remove mixer entry from Zoom R16/24 quirk The device has no mixer (and identifies itself as such), so just skip the mixer definition. Signed-off-by: Ricard Wanderlof Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sound') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 4d3848ce4cff..1a1e2e4df35e 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3191,8 +3191,6 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), { /* * ZOOM R16/24 in audio interface mode. - * Mixer descriptors are garbage, further quirks will be needed - * to make any of it functional, thus disabled for now. * Playback requires an extra four byte LE length indicator * at the start of each isochronous packet. This quirk is * enabled in create_standard_audio_quirk(). @@ -3202,11 +3200,6 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), .ifnum = QUIRK_ANY_INTERFACE, .type = QUIRK_COMPOSITE, .data = (const struct snd_usb_audio_quirk[]) { - { - /* Mixer */ - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE, - }, { /* Playback */ .ifnum = 1, -- cgit v1.2.3-59-g8ed1b From 9a30ae2df29c27eca58581862928ee2c7bbdfa76 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 19 Oct 2015 14:29:27 +0300 Subject: ALSA: firewire-tascam: off by one in identify_model() Let's leave space for the NUL char otherwise the static checkers complain that we go beyond the end of the array. Fixes: 53b3ffee7885 ('ALSA: firewire-tascam: change device probing processing') Signed-off-by: Dan Carpenter Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index c2f42cd3f3b8..ee0bc1839508 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -40,7 +40,7 @@ static int identify_model(struct snd_tscm *tscm) { struct fw_device *fw_dev = fw_parent_device(tscm->unit); const u32 *config_rom = fw_dev->config_rom; - char model[8]; + char model[9]; unsigned int i; u8 c; -- cgit v1.2.3-59-g8ed1b From 2057020db384cb971cfd51296426f447e6d66b64 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 13 Oct 2015 10:37:10 +0800 Subject: ASoC: rockchip: spdif: Convert to use devm_snd_dmaengine_pcm_register Use resource managed API then we can remove snd_dmaengine_pcm_unregister() and snd_soc_unregister_component() calls in .probe error path and .remove. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 9d5c470cee82..a38a3029062c 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -355,16 +355,14 @@ static int rk_spdif_probe(struct platform_device *pdev) goto err_pm_runtime; } - ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); - goto err_pcm_register; + goto err_pm_runtime; } return 0; -err_pcm_register: - snd_dmaengine_pcm_unregister(&pdev->dev); err_pm_runtime: pm_runtime_disable(&pdev->dev); @@ -381,8 +379,6 @@ static int rk_spdif_remove(struct platform_device *pdev) clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); - snd_dmaengine_pcm_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); return 0; } -- cgit v1.2.3-59-g8ed1b From 624729fd51871bfbddb647764f180126789a29ee Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Wed, 14 Oct 2015 21:11:43 +0530 Subject: ASoC: Intel: Skylake - Add Skylake RT286 I2S machine driver Add the SKL I2S machine driver using Realtek ALC286S codec in I2S mode. Signed-off-by: Omair M Abdullah Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 13 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/skl_rt286.c | 266 +++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 sound/soc/intel/boards/skl_rt286.c (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 664df1f44530..6d67357f2d74 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -140,3 +140,16 @@ config SND_SOC_INTEL_SKYLAKE select SND_HDA_EXT_CORE select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + +config SND_SOC_INTEL_SKL_RT286_MACH + tristate "ASoC Audio driver for SKL with RT286 I2S mode" + depends on X86 && ACPI + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT286 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Skylake platforms + with RT286 I2S audio codec. + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index cb94895c9edb..371c4565cad8 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o +snd-soc-skl_rt286-objs := skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c new file mode 100644 index 000000000000..488c98ee1ded --- /dev/null +++ b/sound/soc/intel/boards/skl_rt286.c @@ -0,0 +1,266 @@ +/* + * Intel Skylake I2S Machine Driver + * + * Copyright (C) 2014-2015, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Broadwell Wildcatpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/rt286.h" + +static struct snd_soc_jack skylake_headset; +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin skylake_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new skylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget skylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route skylake_rt286_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack deteck */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMIC AIF", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + { "ssp0 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "Capture" }, + + { "hif1", NULL, "iDisp Tx"}, + { "iDisp Tx", NULL, "iDisp_out"}, + +}; + +static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &skylake_headset, + skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins)); + + if (ret) + return ret; + + rt286_mic_detect(codec, &skylake_headset); + + return 0; +} + + +static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The output is 48KHz, stereo, 16bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static int skylake_rt286_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops skylake_rt286_ops = { + .hw_params = skylake_rt286_hw_params, +}; + +/* skylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link skylake_rt286_dais[] = { + /* Front End DAI links */ + { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + }, + { + .name = "Skl Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .be_id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt286-aif1", + .init = skylake_rt286_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp0_fixup, + .ops = &skylake_rt286_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, +}; + +/* skylake audio machine driver for SPT + RT286S */ +static struct snd_soc_card skylake_rt286 = { + .name = "skylake-rt286", + .owner = THIS_MODULE, + .dai_link = skylake_rt286_dais, + .num_links = ARRAY_SIZE(skylake_rt286_dais), + .controls = skylake_controls, + .num_controls = ARRAY_SIZE(skylake_controls), + .dapm_widgets = skylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(skylake_widgets), + .dapm_routes = skylake_rt286_map, + .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), + .fully_routed = true, +}; + +static int skylake_audio_probe(struct platform_device *pdev) +{ + skylake_rt286.dev = &pdev->dev; + + return snd_soc_register_card(&skylake_rt286); +} + +static int skylake_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&skylake_rt286); + return 0; +} + +static struct platform_driver skylake_audio = { + .probe = skylake_audio_probe, + .remove = skylake_audio_remove, + .driver = { + .name = "skl_alc286s_i2s", + }, +}; + +module_platform_driver(skylake_audio) + +/* Module information */ +MODULE_AUTHOR("Omair Mohammed Abdullah "); +MODULE_DESCRIPTION("Intel SST Audio for Skylake"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_alc286s_i2s"); -- cgit v1.2.3-59-g8ed1b From 355b27e181f667dad248a40d3d4d48150e9a593c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 18 Oct 2015 10:06:11 +0800 Subject: ASoC: ad193x-spi: Add adau1328 to ad193x_spi_id table This driver also supports adau1328, thus add adau1328 to ad193x_spi_id. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x-spi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c index b55055a6a265..431f95da1de1 100644 --- a/sound/soc/codecs/ad193x-spi.c +++ b/sound/soc/codecs/ad193x-spi.c @@ -41,6 +41,7 @@ static const struct spi_device_id ad193x_spi_id[] = { { "ad1934", AD1934 }, { "ad1938", AD193X }, { "ad1939", AD193X }, + { "adau1328", AD193X }, { } }; MODULE_DEVICE_TABLE(spi, ad193x_spi_id); -- cgit v1.2.3-59-g8ed1b From da23ac1e40ce844d1a9553906bdacce160af76f6 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Tue, 29 Sep 2015 13:56:10 +0530 Subject: ALSA: hda - Add hduadio support to DEVTABLE For generating modalias entries automatically, move the definition of struct hda_device_id to linux/mod_devicetable.h and add the handling of this record in file2alias helper. The new modalias is represented with combination of vendor id, device id, and api version as "hdaudio:vNrNaN". This patch itself doesn't convert the existing modaliases. Since they were added manually, this patch won't give any regression by itself at this point. [Modified the modalias format to adapt the api_version field, and drop invalid ANY_ID definition by tiwai] Signed-off-by: Subhransu S. Prusty Reviewed-by: Vinod Koul Tested-by: Subhransu S Prusty Signed-off-by: Takashi Iwai --- include/linux/mod_devicetable.h | 8 ++++++++ include/sound/hdaudio.h | 12 +----------- scripts/mod/devicetable-offsets.c | 5 +++++ scripts/mod/file2alias.c | 17 +++++++++++++++++ sound/hda/hda_bus_type.c | 1 + 5 files changed, 32 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 688997a24aad..00825672d256 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -219,6 +219,14 @@ struct serio_device_id { __u8 proto; }; +struct hda_device_id { + __u32 vendor_id; + __u32 rev_id; + __u8 api_version; + const char *name; + unsigned long driver_data; +}; + /* * Struct used for matching a device */ diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index b35bf59a1ecc..ddca48eb02e0 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -21,23 +21,13 @@ struct hdac_stream; struct hdac_device; struct hdac_driver; struct hdac_widget_tree; +struct hda_device_id; /* * exported bus type */ extern struct bus_type snd_hda_bus_type; -/* - * HDA device table - */ -struct hda_device_id { - __u32 vendor_id; - __u32 rev_id; - __u8 api_version; - const char *name; - unsigned long driver_data; -}; - /* * generic arrays */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index e70fcd12eeeb..e1a5110bd63b 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -196,5 +196,10 @@ int main(void) DEVID_FIELD(ulpi_device_id, vendor); DEVID_FIELD(ulpi_device_id, product); + DEVID(hda_device_id); + DEVID_FIELD(hda_device_id, vendor_id); + DEVID_FIELD(hda_device_id, rev_id); + DEVID_FIELD(hda_device_id, api_version); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 5f2088209132..fc51d4bff3f8 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1250,6 +1250,23 @@ static int do_ulpi_entry(const char *filename, void *symval, } ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry); +/* Looks like: hdaudio:vNrNaN */ +static int do_hda_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, hda_device_id, vendor_id); + DEF_FIELD(symval, hda_device_id, rev_id); + DEF_FIELD(symval, hda_device_id, api_version); + + strcpy(alias, "hdaudio:"); + ADD(alias, "v", vendor_id != 0, vendor_id); + ADD(alias, "r", rev_id != 0, rev_id); + ADD(alias, "a", api_version != 0, api_version); + + add_wildcard(alias); + return 1; +} +ADD_TO_DEVTABLE("hdaudio", hda_device_id, do_hda_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index 89c2711baaaf..bcb1a79eec38 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3-59-g8ed1b From 4f9e0c38c5e991e2d050d13e28be74b93ab704c0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 16 Oct 2015 11:35:49 +0200 Subject: ALSA: hda - Add a common helper to give the codec modalias string This patch provide a new common helper function, snd_hdac_codec_modalias(), to give the codec modalias name string. This function will be used by multiple places in the later patches. Reviewed-by: Vinod Koul Tested-by: Subhransu S Prusty Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 1 + sound/hda/hdac_device.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index ddca48eb02e0..e2b712c90d3f 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -109,6 +109,7 @@ void snd_hdac_device_exit(struct hdac_device *dev); int snd_hdac_device_register(struct hdac_device *codec); void snd_hdac_device_unregister(struct hdac_device *codec); int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name); +int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size); int snd_hdac_refresh_widgets(struct hdac_device *codec); int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 4b06b26cee06..bbdb25f5bbb9 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -185,6 +185,21 @@ int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name) } EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name); +/** + * snd_hdac_codec_modalias - give the module alias name + * @codec: HDAC device + * @buf: string buffer to store + * @size: string buffer size + * + * Returns the size of string, like snprintf(), or a negative error code. + */ +int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size) +{ + return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n", + codec->vendor_id, codec->revision_id, codec->type); +} +EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias); + /** * snd_hdac_make_cmd - compose a 32bit command word to be sent to the * HD-audio controller -- cgit v1.2.3-59-g8ed1b From 78abb2afaf3d7635e9b1770c50e50b59820c4e5d Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Tue, 29 Sep 2015 13:56:47 +0530 Subject: ALSA: hda - Add hdaudio bus modalias support This patch just adds modalias sysfs entry to each hdaudio bus entry. [rewritten to call the common helper function by tiwai] Signed-off-by: Subhransu S. Prusty Reviewed-by: Vinod Koul Tested-by: Subhransu S Prusty Signed-off-by: Takashi Iwai --- sound/hda/hdac_sysfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound') diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index c71142dea98a..42d61bf41969 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -45,6 +45,13 @@ CODEC_ATTR(mfg); CODEC_ATTR_STR(vendor_name); CODEC_ATTR_STR(chip_name); +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256); +} +static DEVICE_ATTR_RO(modalias); + static struct attribute *hdac_dev_attrs[] = { &dev_attr_type.attr, &dev_attr_vendor_id.attr, @@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = { &dev_attr_mfg.attr, &dev_attr_vendor_name.attr, &dev_attr_chip_name.attr, + &dev_attr_modalias.attr, NULL }; -- cgit v1.2.3-59-g8ed1b From b9a94a9c787d053e8d3bb7e7dff9648e723a4533 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 1 Oct 2015 16:20:04 +0200 Subject: ALSA: hda - convert to hda_device_id Finally we have a proper infrastructure to generate the modaliases automatically, let's move to hda_device_id from the legacy hda_codec_preset that contains basically the same information. The patch function hook is stored in driver_data field, which is long, and we need an explicit cast. Other than that, the conversion is mostly straightforward. Each entry is even simplified using a macro, and the lengthy (and error-prone) manual modaliases got removed. As a result, we achieved a quite good diet: 14 files changed, 407 insertions(+), 595 deletions(-) Reviewed-by: Vinod Koul Tested-by: Subhransu S Prusty Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_bind.c | 32 +++--- sound/pci/hda/hda_codec.h | 19 ++-- sound/pci/hda/hda_generic.c | 7 +- sound/pci/hda/patch_analog.c | 37 ++++--- sound/pci/hda/patch_ca0110.c | 15 ++- sound/pci/hda/patch_ca0132.c | 9 +- sound/pci/hda/patch_cirrus.c | 21 ++-- sound/pci/hda/patch_cmedia.c | 15 ++- sound/pci/hda/patch_conexant.c | 118 ++++++----------------- sound/pci/hda/patch_hdmi.c | 201 ++++++++++++++------------------------ sound/pci/hda/patch_realtek.c | 122 +++++++++++------------ sound/pci/hda/patch_si3054.c | 39 +++----- sound/pci/hda/patch_sigmatel.c | 214 ++++++++++++++++++++--------------------- sound/pci/hda/patch_via.c | 153 +++++++++++------------------ 14 files changed, 407 insertions(+), 595 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 021bcce59447..152acdaa0a45 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -15,21 +15,22 @@ #include "hda_local.h" /* - * find a matching codec preset + * find a matching codec id */ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) { struct hda_codec *codec = container_of(dev, struct hda_codec, core); struct hda_codec_driver *driver = container_of(drv, struct hda_codec_driver, core); - const struct hda_codec_preset *preset; + const struct hda_device_id *list; /* check probe_id instead of vendor_id if set */ u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; + u32 rev_id = codec->core.revision_id; - for (preset = driver->preset; preset->id; preset++) { - if (preset->id == id && - (!preset->rev || preset->rev == codec->core.revision_id)) { - codec->preset = preset; + for (list = driver->id; list->vendor_id; list++) { + if (list->vendor_id == id && + (!list->rev_id || list->rev_id == rev_id)) { + codec->preset = list; return 1; } } @@ -77,6 +78,7 @@ static int hda_codec_driver_probe(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); struct module *owner = dev->driver->owner; + hda_codec_patch_t patch; int err; if (WARN_ON(!codec->preset)) @@ -94,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev) goto error; } - err = codec->preset->patch(codec); - if (err < 0) - goto error_module; + patch = (hda_codec_patch_t)codec->preset->driver_data; + if (patch) { + err = patch(codec); + if (err < 0) + goto error_module; + } err = snd_hda_codec_build_pcms(codec); if (err < 0) @@ -173,11 +178,10 @@ static inline bool codec_probed(struct hda_codec *codec) static void codec_bind_module(struct hda_codec *codec) { #ifdef MODULE - request_module("snd-hda-codec-id:%08x", codec->core.vendor_id); - if (codec_probed(codec)) - return; - request_module("snd-hda-codec-id:%04x*", - (codec->core.vendor_id >> 16) & 0xffff); + char modalias[32]; + + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + request_module(modalias); if (codec_probed(codec)) return; #endif diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 891864ac60f5..055a80522282 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -22,6 +22,7 @@ #define __SOUND_HDA_CODEC_H #include +#include #include #include #include @@ -80,19 +81,21 @@ struct hda_bus { * Known codecs have the patch to build and set up the controls/PCMs * better than the generic parser. */ -struct hda_codec_preset { - unsigned int id; - unsigned int rev; - const char *name; - int (*patch)(struct hda_codec *codec); -}; +typedef int (*hda_codec_patch_t)(struct hda_codec *); #define HDA_CODEC_ID_GENERIC_HDMI 0x00000101 #define HDA_CODEC_ID_GENERIC 0x00000201 +#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \ + { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \ + .api_version = HDA_DEV_LEGACY, \ + .driver_data = (unsigned long)(_patch) } +#define HDA_CODEC_ENTRY(_vid, _name, _patch) \ + HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch) + struct hda_codec_driver { struct hdac_driver core; - const struct hda_codec_preset *preset; + const struct hda_device_id *id; }; int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, @@ -183,7 +186,7 @@ struct hda_codec { u32 probe_id; /* overridden id for probing */ /* detected preset */ - const struct hda_codec_preset *preset; + const struct hda_device_id *preset; const char *modelname; /* model name for preset */ /* set by patch */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 24f91114a32c..c6e8a651cea1 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5877,13 +5877,14 @@ error: return err; } -static const struct hda_codec_preset snd_hda_preset_generic[] = { - { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec }, +static const struct hda_device_id snd_hda_id_generic[] = { + HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), {} /* terminator */ }; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); static struct hda_codec_driver generic_driver = { - .preset = snd_hda_preset_generic, + .id = snd_hda_id_generic, }; module_hda_codec_driver(generic_driver); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index c033a4ee6547..e0fb8c6d1bc2 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1165,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_analog[] = { - { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 }, - { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, - { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 }, - { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, - { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 }, - { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 }, - { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, - { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, - { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 }, - { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, - { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, - { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, - { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 }, - { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 }, - { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, +static const struct hda_device_id snd_hda_id_analog[] = { + HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884), + HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882), + HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884), + HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884), + HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884), + HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884), + HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981), + HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983), + HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884), + HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a), + HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988), + HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988), + HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882), + HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988), + HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:11d4*"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Analog Devices HD-audio codec"); static struct hda_codec_driver analog_driver = { - .preset = snd_hda_preset_analog, + .id = snd_hda_id_analog, }; module_hda_codec_driver(analog_driver); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 484bbf4134cd..c2d9ee9cfdc0 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -83,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_ca0110[] = { - { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 }, - { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 }, - { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 }, +static const struct hda_device_id snd_hda_id_ca0110[] = { + HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110), + HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110), + HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:1102000a"); -MODULE_ALIAS("snd-hda-codec-id:1102000b"); -MODULE_ALIAS("snd-hda-codec-id:1102000d"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); static struct hda_codec_driver ca0110_driver = { - .preset = snd_hda_preset_ca0110, + .id = snd_hda_id_ca0110, }; module_hda_codec_driver(ca0110_driver); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 186792fe226e..3a02e5c14d09 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4778,18 +4778,17 @@ static int patch_ca0132(struct hda_codec *codec) /* * patch entries */ -static struct hda_codec_preset snd_hda_preset_ca0132[] = { - { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 }, +static struct hda_device_id snd_hda_id_ca0132[] = { + HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:11020011"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Creative Sound Core3D codec"); static struct hda_codec_driver ca0132_driver = { - .preset = snd_hda_preset_ca0132, + .id = snd_hda_id_ca0132, }; module_hda_codec_driver(ca0132_driver); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 01c464a468f6..a12ae8ac0914 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1201,26 +1201,21 @@ static int patch_cs4213(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_cirrus[] = { - { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x }, - { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x }, - { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 }, - { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 }, - { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 }, +static const struct hda_device_id snd_hda_id_cirrus[] = { + HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x), + HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x), + HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208), + HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210), + HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:10134206"); -MODULE_ALIAS("snd-hda-codec-id:10134207"); -MODULE_ALIAS("snd-hda-codec-id:10134208"); -MODULE_ALIAS("snd-hda-codec-id:10134210"); -MODULE_ALIAS("snd-hda-codec-id:10134213"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); static struct hda_codec_driver cirrus_driver = { - .preset = snd_hda_preset_cirrus, + .id = snd_hda_id_cirrus, }; module_hda_codec_driver(cirrus_driver); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index f5ed078710f8..1b2195dd2b26 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_cmedia[] = { - { .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 }, - { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 }, - { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 }, +static const struct hda_device_id snd_hda_id_cmedia[] = { + HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888), + HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880), + HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:13f68888"); -MODULE_ALIAS("snd-hda-codec-id:13f69880"); -MODULE_ALIAS("snd-hda-codec-id:434d4980"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("C-Media HD-audio codec"); static struct hda_codec_driver cmedia_driver = { - .preset = snd_hda_preset_cmedia, + .id = snd_hda_id_cmedia, }; module_hda_codec_driver(cmedia_driver); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2f0ec7c45fc7..c8b8ef5246a6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -954,100 +954,44 @@ static int patch_conexant_auto(struct hda_codec *codec) /* */ -static const struct hda_codec_preset snd_hda_preset_conexant[] = { - { .id = 0x14f15045, .name = "CX20549 (Venice)", - .patch = patch_conexant_auto }, - { .id = 0x14f15047, .name = "CX20551 (Waikiki)", - .patch = patch_conexant_auto }, - { .id = 0x14f15051, .name = "CX20561 (Hermosa)", - .patch = patch_conexant_auto }, - { .id = 0x14f15066, .name = "CX20582 (Pebble)", - .patch = patch_conexant_auto }, - { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", - .patch = patch_conexant_auto }, - { .id = 0x14f15068, .name = "CX20584", - .patch = patch_conexant_auto }, - { .id = 0x14f15069, .name = "CX20585", - .patch = patch_conexant_auto }, - { .id = 0x14f1506c, .name = "CX20588", - .patch = patch_conexant_auto }, - { .id = 0x14f1506e, .name = "CX20590", - .patch = patch_conexant_auto }, - { .id = 0x14f15097, .name = "CX20631", - .patch = patch_conexant_auto }, - { .id = 0x14f15098, .name = "CX20632", - .patch = patch_conexant_auto }, - { .id = 0x14f150a1, .name = "CX20641", - .patch = patch_conexant_auto }, - { .id = 0x14f150a2, .name = "CX20642", - .patch = patch_conexant_auto }, - { .id = 0x14f150ab, .name = "CX20651", - .patch = patch_conexant_auto }, - { .id = 0x14f150ac, .name = "CX20652", - .patch = patch_conexant_auto }, - { .id = 0x14f150b8, .name = "CX20664", - .patch = patch_conexant_auto }, - { .id = 0x14f150b9, .name = "CX20665", - .patch = patch_conexant_auto }, - { .id = 0x14f150f1, .name = "CX20721", - .patch = patch_conexant_auto }, - { .id = 0x14f150f2, .name = "CX20722", - .patch = patch_conexant_auto }, - { .id = 0x14f150f3, .name = "CX20723", - .patch = patch_conexant_auto }, - { .id = 0x14f150f4, .name = "CX20724", - .patch = patch_conexant_auto }, - { .id = 0x14f1510f, .name = "CX20751/2", - .patch = patch_conexant_auto }, - { .id = 0x14f15110, .name = "CX20751/2", - .patch = patch_conexant_auto }, - { .id = 0x14f15111, .name = "CX20753/4", - .patch = patch_conexant_auto }, - { .id = 0x14f15113, .name = "CX20755", - .patch = patch_conexant_auto }, - { .id = 0x14f15114, .name = "CX20756", - .patch = patch_conexant_auto }, - { .id = 0x14f15115, .name = "CX20757", - .patch = patch_conexant_auto }, - { .id = 0x14f151d7, .name = "CX20952", - .patch = patch_conexant_auto }, +static const struct hda_device_id snd_hda_id_conexant[] = { + HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:14f15045"); -MODULE_ALIAS("snd-hda-codec-id:14f15047"); -MODULE_ALIAS("snd-hda-codec-id:14f15051"); -MODULE_ALIAS("snd-hda-codec-id:14f15066"); -MODULE_ALIAS("snd-hda-codec-id:14f15067"); -MODULE_ALIAS("snd-hda-codec-id:14f15068"); -MODULE_ALIAS("snd-hda-codec-id:14f15069"); -MODULE_ALIAS("snd-hda-codec-id:14f1506c"); -MODULE_ALIAS("snd-hda-codec-id:14f1506e"); -MODULE_ALIAS("snd-hda-codec-id:14f15097"); -MODULE_ALIAS("snd-hda-codec-id:14f15098"); -MODULE_ALIAS("snd-hda-codec-id:14f150a1"); -MODULE_ALIAS("snd-hda-codec-id:14f150a2"); -MODULE_ALIAS("snd-hda-codec-id:14f150ab"); -MODULE_ALIAS("snd-hda-codec-id:14f150ac"); -MODULE_ALIAS("snd-hda-codec-id:14f150b8"); -MODULE_ALIAS("snd-hda-codec-id:14f150b9"); -MODULE_ALIAS("snd-hda-codec-id:14f150f1"); -MODULE_ALIAS("snd-hda-codec-id:14f150f2"); -MODULE_ALIAS("snd-hda-codec-id:14f150f3"); -MODULE_ALIAS("snd-hda-codec-id:14f150f4"); -MODULE_ALIAS("snd-hda-codec-id:14f1510f"); -MODULE_ALIAS("snd-hda-codec-id:14f15110"); -MODULE_ALIAS("snd-hda-codec-id:14f15111"); -MODULE_ALIAS("snd-hda-codec-id:14f15113"); -MODULE_ALIAS("snd-hda-codec-id:14f15114"); -MODULE_ALIAS("snd-hda-codec-id:14f15115"); -MODULE_ALIAS("snd-hda-codec-id:14f151d7"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); static struct hda_codec_driver conexant_driver = { - .preset = snd_hda_preset_conexant, + .id = snd_hda_id_conexant, }; module_hda_codec_driver(conexant_driver); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 3a2d4a5a1714..f503a883bef3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2580,7 +2580,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hdmi_spec *spec = codec->spec; struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; - switch (codec->preset->id) { + switch (codec->preset->vendor_id) { case 0x10de0002: case 0x10de0003: case 0x10de0005: @@ -2898,7 +2898,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) snd_pcm_alt_chmaps, 8, 0, &chmap); if (err < 0) return err; - switch (codec->preset->id) { + switch (codec->preset->vendor_id) { case 0x10de0002: case 0x10de0003: case 0x10de0005: @@ -3506,138 +3506,77 @@ static int patch_via_hdmi(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_hdmi[] = { -{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, -{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, -{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, -{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, -{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, -{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi }, -{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi }, +static const struct hda_device_id snd_hda_id_hdmi[] = { +HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi), /* 17 is known to be absent */ -{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi }, -{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi }, -{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi }, -{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi }, -{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, -{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, -{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, -{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, -{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi }, -{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x8086280a, .name = "Broxton HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi }, -{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, +HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch), +HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), +HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), +HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), +HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), /* special ID for generic HDMI */ -{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi }, +HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:1002793c"); -MODULE_ALIAS("snd-hda-codec-id:10027919"); -MODULE_ALIAS("snd-hda-codec-id:1002791a"); -MODULE_ALIAS("snd-hda-codec-id:1002aa01"); -MODULE_ALIAS("snd-hda-codec-id:10951390"); -MODULE_ALIAS("snd-hda-codec-id:10951392"); -MODULE_ALIAS("snd-hda-codec-id:10de0002"); -MODULE_ALIAS("snd-hda-codec-id:10de0003"); -MODULE_ALIAS("snd-hda-codec-id:10de0005"); -MODULE_ALIAS("snd-hda-codec-id:10de0006"); -MODULE_ALIAS("snd-hda-codec-id:10de0007"); -MODULE_ALIAS("snd-hda-codec-id:10de000a"); -MODULE_ALIAS("snd-hda-codec-id:10de000b"); -MODULE_ALIAS("snd-hda-codec-id:10de000c"); -MODULE_ALIAS("snd-hda-codec-id:10de000d"); -MODULE_ALIAS("snd-hda-codec-id:10de0010"); -MODULE_ALIAS("snd-hda-codec-id:10de0011"); -MODULE_ALIAS("snd-hda-codec-id:10de0012"); -MODULE_ALIAS("snd-hda-codec-id:10de0013"); -MODULE_ALIAS("snd-hda-codec-id:10de0014"); -MODULE_ALIAS("snd-hda-codec-id:10de0015"); -MODULE_ALIAS("snd-hda-codec-id:10de0016"); -MODULE_ALIAS("snd-hda-codec-id:10de0018"); -MODULE_ALIAS("snd-hda-codec-id:10de0019"); -MODULE_ALIAS("snd-hda-codec-id:10de001a"); -MODULE_ALIAS("snd-hda-codec-id:10de001b"); -MODULE_ALIAS("snd-hda-codec-id:10de001c"); -MODULE_ALIAS("snd-hda-codec-id:10de0028"); -MODULE_ALIAS("snd-hda-codec-id:10de0040"); -MODULE_ALIAS("snd-hda-codec-id:10de0041"); -MODULE_ALIAS("snd-hda-codec-id:10de0042"); -MODULE_ALIAS("snd-hda-codec-id:10de0043"); -MODULE_ALIAS("snd-hda-codec-id:10de0044"); -MODULE_ALIAS("snd-hda-codec-id:10de0051"); -MODULE_ALIAS("snd-hda-codec-id:10de0060"); -MODULE_ALIAS("snd-hda-codec-id:10de0067"); -MODULE_ALIAS("snd-hda-codec-id:10de0070"); -MODULE_ALIAS("snd-hda-codec-id:10de0071"); -MODULE_ALIAS("snd-hda-codec-id:10de0072"); -MODULE_ALIAS("snd-hda-codec-id:10de007d"); -MODULE_ALIAS("snd-hda-codec-id:10de8001"); -MODULE_ALIAS("snd-hda-codec-id:11069f80"); -MODULE_ALIAS("snd-hda-codec-id:11069f81"); -MODULE_ALIAS("snd-hda-codec-id:11069f84"); -MODULE_ALIAS("snd-hda-codec-id:11069f85"); -MODULE_ALIAS("snd-hda-codec-id:17e80047"); -MODULE_ALIAS("snd-hda-codec-id:80860054"); -MODULE_ALIAS("snd-hda-codec-id:80862801"); -MODULE_ALIAS("snd-hda-codec-id:80862802"); -MODULE_ALIAS("snd-hda-codec-id:80862803"); -MODULE_ALIAS("snd-hda-codec-id:80862804"); -MODULE_ALIAS("snd-hda-codec-id:80862805"); -MODULE_ALIAS("snd-hda-codec-id:80862806"); -MODULE_ALIAS("snd-hda-codec-id:80862807"); -MODULE_ALIAS("snd-hda-codec-id:80862808"); -MODULE_ALIAS("snd-hda-codec-id:80862809"); -MODULE_ALIAS("snd-hda-codec-id:8086280a"); -MODULE_ALIAS("snd-hda-codec-id:80862880"); -MODULE_ALIAS("snd-hda-codec-id:80862882"); -MODULE_ALIAS("snd-hda-codec-id:80862883"); -MODULE_ALIAS("snd-hda-codec-id:808629fb"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("HDMI HD-audio codec"); @@ -3646,7 +3585,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi"); MODULE_ALIAS("snd-hda-codec-atihdmi"); static struct hda_codec_driver hdmi_driver = { - .preset = snd_hda_preset_hdmi, + .id = snd_hda_id_hdmi, }; module_hda_codec_driver(hdmi_driver); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e1ffb0202ebc..0214b7df6d9d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6617,78 +6617,70 @@ static int patch_alc680(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_realtek[] = { - { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 }, - { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 }, - { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 }, - { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 }, - { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 }, - { .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 }, - { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, - { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, - { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, - { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, - { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, - { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 }, - { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 }, - { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 }, - { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 }, - { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 }, - { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 }, - { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 }, - { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 }, - { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 }, - { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 }, - { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 }, - { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 }, - { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 }, - { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 }, - { .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 }, - { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", - .patch = patch_alc861 }, - { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, - { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, - { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, - { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2", - .patch = patch_alc882 }, - { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", - .patch = patch_alc662 }, - { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3", - .patch = patch_alc662 }, - { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, - { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 }, - { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 }, - { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 }, - { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 }, - { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 }, - { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 }, - { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 }, - { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, - { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, - { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, - { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A", - .patch = patch_alc882 }, - { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", - .patch = patch_alc882 }, - { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, - { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 }, - { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", - .patch = patch_alc882 }, - { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 }, - { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 }, - { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 }, - { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 }, - { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 }, +static const struct hda_device_id snd_hda_id_realtek[] = { + HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), + HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), + HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), + HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), + HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), + HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), + HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), + HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), + HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd), + HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662), + HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680), + HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880), + HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:10ec*"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek HD-audio codec"); static struct hda_codec_driver realtek_driver = { - .preset = snd_hda_preset_realtek, + .id = snd_hda_id_realtek, }; module_hda_codec_driver(realtek_driver); diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 5104bebb2286..ffda38c45509 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_si3054[] = { - { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 }, +static const struct hda_device_id snd_hda_id_si3054[] = { + HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054), + HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054), /* VIA HDA on Clevo m540 */ - { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 }, + HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054), /* Asus A8J Modem (SM56) */ - { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 }, + HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054), /* LG LW20 modem */ - { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 }, + HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054), {} }; - -MODULE_ALIAS("snd-hda-codec-id:163c3055"); -MODULE_ALIAS("snd-hda-codec-id:163c3155"); -MODULE_ALIAS("snd-hda-codec-id:11c13026"); -MODULE_ALIAS("snd-hda-codec-id:11c13055"); -MODULE_ALIAS("snd-hda-codec-id:11c13155"); -MODULE_ALIAS("snd-hda-codec-id:10573055"); -MODULE_ALIAS("snd-hda-codec-id:10573057"); -MODULE_ALIAS("snd-hda-codec-id:10573155"); -MODULE_ALIAS("snd-hda-codec-id:11063288"); -MODULE_ALIAS("snd-hda-codec-id:15433155"); -MODULE_ALIAS("snd-hda-codec-id:18540018"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); static struct hda_codec_driver si3054_driver = { - .preset = snd_hda_preset_si3054, + .id = snd_hda_id_si3054, }; module_hda_codec_driver(si3054_driver); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index def5cc8dff02..08a0f6a35cca 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5012,121 +5012,119 @@ static int patch_stac9872(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_sigmatel[] = { - { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 }, - { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x }, - { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x }, - { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x }, - { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x }, - { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x }, - { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x }, - { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x }, - { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x }, - { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x }, - { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x }, - { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x }, - { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x }, - { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x }, - { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x }, - { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x }, - { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x }, - { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x }, - { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x }, - { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x }, - { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x }, - { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x }, - { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x }, - { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x }, - { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x }, - { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x }, - { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x }, - { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x }, - { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x }, - { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x }, - { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x }, - /* The following does not take into account .id=0x83847661 when subsys = - * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are - * currently not fully supported. - */ - { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 }, - { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 }, - { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 }, - { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 }, - { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 }, - { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 }, - { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 }, - { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 }, - { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 }, - { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 }, - { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 }, - { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, - { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx}, - { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx}, - { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx}, - { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 }, - { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, - { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx }, - { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx }, - { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx }, - { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx}, - { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx}, +static const struct hda_device_id snd_hda_id_sigmatel[] = { + HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200), + HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x), + HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x), + HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x), + HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x), + HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x), + HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x), + HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x), + HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x), + HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x), + HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x), + HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x), + HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x), + HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x), + HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x), + HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x), + HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x), + HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x), + HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x), + HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x), + HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x), + HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x), + HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x), + HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x), + HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x), + HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x), + HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x), + HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x), + HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x), + HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x), + HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x), + /* The following does not take into account .id=0x83847661 when subsys = + * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are + * currently not fully supported. + */ + HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872), + HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872), + HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872), + HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205), + HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205), + HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95), + HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx), + HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx), + HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx), + HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:8384*"); -MODULE_ALIAS("snd-hda-codec-id:111d*"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); static struct hda_codec_driver sigmatel_driver = { - .preset = snd_hda_preset_sigmatel, + .id = snd_hda_id_sigmatel, }; module_hda_codec_driver(sigmatel_driver); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d714a57e9460..fc30d1e8aa76 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1200,109 +1200,64 @@ static int patch_vt3476(struct hda_codec *codec) /* * patch entries */ -static const struct hda_codec_preset snd_hda_preset_via[] = { - { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, - { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, - { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, - { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, - { .id = 0x1106e710, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e711, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e712, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e713, .name = "VT1709 10-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e714, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e715, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e716, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e717, .name = "VT1709 6-Ch", - .patch = patch_vt1709}, - { .id = 0x1106e720, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e721, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e722, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e723, .name = "VT1708B 8-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e724, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e725, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e726, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x1106e727, .name = "VT1708B 4-Ch", - .patch = patch_vt1708B}, - { .id = 0x11060397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11061397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11062397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11063397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11064397, .name = "VT1705", - .patch = patch_vt1708S}, - { .id = 0x11065397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11066397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11067397, .name = "VT1708S", - .patch = patch_vt1708S}, - { .id = 0x11060398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11061398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11062398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11063398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11064398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11065398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11066398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11067398, .name = "VT1702", - .patch = patch_vt1702}, - { .id = 0x11060428, .name = "VT1718S", - .patch = patch_vt1718S}, - { .id = 0x11064428, .name = "VT1718S", - .patch = patch_vt1718S}, - { .id = 0x11060441, .name = "VT2020", - .patch = patch_vt1718S}, - { .id = 0x11064441, .name = "VT1828S", - .patch = patch_vt1718S}, - { .id = 0x11060433, .name = "VT1716S", - .patch = patch_vt1716S}, - { .id = 0x1106a721, .name = "VT1716S", - .patch = patch_vt1716S}, - { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, - { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, - { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, - { .id = 0x11060440, .name = "VT1818S", - .patch = patch_vt1708S}, - { .id = 0x11060446, .name = "VT1802", - .patch = patch_vt2002P}, - { .id = 0x11068446, .name = "VT1802", - .patch = patch_vt2002P}, - { .id = 0x11064760, .name = "VT1705CF", - .patch = patch_vt3476}, - { .id = 0x11064761, .name = "VT1708SCE", - .patch = patch_vt3476}, - { .id = 0x11064762, .name = "VT1808", - .patch = patch_vt3476}, +static const struct hda_device_id snd_hda_id_via[] = { + HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), + HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), + HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), + HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), + HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), + HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), + HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), + HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), + HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), + HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), + HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), + HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), + HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), + HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), + HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), + HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), + HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), + HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), + HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), + HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), + HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), + HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), + HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), {} /* terminator */ }; - -MODULE_ALIAS("snd-hda-codec-id:1106*"); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); static struct hda_codec_driver via_driver = { - .preset = snd_hda_preset_via, + .id = snd_hda_id_via, }; MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 975c947e75f09ca4910053b626be2376e3313cf2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 17 Sep 2015 12:00:05 +0200 Subject: ALSA: hda - Advertise MODALIAS in uevent By setting the MODALIAS variable in uevents, userspace helpers will be enabled to load modules via the module alias associated with a device. This information is required to automatically load HDA codec drivers instead of having to explicitly request the various modules in the HDA core code. [Note that currently the legacy HDA controller driver tries to bind codec modules manually. It's for supporting the fallback generic drivers. This new udev modalias support was added rather for ASoC HDA ext drivers, since this addition itself won't hurt the legacy HDA -- tiwai] [Use the common helper function to generate the modalias -- tiwai] Signed-off-by: Thierry Reding Reviewed-by: Vinod Koul Tested-by: Subhransu S Prusty Signed-off-by: Takashi Iwai --- sound/hda/hda_bus_type.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'sound') diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index bcb1a79eec38..3060e2aee36f 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -64,9 +64,21 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv) return 1; } +static int hda_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char modalias[32]; + + snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias, + sizeof(modalias)); + if (add_uevent_var(env, "MODALIAS=%s", modalias)) + return -ENOMEM; + return 0; +} + struct bus_type snd_hda_bus_type = { .name = "hdaudio", .match = hda_bus_match, + .uevent = hda_uevent, }; EXPORT_SYMBOL_GPL(snd_hda_bus_type); -- cgit v1.2.3-59-g8ed1b From 9f9c5617c8f7dfd1190c438894a8701db25f171f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 20 Oct 2015 23:46:55 +0900 Subject: ALSA: firewire-tascam: remove buffer initialization in driver side The given buffer to callback function is cleared in caller side. This commit removes buffer initialization in callee side. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index d4f64ae182e7..370f9323b3df 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -67,8 +67,6 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) u8 status; int consume; - buf[0] = buf[1] = buf[2] = buf[3] = 0x00; - len = snd_rawmidi_transmit_peek(substream, buf + 1, 3); if (len == 0) return 0; -- cgit v1.2.3-59-g8ed1b From 516a30615635fdec6a298d7b7cedca1270bc641c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 20 Oct 2015 23:46:56 +0900 Subject: ALSA: firewire-tascam: change type of valiables according to function prototype In the callback function of asynchronous MIDI port, some local variables are declared 'unsigned int', while they're assigned to int value of return from snd_rawmidi_transmit_peek(). This commit fixes the type. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 370f9323b3df..7c8068469f57 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -62,10 +62,8 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) { struct snd_tscm *tscm = substream->rmidi->private_data; unsigned int port = substream->number; - unsigned int len; - unsigned int i; + int i, len, consume; u8 status; - int consume; len = snd_rawmidi_transmit_peek(substream, buf + 1, 3); if (len == 0) -- cgit v1.2.3-59-g8ed1b From b7ab614f301741ae3cb61fb6a90e290083d3b95d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 20 Oct 2015 23:46:57 +0900 Subject: ALSA: firewire-tascam: use better name for local variables to describe their intension In the callback function of asynchronous MIDI port, the intension of some local variables are not clear. This commit improves them. The 'len' variable is used to calculate the number of MIDI bytes including in the transaction. The 'consume' variable is used to return the actual number of consumed bytes in ALSA MIDI buffer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 7c8068469f57..ea88655e7e08 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -65,14 +65,14 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) int i, len, consume; u8 status; - len = snd_rawmidi_transmit_peek(substream, buf + 1, 3); - if (len == 0) + consume = snd_rawmidi_transmit_peek(substream, buf + 1, 3); + if (consume == 0) return 0; /* On exclusive message. */ if (tscm->on_sysex[port]) { /* Seek the end of exclusives. */ - for (i = 1; i < 4 || i < len; ++i) { + for (i = 1; i < 4 || i < consume; ++i) { if (buf[i] == 0xf7) { tscm->on_sysex[port] = false; break; @@ -81,14 +81,14 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) /* At the end of exclusive message, use label 0x07. */ if (!tscm->on_sysex[port]) { - len = i; + consume = i; buf[0] = (port << 4) | 0x07; /* During exclusive message, use label 0x04. */ - } else if (len == 3) { + } else if (consume == 3) { buf[0] = (port << 4) | 0x04; /* We need to fill whole 3 bytes. Go to next change. */ } else { - len = 0; + consume = 0; } } else { /* The beginning of exclusives. */ @@ -104,8 +104,8 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) status = buf[1]; /* Calculate consume bytes. */ - consume = calculate_message_bytes(status); - if (consume <= 0) + len = calculate_message_bytes(status); + if (len <= 0) return 0; /* On running-status. */ @@ -119,16 +119,16 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) } /* Confirm length. */ - if (len < consume) + if (consume < len) return 0; - if (len > consume) - len = consume; + if (consume > len) + consume = len; } buf[0] = (port << 4) | (buf[1] >> 4); } - return len; + return consume; } static void handle_midi_tx(struct fw_card *card, struct fw_request *request, -- cgit v1.2.3-59-g8ed1b From 123990e930ac1213df2dfa0e2d57cfc0e1dd5e02 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 20 Oct 2015 23:46:58 +0900 Subject: ALSA: firewire-tascam: fix loop condition with some readable variables In transactions for MIDI messages, the first byte is used for label and the rest is for MIDI bytes. In current code, these are handled correctly, while there's a small mistake for loop condition to include meaningless statement. This commit adds two local variables for them and improve the loop condition. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index ea88655e7e08..99098aa2391e 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -63,17 +63,22 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) struct snd_tscm *tscm = substream->rmidi->private_data; unsigned int port = substream->number; int i, len, consume; + u8 *label, *msg; u8 status; - consume = snd_rawmidi_transmit_peek(substream, buf + 1, 3); + /* The first byte is used for label, the rest for MIDI bytes. */ + label = buf; + msg = buf + 1; + + consume = snd_rawmidi_transmit_peek(substream, msg, 3); if (consume == 0) return 0; /* On exclusive message. */ if (tscm->on_sysex[port]) { /* Seek the end of exclusives. */ - for (i = 1; i < 4 || i < consume; ++i) { - if (buf[i] == 0xf7) { + for (i = 0; i < consume; ++i) { + if (msg[i] == 0xf7) { tscm->on_sysex[port] = false; break; } @@ -81,27 +86,27 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) /* At the end of exclusive message, use label 0x07. */ if (!tscm->on_sysex[port]) { - consume = i; - buf[0] = (port << 4) | 0x07; + consume = i + 1; + *label = (port << 4) | 0x07; /* During exclusive message, use label 0x04. */ } else if (consume == 3) { - buf[0] = (port << 4) | 0x04; + *label = (port << 4) | 0x04; /* We need to fill whole 3 bytes. Go to next change. */ } else { consume = 0; } } else { /* The beginning of exclusives. */ - if (buf[1] == 0xf0) { + if (msg[0] == 0xf0) { /* Transfer it in next chance in another condition. */ tscm->on_sysex[port] = true; return 0; } else { /* On running-status. */ - if ((buf[1] & 0x80) != 0x80) + if ((msg[0] & 0x80) != 0x80) status = tscm->running_status[port]; else - status = buf[1]; + status = msg[0]; /* Calculate consume bytes. */ len = calculate_message_bytes(status); @@ -109,13 +114,13 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) return 0; /* On running-status. */ - if ((buf[1] & 0x80) != 0x80) { - buf[3] = buf[2]; - buf[2] = buf[1]; - buf[1] = tscm->running_status[port]; + if ((msg[0] & 0x80) != 0x80) { + msg[2] = msg[1]; + msg[1] = msg[0]; + msg[0] = tscm->running_status[port]; consume--; } else { - tscm->running_status[port] = buf[1]; + tscm->running_status[port] = msg[0]; } /* Confirm length. */ @@ -125,7 +130,7 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) consume = len; } - buf[0] = (port << 4) | (buf[1] >> 4); + *label = (port << 4) | (msg[0] >> 4); } return consume; -- cgit v1.2.3-59-g8ed1b From f937b43d48f1080e39de723d15680b2ad5d7e6fd Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 20 Oct 2015 23:46:59 +0900 Subject: ALSA: firewire-tascam: clear extra MIDI bytes in an asynchronous transaction When MIDI buffer stores two or more MIDI messages, TASCAM driver transfers asynchronous transactions including one MIDI message and extra bytes from second MIDI message. This commit fixes this bug by clearing needless bytes in the buffer. The consumed bytes are already calculated correctly, thus the sequence of transactions is already correct. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/tascam/tascam-transaction.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 99098aa2391e..904ce0329fa1 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -93,8 +93,10 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) *label = (port << 4) | 0x04; /* We need to fill whole 3 bytes. Go to next change. */ } else { - consume = 0; + return 0; } + + len = consume; } else { /* The beginning of exclusives. */ if (msg[0] == 0xf0) { @@ -115,24 +117,30 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf) /* On running-status. */ if ((msg[0] & 0x80) != 0x80) { + /* Enough MIDI bytes were not retrieved. */ + if (consume < len - 1) + return 0; + consume = len - 1; + msg[2] = msg[1]; msg[1] = msg[0]; msg[0] = tscm->running_status[port]; - consume--; } else { + /* Enough MIDI bytes were not retrieved. */ + if (consume < len) + return 0; + consume = len; + tscm->running_status[port] = msg[0]; } - - /* Confirm length. */ - if (consume < len) - return 0; - if (consume > len) - consume = len; } *label = (port << 4) | (msg[0] >> 4); } + if (len > 0 && len < 3) + memset(msg + len, 0, 3 - len); + return consume; } -- cgit v1.2.3-59-g8ed1b From 92b822a499607c32ce12ffd798a095babedc3202 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 16:56:11 +0800 Subject: ASoC: ad193x: Drop .volatile_reg implementation adau193x_reg_volatile() always return false. This seems pointless because current code uses REGCACHE_NONE cache_type which is supposed to be volatile. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ad193x.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 76d7966bd72c..3a3f3f2343d7 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -418,14 +418,8 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = { .num_dapm_routes = ARRAY_SIZE(audio_paths), }; -static bool adau193x_reg_volatile(struct device *dev, unsigned int reg) -{ - return false; -} - const struct regmap_config ad193x_regmap_config = { .max_register = AD193X_NUM_REGS - 1, - .volatile_reg = adau193x_reg_volatile, }; EXPORT_SYMBOL_GPL(ad193x_regmap_config); -- cgit v1.2.3-59-g8ed1b From ce7b8dbdded8552d1fd25cc9289da361c5cc765e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 16:54:46 +0800 Subject: ASoC: ssm2518: Drop .volatile_reg implementation The implementation of ssm2518_register_volatile always returns false, this behavior is the same as no .volatile_reg callback implementation when cache_type != REGCACHE_NONE. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ssm2518.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index ddb0203fc649..86b81a60ac52 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -723,17 +723,11 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = { .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), }; -static bool ssm2518_register_volatile(struct device *dev, unsigned int reg) -{ - return false; -} - static const struct regmap_config ssm2518_regmap_config = { .val_bits = 8, .reg_bits = 8, .max_register = SSM2518_REG_DRC_9, - .volatile_reg = ssm2518_register_volatile, .cache_type = REGCACHE_RBTREE, .reg_defaults = ssm2518_reg_defaults, -- cgit v1.2.3-59-g8ed1b From 9c9cb687d9b32bc8aea4b3aec5dd7bf9f912c744 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:17 +0200 Subject: ALSA: korg1212: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. While we are at it also fix some code style issues in the affected lines. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- sound/pci/korg1212/korg1212.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 7acbc21d642a..9e1ad119a3ce 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream) spin_unlock_irqrestore(&korg1212->lock, flags); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames); + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + kPlayBufferFrames); + return 0; } @@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream) spin_unlock_irqrestore(&korg1212->lock, flags); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - kPlayBufferFrames, kPlayBufferFrames); + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + kPlayBufferFrames); return 0; } -- cgit v1.2.3-59-g8ed1b From dfcdb0280b410e4f132c8e4ef2c3e5c9c4797965 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:18 +0200 Subject: ALSA: lx6464es: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- sound/pci/lx6464es/lx6464es.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index cba89beb2b38..8b8e2e54fba3 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -234,8 +234,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream) /* the clock rate cannot be changed */ board_rate = chip->board_sample_rate; - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, - board_rate, board_rate); + err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE, + board_rate); if (err < 0) { dev_warn(chip->card->dev, "could not constrain periods\n"); -- cgit v1.2.3-59-g8ed1b From 80ec88938a2bbdd1f9d84e13b4e730abaf24f5f9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:19 +0200 Subject: ALSA: rme32: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- sound/pci/rme32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 23d7f5d30c41..cd94ac548ba3 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -831,9 +831,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = { static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime) { if (! rme32->fullduplex_mode) { - snd_pcm_hw_constraint_minmax(runtime, + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + RME32_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); -- cgit v1.2.3-59-g8ed1b From 1a8e41efe3f403678cd6099fea8a05af7391de4c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:20 +0200 Subject: ALSA: rme96: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- sound/pci/rme96.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 2306ccf7281e..714df906249e 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1152,13 +1152,13 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96, { unsigned int size; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME96_BUFFER_SIZE); if ((size = rme96->playback_periodsize) != 0 || (size = rme96->capture_periodsize) != 0) - snd_pcm_hw_constraint_minmax(runtime, + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - size, size); + size); else snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -- cgit v1.2.3-59-g8ed1b From b4ffc1be9fde347f0407b96a7074a8dc2bf61a8a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:21 +0200 Subject: ALSA: rme9652: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdspm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index cb666c73712d..8bc8016c173d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6080,18 +6080,17 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32, 4096); /* RayDAT & AIO have a fixed buffer of 16384 samples per channel */ - snd_pcm_hw_constraint_minmax(runtime, + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - 16384, 16384); + 16384); break; default: snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 64, 8192); - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIODS, - 2, 2); + snd_pcm_hw_constraint_single(runtime, + SNDRV_PCM_HW_PARAM_PERIODS, 2); break; } -- cgit v1.2.3-59-g8ed1b From 153e2f5ca1ad632693e6a560546f27bddd91f6d8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:22 +0200 Subject: ASoC: adav80x: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/codecs/adav80x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 198c924551b7..acff8d62059c 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -728,8 +728,8 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream, if (!snd_soc_codec_is_active(codec) || !adav80x->rate) return 0; - return snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate); + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, adav80x->rate); } static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, -- cgit v1.2.3-59-g8ed1b From e795d83155ec2f45b52977ab492c42647e4dc3cf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:23 +0200 Subject: ASoC: twl4030: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer and is slightly shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/codecs/twl4030.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 2713e1845cbc..a5a4e9f75c57 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1612,19 +1612,16 @@ static void twl4030_constraints(struct twl4030_priv *twl4030, return; /* Set the constraints according to the already configured stream */ - snd_pcm_hw_constraint_minmax(slv_substream->runtime, + snd_pcm_hw_constraint_single(slv_substream->runtime, SNDRV_PCM_HW_PARAM_RATE, - twl4030->rate, twl4030->rate); - snd_pcm_hw_constraint_minmax(slv_substream->runtime, + snd_pcm_hw_constraint_single(slv_substream->runtime, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - twl4030->sample_bits, twl4030->sample_bits); - snd_pcm_hw_constraint_minmax(slv_substream->runtime, + snd_pcm_hw_constraint_single(slv_substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, - twl4030->channels, twl4030->channels); } @@ -1669,9 +1666,9 @@ static int twl4030_startup(struct snd_pcm_substream *substream, /* In option2 4 channel is not supported, set the * constraint for the first stream for channels, the * second stream will 'inherit' this cosntraint */ - snd_pcm_hw_constraint_minmax(substream->runtime, + snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, - 2, 2); + 2); } twl4030->master_substream = substream; } -- cgit v1.2.3-59-g8ed1b From 0de8ab983faa847fff0d7c5ba4e1b8678ddb09ca Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:24 +0200 Subject: ASoC: uda134x: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer and is slightly shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/codecs/uda134x.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index e19026380534..e4c694c758b8 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -150,14 +150,12 @@ static int uda134x_startup(struct snd_pcm_substream *substream, master_runtime->sample_bits, master_runtime->rate); - snd_pcm_hw_constraint_minmax(substream->runtime, + snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, - master_runtime->rate, master_runtime->rate); - snd_pcm_hw_constraint_minmax(substream->runtime, + snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - master_runtime->sample_bits, master_runtime->sample_bits); uda134x->slave_substream = substream; -- cgit v1.2.3-59-g8ed1b From 95c68b86befd1fe796c7928f23d7b5f3f8ddf900 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:25 +0200 Subject: ASoC: wl1273: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/codecs/wl1273.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 80fb1dc81f6c..7693c1129bab 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -307,11 +307,10 @@ static int wl1273_startup(struct snd_pcm_substream *substream, switch (wl1273->mode) { case WL1273_MODE_BT: - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - 8000, 8000); - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1); + snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 8000); + snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 1); break; case WL1273_MODE_FM_RX: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -- cgit v1.2.3-59-g8ed1b From 8dfabe7ab17a7c731bcb560fa42f9e4b745eeadf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:26 +0200 Subject: ASoC: n810: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer and is slightly shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/omap/n810.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index dcb5336b5698..190f868e78b2 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -99,8 +99,7 @@ static int n810_startup(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); n810_ext_control(&rtd->card->dapm); return clk_prepare_enable(sys_clkout2); -- cgit v1.2.3-59-g8ed1b From be448b4fa464418b14cd1a05771bd5f5bf8e1480 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:27 +0200 Subject: ASoC: rx51: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer and is slightly shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/omap/rx51.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 3bebfb1d3a6f..20728d07de5e 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -107,8 +107,7 @@ static int rx51_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); rx51_ext_control(&card->dapm); return 0; -- cgit v1.2.3-59-g8ed1b From 4dcdd43b460304b0555a99fdaae7bb931cb71d8f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:28 +0200 Subject: ASoC: pcm: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer and is slightly shorter. Signed-off-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/soc-pcm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 70e4b9d8bdcd..1e5beef6dbcc 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -182,9 +182,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate); - ret = snd_pcm_hw_constraint_minmax(substream->runtime, + ret = snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, - soc_dai->rate, soc_dai->rate); + soc_dai->rate); if (ret < 0) { dev_err(soc_dai->dev, "ASoC: Unable to apply rate constraint: %d\n", @@ -198,9 +198,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", soc_dai->channels); - ret = snd_pcm_hw_constraint_minmax(substream->runtime, + ret = snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, - soc_dai->channels, soc_dai->channels); if (ret < 0) { dev_err(soc_dai->dev, @@ -215,9 +214,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", soc_dai->sample_bits); - ret = snd_pcm_hw_constraint_minmax(substream->runtime, + ret = snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - soc_dai->sample_bits, soc_dai->sample_bits); if (ret < 0) { dev_err(soc_dai->dev, -- cgit v1.2.3-59-g8ed1b From 1bf2d35b87315431e8eb1a461783f7939520a7c5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:29 +0200 Subject: ASoC: ux500: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function instead of calling snd_pcm_hw_constraint_minmax() with the same value for min and max to install a constraint that limits the possible configuration values to a single value. Using snd_pcm_hw_constraint_single() makes the indented result clearer. Signed-off-by: Lars-Peter Clausen Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/ux500/ux500_msp_dai.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index f5df08ded770..9e5726b9e070 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -522,9 +522,9 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, slots_active = hweight32(mask); dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); - snd_pcm_hw_constraint_minmax(runtime, + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, - slots_active, slots_active); + slots_active); break; default: -- cgit v1.2.3-59-g8ed1b From d0a1b6607480e9ded3df22d09ff77bc55db586f7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:30 +0200 Subject: ASoC: bytcr_rt5640: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function rather than installing a list constraint with a single value. Since snd_pcm_hw_constraint_single() sets a static constraint while snd_pcm_hw_constraint_list() sets a dynamic constraint the former is slightly more efficient and it also needs less code. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/intel/boards/bytcr_rt5640.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index c4453120b11a..7a5c9a36c1db 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -117,20 +117,10 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int rates_48000[] = { - 48000, -}; - -static struct snd_pcm_hw_constraint_list constraints_48000 = { - .count = ARRAY_SIZE(rates_48000), - .list = rates_48000, -}; - static int byt_aif1_startup(struct snd_pcm_substream *substream) { - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_48000); + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); } static struct snd_soc_ops byt_aif1_ops = { -- cgit v1.2.3-59-g8ed1b From a6553a24d44f30aa0ae3ca3bff736d346c9d42d6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:31 +0200 Subject: ASoC: cht_bsw_max98090: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function rather than installing a list constraint with a single value. Since snd_pcm_hw_constraint_single() sets a static constraint while snd_pcm_hw_constraint_list() sets a dynamic constraint the former is slightly more efficient and it also needs less code. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/intel/boards/cht_bsw_max98090_ti.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 49f4869cec48..4e2fcf188dd1 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -193,20 +193,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int rates_48000[] = { - 48000, -}; - -static struct snd_pcm_hw_constraint_list constraints_48000 = { - .count = ARRAY_SIZE(rates_48000), - .list = rates_48000, -}; - static int cht_aif1_startup(struct snd_pcm_substream *substream) { - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_48000); + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); } static int cht_max98090_headset_init(struct snd_soc_component *component) -- cgit v1.2.3-59-g8ed1b From 3d6a76c48e7b6a80f1311a7ec71db0c6208c6de4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:32 +0200 Subject: ASoC: cht_bsw_rt5645: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function rather than installing a list constraint with a single value. Since snd_pcm_hw_constraint_single() sets a static constraint while snd_pcm_hw_constraint_list() sets a dynamic constraint the former is slightly more efficient and it also needs less code. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/intel/boards/cht_bsw_rt5645.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 7be8461e4d3b..38d65a3529c4 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -235,20 +235,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int rates_48000[] = { - 48000, -}; - -static struct snd_pcm_hw_constraint_list constraints_48000 = { - .count = ARRAY_SIZE(rates_48000), - .list = rates_48000, -}; - static int cht_aif1_startup(struct snd_pcm_substream *substream) { - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_48000); + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); } static struct snd_soc_ops cht_aif1_ops = { -- cgit v1.2.3-59-g8ed1b From a5be88f63eaff1c03774aecd7388015cb87f6b2c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 15:39:33 +0200 Subject: ASoC: cht_bsw_rt5672: Use snd_pcm_hw_constraint_single() Use the new snd_pcm_hw_constraint_single() helper function rather than installing a list constraint with a single value. Since snd_pcm_hw_constraint_single() sets a static constraint while snd_pcm_hw_constraint_list() sets a dynamic constraint the former is slightly more efficient and it also needs less code. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Acked-by: Mark Brown Signed-off-by: Takashi Iwai --- sound/soc/intel/boards/cht_bsw_rt5672.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 23fe04075142..5621ccd92992 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -222,20 +222,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int rates_48000[] = { - 48000, -}; - -static struct snd_pcm_hw_constraint_list constraints_48000 = { - .count = ARRAY_SIZE(rates_48000), - .list = rates_48000, -}; - static int cht_aif1_startup(struct snd_pcm_substream *substream) { - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_48000); + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); } static struct snd_soc_ops cht_aif1_ops = { -- cgit v1.2.3-59-g8ed1b From c5cff89b5f64b4fc7f6fd9be44b1aa7e766a94e4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 21 Oct 2015 08:34:37 +0800 Subject: ASoC: da7219: Fix da7219->alc_en state when enabling ALC Signed-off-by: Axel Lin Tested-by: Adam Thomson Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/da7219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index abba4b36caa0..f238c1e8a69c 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -402,7 +402,7 @@ static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol, /* Force ALC offset calibration if enabling ALC */ if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) { da7219_alc_calib(codec); - da7219->alc_en = false; + da7219->alc_en = true; } else { da7219->alc_en = false; } -- cgit v1.2.3-59-g8ed1b From c9b9638f617871aa83c197eed8f068294c843b69 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:13:06 +0000 Subject: ASoC: rsnd: fixup print debug message after read debug meesage for rsnd_mod_read() should be prints after read Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/gen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index f04d17bc6e3d..0c69f83f0d58 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -79,11 +79,11 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + dev_dbg(dev, "r %s[%d] - %4d : %08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); - return val; } -- cgit v1.2.3-59-g8ed1b From 9993c16d460e2965da4357575060373a5577167a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:13:27 +0000 Subject: ASoC: rsnd: fixup struct rsnd_gen::res array size struct rsnd_gen :: res array size should be RSND_BASE_MAX, not RSND_REG_MAX. This patch fixup it, and indicates whether each data array size is based on what Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/gen.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 0c69f83f0d58..76da7620904c 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -22,13 +22,15 @@ #include "rsnd.h" struct rsnd_gen { - void __iomem *base[RSND_BASE_MAX]; - struct rsnd_gen_ops *ops; + /* RSND_BASE_MAX base */ + void __iomem *base[RSND_BASE_MAX]; + phys_addr_t res[RSND_BASE_MAX]; struct regmap *regmap[RSND_BASE_MAX]; + + /* RSND_REG_MAX base */ struct regmap_field *regs[RSND_REG_MAX]; - phys_addr_t res[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -182,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regmap)) return PTR_ERR(regmap); + /* RSND_BASE_MAX base */ gen->base[reg_id] = base; gen->regmap[reg_id] = regmap; gen->res[reg_id] = res->start; @@ -198,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, if (IS_ERR(regs)) return PTR_ERR(regs); + /* RSND_REG_MAX base */ gen->regs[conf[i].idx] = regs; } -- cgit v1.2.3-59-g8ed1b From b05ce4c0916dec0e31a12c35a3386e3ca3ed989a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:13:44 +0000 Subject: ASoC: rsnd: fixup devm_request_irq() option on ssi.c bfc0cfe("ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_ssi_xxx()") tidyuped devm_request_irq() option from ssi to mod, but devm_free_irq() on rsnd_ssi_dma_remove() didn't modified. This patch fixups this issue. Otherwise kernel will output WARNING message. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 842a35b1363a..40e2b5de5875 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -555,7 +555,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ - devm_free_irq(dev, irq, ssi); + devm_free_irq(dev, irq, mod); return 0; } -- cgit v1.2.3-59-g8ed1b From 1355720a3b3eba5604431d89d5cf69ce4ad51311 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:14:02 +0000 Subject: ASoC: rsnd: fixup rsnd_mod_call() behavior for debug Indicating each module method as debug message before executing is readable/understandable. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6ef9a884ca7c..c0182ac254c9 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -308,16 +308,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - int called = 0; \ - if (val == __rsnd_mod_call_##func) { \ - called = 1; \ - ret = (mod)->ops->func(mod, io, param); \ - } \ + int call = (val == __rsnd_mod_call_##func); \ mod->status = (mod->status & ~mask) + \ (add << __rsnd_mod_shift_##func); \ - dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ - called ? #func : ""); \ + dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), \ + mod->status, call ? #func : ""); \ + if (call) \ + ret = (mod)->ops->func(mod, io, param); \ ret; \ }) -- cgit v1.2.3-59-g8ed1b From 32a96d558d02581c32b09401b8aa5eecb3965d3e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:14:21 +0000 Subject: ASoC: rsnd: fixup rsnd_dai_call() behavior for unimplemented method Current rsnd_dai_call didn't count callback-count if callback wasn't implemented. Thus, it counts can be unbalance. ex) .start : implemented .stop : not implemented This patch solve this issue Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index c0182ac254c9..5c6714481f59 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -300,7 +300,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) /* * rsnd_dai functions */ -#define __rsnd_mod_call(mod, io, func, param...) \ +#define rsnd_mod_call(mod, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ @@ -308,7 +308,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - int call = (val == __rsnd_mod_call_##func); \ + int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ mod->status = (mod->status & ~mask) + \ (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ @@ -319,11 +319,6 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ret; \ }) -#define rsnd_mod_call(mod, io, func, param...) \ - (!(mod) ? -ENODEV : \ - !((mod)->ops->func) ? 0 : \ - __rsnd_mod_call(mod, io, func, param)) - #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ -- cgit v1.2.3-59-g8ed1b From 89e3e2c352a523be46be5104bf18e200a8ccd444 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:14:43 +0000 Subject: ASoC: rsnd: fixup rsnd_dai_call() behavior for .stop/.quit Current rsnd_dai_call returns immediately if rsnd_mod_call return fail. Thus, each callback-count can be unbalanced for example .init was OK, start was OK, but, .stop was not OK. This case .quit should be called but isn't called. And, rsnd_dai_stream_quit() also not be called. rsnd_dai_call() should call all .stop/.quit eventhough it returns error. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 5c6714481f59..deed48ef28b8 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -327,9 +327,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret = rsnd_mod_call(mod, io, fn, param); \ - if (ret < 0) \ - break; \ + ret |= rsnd_mod_call(mod, io, fn, param); \ } \ ret; \ }) @@ -495,16 +493,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; case SNDRV_PCM_TRIGGER_STOP: ret = rsnd_dai_call(stop, io, priv); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_dai_call(quit, io, priv); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_dai_call(quit, io, priv); - ret = rsnd_platform_call(priv, dai, stop, ssi_id); - if (ret < 0) - goto dai_trigger_end; + ret |= rsnd_platform_call(priv, dai, stop, ssi_id); rsnd_dai_stream_quit(io); break; -- cgit v1.2.3-59-g8ed1b From c9929345018927acaf52c14c57d78116067be6c9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:15:04 +0000 Subject: ASoC: rsnd: rename rsnd_mod_hw_start/stop to rsnd_mod_power_on/off rsnd_mod_hw_start/stop were unclear naming. It became rsnd_mod_power_on/off by this patch Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 4 ++-- sound/soc/sh/rcar/dvc.c | 4 ++-- sound/soc/sh/rcar/mix.c | 4 ++-- sound/soc/sh/rcar/rsnd.h | 4 ++-- sound/soc/sh/rcar/src.c | 4 ++-- sound/soc/sh/rcar/ssi.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index f1541f464f30..3cb214ab848b 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -35,7 +35,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_ctu_initialize_lock(mod); @@ -50,7 +50,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e36c0ac3374b..58f690900e6d 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -153,7 +153,7 @@ static int rsnd_dvc_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_dvc_soft_reset(mod); @@ -175,7 +175,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index ac2687d9ee55..953dd0be9b60 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -58,7 +58,7 @@ static int rsnd_mix_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_mix_soft_reset(mod); @@ -83,7 +83,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e9fef53968b4..38c16d7cd2e8 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -330,8 +330,8 @@ struct rsnd_mod { #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) -#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) +#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) +#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) int rsnd_mod_init(struct rsnd_priv *priv, diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 1d379e825a9d..37d41f06b3d0 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -352,7 +352,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); rsnd_src_soft_reset(mod); @@ -373,7 +373,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); if (src->err) dev_warn(dev, "%s[%d] under/over flow err = %d\n", diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 40e2b5de5875..d70720a42cfd 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -184,7 +184,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, u32 cr; if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(mod); + rsnd_mod_power_on(mod); if (rsnd_rdai_is_clk_master(rdai)) { struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); @@ -265,7 +265,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) rsnd_ssi_master_clk_stop(ssi); } - rsnd_mod_hw_stop(mod); + rsnd_mod_power_off(mod); ssi->chan = 0; } -- cgit v1.2.3-59-g8ed1b From 69819f527afba7a909a2aba32521a3bea8e3b60b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:15:26 +0000 Subject: ASoC: rsnd: remove unused rsnd_dma_to_ssi() macro rsnd_dma_to_ssi() is no longer used, let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d70720a42cfd..e550621d0ca7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -79,7 +79,6 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) #define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) -- cgit v1.2.3-59-g8ed1b From b415b4d3122a466f3a73d86a1dd2dcdc13de7ef3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Oct 2015 03:15:46 +0000 Subject: ASoC: rsnd: remove duplicate parameter from rsnd_ssi_xxx() rsnd_ssi_use_busif() and rsnd_ssi_is_pin_sharing() are the function which returns current SSI status. But these requests duplicated parameter. This patch removes duplicated parameter. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 3 +-- sound/soc/sh/rcar/dma.c | 2 +- sound/soc/sh/rcar/rsnd.h | 7 +++++-- sound/soc/sh/rcar/src.c | 2 +- sound/soc/sh/rcar/ssi.c | 11 ++++++----- 5 files changed, 14 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index c4ebbb7a7b6f..2a5b3a293cd2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -69,11 +69,10 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div) static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int id = rsnd_mod_id(mod); int ws = id; - if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { + if (rsnd_ssi_is_pin_sharing(io)) { switch (id) { case 1: case 2: diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index bfbb8a5e93bd..5d084d040961 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -470,7 +470,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(io, mod)) + if (is_ssi && rsnd_ssi_use_busif(io)) is_ssi++; return (is_from) ? diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 38c16d7cd2e8..085329878525 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -572,9 +572,12 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); + +#define rsnd_ssi_is_pin_sharing(io) \ + __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); /* * R-Car SRC diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 37d41f06b3d0..261b50217c48 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -159,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, /* * SSI_MODE1 */ - if (rsnd_ssi_is_pin_sharing(ssi_mod)) { + if (rsnd_ssi_is_pin_sharing(io)) { int shift = -1; switch (ssi_id) { case 1: diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e550621d0ca7..1427ec21bd7e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -86,8 +86,9 @@ struct rsnd_ssi { #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int use_busif = 0; @@ -394,7 +395,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod)); + rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); rsnd_ssi_hw_start(ssi, io); @@ -613,7 +614,7 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, int is_play = rsnd_io_is_play(io); char *name; - if (rsnd_ssi_use_busif(io, mod)) + if (rsnd_ssi_use_busif(io)) name = is_play ? "rxu" : "txu"; else name = is_play ? "rx" : "tx"; @@ -659,7 +660,7 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); } -int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -670,7 +671,7 @@ static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { struct rsnd_mod *mod = rsnd_mod_get(ssi); - if (!rsnd_ssi_is_pin_sharing(mod)) + if (!__rsnd_ssi_is_pin_sharing(mod)) return; switch (rsnd_mod_id(mod)) { -- cgit v1.2.3-59-g8ed1b From 16566e47098211e30b3d8a0bc6a3576871ada8e8 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 21 Oct 2015 09:46:05 +0800 Subject: ASoC: rt5640: Fill up the IN3's support Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5640.txt | 5 ++++- include/sound/rt5640.h | 3 ++- sound/soc/codecs/rt5640.c | 22 +++++++++++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt index 5d062a567996..9e62f6eb348f 100644 --- a/Documentation/devicetree/bindings/sound/rt5640.txt +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -14,7 +14,8 @@ Optional properties: - realtek,in1-differential - realtek,in2-differential - Boolean. Indicate MIC1/2 input are differential, rather than single-ended. +- realtek,in3-differential + Boolean. Indicate MIC1/2/3 input are differential, rather than single-ended. - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. @@ -27,6 +28,8 @@ Pins on the device (for linking into audio routes) for RT5639/RT5640: * IN1N * IN2P * IN2N + * IN3P + * IN3N * HPOL * HPOR * LOUTL diff --git a/include/sound/rt5640.h b/include/sound/rt5640.h index 59d26dd81e45..e3c84b92ff70 100644 --- a/include/sound/rt5640.h +++ b/include/sound/rt5640.h @@ -12,9 +12,10 @@ #define __LINUX_SND_RT5640_H struct rt5640_platform_data { - /* IN1 & IN2 can optionally be differential */ + /* IN1 & IN2 & IN3 can optionally be differential */ bool in1_diff; bool in2_diff; + bool in3_diff; bool dmic_en; bool dmic1_data_pin; /* 0 = IN1P; 1 = GPIO3 */ diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e1ceeb885f7d..f2beb1aa5763 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -405,11 +405,14 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv), - /* IN1/IN2 Control */ + /* IN1/IN2/IN3 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2, RT5640_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4, RT5640_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT2, 8, 0, bst_tlv), + /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, @@ -598,6 +601,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = { RT5640_M_HP_L_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER, RT5640_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST2_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER, RT5640_M_BST4_RM_L_SFT, 1, 1), SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER, @@ -611,6 +616,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = { RT5640_M_HP_R_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER, RT5640_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST2_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER, RT5640_M_BST4_RM_R_SFT, 1, 1), SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER, @@ -1065,6 +1072,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN1N"), SND_SOC_DAPM_INPUT("IN2P"), SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1081,6 +1090,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { RT5640_PWR_BST1_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2, RT5640_PWR_BST4_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2, + RT5640_PWR_BST2_BIT, 0, NULL, 0), /* Input Volume */ SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL, RT5640_PWR_IN_L_BIT, 0, NULL, 0), @@ -1310,6 +1321,7 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = { static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"IN1P", NULL, "LDO2"}, {"IN2P", NULL, "LDO2"}, + {"IN3P", NULL, "LDO2"}, {"DMIC L1", NULL, "DMIC1"}, {"DMIC R1", NULL, "DMIC1"}, @@ -1320,18 +1332,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"BST1", NULL, "IN1N"}, {"BST2", NULL, "IN2P"}, {"BST2", NULL, "IN2N"}, + {"BST3", NULL, "IN3P"}, + {"BST3", NULL, "IN3N"}, {"INL VOL", NULL, "IN2P"}, {"INR VOL", NULL, "IN2N"}, {"RECMIXL", "HPOL Switch", "HPOL"}, {"RECMIXL", "INL Switch", "INL VOL"}, + {"RECMIXL", "BST3 Switch", "BST3"}, {"RECMIXL", "BST2 Switch", "BST2"}, {"RECMIXL", "BST1 Switch", "BST1"}, {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"}, {"RECMIXR", "HPOR Switch", "HPOR"}, {"RECMIXR", "INR Switch", "INR VOL"}, + {"RECMIXR", "BST3 Switch", "BST3"}, {"RECMIXR", "BST2 Switch", "BST2"}, {"RECMIXR", "BST1 Switch", "BST1"}, {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"}, @@ -2260,6 +2276,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, RT5640_IN_DF2, RT5640_IN_DF2); + if (rt5640->pdata.in3_diff) + regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, + RT5640_IN_DF2, RT5640_IN_DF2); + rt5640->hp_mute = 1; return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, -- cgit v1.2.3-59-g8ed1b From 26d9ca3462df8f7e83fc372b23c8da5ed2b1c4f3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 18 Oct 2015 17:04:33 +0200 Subject: ASoC: Let snd_soc_limit_volume() take a snd_soc_card snd_soc_limit_volume() operates on a card and the CODEC that is passed in is only used to look up the card. Let it directly take the card instead. This makes it possible to use it when no snd_soc_codec is available. Signed-off-by: Lars-Peter Clausen Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- sound/soc/omap/rx51.c | 2 +- sound/soc/soc-ops.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 884e728b09d9..71e0c0566b6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -591,7 +591,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_limit_volume(struct snd_soc_codec *codec, +int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 3bebfb1d3a6f..99538900a253 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -297,7 +297,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); return err; } - snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42); + snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42); err = omap_mcbsp_st_add_controls(rtd, 2); if (err < 0) { diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 100d92b5b77e..20f702add3f8 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -560,16 +560,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); /** * snd_soc_limit_volume - Set new limit to an existing volume control. * - * @codec: where to look for the control + * @card: where to look for the control * @name: Name of the control * @max: new maximum limit * * Return 0 for success, else error. */ -int snd_soc_limit_volume(struct snd_soc_codec *codec, +int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { - struct snd_card *card = codec->component.card->snd_card; + struct snd_card *snd_card = card->snd_card; struct snd_kcontrol *kctl; struct soc_mixer_control *mc; int found = 0; @@ -579,7 +579,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec, if (unlikely(!name || max <= 0)) return -EINVAL; - list_for_each_entry(kctl, &card->controls, list) { + list_for_each_entry(kctl, &snd_card->controls, list) { if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { found = 1; break; -- cgit v1.2.3-59-g8ed1b From 6f0c42269f000b1e346c84d9a589f17aa94c96d8 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 13 Oct 2015 23:41:00 +0800 Subject: ASoC: compress: add config item for soc-compress to make it compiled only when needed We don't always need soc-compress in soc, here add a config item SND_SOC_COMPRESS, when nobody select it, the soc-compress will not be compiled. Here also change Kconfig to 'select SND_SOC_COMPRESS' for drivers that needed soc-compress. Signed-off-by: Jie Yang Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 2 +- include/sound/soc.h | 4 +++- sound/soc/Kconfig | 5 ++++- sound/soc/Makefile | 3 ++- sound/soc/intel/Kconfig | 1 + sound/soc/intel/atom/sst-mfld-platform-pcm.c | 2 +- sound/soc/soc-compress.c | 12 ++++++++++-- sound/soc/soc-core.c | 4 ++-- 8 files changed, 24 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 2df96b1384c7..238200ffba5b 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -214,7 +214,7 @@ struct snd_soc_dai_driver { int (*suspend)(struct snd_soc_dai *dai); int (*resume)(struct snd_soc_dai *dai); /* compress dai */ - bool compress_dai; + int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); /* DAI is also used for the control bus */ bool bus_control; diff --git a/include/sound/soc.h b/include/sound/soc.h index 71e0c0566b6e..a7bc82b08cd4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -440,7 +440,9 @@ int snd_soc_platform_read(struct snd_soc_platform *platform, int snd_soc_platform_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); -int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num); +#ifdef CONFIG_SND_SOC_COMPRESS +int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num); +#endif struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 225bfda414e9..3e4f44434600 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -9,7 +9,6 @@ menuconfig SND_SOC select SND_JACK if INPUT=y || INPUT=SND select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER - select SND_COMPRESS_OFFLOAD ---help--- If you want ASoC support, you should say Y here and also to the @@ -30,6 +29,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM bool select SND_DMAENGINE_PCM +config SND_SOC_COMPRESS + bool + select SND_COMPRESS_OFFLOAD + config SND_SOC_TOPOLOGY bool diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 134aca150a50..280ac8257d3e 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,6 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o -snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o +snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o +snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o ifneq ($(CONFIG_SND_SOC_TOPOLOGY),) snd-soc-core-objs += soc-topology.o diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 05fde5e6e257..221e3bd73adb 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -12,6 +12,7 @@ config SND_MFLD_MACHINE config SND_SST_MFLD_PLATFORM tristate + select SND_SOC_COMPRESS config SND_SST_IPC tristate diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 683e50116152..4c734f5aeb8b 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -529,7 +529,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "compress-cpu-dai", - .compress_dai = 1, + .compress_new = snd_soc_new_compress, .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 025c38fbe3c0..12a9820feac1 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = { .get_codec_caps = soc_compr_get_codec_caps }; -/* create a new compress */ -int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) +/** + * snd_soc_new_compress - create a new compress. + * + * @rtd: The runtime for which we will create compress + * @num: the device index number (zero based - shared with normal PCMs) + * + * Return: 0 for success, else error. + */ +int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; @@ -703,3 +710,4 @@ compr_err: kfree(compr); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_new_compress); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6173d15236c3..6e543e18b4fa 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1370,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) soc_dpcm_debugfs_add(rtd); #endif - if (cpu_dai->driver->compress_dai) { + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = soc_new_compress(rtd, num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); -- cgit v1.2.3-59-g8ed1b From 931dfa6931448ccfd12d4ccd8d1d9289ceb92439 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:08:18 +0300 Subject: ASoC: wm8904: harmless underflow in wm8904_put_deemph() We try to check that "deemph" is zero or one, but because of an underflow bug, it can also be negative. It's fine, negative values are handled as non-zero. But it's messy and static checkers complain so let's fix it. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8904.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index b783743dc97e..2aa23f1b9e3c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 523bade22428d4c6742dd7bfb4c0259a23bb9ec3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:08:57 +0300 Subject: ASoC: wm8731: harmless underflow in wm8731_put_deemph() The code tries to verify that "deemph" is zero or one, but it fails to account for that it can be negative. It's harmless because negatives are treated the same as one, but we should fix it just to silence the static checker warning. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index ace8645245a0..07cf1bd7913a 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -132,7 +132,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) -- cgit v1.2.3-59-g8ed1b From c1fe81f2cd30c18d7df444697bac8051fdf2aee6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:09:19 +0300 Subject: ASoC: wm8960: harmless underflow in wm8960_put_deemph() We should only accept "deemph" values of zero and one, but by mistake we accept negatives as well. It's harmless but let's clean it up anyway. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index e3b7d0c57411..dc47c2fa6622 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -201,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 4d0197a5cad29ee58593eceac85cbb712430cd89 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:10:18 +0300 Subject: ASoC: wm8903: harmless underflow in wm8903_put_deemph() Valid values for "deemph" are zero and one, but we accidentally allow negative values as well. It's harmless, but static checkers complain and we may as well clean it up. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8903.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index b011253459af..e4cc41e6c23e 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) -- cgit v1.2.3-59-g8ed1b From 3e2a71b232d6b6270eda72ff659c31c97899c00d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:10:48 +0300 Subject: ASoC: wm8955: harmless underflow in wm8955_put_deemph() Valid values for "deemph" are zero and one, but we mistakenly allow negative values as well. It's harmless but we may as well clean it up. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8955.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 12e4435f00f8..9db00d53abe7 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 7ab8a54ea7914574c04c0ac441f9ab65ff21abd9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:11:09 +0300 Subject: ASoC: es8328: harmless underflow in es8328_put_deemph() Valid values for "deemph" are zero and one but we accidentally allow negative values as well. It's harmless but it causes static checker warnings and we may as well clean it up. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/codecs/es8328.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 6a091016e0fc..969e337dc17c 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.integer.value[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; int ret; if (deemph > 1) -- cgit v1.2.3-59-g8ed1b From 8444f59fd7bd8aa079609e575c0688669b85bfcc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Oct 2015 10:12:45 +0300 Subject: ASoC: wm2000: a couple harmless underflows We want these to be zero or one, but by mistake we also accept negative values. It's harmless but we should still clean it up. Signed-off-by: Dan Carpenter Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm2000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 786abd02b140..a67ea10f41a1 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int anc_active = ucontrol->value.integer.value[0]; + unsigned int anc_active = ucontrol->value.integer.value[0]; int ret; if (anc_active > 1) @@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int val = ucontrol->value.integer.value[0]; + unsigned int val = ucontrol->value.integer.value[0]; int ret; if (val > 1) -- cgit v1.2.3-59-g8ed1b From ef3e199a49c8e605e326ae60c5e156bfb1ca7e3d Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 22 Oct 2015 13:55:07 +0800 Subject: ASoC: Intel: sst: only use sst-firmware when DW DMAC is available Currentlly, we use Synopsys DesignWare DMA Controller for baytrail/haswell/broadwell ADSP firmware loading, but for skylake, we don't use it, compiling sst-firmware.c may introduce error when CONFIG_DW_DMAC_CORE is not enabled: sound/built-in.o: In function `sst_dma_new': (.text+0xd7b38): undefined reference to `dw_dma_probe' sound/built-in.o: In function `sst_dma_free': (.text+0xd7c0a): undefined reference to `dw_dma_remove' Here we only compile sst-firmware when CONFIG_DW_DMAC_CORE is selected, to fix the linking error issue. Reported-by: Randy Dunlap Signed-off-by: Jie Yang Signed-off-by: Mark Brown --- sound/soc/intel/common/Makefile | 6 +++++- sound/soc/intel/common/sst-dsp.c | 2 ++ sound/soc/intel/common/sst-dsp.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index f24154ca4e98..d9105584c51f 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,7 +1,11 @@ -snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o +ifneq ($(CONFIG_DW_DMAC_CORE),) +snd-soc-sst-dsp-objs += sst-firmware.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index a627236dd1f5..c9452e02e0dd 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) } EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) { @@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst) sst_dma_free(sst->dma); } EXPORT_SYMBOL_GPL(sst_dsp_free); +#endif /* Module information */ MODULE_AUTHOR("Liam Girdwood"); diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 1f45f18715c0..859f0de00339 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -216,10 +216,12 @@ struct sst_pdata { void *dsp; }; +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) /* Initialization */ struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); void sst_dsp_free(struct sst_dsp *sst); +#endif /* SHIM Read / Write */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); -- cgit v1.2.3-59-g8ed1b From ba40a854ea4f9d81368dc023bd25ac4073058039 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 9 Oct 2015 18:16:36 -0400 Subject: ASoC: Intel: switch from ioremap_cache to memremap In preparation for deprecating ioremap_cache() convert its usage in skl-nhlt to memremap. Signed-off-by: Dan Williams Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 10 +++++----- sound/soc/intel/skylake/skl.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 13036b19d7e5..b0c7bd113aac 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" -void __iomem *skl_nhlt_init(struct device *dev) +void *skl_nhlt_init(struct device *dev) { acpi_handle handle; union acpi_object *obj; @@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev) if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length); + return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, + MEMREMAP_WB); } dev_err(dev, "device specific method to extract NHLT blob failed\n"); return NULL; } -void skl_nhlt_free(void __iomem *addr) +void skl_nhlt_free(void *addr) { - iounmap(addr); - addr = NULL; + memunmap(addr); } static struct nhlt_specific_cfg *skl_get_specific_cfg( diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index e980d7897642..dd2e79ae45a8 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -62,7 +62,7 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; - void __iomem *nhlt; /* nhlt ptr */ + void *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ struct skl_dsp_resource resource; @@ -83,8 +83,8 @@ struct skl_dma_params { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -void __iomem *skl_nhlt_init(struct device *dev); -void skl_nhlt_free(void __iomem *addr); +void *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(void *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); -- cgit v1.2.3-59-g8ed1b From 19a2557b76d64f26c761925cb4fecefb5d72c099 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 20 Oct 2015 22:30:06 +0530 Subject: ASoC: dapm: Add kcontrol support for PGAs For DSPs we can define processing blocks as DAPM PGA widgets. Some of these proceesing blocks can be configured by usermode like EQ etc. So we need to add support of kcontrol for PGA widgets. Signed-off-by: Jeeja KP Signed-off-by: Mythri P K Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f4bf21a5539b..9762ac4efdeb 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -779,7 +779,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, * Determine if a kcontrol is shared. If it is, look it up. If it isn't, * create it. Either way, add the widget into the control's widget list */ -static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, +static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, int kci) { struct snd_soc_dapm_context *dapm = w->dapm; @@ -810,6 +810,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, switch (w->id) { case snd_soc_dapm_switch: case snd_soc_dapm_mixer: + case snd_soc_dapm_pga: wname_in_long_name = true; kcname_in_long_name = true; break; @@ -899,7 +900,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; if (!w->kcontrols[i]) { - ret = dapm_create_or_share_mixmux_kcontrol(w, i); + ret = dapm_create_or_share_kcontrol(w, i); if (ret < 0) return ret; } @@ -952,7 +953,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return -EINVAL; } - ret = dapm_create_or_share_mixmux_kcontrol(w, 0); + ret = dapm_create_or_share_kcontrol(w, 0); if (ret < 0) return ret; @@ -967,9 +968,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) /* create new dapm volume control */ static int dapm_new_pga(struct snd_soc_dapm_widget *w) { - if (w->num_kcontrols) - dev_err(w->dapm->dev, - "ASoC: PGA controls not supported: '%s'\n", w->name); + int i, ret; + + for (i = 0; i < w->num_kcontrols; i++) { + ret = dapm_create_or_share_kcontrol(w, i); + if (ret < 0) + return ret; + } return 0; } -- cgit v1.2.3-59-g8ed1b From 9b8ef9f6b3fcccc2b6ce4bb59d8ab55b36a8b8f0 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 20 Oct 2015 22:30:07 +0530 Subject: ASoC: dapm: Add startup & shutdown for dai_links For DAI link events, DSPs would like to get notified for startup and shutdown event as well apart for existing hw_params. This helps managing DSP resource allocation and freeup on these events So add support for startup and shutdown for snd_soc_dai_link_event() Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9762ac4efdeb..a28d6a10bad0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3478,11 +3478,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: substream.stream = SNDRV_PCM_STREAM_CAPTURE; + if (source->driver->ops && source->driver->ops->startup) { + ret = source->driver->ops->startup(&substream, source); + if (ret < 0) { + dev_err(source->dev, + "ASoC: startup() failed: %d\n", ret); + goto out; + } + source->active++; + } ret = soc_dai_hw_params(&substream, params, source); if (ret < 0) goto out; substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + if (sink->driver->ops && sink->driver->ops->startup) { + ret = sink->driver->ops->startup(&substream, sink); + if (ret < 0) { + dev_err(sink->dev, + "ASoC: startup() failed: %d\n", ret); + goto out; + } + sink->active++; + } ret = soc_dai_hw_params(&substream, params, sink); if (ret < 0) goto out; @@ -3502,6 +3520,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; + + source->active--; + if (source->driver->ops && source->driver->ops->shutdown) { + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + source->driver->ops->shutdown(&substream, source); + } + + sink->active--; + if (sink->driver->ops && sink->driver->ops->shutdown) { + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + sink->driver->ops->shutdown(&substream, sink); + } break; default: -- cgit v1.2.3-59-g8ed1b From 93e39a11520c000c9086215460bf27b35b09c724 Mon Sep 17 00:00:00 2001 From: Mythri P K Date: Tue, 20 Oct 2015 22:30:08 +0530 Subject: ASoC: dapm: Add snd_soc_dapm_kcontrol_widget() Given a kcontrol, we may want to access the parent widget and it's associated data. So export function to return it. Signed-off-by: Mythri P K Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 3 +++ sound/soc/soc-dapm.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) (limited to 'sound') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 5abba037d245..7855cfe46b69 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -451,6 +451,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( struct snd_kcontrol *kcontrol); +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( + struct snd_kcontrol *kcontrol); + int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a28d6a10bad0..38281c2325ff 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -508,6 +508,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, return true; } +/** + * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a + * kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( + struct snd_kcontrol *kcontrol) +{ + return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget); + /** * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a * kcontrol -- cgit v1.2.3-59-g8ed1b From 53e597b1d194910bef53ed0632da329fef497904 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 13:11:56 +0200 Subject: ALSA: Remove transfer_ack_{begin,end} callbacks from struct snd_pcm_runtime While there is nothing wrong with the transfer_ack_begin and transfer_ack_end callbacks per-se, the last documented user was part of the alsa-driver 0.5.12a package, which was released 14 years ago and even predates the upstream integration of the ALSA core and has subsequently been superseded by newer alsa-driver releases. This seems to indicate that there is no need for having these callbacks and they are just cruft that can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- Documentation/DocBook/writing-an-alsa-driver.tmpl | 19 ++----------------- include/sound/pcm.h | 4 ---- sound/core/pcm_lib.c | 5 ----- 3 files changed, 2 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index 84ef6a90131c..a27ab9f53fb6 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -2181,10 +2181,6 @@ struct _snd_pcm_runtime { struct snd_pcm_hardware hw; struct snd_pcm_hw_constraints hw_constraints; - /* -- interrupt callbacks -- */ - void (*transfer_ack_begin)(struct snd_pcm_substream *substream); - void (*transfer_ack_end)(struct snd_pcm_substream *substream); - /* -- timer -- */ unsigned int timer_resolution; /* timer resolution */ @@ -2209,9 +2205,8 @@ struct _snd_pcm_runtime { For the operators (callbacks) of each sound driver, most of these records are supposed to be read-only. Only the PCM middle-layer changes / updates them. The exceptions are - the hardware description (hw), interrupt callbacks - (transfer_ack_xxx), DMA buffer information, and the private - data. Besides, if you use the standard buffer allocation + the hardware description (hw) DMA buffer information and the + private data. Besides, if you use the standard buffer allocation method via snd_pcm_lib_malloc_pages(), you don't need to set the DMA buffer information by yourself. @@ -2538,16 +2533,6 @@ struct _snd_pcm_runtime { -
- Interrupt Callbacks - - The field transfer_ack_begin and - transfer_ack_end are called at - the beginning and at the end of - snd_pcm_period_elapsed(), respectively. - -
-
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 2882dddfc91c..3e0ffd21901f 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -402,10 +402,6 @@ struct snd_pcm_runtime { struct snd_pcm_hardware hw; struct snd_pcm_hw_constraints hw_constraints; - /* -- interrupt callbacks -- */ - void (*transfer_ack_begin)(struct snd_pcm_substream *substream); - void (*transfer_ack_end)(struct snd_pcm_substream *substream); - /* -- timer -- */ unsigned int timer_resolution; /* timer resolution */ int tstamp_type; /* timestamp type */ diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 6dc4277937b8..05a3ca93c647 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1875,9 +1875,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) return; runtime = substream->runtime; - if (runtime->transfer_ack_begin) - runtime->transfer_ack_begin(substream); - snd_pcm_stream_lock_irqsave(substream, flags); if (!snd_pcm_running(substream) || snd_pcm_update_hw_ptr0(substream, 1) < 0) @@ -1889,8 +1886,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) #endif _end: snd_pcm_stream_unlock_irqrestore(substream, flags); - if (runtime->transfer_ack_end) - runtime->transfer_ack_end(substream); kill_fasync(&runtime->fasync, SIGIO, POLL_IN); } -- cgit v1.2.3-59-g8ed1b From c86ba612bdba4d267ce5aad30ff10b4d994e2b33 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 19 Oct 2015 16:49:05 -0700 Subject: ASoC: nau8825: Add FLL configuration snd_soc_codec_driver.set_pll is implemented to configure the FLL. The codec internal SYSCLK can be from either the MCLK pin directly, or the FLL. This is configured by snd_soc_codec_driver.set_pll. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 163 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/nau8825.h | 28 ++++++-- 2 files changed, 186 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 5c1badf47ac9..bd58b9697731 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,58 @@ #include "nau8825.h" +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +struct nau8825_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8825_fll_attr { + unsigned int param; + unsigned int val; +}; + +/* scaling for mclk from sysclk_src output */ +static const struct nau8825_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, + { 48, 0xd }, + { 96, 0xe }, + { 5, 0xf }, +}; + +/* ratio for input clk freq */ +static const struct nau8825_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8825_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_ENA_CTRL, 0x00ff }, { NAU8825_REG_CLK_DIVIDER, 0x0050 }, @@ -808,6 +861,115 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec) return 0; } +/** + * nau8825_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, + struct nau8825_fll *fll_param) +{ + u64 fvco; + unsigned int fref, i; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 100MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX) + break; + } + if (i == ARRAY_SIZE(mclk_src_scaling)) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[i].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco << 16, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 16) & 0x3FF; + fll_param->fll_frac = fvco & 0xFFFF; + return 0; +} + +static void nau8825_fll_apply(struct nau8825 *nau8825, + struct nau8825_fll *fll_param) +{ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, + NAU8825_FLL_RATIO_MASK, fll_param->ratio); + /* FLL 16-bit fractional input */ + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); + /* FLL 10-bit integer input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, + NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, + NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); + /* select divided VCO input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_FILTER_SW_MASK, 0x0000); + /* FLL sigma delta modulator enable */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN_MASK, NAU8825_SDM_EN); +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + struct nau8825_fll fll_param; + int ret, fs; + + fs = freq_out / 256; + ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(codec->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8825_fll_apply(nau8825, &fll_param); + mdelay(2); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + return 0; +} + static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, unsigned int freq) { @@ -920,6 +1082,7 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec, static struct snd_soc_codec_driver nau8825_codec_driver = { .probe = nau8825_codec_probe, .set_sysclk = nau8825_set_sysclk, + .set_pll = nau8825_set_pll, .set_bias_level = nau8825_set_bias_level, .suspend_bias_off = true, diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 8774923502b4..dff8edb83bfd 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -101,13 +101,31 @@ #define NAU8825_ENABLE_SAR_SFT 1 /* CLK_DIVIDER (0x3) */ -#define NAU8825_CLK_SRC_SFT 15 -#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) -#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) -#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_SFT 15 +#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) + +/* FLL1 (0x04) */ +#define NAU8825_FLL_RATIO_MASK (0x7f << 0) + +/* FLL3 (0x06) */ +#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) + +/* FLL4 (0x07) */ +#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10) + +/* FLL5 (0x08) */ +#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14) /* FLL6 (0x9) */ -#define NAU8825_DCO_EN (1 << 15) +#define NAU8825_DCO_EN_MASK (0x1 << 15) +#define NAU8825_DCO_EN (0x1 << 15) +#define NAU8825_DCO_DIS (0x0 << 15) +#define NAU8825_SDM_EN_MASK (0x1 << 14) +#define NAU8825_SDM_EN (0x1 << 14) +#define NAU8825_SDM_DIS (0x0 << 14) /* HSD_CTRL (0xc) */ #define NAU8825_HSD_AUTO_MODE (1 << 6) -- cgit v1.2.3-59-g8ed1b From 218d2ce2036f50d259dbcdd37a4db72cad6fc0d1 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 19 Oct 2015 16:49:06 -0700 Subject: ASoC: nau8825: Show device properties The codec device properties are printed for debugging. Signed-off-by: Ben Zhang Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index bd58b9697731..7fc7b4e3f444 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1100,6 +1100,34 @@ static void nau8825_reset_chip(struct regmap *regmap) regmap_write(regmap, NAU8825_REG_RESET, 0x00); } +static void nau8825_print_device_properties(struct nau8825 *nau8825) +{ + int i; + struct device *dev = nau8825->dev; + + dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable); + dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable); + dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up); + dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance); + + dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num); + for (i = 0; i < nau8825->sar_threshold_num; i++) + dev_dbg(dev, "sar-threshold[%d]=%d\n", i, + nau8825->sar_threshold[i]); + + dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis); + dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage); + dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time); + dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time); + dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce); + dev_dbg(dev, "jack-insert-debounce: %d\n", + nau8825->jack_insert_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8825->jack_eject_debounce); +} + static int nau8825_read_device_properties(struct device *dev, struct nau8825 *nau8825) { @@ -1213,6 +1241,8 @@ static int nau8825_i2c_probe(struct i2c_client *i2c, nau8825->dev = dev; nau8825->irq = i2c->irq; + nau8825_print_device_properties(nau8825); + nau8825_reset_chip(nau8825->regmap); ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value); if (ret < 0) { -- cgit v1.2.3-59-g8ed1b From 50760cad9de969fe85b24465afe6396b8bbc6a3f Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 19 Sep 2015 02:00:25 +0200 Subject: ASoC: fsl-asoc-card: add AC'97 support Add AC'97 support to fsl-asoc-card using generic ASoC AC'97 CODEC. The SSI controller will silently enable any TX AC'97 slots that have their bits set in SLOTREQ received from CODEC and then will redirect some of playback samples there. That's why it is important to make sure that any of CODEC playback slots that can pull samples are set to slots 3/4 (standard PCM playback slots). Currently, this applies to S/PDIF slots as they were seen to pull samples sometimes even with S/PDIF output being disabled. Signed-off-by: Maciej Szmigiero Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl-asoc-card.txt | 10 +- sound/soc/fsl/fsl-asoc-card.c | 140 ++++++++++++++++----- 2 files changed, 116 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt index a96774c194c8..ce55c0a6f757 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt @@ -13,13 +13,15 @@ So having this generic sound card allows all Freescale SoC users to benefit from the simplification of a new card support and the capability of the wide sample rates support through ASRC. -Note: The card is initially designed for those sound cards who use I2S and - PCM DAI formats. However, it'll be also possible to support those non - I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long - as the driver has been properly upgraded. +Note: The card is initially designed for those sound cards who use AC'97, I2S + and PCM DAI formats. However, it'll be also possible to support those non + AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as + long as the driver has been properly upgraded. The compatible list for this generic sound card currently: + "fsl,imx-audio-ac97" + "fsl,imx-audio-cs42888" "fsl,imx-audio-wm8962" diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5aeb6ed4827e..33628a09fcf5 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -14,6 +14,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_SND_AC97_CODEC) +#include +#endif #include #include @@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; +static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv) +{ + return priv->dai_fmt == SND_SOC_DAIFMT_AC97; +} + static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, * set_bias_level(), bypass the remaining settings in hw_params(). * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. */ - if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) + if ((priv->card.set_bias_level && + priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) || + fsl_asoc_card_is_ac97(priv)) return 0; /* Specific configurations of DAIs starts from here */ @@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, ext_port--; /* - * Use asynchronous mode (6 wires) for all cases. + * Use asynchronous mode (6 wires) for all cases except AC97. * If only 4 wires are needed, just set SSI into * synchronous mode and enable 4 PADs in IOMUX. */ @@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_TCLKDIR; break; default: - return -EINVAL; + if (!fsl_asoc_card_is_ac97(priv)) + return -EINVAL; + } + + if (fsl_asoc_card_is_ac97(priv)) { + int_ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCLKDIR; + ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR; } /* Asynchronous mode can not be set along with RCLKDIR */ - ret = imx_audmux_v2_configure_port(int_port, 0, - IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); - if (ret) { - dev_err(dev, "audmux internal port setup failed\n"); - return ret; + if (!fsl_asoc_card_is_ac97(priv)) { + unsigned int pdcr = + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port); + + ret = imx_audmux_v2_configure_port(int_port, 0, + pdcr); + if (ret) { + dev_err(dev, "audmux internal port setup failed\n"); + return ret; + } } ret = imx_audmux_v2_configure_port(int_port, int_ptcr, @@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return ret; } - ret = imx_audmux_v2_configure_port(ext_port, 0, - IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); - if (ret) { - dev_err(dev, "audmux external port setup failed\n"); - return ret; + if (!fsl_asoc_card_is_ac97(priv)) { + unsigned int pdcr = + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port); + + ret = imx_audmux_v2_configure_port(ext_port, 0, + pdcr); + if (ret) { + dev_err(dev, "audmux external port setup failed\n"); + return ret; + } } ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, @@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct device *dev = card->dev; int ret; + if (fsl_asoc_card_is_ac97(priv)) { +#if IS_ENABLED(CONFIG_SND_AC97_CODEC) + struct snd_soc_codec *codec = card->rtd[0].codec; + struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + + /* + * Use slots 3/4 for S/PDIF so SSI won't try to enable + * other slots and send some samples there + * due to SLOTREQ bits for S/PDIF received from codec + */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, + AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4); +#endif + + return 0; + } + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); if (ret) { @@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; struct i2c_client *codec_dev; - struct clk *codec_clk; const char *codec_dai_name; u32 width; int ret; @@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Give a chance to old DT binding */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); - codec_np = of_parse_phandle(np, "audio-codec", 0); - if (!cpu_np || !codec_np) { - dev_err(&pdev->dev, "phandle missing or invalid\n"); + if (!cpu_np) { + dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; goto fail; } @@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - codec_dev = of_find_i2c_device_by_node(codec_np); - if (!codec_dev) { - dev_err(&pdev->dev, "failed to find codec platform device\n"); - ret = -EINVAL; - goto fail; - } + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (codec_np) + codec_dev = of_find_i2c_device_by_node(codec_np); + else + codec_dev = NULL; asrc_np = of_parse_phandle(np, "audio-asrc", 0); if (asrc_np) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - codec_clk = clk_get(&codec_dev->dev, NULL); - if (!IS_ERR(codec_clk)) { - priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); + if (codec_dev) { + struct clk *codec_clk = clk_get(&codec_dev->dev, NULL); + + if (!IS_ERR(codec_clk)) { + priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } } /* Default sample rate and format, will be updated in hw_params() */ @@ -486,11 +533,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { + codec_dai_name = "ac97-hifi"; + priv->card.set_bias_level = NULL; + priv->dai_fmt = SND_SOC_DAIFMT_AC97; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); return -EINVAL; } + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { + dev_err(&pdev->dev, "failed to find codec device\n"); + ret = -EINVAL; + goto asrc_fail; + } + /* Common settings for corresponding Freescale CPU DAI driver */ if (strstr(cpu_np->name, "ssi")) { /* Only SSI needs to configure AUDMUX */ @@ -507,7 +564,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } - sprintf(priv->name, "%s-audio", codec_dev->name); + snprintf(priv->name, sizeof(priv->name), "%s-audio", + fsl_asoc_card_is_ac97(priv) ? "ac97" : + codec_dev->name); /* Initialize sound card */ priv->pdev = pdev; @@ -531,8 +590,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; - priv->dai_link[0].codec_of_node = codec_np; priv->dai_link[0].codec_dai_name = codec_dai_name; + + if (!fsl_asoc_card_is_ac97(priv)) + priv->dai_link[0].codec_of_node = codec_np; + else { + u32 idx; + + ret = of_property_read_u32(cpu_np, "cell-index", &idx); + if (ret) { + dev_err(&pdev->dev, + "cannot get CPU index property\n"); + goto asrc_fail; + } + + priv->dai_link[0].codec_name = + devm_kasprintf(&pdev->dev, GFP_KERNEL, + "ac97-codec.%u", + (unsigned int)idx); + } + priv->dai_link[0].platform_of_node = cpu_np; priv->dai_link[0].dai_fmt = priv->dai_fmt; priv->card.num_links = 1; @@ -543,6 +620,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_link[1].platform_of_node = asrc_np; priv->dai_link[2].codec_dai_name = codec_dai_name; priv->dai_link[2].codec_of_node = codec_np; + priv->dai_link[2].codec_name = + priv->dai_link[0].codec_name; priv->dai_link[2].cpu_of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; @@ -578,14 +657,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); -fail: of_node_put(codec_np); +fail: of_node_put(cpu_np); return ret; } static const struct of_device_id fsl_asoc_card_dt_ids[] = { + { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, -- cgit v1.2.3-59-g8ed1b From 942e4a30eb87a7565c1cd28a08825f58b0794711 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 21 Sep 2015 18:38:19 +0800 Subject: ASoC: rt5645: Add dmi "Google Reks" for chrome Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index b3eff733d778..65ea712cef82 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3228,6 +3228,13 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"), }, }, + { + .ident = "Google Reks", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Reks"), + }, + }, { } }; -- cgit v1.2.3-59-g8ed1b From e0a25b6d18624140905d79775f9e1b05c12502f5 Mon Sep 17 00:00:00 2001 From: Songjun Wu Date: Thu, 8 Oct 2015 18:13:31 +0800 Subject: ASoC: atmel-classd: add the Audio Class D Amplifier Add driver for the digital imput to PWM output stereo class D amplifier. It comes with filter, digitally controlled gain, an equalizer and a dmphase filter. Signed-off-by: Songjun Wu Signed-off-by: Mark Brown --- sound/soc/atmel/Kconfig | 9 + sound/soc/atmel/Makefile | 2 + sound/soc/atmel/atmel-classd.c | 679 +++++++++++++++++++++++++++++++++++++++++ sound/soc/atmel/atmel-classd.h | 120 ++++++++ 4 files changed, 810 insertions(+) create mode 100644 sound/soc/atmel/atmel-classd.c create mode 100644 sound/soc/atmel/atmel-classd.h (limited to 'sound') diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 1489cd461aec..2d30464b81ce 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -59,4 +59,13 @@ config SND_AT91_SOC_SAM9X5_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. + +config SND_ATMEL_SOC_CLASSD + tristate "Atmel ASoC driver for boards using CLASSD" + depends on ARCH_AT91 || COMPILE_TEST + select SND_ATMEL_SOC_DMA + select REGMAP_MMIO + help + Say Y if you want to add support for Atmel ASoC driver for boards using + CLASSD. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index b327e5cc8de3..f6f7db428216 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o snd-atmel-soc-wm8904-objs := atmel_wm8904.o snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o +snd-atmel-soc-classd-objs := atmel-classd.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o +obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c new file mode 100644 index 000000000000..8276675730ef --- /dev/null +++ b/sound/soc/atmel/atmel-classd.c @@ -0,0 +1,679 @@ +/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver + * + * Copyright (C) 2015 Atmel + * + * Author: Songjun Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or later + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atmel-classd.h" + +struct atmel_classd_pdata { + bool non_overlap_enable; + int non_overlap_time; + int pwm_type; + const char *card_name; +}; + +struct atmel_classd { + dma_addr_t phy_base; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + struct clk *aclk; + int irq; + const struct atmel_classd_pdata *pdata; +}; + +#ifdef CONFIG_OF +static const struct of_device_id atmel_classd_of_match[] = { + { + .compatible = "atmel,sama5d2-classd", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_classd_of_match); + +static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct atmel_classd_pdata *pdata; + const char *pwm_type; + int ret; + + if (!np) { + dev_err(dev, "device node not found\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type); + if ((ret == 0) && (strcmp(pwm_type, "diff") == 0)) + pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF; + else + pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE; + + ret = of_property_read_u32(np, + "atmel,non-overlap-time", &pdata->non_overlap_time); + if (ret) + pdata->non_overlap_enable = false; + else + pdata->non_overlap_enable = true; + + ret = of_property_read_string(np, "atmel,model", &pdata->card_name); + if (ret) + pdata->card_name = "CLASSD"; + + return pdata; +} +#else +static inline struct atmel_classd_pdata * +atmel_classd_dt_init(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \ + | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \ + | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \ + | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \ + | SNDRV_PCM_RATE_96000) + +static const struct snd_pcm_hardware atmel_classd_hw = { + .info = SNDRV_PCM_INFO_MMAP + | SNDRV_PCM_INFO_MMAP_VALID + | SNDRV_PCM_INFO_INTERLEAVED + | SNDRV_PCM_INFO_RESUME + | SNDRV_PCM_INFO_PAUSE, + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = ATMEL_CLASSD_RATES, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 32 * 1024, + .periods_min = 2, + .periods_max = 256, +}; + +#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024) + +/* cpu dai component */ +static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + regmap_write(dd->regmap, CLASSD_THR, 0x0); + + return clk_prepare_enable(dd->pclk); +} + +static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(dd->pclk); +} + +static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { + .startup = atmel_classd_cpu_dai_startup, + .shutdown = atmel_classd_cpu_dai_shutdown, +}; + +static struct snd_soc_dai_driver atmel_classd_cpu_dai = { + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = ATMEL_CLASSD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &atmel_classd_cpu_dai_ops, +}; + +static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { + .name = "atmel-classd", +}; + +/* platform */ +static int +atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + if (params_physical_width(params) != 16) { + dev_err(rtd->platform->dev, + "only supports 16-bit audio data\n"); + return -EINVAL; + } + + slave_config->direction = DMA_MEM_TO_DEV; + slave_config->dst_addr = dd->phy_base + CLASSD_THR; + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config->dst_maxburst = 1; + slave_config->src_maxburst = 1; + slave_config->device_fc = false; + + return 0; +} + +static const struct snd_dmaengine_pcm_config +atmel_classd_dmaengine_pcm_config = { + .prepare_slave_config = atmel_classd_platform_configure_dma, + .pcm_hardware = &atmel_classd_hw, + .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE, +}; + +/* codec */ +static const char * const mono_mode_text[] = { + "mix", "sat", "left", "right" +}; + +static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum, + CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT, + mono_mode_text); + +static const char * const eqcfg_text[] = { + "Treble-12dB", "Treble-6dB", + "Medium-8dB", "Medium-3dB", + "Bass-12dB", "Bass-6dB", + "0 dB", + "Bass+6dB", "Bass+12dB", + "Medium+3dB", "Medium+8dB", + "Treble+6dB", "Treble+12dB", +}; + +static const unsigned int eqcfg_value[] = { + CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6, + CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3, + CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6, + CLASSD_INTPMR_EQCFG_FLAT, + CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12, + CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8, + CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum, + CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf, + eqcfg_text, eqcfg_value); + +static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1); + +static const struct snd_kcontrol_new atmel_classd_snd_controls[] = { +SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR, + CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT, + 78, 1, classd_digital_tlv), + +SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR, + CLASSD_INTPMR_DEEMP_SHIFT, 1, 0), + +SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0), + +SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0), + +SOC_ENUM("Mono Mode", classd_mono_mode_enum), + +SOC_ENUM("EQ", classd_eqcfg_enum), +}; + +static const char * const pwm_type[] = { + "Single ended", "Differential" +}; + +static int atmel_classd_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec); + struct atmel_classd *dd = snd_soc_card_get_drvdata(card); + const struct atmel_classd_pdata *pdata = dd->pdata; + u32 mask, val; + + mask = CLASSD_MR_PWMTYP_MASK; + val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT; + + mask |= CLASSD_MR_NON_OVERLAP_MASK; + if (pdata->non_overlap_enable) { + val |= (CLASSD_MR_NON_OVERLAP_EN + << CLASSD_MR_NON_OVERLAP_SHIFT); + + mask |= CLASSD_MR_NOVR_VAL_MASK; + switch (pdata->non_overlap_time) { + case 5: + val |= (CLASSD_MR_NOVR_VAL_5NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 10: + val |= (CLASSD_MR_NOVR_VAL_10NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 15: + val |= (CLASSD_MR_NOVR_VAL_15NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + case 20: + val |= (CLASSD_MR_NOVR_VAL_20NS + << CLASSD_MR_NOVR_VAL_SHIFT); + break; + default: + val |= (CLASSD_MR_NOVR_VAL_10NS + << CLASSD_MR_NOVR_VAL_SHIFT); + dev_warn(codec->dev, + "non-overlapping value %d is invalid, the default value 10 is specified\n", + pdata->non_overlap_time); + break; + } + } + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + dev_info(codec->dev, + "PWM modulation type is %s, non-overlapping is %s\n", + pwm_type[pdata->pwm_type], + pdata->non_overlap_enable?"enabled":"disabled"); + + return 0; +} + +static struct regmap *atmel_classd_codec_get_remap(struct device *dev) +{ + return dev_get_regmap(dev, NULL); +} + +static struct snd_soc_codec_driver soc_codec_dev_classd = { + .probe = atmel_classd_codec_probe, + .controls = atmel_classd_snd_controls, + .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), + .get_regmap = atmel_classd_codec_get_remap, +}; + +/* codec dai component */ +static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret = clk_prepare_enable(dd->aclk); + if (ret) + return ret; + + return clk_prepare_enable(dd->gclk); +} + +static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai, + int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 mask, val; + + mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK; + + if (mute) + val = mask; + else + val = 0; + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + return 0; +} + +#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) +#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8) + +static struct { + int rate; + int sample_rate; + int dsp_clk; + unsigned long aclk_rate; +} const sample_rates[] = { + { 8000, CLASSD_INTPMR_FRAME_8K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 16000, CLASSD_INTPMR_FRAME_16K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 32000, CLASSD_INTPMR_FRAME_32K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 48000, CLASSD_INTPMR_FRAME_48K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 96000, CLASSD_INTPMR_FRAME_96K, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + { 22050, CLASSD_INTPMR_FRAME_22K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + { 44100, CLASSD_INTPMR_FRAME_44K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + { 88200, CLASSD_INTPMR_FRAME_88K, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, +}; + +static int +atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = codec_dai->codec; + int fs; + int i, best, best_val, cur_val, ret; + u32 mask, val; + + fs = params_rate(params); + + best = 0; + best_val = abs(fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + + dev_dbg(codec->dev, + "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n", + sample_rates[best].rate, sample_rates[best].aclk_rate); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->aclk); + + ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate); + if (ret) + return ret; + + mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK; + val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT) + | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT); + + snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val); + + ret = clk_prepare_enable(dd->aclk); + if (ret) + return ret; + + return clk_prepare_enable(dd->gclk); +} + +static void +atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->aclk); +} + +static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + snd_soc_update_bits(codec, CLASSD_MR, + CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK, + (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) + |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT)); + + return 0; +} + +static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 mask, val; + + mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = mask; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) + | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, CLASSD_MR, mask, val); + + return 0; +} + +static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = { + .digital_mute = atmel_classd_codec_dai_digital_mute, + .startup = atmel_classd_codec_dai_startup, + .shutdown = atmel_classd_codec_dai_shutdown, + .hw_params = atmel_classd_codec_dai_hw_params, + .prepare = atmel_classd_codec_dai_prepare, + .trigger = atmel_classd_codec_dai_trigger, +}; + +#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi" + +static struct snd_soc_dai_driver atmel_classd_codec_dai = { + .name = ATMEL_CLASSD_CODEC_DAI_NAME, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ATMEL_CLASSD_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &atmel_classd_codec_dai_ops, +}; + +/* ASoC sound card */ +static int atmel_classd_asoc_card_init(struct device *dev, + struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + struct atmel_classd *dd = snd_soc_card_get_drvdata(card); + + dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL); + if (!dai_link) + return -ENOMEM; + + dai_link->name = "CLASSD"; + dai_link->stream_name = "CLASSD PCM"; + dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME; + dai_link->cpu_dai_name = dev_name(dev); + dai_link->codec_name = dev_name(dev); + dai_link->platform_name = dev_name(dev); + + card->dai_link = dai_link; + card->num_links = 1; + card->name = dd->pdata->card_name; + card->dev = dev; + + return 0; +}; + +/* regmap configuration */ +static const struct reg_default atmel_classd_reg_defaults[] = { + { CLASSD_INTPMR, 0x00301212 }, +}; + +#define ATMEL_CLASSD_REG_MAX 0xE4 +static const struct regmap_config atmel_classd_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_CLASSD_REG_MAX, + + .cache_type = REGCACHE_FLAT, + .reg_defaults = atmel_classd_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults), +}; + +static int atmel_classd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atmel_classd *dd; + struct resource *res; + void __iomem *io_base; + const struct atmel_classd_pdata *pdata; + struct snd_soc_card *card; + int ret; + + pdata = dev_get_platdata(dev); + if (!pdata) { + pdata = atmel_classd_dt_init(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->pdata = pdata; + + dd->irq = platform_get_irq(pdev, 0); + if (dd->irq < 0) { + ret = dd->irq; + dev_err(dev, "failed to could not get irq: %d\n", ret); + return ret; + } + + dd->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dd->pclk)) { + ret = PTR_ERR(dd->pclk); + dev_err(dev, "failed to get peripheral clock: %d\n", ret); + return ret; + } + + dd->gclk = devm_clk_get(dev, "gclk"); + if (IS_ERR(dd->gclk)) { + ret = PTR_ERR(dd->gclk); + dev_err(dev, "failed to get GCK clock: %d\n", ret); + return ret; + } + + dd->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(dd->aclk)) { + ret = PTR_ERR(dd->aclk); + dev_err(dev, "failed to get audio clock: %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no memory resource\n"); + return -ENXIO; + } + + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) { + ret = PTR_ERR(io_base); + dev_err(dev, "failed to remap register memory: %d\n", ret); + return ret; + } + + dd->phy_base = res->start; + + dd->regmap = devm_regmap_init_mmio(dev, io_base, + &atmel_classd_regmap_config); + if (IS_ERR(dd->regmap)) { + ret = PTR_ERR(dd->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, + &atmel_classd_cpu_dai_component, + &atmel_classd_cpu_dai, 1); + if (ret) { + dev_err(dev, "could not register CPU DAI: %d\n", ret); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(dev, + &atmel_classd_dmaengine_pcm_config, + 0); + if (ret) { + dev_err(dev, "could not register platform: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(dev, &soc_codec_dev_classd, + &atmel_classd_codec_dai, 1); + if (ret) { + dev_err(dev, "could not register codec: %d\n", ret); + return ret; + } + + /* register sound card */ + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, dd); + platform_set_drvdata(pdev, card); + + ret = atmel_classd_asoc_card_init(dev, card); + if (ret) { + dev_err(dev, "failed to init sound card\n"); + return ret; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret) { + dev_err(dev, "failed to register sound card: %d\n", ret); + return ret; + } + + return 0; +} + +static int atmel_classd_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver atmel_classd_driver = { + .driver = { + .name = "atmel-classd", + .of_match_table = of_match_ptr(atmel_classd_of_match), + .pm = &snd_soc_pm_ops, + }, + .probe = atmel_classd_probe, + .remove = atmel_classd_remove, +}; +module_platform_driver(atmel_classd_driver); + +MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture"); +MODULE_AUTHOR("Songjun Wu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h new file mode 100644 index 000000000000..73f8fdd1ca83 --- /dev/null +++ b/sound/soc/atmel/atmel-classd.h @@ -0,0 +1,120 @@ +#ifndef __ATMEL_CLASSD_H_ +#define __ATMEL_CLASSD_H_ + +#define CLASSD_CR 0x00000000 +#define CLASSD_CR_RESET 0x1 + +#define CLASSD_MR 0x00000004 + +#define CLASSD_MR_LEN_DIS 0x0 +#define CLASSD_MR_LEN_EN 0x1 +#define CLASSD_MR_LEN_MASK (0x1 << 0) +#define CLASSD_MR_LEN_SHIFT (0) + +#define CLASSD_MR_LMUTE_DIS 0x0 +#define CLASSD_MR_LMUTE_EN 0x1 +#define CLASSD_MR_LMUTE_SHIFT (0x1) +#define CLASSD_MR_LMUTE_MASK (0x1 << 1) + +#define CLASSD_MR_REN_DIS 0x0 +#define CLASSD_MR_REN_EN 0x1 +#define CLASSD_MR_REN_MASK (0x1 << 4) +#define CLASSD_MR_REN_SHIFT (4) + +#define CLASSD_MR_RMUTE_DIS 0x0 +#define CLASSD_MR_RMUTE_EN 0x1 +#define CLASSD_MR_RMUTE_SHIFT (0x5) +#define CLASSD_MR_RMUTE_MASK (0x1 << 5) + +#define CLASSD_MR_PWMTYP_SINGLE 0x0 +#define CLASSD_MR_PWMTYP_DIFF 0x1 +#define CLASSD_MR_PWMTYP_MASK (0x1 << 8) +#define CLASSD_MR_PWMTYP_SHIFT (8) + +#define CLASSD_MR_NON_OVERLAP_DIS 0x0 +#define CLASSD_MR_NON_OVERLAP_EN 0x1 +#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16) +#define CLASSD_MR_NON_OVERLAP_SHIFT (16) + +#define CLASSD_MR_NOVR_VAL_5NS 0x0 +#define CLASSD_MR_NOVR_VAL_10NS 0x1 +#define CLASSD_MR_NOVR_VAL_15NS 0x2 +#define CLASSD_MR_NOVR_VAL_20NS 0x3 +#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20) +#define CLASSD_MR_NOVR_VAL_SHIFT (20) + +#define CLASSD_INTPMR 0x00000008 + +#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0) +#define CLASSD_INTPMR_ATTL_SHIFT (0) +#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8) +#define CLASSD_INTPMR_ATTR_SHIFT (8) + +#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0 +#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1 +#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16) +#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16) + +#define CLASSD_INTPMR_DEEMP_DIS 0x0 +#define CLASSD_INTPMR_DEEMP_EN 0x1 +#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18) +#define CLASSD_INTPMR_DEEMP_SHIFT (18) + +#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0 +#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1 +#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19) +#define CLASSD_INTPMR_SWAP_SHIFT (19) + +#define CLASSD_INTPMR_FRAME_8K 0x0 +#define CLASSD_INTPMR_FRAME_16K 0x1 +#define CLASSD_INTPMR_FRAME_32K 0x2 +#define CLASSD_INTPMR_FRAME_48K 0x3 +#define CLASSD_INTPMR_FRAME_96K 0x4 +#define CLASSD_INTPMR_FRAME_22K 0x5 +#define CLASSD_INTPMR_FRAME_44K 0x6 +#define CLASSD_INTPMR_FRAME_88K 0x7 +#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20) +#define CLASSD_INTPMR_FRAME_SHIFT (20) + +#define CLASSD_INTPMR_EQCFG_FLAT 0x0 +#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1 +#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2 +#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3 +#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4 +#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5 +#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6 +#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7 +#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8 +#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9 +#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa +#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb +#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc +#define CLASSD_INTPMR_EQCFG_SHIFT (24) + +#define CLASSD_INTPMR_MONO_DIS 0x0 +#define CLASSD_INTPMR_MONO_EN 0x1 +#define CLASSD_INTPMR_MONO_MASK (0x1 << 28) +#define CLASSD_INTPMR_MONO_SHIFT (28) + +#define CLASSD_INTPMR_MONO_MODE_MIX 0x0 +#define CLASSD_INTPMR_MONO_MODE_SAT 0x1 +#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2 +#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3 +#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29) +#define CLASSD_INTPMR_MONO_MODE_SHIFT (29) + +#define CLASSD_INTSR 0x0000000c + +#define CLASSD_THR 0x00000010 + +#define CLASSD_IER 0x00000014 + +#define CLASSD_IDR 0x00000018 + +#define CLASSD_IMR 0x0000001c + +#define CLASSD_ISR 0x00000020 + +#define CLASSD_WPMR 0x000000e4 + +#endif -- cgit v1.2.3-59-g8ed1b From 4c9c018b2ac72e6ffaeae472723023dc4fd99a88 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 8 Oct 2015 20:40:07 +0800 Subject: ASoC: rockchip: i2s: add 8 channels capture support support max 8 channels capture, please add property 'rockchip,capture-channels' in dts to enable this, if not, support 2 channels capture default. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 40 +++++++++++++++++++++++++++++++++++++-- sound/soc/rockchip/rockchip_i2s.h | 10 ++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b93610212e3d..f07833b42edd 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -245,8 +245,34 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val); - regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val); + switch (params_channels(params)) { + case 8: + val |= I2S_CHN_8; + break; + case 6: + val |= I2S_CHN_6; + break; + case 4: + val |= I2S_CHN_4; + break; + case 2: + val |= I2S_CHN_2; + break; + default: + dev_err(i2s->dev, "invalid channel: %d\n", + params_channels(params)); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + regmap_update_bits(i2s->regmap, I2S_RXCR, + I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, + val); + else + regmap_update_bits(i2s->regmap, I2S_TXCR, + I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, + val); + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, I2S_DMACR_TDL(16)); regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, @@ -415,10 +441,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = { static int rockchip_i2s_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; struct rk_i2s_dev *i2s; struct resource *res; void __iomem *regs; int ret; + int val; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) { @@ -475,6 +503,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } + /* refine capture channels */ + if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { + if (val >= 2 && val <= 8) + rockchip_i2s_dai.capture.channels_max = val; + else + rockchip_i2s_dai.capture.channels_max = 2; + } + ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_component, &rockchip_i2s_dai, 1); diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index 93f456f518a9..a54ee35e1e5c 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -49,6 +49,9 @@ * RXCR * receive operation control register */ +#define I2S_RXCR_CSR_SHIFT 15 +#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT) +#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT) #define I2S_RXCR_HWT BIT(14) #define I2S_RXCR_SJM_SHIFT 12 #define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT) @@ -207,6 +210,13 @@ enum { ROCKCHIP_DIV_BCLK, }; +/* channel select */ +#define I2S_CSR_SHIFT 15 +#define I2S_CHN_2 (0 << I2S_CSR_SHIFT) +#define I2S_CHN_4 (1 << I2S_CSR_SHIFT) +#define I2S_CHN_6 (2 << I2S_CSR_SHIFT) +#define I2S_CHN_8 (3 << I2S_CSR_SHIFT) + /* I2S REGS */ #define I2S_TXCR (0x0000) #define I2S_RXCR (0x0004) -- cgit v1.2.3-59-g8ed1b From b3f2dcddd576a2a6e59c407109610206c4062c8f Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 8 Oct 2015 20:40:09 +0800 Subject: ASoC: rockchip: i2s: share tx/rx lrck when symmetric_rates enabled share lrck_tx to lrck_rx when symmetric_rates enabled. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 8 ++++++++ sound/soc/rockchip/rockchip_i2s.h | 6 ++++++ 2 files changed, 14 insertions(+) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index f07833b42edd..58ee64594f07 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_i2s_dev *i2s = to_info(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned int val = 0; switch (params_format(params)) { @@ -278,6 +279,13 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, I2S_DMACR_RDL(16)); + val = I2S_CKR_TRCM_TXRX; + if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates) + val = I2S_CKR_TRCM_TXSHARE; + + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_TRCM_MASK, + val); return 0; } diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index a54ee35e1e5c..dc6e2c74d088 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -78,6 +78,12 @@ * CKR * clock generation register */ +#define I2S_CKR_TRCM_SHIFT 28 +#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT) +#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT) #define I2S_CKR_MSS_SHIFT 27 #define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT) #define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT) -- cgit v1.2.3-59-g8ed1b From 4901aa065bced55be5ae55b58fc032bfc727fdda Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 19:13:45 +0800 Subject: ASoC: Intel: Skylake: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_rt286.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 488c98ee1ded..a73a431bd8b7 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -240,18 +240,11 @@ static int skylake_audio_probe(struct platform_device *pdev) { skylake_rt286.dev = &pdev->dev; - return snd_soc_register_card(&skylake_rt286); -} - -static int skylake_audio_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&skylake_rt286); - return 0; + return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); } static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, - .remove = skylake_audio_remove, .driver = { .name = "skl_alc286s_i2s", }, -- cgit v1.2.3-59-g8ed1b From de687fb133f7ede18f026feefbeab3106081148d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 10:43:21 +0200 Subject: ASoC: h1940_uda1380: Constify rate constraints The rate constraints in this board driver are not modified at runtime, so make them const. While we are at it also remove the unnecessary 0 initializer for the mask field. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/samsung/h1940_uda1380.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index c72e9fb26658..5f5825faeb2a 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -26,16 +26,15 @@ #include #include "s3c24xx-i2s.h" -static unsigned int rates[] = { +static const unsigned int rates[] = { 11025, 22050, 44100, }; -static struct snd_pcm_hw_constraint_list hw_rates = { +static const struct snd_pcm_hw_constraint_list hw_rates = { .count = ARRAY_SIZE(rates), .list = rates, - .mask = 0, }; static struct snd_soc_jack hp_jack; -- cgit v1.2.3-59-g8ed1b From 7a5b8ba412b6217de8badaa52283588e50eeae08 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 10:43:22 +0200 Subject: ASoC: rx1950_uda1380: Constify rate constraints The rate constraints in this board driver are not modified at runtime, so make them const. While we are at it also remove the unnecessary 0 initializer for the mask field. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/samsung/rx1950_uda1380.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 35e37c457f1f..fa096abe9e75 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -38,16 +38,15 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_spk_power(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -static unsigned int rates[] = { +static const unsigned int rates[] = { 16000, 44100, 48000, }; -static struct snd_pcm_hw_constraint_list hw_rates = { +static const struct snd_pcm_hw_constraint_list hw_rates = { .count = ARRAY_SIZE(rates), .list = rates, - .mask = 0, }; static struct snd_soc_jack hp_jack; -- cgit v1.2.3-59-g8ed1b From 444c37aeee46db70ce01199863dc046fa68e88b7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 22 Oct 2015 10:43:23 +0200 Subject: ASoC: fsl_sai: Constify rate constraints The rate constraints in this driver are shared between all device instances. It should not be (and is not) modified at runtime, so make them const. While we are at it also change the type of the rates array from u32 to unsigned int. While both resolve to the same type, u32 is usually used to empathize that the value is for a 32-bit hardware register or similar which makes it slightly confusing in this context. Signed-off-by: Lars-Peter Clausen Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9366b5a42e1d..2150f5aaba7a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -27,13 +27,13 @@ #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) -static u32 fsl_sai_rates[] = { +static const unsigned int fsl_sai_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; -static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { +static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { .count = ARRAY_SIZE(fsl_sai_rates), .list = fsl_sai_rates, }; -- cgit v1.2.3-59-g8ed1b From 9270b7b92615324ecda2b3fa0be98b4ab6c3e361 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:34 +0530 Subject: ASoC: Intel: Skylake: Fix return for pm_runtime_get_sync() pm_runtime_get_sync() will return a negative value in case of error and can return postive value for success. The return check for pm_runtime_get_sync() must be less than 0, so fix it Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a8f53da11ae3..a2f94ce1679d 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -116,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); ret = pm_runtime_get_sync(dai->dev); - if (ret) + if (ret < 0) return ret; stream = snd_hdac_ext_stream_assign(ebus, substream, -- cgit v1.2.3-59-g8ed1b From bc03281a5c706f6372dfc2100b04aa4055a15c88 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:35 +0530 Subject: ASoC: Intel: Skylake: Remove BE copier blob memcpy The BE copier private data allowed endpoint configuration blobs, now these are queried from BIOS, we don't need to copy the blob, but only capability. Removing the blob from private data will not allocate memory for module specific config in which case memcpy will fail. Fix is to assign the ptr queried from the NHLT table for the endpoint configuration. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a5d8420ce69a..7c920857007e 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -926,7 +926,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, params->s_freq, params->stream); if (cfg) { mconfig->formats_config.caps_size = cfg->size; - memcpy(mconfig->formats_config.caps, &cfg->caps, cfg->size); + mconfig->formats_config.caps = (u32 *) &cfg->caps; } else { dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", mconfig->vbus_id, link_type, -- cgit v1.2.3-59-g8ed1b From f0900eb213e0f6f37e1567531f9604b9bed6771d Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 22 Oct 2015 23:22:36 +0530 Subject: ASoC: Intel: Skylake: Fix to use correct macros for the path iteration In case of playback, for the BE dai source path should be iterated to find the pipe params. With sink path iterated, this resulted in a loop and kernel panic with page request failure. Similar are the cases for Capture and FE dais. Using correct macros to fix the panic Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 7c920857007e..0fba39dcb2ab 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -844,7 +844,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { w = dai->playback_widget; - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && is_skl_dsp_widget_type(p->sink)) continue; @@ -857,7 +857,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) } } else { w = dai->capture_widget; - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && is_skl_dsp_widget_type(p->source)) continue; @@ -945,7 +945,7 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, { struct snd_soc_dapm_path *p; - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { @@ -969,7 +969,7 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, { struct snd_soc_dapm_path *p = NULL; - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { -- cgit v1.2.3-59-g8ed1b From 4d8adccb220ca270cfcdd80752618095fdc9990c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 22 Oct 2015 23:22:37 +0530 Subject: ASoC: Intel: Skylake: Fix to fill all sink/source pipe params Currently params only for first copier widget identified in the source/sink path is queried from NHLT. In the dapm route the playback/capture widget may be connected to more than one copier widget. This patch adds return check to return only for any error case. Signed-off-by: Subhransu S. Prusty Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 0fba39dcb2ab..68e1b00ce7ed 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -944,48 +944,60 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, struct skl_pipe_params *params) { struct snd_soc_dapm_path *p; + int ret = -EIO; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { - if (!p->source->power) - return skl_tplg_be_fill_pipe_params( + if (!p->source->power) { + ret = skl_tplg_be_fill_pipe_params( dai, p->source->priv, params); - else + if (ret < 0) + return ret; + } else { return -EBUSY; + } } else { - return skl_tplg_be_set_src_pipe_params( + ret = skl_tplg_be_set_src_pipe_params( dai, p->source, params); + if (ret < 0) + return ret; } } - return -EIO; + return ret; } static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) { struct snd_soc_dapm_path *p = NULL; + int ret = -EIO; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { - if (!p->sink->power) - return skl_tplg_be_fill_pipe_params( + if (!p->sink->power) { + ret = skl_tplg_be_fill_pipe_params( dai, p->sink->priv, params); - else + if (ret < 0) + return ret; + } else { return -EBUSY; + } } else { - return skl_tplg_be_set_sink_pipe_params( + ret = skl_tplg_be_set_sink_pipe_params( dai, p->sink, params); + if (ret < 0) + return ret; } } - return -EIO; + return ret; } /* -- cgit v1.2.3-59-g8ed1b From d7b188131c4823a85d17b992083bf8a8c32a2be3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:38 +0530 Subject: ASoC: Intel: Skylake: Update for ssp node index in copier cfg DSP firmware has interface change for SSP node index structure. New FW interface removes the dual_mono field and adds 4 bits for TDM slot group index. This patch updates the ssp dma to align with the DSP firmware structure. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 7 ++++--- sound/soc/intel/skylake/skl-topology.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 826d4fd8930a..77c0736d3c9e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -303,6 +303,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, struct skl_cpr_cfg *cpr_mconfig) { union skl_connector_node_id node_id = {0}; + union skl_ssp_dma_node ssp_node = {0}; struct skl_pipe_params *params = mconfig->pipe->p_params; switch (mconfig->dev_type) { @@ -320,9 +321,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_I2S_LINK_OUTPUT_CLASS : SKL_DMA_I2S_LINK_INPUT_CLASS; - node_id.node.vindex = params->host_dma_id + - (mconfig->time_slot << 1) + - (mconfig->vbus_id << 3); + ssp_node.dma_node.time_slot_index = mconfig->time_slot; + ssp_node.dma_node.i2s_instance = mconfig->vbus_id; + node_id.node.vindex = ssp_node.val; break; case SKL_DEVICE_DMIC: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 0c83dc3ea3e2..2399535a196a 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -153,8 +153,7 @@ enum skl_dma_type { union skl_ssp_dma_node { u8 val; struct { - u8 dual_mono:1; - u8 time_slot:3; + u8 time_slot_index:4; u8 i2s_instance:4; } dma_node; }; -- cgit v1.2.3-59-g8ed1b From 4e10996ba810031a493d903539f595e053826103 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:39 +0530 Subject: ASoC: Intel: Skylake: Add support to disable module notifications Each FW modules can report underrun/overrun notification from all modules. This patch disables underrun/overrun notification after firmware is loaded. This will be supportted for debug mode only thru debugfs Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 28 ++++++++++++++++++++++++++-- sound/soc/intel/skylake/skl-topology.h | 5 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 77c0736d3c9e..ca7bbeea2cd9 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) return 0; } +#define NOTIFICATION_PARAM_ID 3 +#define NOTIFICATION_MASK 0xf + +/* disable notfication for underruns/overruns from firmware module */ +static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) +{ + struct notification_mask mask; + struct skl_ipc_large_config_msg msg = {0}; + + mask.notify = NOTIFICATION_MASK; + mask.enable = enable; + + msg.large_param_id = NOTIFICATION_PARAM_ID; + msg.param_data_size = sizeof(mask); + + skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); +} + int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; @@ -79,7 +97,7 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); - + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return ret; @@ -122,6 +140,7 @@ int skl_suspend_dsp(struct skl *skl) int skl_resume_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + int ret; /* if ppcap is not supported return 0 */ if (!skl->ebus.ppcap) @@ -131,7 +150,12 @@ int skl_resume_dsp(struct skl *skl) snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); - return skl_dsp_wake(ctx->dsp); + ret = skl_dsp_wake(ctx->dsp); + if (ret < 0) + return ret; + + skl_dsp_enable_notification(skl->skl_sst, false); + return ret; } enum skl_bitdepth skl_get_bit_depth(int params) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 2399535a196a..76053a8de41c 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -129,6 +129,11 @@ struct skl_src_module_cfg { enum skl_s_freq src_cfg; } __packed; +struct notification_mask { + u32 notify; + u32 enable; +} __packed; + struct skl_up_down_mixer_cfg { struct skl_base_cfg base_cfg; enum skl_ch_cfg out_ch_cfg; -- cgit v1.2.3-59-g8ed1b From 2ac454ffe6c2cb923711edc8027bbc0e782be457 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:40 +0530 Subject: ASoC: Intel: Skylake: Fix to check return value of dsp init If DSP initialization fails, ipc to disable notification will cause NULL ptr exception as ipc is not initialized. This patch returns error if dsp init fails before sending disable notification ipc. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ca7bbeea2cd9..a1001a686aa9 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -97,6 +97,9 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); + if (ret < 0) + return ret; + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); -- cgit v1.2.3-59-g8ed1b From bfa764accdea017b1c557f43fa5f2c77a0dc3b15 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:41 +0530 Subject: ASoC: Intel: Skylake: Fix to set Non gateway copier cfg If copier instance is connected to a DMA gateway then driver needs to configure the gateway configuration otherwise set it to invalid. This patch sets the non gateway copier node value to invalid. Signed-off-by: Dharageswari.R Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 8 +++++++- sound/soc/intel/skylake/skl-tplg-interface.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index a1001a686aa9..50a109503a3f 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -321,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, (mconfig->formats_config.caps_size) / 4; } +#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF /* * Calculate the gatewat settings required for copier module, type of * gateway and index of gateway to use @@ -367,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, node_id.node.vindex = params->link_dma_id; break; - default: + case SKL_DEVICE_HDAHOST: node_id.node.dma_type = (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_HDA_HOST_OUTPUT_CLASS : SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; + + default: + cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID; + cpr_mconfig->cpr_feature_mask = 0; + return; } cpr_mconfig->gtw_cfg.node_id = node_id.val; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index d14f7fe64aa8..718d3d5df9a8 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -106,6 +106,7 @@ enum skl_dev_type { SKL_DEVICE_I2S = 0x2, SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, + SKL_DEVICE_HDAHOST = 0x5, SKL_DEVICE_NONE }; -- cgit v1.2.3-59-g8ed1b From 6abca1d71b62090821240ee2a4507c94ddd144e9 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:42 +0530 Subject: ASoC: Intel: Skylake: Add support to topology for module static pin Some module pin connection are static and defined by the topology. This patch adds support for static pin definitions in topology widget private data Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 32 +++++++++++++++------------- sound/soc/intel/skylake/skl-tplg-interface.h | 6 ++++-- 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 68e1b00ce7ed..a7854c8fc523 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1034,18 +1034,17 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { * The topology binary passes the pin info for a module so initialize the pin * info passed into module instance */ -static void skl_fill_module_pin_info(struct device *dev, - struct skl_module_pin *m_pin, - int max_pin) +static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, + struct skl_module_pin *m_pin, + bool is_dynamic, int max_pin) { int i; for (i = 0; i < max_pin; i++) { - m_pin[i].id.module_id = 0; - m_pin[i].id.instance_id = 0; + m_pin[i].id.module_id = dfw_pin[i].module_id; + m_pin[i].id.instance_id = dfw_pin[i].instance_id; m_pin[i].in_use = false; - m_pin[i].is_dynamic = true; - m_pin[i].pin_index = i; + m_pin[i].is_dynamic = is_dynamic; } } @@ -1164,17 +1163,20 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(bus->dev, - (mconfig->max_in_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; - skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin, - mconfig->max_in_queue); - skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin, - mconfig->max_out_queue); + skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin, + dfw_config->is_dynamic_in_pin, + mconfig->max_in_queue); + + skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin, + dfw_config->is_dynamic_out_pin, + mconfig->max_out_queue); + if (mconfig->formats_config.caps_size == 0) goto bind_event; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 718d3d5df9a8..2bc396d54cbe 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -113,8 +113,6 @@ enum skl_dev_type { struct skl_dfw_module_pin { u16 module_id; u16 instance_id; - u8 pin_id; - bool is_dynamic; } __packed; struct skl_dfw_module_fmt { @@ -155,9 +153,13 @@ struct skl_dfw_module { u32 converter; u32 module_type; u32 vbus_id; + u8 is_dynamic_in_pin; + u8 is_dynamic_out_pin; struct skl_dfw_pipe pipe; struct skl_dfw_module_fmt in_fmt; struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; + struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; struct skl_dfw_module_caps caps; } __packed; -- cgit v1.2.3-59-g8ed1b From be77b38a3668306bdc0b0c8dff4660c2a8b38d8e Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Fri, 23 Oct 2015 14:05:53 +0800 Subject: ASoC: rt5645: Add the HWEQ for the speaker output The patch adds the HWEQ function for the speaker output. User can set the HWEQ parameters using the ALSA binary control byte-by-byte. We use the following struct array to store the HWEQ parameters for implementing as simple as possible. struct rt5645_eq_param_s { unsigned short reg; unsigned short val; }; It supports the variant length of the HWEQ parameters that are required. We add the validating function in the function "rt5645_hweq_put" of the ALSA binary control to avoid the user that puts the invalid parameters. In the HWEQ enable function of speaker event, we also add the validating function to prevent that the invalid parameters are applied to codec. The HWEQ parameters should be controlled by DAPM for a specific sequence, so the parameters will be applied to the codec in the speaker power up event of DAPM, and will be disabled in the speaker power down event of DAPM. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 65ea712cef82..f046bb83fd97 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -42,6 +42,8 @@ #define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING)) +#define RT5645_HWEQ_NUM 57 + static const struct regmap_range_cfg rt5645_ranges[] = { { .name = "PR", @@ -224,6 +226,11 @@ static const struct reg_default rt5645_reg[] = { { 0xff, 0x6308 }, }; +struct rt5645_eq_param_s { + unsigned short reg; + unsigned short val; +}; + static const char *const rt5645_supply_names[] = { "avdd", "cpvdd", @@ -240,6 +247,7 @@ struct rt5645_priv { struct snd_soc_jack *btn_jack; struct delayed_work jack_detect_work; struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; + struct rt5645_eq_param_s *eq_param; int codec_type; int sysclk; @@ -477,6 +485,86 @@ static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv, 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0) ); +static int rt5645_hweq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s); + + return 0; +} + +static int rt5645_hweq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s *eq_param = + (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg); + eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val); + } + + return 0; +} + +static bool rt5645_validate_hweq(unsigned short reg) +{ + if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) | + (reg == RT5645_EQ_CTRL2)) + return true; + + return false; +} + +static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s *eq_param = + (struct rt5645_eq_param_s *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = be16_to_cpu(eq_param[i].reg); + eq_param[i].val = be16_to_cpu(eq_param[i].val); + } + + /* The final setting of the table should be RT5645_EQ_CTRL2 */ + for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) { + if (eq_param[i].reg == 0) + continue; + else if (eq_param[i].reg != RT5645_EQ_CTRL2) + return 0; + else + break; + } + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (!rt5645_validate_hweq(eq_param[i].reg) && + eq_param[i].reg != 0) + return 0; + else if (eq_param[i].reg == 0) + break; + } + + memcpy(rt5645->eq_param, eq_param, + RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s)); + + return 0; +} + +#define RT5645_HWEQ(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt5645_hweq_info, \ + .get = rt5645_hweq_get, \ + .put = rt5645_hweq_put \ +} + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -541,6 +629,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* I2S2 function select */ SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT, 1, 1), + RT5645_HWEQ("Speaker HWEQ"), }; /** @@ -631,6 +720,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source, } +static int rt5645_enable_hweq(struct snd_soc_codec *codec) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (rt5645_validate_hweq(rt5645->eq_param[i].reg)) + regmap_write(rt5645->regmap, rt5645->eq_param[i].reg, + rt5645->eq_param[i].val); + else + break; + } + + return 0; +} + /** * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters * @codec: SoC audio codec device. @@ -1535,6 +1640,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: + rt5645_enable_hweq(codec); snd_soc_update_bits(codec, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L, @@ -1543,6 +1649,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, RT5645_EQ_CTRL2, 0); snd_soc_update_bits(codec, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L, 0); @@ -3057,6 +3164,9 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_dapm_sync(dapm); } + rt5645->eq_param = devm_kzalloc(codec->dev, + RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL); + return 0; } -- cgit v1.2.3-59-g8ed1b From ab57b8e90c658a72944c48a81a1352e61e84db02 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 23 Oct 2015 17:15:41 -0400 Subject: ASoC: dwc: add check for master/slave format DW i2s controller's master/slave config can be read from a read-only register. Machine driver can try to set a master/slave format on cpu-dai using 'set_fmt' of dai ops. A check is added to verify codec is master when dwc is slave and vice-versa. Signed-off-by: Maruthi Bayyavarapu Signed-off-by: Alex Deucher Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'sound') diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 3a52f82b5523..f427325ae448 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -341,12 +341,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream, return ret; } +static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + if (dev->capability & DW_I2S_SLAVE) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBS_CFS: + if (dev->capability & DW_I2S_MASTER) + ret = 0; + else + ret = -EINVAL; + break; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + dev_dbg(dev->dev, "dwc : Invalid master/slave format\n"); + ret = -EINVAL; + break; + } + return ret; +} + static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, + .set_fmt = dw_i2s_set_fmt, }; static const struct snd_soc_component_driver dw_i2s_component = { -- cgit v1.2.3-59-g8ed1b From bc86e53a0ae9bb26c1af04034e010d8f22b1b0da Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 24 Oct 2015 14:55:41 +0800 Subject: ASoC: rt5645: Sort the order for register bit defines So we have consistent order for register bit defines. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index d0e0ce52c287..7cea2826279d 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -619,14 +619,14 @@ #define RT5645_G_OM_L_SM_L_SFT 6 #define RT5645_M_BST1_L_SM_L (0x1 << 5) #define RT5645_M_BST1_L_SM_L_SFT 5 +#define RT5645_M_BST3_L_SM_L (0x1 << 4) +#define RT5645_M_BST3_L_SM_L_SFT 4 #define RT5645_M_IN_L_SM_L (0x1 << 3) #define RT5645_M_IN_L_SM_L_SFT 3 -#define RT5645_M_DAC_L1_SM_L (0x1 << 1) -#define RT5645_M_DAC_L1_SM_L_SFT 1 #define RT5645_M_DAC_L2_SM_L (0x1 << 2) #define RT5645_M_DAC_L2_SM_L_SFT 2 -#define RT5645_M_BST3_L_SM_L (0x1 << 4) -#define RT5645_M_BST3_L_SM_L_SFT 4 +#define RT5645_M_DAC_L1_SM_L (0x1 << 1) +#define RT5645_M_DAC_L1_SM_L_SFT 1 /* SPK Right Mixer Control (0x47) */ #define RT5645_G_RM_R_SM_R_MASK (0x3 << 14) @@ -641,14 +641,14 @@ #define RT5645_G_OM_R_SM_R_SFT 6 #define RT5645_M_BST2_R_SM_R (0x1 << 5) #define RT5645_M_BST2_R_SM_R_SFT 5 +#define RT5645_M_BST3_R_SM_R (0x1 << 4) +#define RT5645_M_BST3_R_SM_R_SFT 4 #define RT5645_M_IN_R_SM_R (0x1 << 3) #define RT5645_M_IN_R_SM_R_SFT 3 -#define RT5645_M_DAC_R1_SM_R (0x1 << 1) -#define RT5645_M_DAC_R1_SM_R_SFT 1 #define RT5645_M_DAC_R2_SM_R (0x1 << 2) #define RT5645_M_DAC_R2_SM_R_SFT 2 -#define RT5645_M_BST3_R_SM_R (0x1 << 4) -#define RT5645_M_BST3_R_SM_R_SFT 4 +#define RT5645_M_DAC_R1_SM_R (0x1 << 1) +#define RT5645_M_DAC_R1_SM_R_SFT 1 /* SPOLMIX Control (0x48) */ #define RT5645_M_DAC_L1_SPM_L (0x1 << 15) @@ -673,12 +673,12 @@ #define RT5645_SPK_G_CLSD_SFT 0 /* Mono Output Mixer Control (0x4c) */ +#define RT5645_G_MONOMIX_MASK (0x1 << 10) +#define RT5645_G_MONOMIX_SFT 10 #define RT5645_M_OV_L_MM (0x1 << 9) #define RT5645_M_OV_L_MM_SFT 9 #define RT5645_M_DAC_L2_MA (0x1 << 8) #define RT5645_M_DAC_L2_MA_SFT 8 -#define RT5645_G_MONOMIX_MASK (0x1 << 10) -#define RT5645_G_MONOMIX_SFT 10 #define RT5645_M_BST2_MM (0x1 << 4) #define RT5645_M_BST2_MM_SFT 4 #define RT5645_M_DAC_R1_MM (0x1 << 3) -- cgit v1.2.3-59-g8ed1b From 739146b6144a6541cf94a668d6e7227b7882af30 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 22 Oct 2015 15:56:40 -0700 Subject: ASoC: fsl: Use #ifdef instead of #if for CONFIG_PM_SLEEP Change them to #ifdef as CONFIG_PM_SLEEP might not be defined at all. Reported-by: kbuild test robot Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 2 +- sound/soc/fsl/fsl_sai.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index aab675a9d3f1..59f234e51971 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -900,7 +900,7 @@ static const struct of_device_id fsl_esai_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int fsl_esai_suspend(struct device *dev) { struct fsl_esai *esai = dev_get_drvdata(dev); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 38505f32ebe6..a4435f5e3be9 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -806,7 +806,7 @@ static const struct of_device_id fsl_sai_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int fsl_sai_suspend(struct device *dev) { struct fsl_sai *sai = dev_get_drvdata(dev); -- cgit v1.2.3-59-g8ed1b From d9684bb5b3ef83e22a7270341e722f1acc1d97e5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 26 Oct 2015 16:54:16 +0100 Subject: ALSA: hda/ca0132 - Convert leftover pr_info() and pr_err() Use codec_*() macro instead of pr_*() for improving the log information. The current form even slips from alsa-info.sh. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 3a02e5c14d09..f8a12ca477f1 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2673,13 +2673,13 @@ static bool dspload_wait_loaded(struct hda_codec *codec) do { if (dspload_is_loaded(codec)) { - pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n"); + codec_info(codec, "ca0132 DSP downloaded and running\n"); return true; } msleep(20); } while (time_before(jiffies, timeout)); - pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n"); + codec_err(codec, "ca0132 failed to download DSP\n"); return false; } @@ -4375,7 +4375,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) { - pr_err("ca0132 dspload_image failed.\n"); + codec_err(codec, "ca0132 DSP load image failed\n"); goto exit_download; } -- cgit v1.2.3-59-g8ed1b From 6ed1131fe196ad7ffc13acc1a1eadc08a1db0303 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Mon, 26 Oct 2015 15:37:39 +0800 Subject: ALSA: hda/realtek - Dell XPS one ALC3260 speaker no sound after resume back This machine had I2S codec for speaker output. It need to refill the I2S codec initial verb after resume back. Signed-off-by: Kailang Yang Reported-and-tested-by: George Gugulea Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 16b8dcba5c12..720a9fb32e20 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4596,6 +4596,7 @@ enum { ALC292_FIXUP_DELL_E7X, ALC292_FIXUP_DISABLE_AAMIX, ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC275_FIXUP_DELL_XPS, }; static const struct hda_fixup alc269_fixups[] = { @@ -5165,6 +5166,17 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE }, + [ALC275_FIXUP_DELL_XPS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x1f}, + {0x20, AC_VERB_SET_PROC_COEF, 0x00c0}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x30}, + {0x20, AC_VERB_SET_PROC_COEF, 0x00b1}, + {} + } + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5179,6 +5191,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), -- cgit v1.2.3-59-g8ed1b From cadd16ea33a938d49aee99edd4758cc76048b399 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 27 Oct 2015 14:21:51 +0100 Subject: ALSA: hda - Disable 64bit address for Creative HDA controllers We've had many reports that some Creative sound cards with CA0132 don't work well. Some reported that it starts working after reloading the module, while some reported it starts working when a 32bit kernel is used. All these facts seem implying that the chip fails to communicate when the buffer is located in 64bit address. This patch addresses these issues by just adding AZX_DCAPS_NO_64BIT flag to the corresponding PCI entries. I casually had a chance to test an SB Recon3D board, and indeed this seems helping. Although this hasn't been tested on all Creative devices, it's safer to assume that this restriction applies to the rest of them, too. So the flag is applied to all Creative entries. Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c38c68f57938..61b8b75a3c80 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -334,6 +334,7 @@ enum { #define AZX_DCAPS_PRESET_CTHDA \ (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_NO_64BIT |\ AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF) /* @@ -2284,11 +2285,13 @@ static const struct pci_device_id azx_ids[] = { .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | + AZX_DCAPS_NO_64BIT | AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB }, #else /* this entry seems still valid -- i.e. without emu20kx chip */ { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | + AZX_DCAPS_NO_64BIT | AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB }, #endif /* CM8888 */ -- cgit v1.2.3-59-g8ed1b From e5e113cf0d19392f26c6b63e63ad4680ee4ec5da Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 28 Oct 2015 11:37:53 +0100 Subject: ALSA: Constify ratden/ratnum constraints The ALSA core does not modify the constraints provided by a driver. Most constraint helper functions already take a const pointer to the constraint description, the exception at the moment being the ratden and ratnum constraints. Make those const as well, this allows a driver to declare them as const. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 10 +++++----- sound/core/pcm_lib.c | 17 +++++++++-------- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 4e6ad74fd8a2..b0be09279943 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -265,12 +265,12 @@ struct snd_ratden { struct snd_pcm_hw_constraint_ratnums { int nrats; - struct snd_ratnum *rats; + const struct snd_ratnum *rats; }; struct snd_pcm_hw_constraint_ratdens { int nrats; - struct snd_ratden *rats; + const struct snd_ratden *rats; }; struct snd_pcm_hw_constraint_list { @@ -970,7 +970,7 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, int snd_interval_ranges(struct snd_interval *i, unsigned int count, const struct snd_interval *list, unsigned int mask); int snd_interval_ratnum(struct snd_interval *i, - unsigned int rats_count, struct snd_ratnum *rats, + unsigned int rats_count, const struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp); void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); @@ -1000,11 +1000,11 @@ int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratnums *r); + const struct snd_pcm_hw_constraint_ratnums *r); int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratdens *r); + const struct snd_pcm_hw_constraint_ratdens *r); int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, unsigned int cond, unsigned int width, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 05a3ca93c647..6b5a811e01a5 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -801,7 +801,7 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, * negative error code. */ int snd_interval_ratnum(struct snd_interval *i, - unsigned int rats_count, struct snd_ratnum *rats, + unsigned int rats_count, const struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp) { unsigned int best_num, best_den; @@ -920,7 +920,8 @@ EXPORT_SYMBOL(snd_interval_ratnum); * negative error code. */ static int snd_interval_ratden(struct snd_interval *i, - unsigned int rats_count, struct snd_ratden *rats, + unsigned int rats_count, + const struct snd_ratden *rats, unsigned int *nump, unsigned int *denp) { unsigned int best_num, best_diff, best_den; @@ -1339,7 +1340,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_pcm_hw_constraint_ratnums *r = rule->private; + const struct snd_pcm_hw_constraint_ratnums *r = rule->private; unsigned int num = 0, den = 0; int err; err = snd_interval_ratnum(hw_param_interval(params, rule->var), @@ -1363,10 +1364,10 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratnums *r) + const struct snd_pcm_hw_constraint_ratnums *r) { return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_ratnums, r, + snd_pcm_hw_rule_ratnums, (void *)r, var, -1); } @@ -1375,7 +1376,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_pcm_hw_constraint_ratdens *r = rule->private; + const struct snd_pcm_hw_constraint_ratdens *r = rule->private; unsigned int num = 0, den = 0; int err = snd_interval_ratden(hw_param_interval(params, rule->var), r->nrats, r->rats, &num, &den); @@ -1398,10 +1399,10 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratdens *r) + const struct snd_pcm_hw_constraint_ratdens *r) { return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_ratdens, r, + snd_pcm_hw_rule_ratdens, (void *)r, var, -1); } -- cgit v1.2.3-59-g8ed1b From 78dd5e21b075053e67194ea8f496439bebc52728 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 28 Oct 2015 12:26:48 +0100 Subject: ALSA: hda - Add / fix kernel doc comments Give some readable comment in kernel doc style for each exported function, as I promised in the previous meetings. While we're at it, fix the wrong comments, too. Signed-off-by: Takashi Iwai --- sound/hda/hdac_bus.c | 14 ++++++++++ sound/hda/hdac_device.c | 10 ++++--- sound/hda/hdac_i915.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ sound/hda/hdac_regmap.c | 10 +++++++ sound/hda/hdac_stream.c | 3 ++- 5 files changed, 102 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 27c447e4fe5c..0e81ea89a596 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -172,6 +172,15 @@ static void process_unsol_events(struct work_struct *work) } } +/** + * snd_hdac_bus_add_device - Add a codec to bus + * @bus: HDA core bus + * @codec: HDA core device to add + * + * Adds the given codec to the list in the bus. The caddr_tbl array + * and codec_powered bits are updated, as well. + * Returns zero if success, or a negative error code. + */ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec) { if (bus->caddr_tbl[codec->addr]) { @@ -188,6 +197,11 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device); +/** + * snd_hdac_bus_remove_device - Remove a codec from bus + * @bus: HDA core bus + * @codec: HDA core device to remove + */ void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec) { diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index bbdb25f5bbb9..e361024eabb6 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -629,8 +629,10 @@ int snd_hdac_power_down_pm(struct hdac_device *codec) EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); #endif -/* - * Enable/disable the link power for a codec. +/** + * snd_hdac_link_power - Enable/disable the link power for a codec + * @codec: the codec object + * @bool: enable or disable the link power */ int snd_hdac_link_power(struct hdac_device *codec, bool enable) { @@ -1048,8 +1050,8 @@ int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hdac_codec_write); -/* - * snd_hdac_check_power_state: check whether the actual power state matches +/** + * snd_hdac_check_power_state - check whether the actual power state matches * with the target state * * @hdac: the HDAC device diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 55c3df4458f7..8fef1b8d1fd8 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -23,6 +23,19 @@ static struct i915_audio_component *hdac_acomp; +/** + * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup + * @bus: HDA core bus + * @enable: enable or disable the wakeup + * + * This function is supposed to be used only by a HD-audio controller + * driver that needs the interaction with i915 graphics. + * + * This function should be called during the chip reset, also called at + * resume for updating STATESTS register read. + * + * Returns zero for success or a negative error code. + */ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) { struct i915_audio_component *acomp = bus->audio_component; @@ -45,6 +58,19 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) } EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup); +/** + * snd_hdac_display_power - Power up / down the power refcount + * @bus: HDA core bus + * @enable: power up or down + * + * This function is supposed to be used only by a HD-audio controller + * driver that needs the interaction with i915 graphics. + * + * This function manages a refcount and calls the i915 get_power() and + * put_power() ops accordingly, toggling the codec wakeup, too. + * + * Returns zero for success or a negative error code. + */ int snd_hdac_display_power(struct hdac_bus *bus, bool enable) { struct i915_audio_component *acomp = bus->audio_component; @@ -71,6 +97,16 @@ int snd_hdac_display_power(struct hdac_bus *bus, bool enable) } EXPORT_SYMBOL_GPL(snd_hdac_display_power); +/** + * snd_hdac_get_display_clk - Get CDCLK in kHz + * @bus: HDA core bus + * + * This function is supposed to be used only by a HD-audio controller + * driver that needs the interaction with i915 graphics. + * + * This function queries CDCLK value in kHz from the graphics driver and + * returns the value. A negative code is returned in error. + */ int snd_hdac_get_display_clk(struct hdac_bus *bus) { struct i915_audio_component *acomp = bus->audio_component; @@ -134,6 +170,17 @@ static int hdac_component_master_match(struct device *dev, void *data) return !strcmp(dev->driver->name, "i915"); } +/** + * snd_hdac_i915_register_notifier - Register i915 audio component ops + * @aops: i915 audio component ops + * + * This function is supposed to be used only by a HD-audio controller + * driver that needs the interaction with i915 graphics. + * + * This function sets the given ops to be called by the i915 graphics driver. + * + * Returns zero for success or a negative error code. + */ int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops) { if (WARN_ON(!hdac_acomp)) @@ -144,6 +191,18 @@ int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops } EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier); +/** + * snd_hdac_i915_init - Initialize i915 audio component + * @bus: HDA core bus + * + * This function is supposed to be used only by a HD-audio controller + * driver that needs the interaction with i915 graphics. + * + * This function initializes and sets up the audio component to communicate + * with i915 graphics driver. + * + * Returns zero for success or a negative error code. + */ int snd_hdac_i915_init(struct hdac_bus *bus) { struct component_match *match = NULL; @@ -187,6 +246,17 @@ out_err: } EXPORT_SYMBOL_GPL(snd_hdac_i915_init); +/** + * snd_hdac_i915_exit - Finalize i915 audio component + * @bus: HDA core bus + * + * This function is supposed to be used only by a HD-audio controller + * driver that needs the interaction with i915 graphics. + * + * This function releases the i915 audio component that has been used. + * + * Returns zero for success or a negative error code. + */ int snd_hdac_i915_exit(struct hdac_bus *bus) { struct device *dev = bus->dev; diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index b0ed870ffb88..eb8f7c30cb09 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -339,6 +339,12 @@ static const struct regmap_config hda_regmap_cfg = { .use_single_rw = true, }; +/** + * snd_hdac_regmap_init - Initialize regmap for HDA register accesses + * @codec: the codec object + * + * Returns zero for success or a negative error code. + */ int snd_hdac_regmap_init(struct hdac_device *codec) { struct regmap *regmap; @@ -352,6 +358,10 @@ int snd_hdac_regmap_init(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_regmap_init); +/** + * snd_hdac_regmap_init - Release the regmap from HDA codec + * @codec: the codec object + */ void snd_hdac_regmap_exit(struct hdac_device *codec) { if (codec->regmap) { diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 8981159813ef..38990a77d7b7 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -426,7 +426,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) } EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); -/* snd_hdac_stream_set_params - set stream parameters +/** + * snd_hdac_stream_set_params - set stream parameters * @azx_dev: HD-audio core stream for which parameters are to be set * @format_val: format value parameter * -- cgit v1.2.3-59-g8ed1b From eed273b785c531ced49adca87efcee2d96bdb2ef Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 29 Oct 2015 20:22:06 +0100 Subject: ALSA: cs46xx: Fix Duplicate front for CS4294 and CS4298 codecs Currently, the Duplicate front control is enabled only for two specific revisions of CS4294. Enable it for all CS4294 and CS4298 codecs instead. This fixes rear output on my VC-4620N sound card (CS4620 + CS4298 rev.5). Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 2a9f4a345171..e62c8416ddfd 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2528,7 +2528,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device) #ifdef CONFIG_SND_CS46XX_NEW_DSP if (chip->nr_ac97_codecs == 1) { unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff; - if (id2 == 0x592b || id2 == 0x592d) { + if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip)); if (err < 0) return err; -- cgit v1.2.3-59-g8ed1b From b43ddca4a8d8c9b5439898bd3077736091696ceb Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 29 Oct 2015 20:52:24 +0100 Subject: ALSA: cs46xx: Fix suspend for all channels snd_cs46xx_suspend() is missing snd_pcm_suspend_all calls for pcm_rear, pcm_center_lfe and pcm_iec958. There's also a copy/paste bug in snd_cs46xx_pcm_iec958(), overwriting pcm_rear in struct snd_cs46xx, leaving pcm_iec958 unused. Fix all this to allow all channels to survive suspend/resume. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index e62c8416ddfd..2706f271a83b 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1864,7 +1864,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; strcpy(pcm->name, "CS46xx - IEC958"); - chip->pcm_rear = pcm; + chip->pcm_iec958 = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 256*1024); @@ -3780,6 +3780,11 @@ static int snd_cs46xx_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); chip->in_suspend = 1; snd_pcm_suspend_all(chip->pcm); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + snd_pcm_suspend_all(chip->pcm_rear); + snd_pcm_suspend_all(chip->pcm_center_lfe); + snd_pcm_suspend_all(chip->pcm_iec958); +#endif // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); -- cgit v1.2.3-59-g8ed1b From 5918f962207329089371d87adbd033830d3f5cf5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 31 Oct 2015 02:43:14 +0900 Subject: ALSA: firewire-digi00x: rename identifiers of MIDI operation for physical ports In following commit, new functions and variables are added for operations of MIDI control port. This commit is a preparation. Current identifiers are renamed so that they mean physical MIDI ports. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x-midi.c | 52 +++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 9aa8b4623108..1d649e3975a0 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -8,7 +8,7 @@ #include "digi00x.h" -static int midi_open(struct snd_rawmidi_substream *substream) +static int midi_phys_open(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data; int err; @@ -31,7 +31,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) return err; } -static int midi_close(struct snd_rawmidi_substream *substream) +static int midi_phys_close(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data; @@ -48,65 +48,69 @@ static int midi_close(struct snd_rawmidi_substream *substream) return 0; } -static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream, + int up) { - struct snd_dg00x *dg00x = substrm->rmidi->private_data; + struct snd_dg00x *dg00x = substream->rmidi->private_data; unsigned long flags; spin_lock_irqsave(&dg00x->lock, flags); /* This port is for asynchronous transaction. */ - if (substrm->number == 0) { + if (substream->number == 0) { if (up) - dg00x->in_control = substrm; + dg00x->in_control = substream; else dg00x->in_control = NULL; } else { if (up) amdtp_dot_midi_trigger(&dg00x->tx_stream, - substrm->number - 1, substrm); + substream->number - 1, + substream); else amdtp_dot_midi_trigger(&dg00x->tx_stream, - substrm->number - 1, NULL); + substream->number - 1, NULL); } spin_unlock_irqrestore(&dg00x->lock, flags); } -static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream, + int up) { - struct snd_dg00x *dg00x = substrm->rmidi->private_data; + struct snd_dg00x *dg00x = substream->rmidi->private_data; unsigned long flags; spin_lock_irqsave(&dg00x->lock, flags); /* This port is for asynchronous transaction. */ - if (substrm->number == 0) { + if (substream->number == 0) { if (up) snd_fw_async_midi_port_run(&dg00x->out_control, - substrm); + substream); } else { if (up) amdtp_dot_midi_trigger(&dg00x->rx_stream, - substrm->number - 1, substrm); + substream->number - 1, + substream); else amdtp_dot_midi_trigger(&dg00x->rx_stream, - substrm->number - 1, NULL); + substream->number - 1, NULL); } spin_unlock_irqrestore(&dg00x->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_open, - .close = midi_close, - .trigger = midi_capture_trigger, +static struct snd_rawmidi_ops midi_phys_capture_ops = { + .open = midi_phys_open, + .close = midi_phys_close, + .trigger = midi_phys_capture_trigger, }; -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_open, - .close = midi_close, - .trigger = midi_playback_trigger, +static struct snd_rawmidi_ops midi_phys_playback_ops = { + .open = midi_phys_open, + .close = midi_phys_close, + .trigger = midi_phys_playback_trigger, }; static void set_midi_substream_names(struct snd_dg00x *dg00x, @@ -144,13 +148,13 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &midi_phys_capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; set_midi_substream_names(dg00x, str); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &midi_phys_playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; set_midi_substream_names(dg00x, str); -- cgit v1.2.3-59-g8ed1b From c5fcee0373b390ab8508022951eabd0bd0e06a48 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 31 Oct 2015 02:43:15 +0900 Subject: ALSA: firewire-digi00x: add MIDI operations for MIDI control port Digi 002/003 family has two types of MIDI port; one is for physical MIDI port and another is for MIDI control message. The former is transferred in isochronous packet, and the latter is transferred by asynchronous transaction. These transmission mechanisms are completely different, while current ALSA digi00x driver defines a set of operations for them with several condition statements. As a result, codes for the operation are messy. This commit adds a set of MIDI operation for control MIDI ports. In later commit, it's applied as an operation for ALSA rawmidi character device. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x-midi.c | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 1d649e3975a0..527f4b31f5c6 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -113,6 +113,69 @@ static struct snd_rawmidi_ops midi_phys_playback_ops = { .trigger = midi_phys_playback_trigger, }; +static int midi_ctl_open(struct snd_rawmidi_substream *substream) +{ + /* Do nothing. */ + return 0; +} + +static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream) +{ + /* Do nothing. */ + return 0; +} + +static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + + snd_fw_async_midi_port_finish(&dg00x->out_control); + + return 0; +} + +static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + + if (up) + dg00x->in_control = substream; + else + dg00x->in_control = NULL; + + spin_unlock_irqrestore(&dg00x->lock, flags); +} + +static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + + if (up) + snd_fw_async_midi_port_run(&dg00x->out_control, substream); + + spin_unlock_irqrestore(&dg00x->lock, flags); +} + +static struct snd_rawmidi_ops midi_ctl_capture_ops = { + .open = midi_ctl_open, + .close = midi_ctl_capture_close, + .trigger = midi_ctl_capture_trigger, +}; + +static struct snd_rawmidi_ops midi_ctl_playback_ops = { + .open = midi_ctl_open, + .close = midi_ctl_playback_close, + .trigger = midi_ctl_playback_trigger, +}; + static void set_midi_substream_names(struct snd_dg00x *dg00x, struct snd_rawmidi_str *str) { -- cgit v1.2.3-59-g8ed1b From d1482fb335b8f3f5e667df72b043c5b3d9d03106 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 31 Oct 2015 02:43:16 +0900 Subject: ALSA: firewire-digi00x: add another rawmidi character device for MIDI control ports Digi 002/003 family uses two ways to transfer MIDI messages. They're different mechanisms, while it's better to handle the ways in different ALSA rawmidi character devices because one character device has just a set of operations. This commit adds another rawmidi character device for control MIDI port. As a result, first rawmidi character device is just for MIDI messages transferred by isochronous packets. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/digi00x/digi00x-midi.c | 104 ++++++++++++++++------------------ 1 file changed, 50 insertions(+), 54 deletions(-) (limited to 'sound') diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 527f4b31f5c6..1a72a382b384 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -13,10 +13,6 @@ static int midi_phys_open(struct snd_rawmidi_substream *substream) struct snd_dg00x *dg00x = substream->rmidi->private_data; int err; - /* This port is for asynchronous transaction. */ - if (substream->number == 0) - return 0; - err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) return err; @@ -35,10 +31,6 @@ static int midi_phys_close(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data; - /* This port is for asynchronous transaction. */ - if (substream->number == 0) - return 0; - mutex_lock(&dg00x->mutex); dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); @@ -56,21 +48,12 @@ static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream, spin_lock_irqsave(&dg00x->lock, flags); - /* This port is for asynchronous transaction. */ - if (substream->number == 0) { - if (up) - dg00x->in_control = substream; - else - dg00x->in_control = NULL; - } else { - if (up) - amdtp_dot_midi_trigger(&dg00x->tx_stream, - substream->number - 1, - substream); - else - amdtp_dot_midi_trigger(&dg00x->tx_stream, - substream->number - 1, NULL); - } + if (up) + amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number, + substream); + else + amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number, + NULL); spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -83,20 +66,12 @@ static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream, spin_lock_irqsave(&dg00x->lock, flags); - /* This port is for asynchronous transaction. */ - if (substream->number == 0) { - if (up) - snd_fw_async_midi_port_run(&dg00x->out_control, - substream); - } else { - if (up) - amdtp_dot_midi_trigger(&dg00x->rx_stream, - substream->number - 1, - substream); - else - amdtp_dot_midi_trigger(&dg00x->rx_stream, - substream->number - 1, NULL); - } + if (up) + amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number, + substream); + else + amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number, + NULL); spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -177,15 +152,16 @@ static struct snd_rawmidi_ops midi_ctl_playback_ops = { }; static void set_midi_substream_names(struct snd_dg00x *dg00x, - struct snd_rawmidi_str *str) + struct snd_rawmidi_str *str, + bool is_ctl) { struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - if (subs->number > 0) + if (!is_ctl) snprintf(subs->name, sizeof(subs->name), "%s MIDI %d", - dg00x->card->shortname, subs->number); + dg00x->card->shortname, subs->number + 1); else /* This port is for asynchronous transaction. */ snprintf(subs->name, sizeof(subs->name), @@ -196,32 +172,52 @@ static void set_midi_substream_names(struct snd_dg00x *dg00x, int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) { - struct snd_rawmidi *rmidi; + struct snd_rawmidi *rmidi[2]; struct snd_rawmidi_str *str; + unsigned int i; int err; + /* Add physical midi ports. */ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, - DOT_MIDI_OUT_PORTS + 1, DOT_MIDI_IN_PORTS + 1, &rmidi); + DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]); if (err < 0) return err; - snprintf(rmidi->name, sizeof(rmidi->name), + snprintf(rmidi[0]->name, sizeof(rmidi[0]->name), "%s MIDI", dg00x->card->shortname); - rmidi->private_data = dg00x; - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT, &midi_phys_capture_ops); - str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; - set_midi_substream_names(dg00x, str); - - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT, &midi_phys_playback_ops); - str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; - set_midi_substream_names(dg00x, str); - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + /* Add a pair of control midi ports. */ + err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1, + 1, 1, &rmidi[1]); + if (err < 0) + return err; + + snprintf(rmidi[1]->name, sizeof(rmidi[1]->name), + "%s control", dg00x->card->shortname); + + snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT, + &midi_ctl_capture_ops); + snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT, + &midi_ctl_playback_ops); + + for (i = 0; i < ARRAY_SIZE(rmidi); i++) { + rmidi[i]->private_data = dg00x; + + rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + set_midi_substream_names(dg00x, str, i); + + rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + set_midi_substream_names(dg00x, str, i); + + rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + } return 0; } -- cgit v1.2.3-59-g8ed1b From 030270ba1aa2072d2967c11129c67d9de9aa762b Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sun, 1 Nov 2015 14:32:03 +0100 Subject: ALSA: maestro3: Enable docking support for Dell Latitude C810 Enable I2S docking for Dell Latitude C810. This allows the audio output on C/Port II to work (and probably also the audio inputs on C/Dock II). The volume can't be adjusted - none of the mixer controls currently supported affects the I2S output (they belong to the STAC9721/23 AC'97 codec responsible for the laptop internal connectors & speakers). Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/maestro3.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 72e89cedc52d..965d5c6f2fa0 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1932,12 +1932,19 @@ snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) } -static void snd_m3_remote_codec_config(int io, int isremote) +static void snd_m3_remote_codec_config(struct snd_m3 *chip, int isremote) { + int io = chip->iobase; + u16 tmp; + isremote = isremote ? 1 : 0; - outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, - io + RING_BUS_CTRL_B); + tmp = inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK; + /* enable dock on Dell Latitude C810 */ + if (chip->pci->subsystem_vendor == 0x1028 && + chip->pci->subsystem_device == 0x00e5) + tmp |= M3I_DOCK_ENABLE; + outw(tmp | isremote, io + RING_BUS_CTRL_B); outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, io + SDO_OUT_DEST_CTRL); outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, @@ -1989,7 +1996,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip) if (!chip->irda_workaround) dir |= 0x10; /* assuming pci bus master? */ - snd_m3_remote_codec_config(io, 0); + snd_m3_remote_codec_config(chip, 0); outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); udelay(20); -- cgit v1.2.3-59-g8ed1b From 2c8d08925272b9be383cd81f1973c6faf9fbf53a Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sun, 1 Nov 2015 20:53:14 +0100 Subject: ALSA: maestro3: Fix Allegro mute until master volume/mute is touched ESS Allegro (marked ES1989S), [125d:1988] (rev 10) is mute after loading snd-maestro3 and running alsactl restore. Touching master volume or mute makes it work. Looks like a bug in the AC'97 codec integrated into the chip. Write AC97_MASTER register twice to work-around the problem. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/maestro3.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 965d5c6f2fa0..17ae92613de4 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1929,6 +1929,16 @@ snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) return; snd_m3_outw(chip, val, CODEC_DATA); snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); + /* + * Workaround for buggy ES1988 integrated AC'97 codec. It remains silent + * until the MASTER volume or mute is touched (alsactl restore does not + * work). + */ + if (ac97->id == 0x45838308 && reg == AC97_MASTER) { + snd_m3_ac97_wait(chip); + snd_m3_outw(chip, val, CODEC_DATA); + snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); + } } -- cgit v1.2.3-59-g8ed1b From de1ab6af5c3d92c0a031083962a7ff270cf301b7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Nov 2015 17:35:34 +0100 Subject: ALSA: hda - Fix lost 4k BDL boundary workaround During the migration to HDA core code, we lost the workaround for 4k BDL boundary. The flag exists in the new hdac_bus, but it's never set. This resulted in the sudden sound stall on some controllers that require this workaround like Creative Recon3D. This patch fixes the issue by setting the flag for such controllers properly. Fixes: ccc98865aa44 ('ALSA: hda - Migrate more hdac_stream codes') Cc: # v4.2+ Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_controller.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index d6b93a20361b..22dbfa563919 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1060,6 +1060,9 @@ int azx_bus_init(struct azx *chip, const char *model, bus->needs_damn_long_delay = 1; } + if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) + bus->core.align_bdle_4k = true; + /* AMD chipsets often cause the communication stalls upon certain * sequence like the pin-detection. It seems that forcing the synced * access works around the stall. Grrr... -- cgit v1.2.3-59-g8ed1b From c932b98c1e47312822d911c1bb76e81ef50e389c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Nov 2015 22:39:16 +0100 Subject: ALSA: hda - Apply pin fixup for HP ProBook 6550b HP ProBook 6550b needs the same pin fixup applied to other HP B-series laptops with docks for making its headphone and dock headphone jacks working properly. We just need to add the codec SSID to the list. Bugzilla: https://bugzilla.kernel.org/attachment.cgi?id=191971 Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 08a0f6a35cca..826122d8acee 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -702,6 +702,7 @@ static bool hp_bnb2011_with_dock(struct hda_codec *codec) static bool hp_blike_system(u32 subsystem_id) { switch (subsystem_id) { + case 0x103c1473: /* HP ProBook 6550b */ case 0x103c1520: case 0x103c1521: case 0x103c1523: -- cgit v1.2.3-59-g8ed1b From 5cf92c8b3dc5da59e05dc81bdc069cedf6f38313 Mon Sep 17 00:00:00 2001 From: Alexandra Yates Date: Wed, 4 Nov 2015 15:56:09 -0800 Subject: ALSA: hda - Add Intel Lewisburg device IDs Audio Adding Intel codename Lewisburg platform device IDs for audio. [rearranged the position by tiwai] Signed-off-by: Alexandra Yates Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 61b8b75a3c80..4d2cbe2ca141 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2105,6 +2105,11 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, { PCI_DEVICE(0x8086, 0x8d21), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + /* Lewisburg */ + { PCI_DEVICE(0x8086, 0xa1f0), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE(0x8086, 0xa270), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, -- cgit v1.2.3-59-g8ed1b