[alsa-devel] [PATCH v2 1/3] ASoC: AMD: Use single dai for da7219 playback and capture
BT I2S is a bi-directional dai, we will use the same cpu dai for playback and capture.
V2: reworded the commit message.
TEST=aplay -D hw:0,0 -vv <file> arecord -D hw:0,0 -f dat -d 5 -vv <file>
Signed-off-by: Akshu Agrawal akshu.agrawal@amd.com --- sound/soc/amd/acp-da7219-max98357a.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index bbe0f10..d07c2a1 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -124,8 +124,8 @@ static int cz_fe_startup(struct snd_pcm_substream *substream)
static struct snd_soc_dai_link cz_dai_7219_98357[] = { { - .name = "amd-da7219-play", - .stream_name = "Playback", + .name = "amd-da7219-play-cap", + .stream_name = "Playback and Capture", .platform_name = "acp_audio_dma.0.auto", .cpu_dai_name = "designware-i2s.3.auto", .codec_dai_name = "da7219-hifi", @@ -134,16 +134,6 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) | SND_SOC_DAIFMT_CBM_CFM, .init = cz_da7219_init, .dpcm_playback = 1, - }, - { - .name = "amd-da7219-cap", - .stream_name = "Capture", - .platform_name = "acp_audio_dma.0.auto", - .cpu_dai_name = "designware-i2s.4.auto", - .codec_dai_name = "da7219-hifi", - .codec_name = "i2c-DLGS7219:00", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, .dpcm_capture = 1, .ops = &cz_da7219_cap_ops, },
DA7219 is clock master for other codecs. DA7219 has exposed clock control by using common clock framework and same is used to enable and disable clock for all codecs in the system.
V2: Removed compiler errors and warning.
TEST=aplay -D hw:0,0 -vv <file> arecord -D hw:0,0 -f dat -d 5 -vv <file> aplay -D hw:0,1 -vv <file>
Signed-off-by: Akshu Agrawal akshu.agrawal@amd.com --- sound/soc/amd/acp-da7219-max98357a.c | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index d07c2a1..99c6b5c 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -29,6 +29,7 @@ #include <sound/pcm_params.h> #include <sound/soc-dapm.h> #include <sound/jack.h> +#include <linux/clk.h> #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> @@ -42,11 +43,13 @@ #define DUAL_CHANNEL 2
static struct snd_soc_jack cz_jack; +struct clk *da7219_dai_clk;
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { int ret; struct snd_soc_card *card = rtd->card; + struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_component *component = codec_dai->component;
@@ -66,6 +69,8 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) return ret; }
+ da7219_dai_clk = clk_get(codec->dev, "da7219-dai-clks"); + ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | @@ -81,6 +86,28 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) return 0; }
+static int cz_da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + ret = clk_prepare_enable(da7219_dai_clk); + if (ret < 0) { + dev_err(rtd->dev, "can't enable master clock %d\n", ret); + return ret; + } + + return ret; +} + +static int cz_da7219_hw_free(struct snd_pcm_substream *substream) +{ + clk_disable_unprepare(da7219_dai_clk); + + return 0; +} + static const unsigned int channels[] = { DUAL_CHANNEL, }; @@ -119,9 +146,21 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) }
static struct snd_soc_ops cz_da7219_cap_ops = { + .hw_params = cz_da7219_hw_params, + .hw_free = cz_da7219_hw_free, .startup = cz_fe_startup, };
+static struct snd_soc_ops cz_max_play_ops = { + .hw_params = cz_da7219_hw_params, + .hw_free = cz_da7219_hw_free, +}; + +static struct snd_soc_ops cz_dmic_cap_ops = { + .hw_params = cz_da7219_hw_params, + .hw_free = cz_da7219_hw_free, +}; + static struct snd_soc_dai_link cz_dai_7219_98357[] = { { .name = "amd-da7219-play-cap", @@ -147,6 +186,7 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .dpcm_playback = 1, + .ops = &cz_max_play_ops, }, { .name = "dmic", @@ -158,6 +198,7 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .dpcm_capture = 1, + .ops = &cz_dmic_cap_ops, }, };
This enables/disables and sets auxiliary clock at 25Mhz. It uses common clock framework for proper ref counting. This approach will save power in comparison to keeping it always On in firmware.
TEST= aplay -vv <file> check register to see clock enabled kill aplay check register to see clock disabled
Signed-off-by: Akshu Agrawal akshu.agrawal@amd.com --- sound/soc/amd/acp-da7219-max98357a.c | 133 ++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-)
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 99c6b5c..e37a622 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -34,6 +34,7 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/acpi.h> +#include <linux/types.h>
#include "../codecs/da7219.h" #include "../codecs/da7219-aad.h" @@ -41,6 +42,18 @@ #define CZ_PLAT_CLK 24000000 #define MCLK_RATE 24576000 #define DUAL_CHANNEL 2 +#define CLKDRVSTR2 0x28 +#define MISCCLKCNTL1 0x40 +#define OSCCLKENB 2 +#define OSCOUTCLK25MHZ 19 + +struct cz_clock { + const char* acp_clks_name; + struct clk_hw acp_clks_hw; + struct clk_lookup *acp_clks_lookup; + struct clk *acp_clks; + void __iomem *res_base; +};
static struct snd_soc_jack cz_jack; struct clk *da7219_dai_clk; @@ -91,6 +104,8 @@ static int cz_da7219_hw_params(struct snd_pcm_substream *substream, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct clk *acpd7219_clk;
ret = clk_prepare_enable(da7219_dai_clk); if (ret < 0) { @@ -98,13 +113,27 @@ static int cz_da7219_hw_params(struct snd_pcm_substream *substream, return ret; }
+ acpd7219_clk = clk_get(card->dev, "acpd7219-clks"); + ret = clk_prepare_enable(acpd7219_clk); + if (ret < 0) { + dev_err(rtd->dev, "can't enable oscillator clock %d\n", ret); + return ret; + } + return ret; }
static int cz_da7219_hw_free(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct clk *acpd7219_clk; + clk_disable_unprepare(da7219_dai_clk);
+ acpd7219_clk = clk_get(card->dev, "acpd7219-clks"); + clk_disable_unprepare(acpd7219_clk); + return 0; }
@@ -237,9 +266,106 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) .num_controls = ARRAY_SIZE(cz_mc_controls), };
+static int acpd7219_clks_prepare(struct clk_hw *hw) +{ + u32 reg_val; + struct cz_clock *cz_clock_obj = + container_of(hw, struct cz_clock, acp_clks_hw); + void __iomem *base = cz_clock_obj->res_base; + + reg_val = readl(base + MISCCLKCNTL1); + reg_val &= ~(0x1 << OSCCLKENB); + writel(reg_val, base + MISCCLKCNTL1); + reg_val = readl(base + CLKDRVSTR2); + reg_val |= (0x1 << OSCOUTCLK25MHZ); + writel(reg_val, base + CLKDRVSTR2); + + return 0; +} + +static void acpd7219_clks_unprepare(struct clk_hw *hw) +{ + u32 reg_val; + struct cz_clock *cz_clock_obj = + container_of(hw, struct cz_clock, acp_clks_hw); + void __iomem *base = cz_clock_obj->res_base; + + reg_val = readl(base + MISCCLKCNTL1); + reg_val |= (0x1 << OSCCLKENB); + writel(reg_val, base + MISCCLKCNTL1); +} + +static int acpd7219_clks_is_enabled(struct clk_hw *hw) +{ + u32 reg_val; + struct cz_clock *cz_clock_obj = + container_of(hw, struct cz_clock, acp_clks_hw); + void __iomem *base = cz_clock_obj->res_base; + + reg_val = readl(base + MISCCLKCNTL1); + + return !(reg_val & OSCCLKENB); +} + +const struct clk_ops acpd7219_clks_ops = { + .prepare = acpd7219_clks_prepare, + .unprepare = acpd7219_clks_unprepare, + .is_enabled = acpd7219_clks_is_enabled, +}; + +static int register_acpd7219_clocks(struct platform_device *pdev) +{ + struct clk_init_data init = {}; + struct clk *acp_clks; + struct clk_lookup *acp_clks_lookup; + struct cz_clock *cz_clock_obj; + struct resource *res; + struct device dev = pdev->dev; + + cz_clock_obj = kzalloc(sizeof(struct cz_clock), GFP_KERNEL); + if (!cz_clock_obj) + return -ENOMEM; + + cz_clock_obj->acp_clks_name = "acpd7219-clks"; + + init.parent_names = NULL; + init.num_parents = 0; + init.name = cz_clock_obj->acp_clks_name; + init.ops = &acpd7219_clks_ops; + cz_clock_obj->acp_clks_hw.init = &init; + + acp_clks = devm_clk_register(&dev, &cz_clock_obj->acp_clks_hw); + if (IS_ERR(acp_clks)) + { + dev_err(&dev, "Failed to register DAI clocks: %ld\n", + PTR_ERR(acp_clks)); + return -EINVAL; + } + cz_clock_obj->acp_clks = acp_clks; + + acp_clks_lookup = clkdev_create(acp_clks, cz_clock_obj->acp_clks_name, + "%s", dev_name(&dev)); + if (!acp_clks_lookup) + dev_warn(&dev, "Failed to create DAI clkdev"); + else + cz_clock_obj->acp_clks_lookup = acp_clks_lookup; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get misc io resource.\n"); + return -EINVAL; + } + cz_clock_obj->res_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!cz_clock_obj->res_base) + return -ENOMEM; + + return 0; +} + static int cz_probe(struct platform_device *pdev) { - int ret; + int ret = 0; struct snd_soc_card *card;
card = &cz_card; @@ -252,7 +378,10 @@ static int cz_probe(struct platform_device *pdev) cz_card.name, ret); return ret; } - return 0; + + ret = register_acpd7219_clocks(pdev); + + return ret; }
static const struct acpi_device_id cz_audio_acpi_match[] = {
On Mon, Mar 19, 2018 at 11:07:41AM +0530, Akshu Agrawal wrote:
BT I2S is a bi-directional dai, we will use the same cpu dai for playback and capture.
V2: reworded the commit message.
Place procedural noise like this after the --- as covered by SubmittingPatches - it's not really relevant after the patch has been applied. The graphics tree is unusual in handling this differently.
participants (2)
-
Akshu Agrawal
-
Mark Brown