[alsa-devel] [PATCH v2, 1/2] ASoC: AMD: Support headset button on Stoney DA7219
Adds headset button support.
TEST=Tested Volume UP/Down, Play/Pause functionality
Signed-off-by: Akshu Agrawal akshu.agrawal@amd.com --- v2: Changed KEY_MEDIA to KEY_PLAYPAUSE
sound/soc/amd/acp-da7219-max98357a.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index b205c78..1012a80 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -33,6 +33,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/input.h> #include <linux/acpi.h>
#include "../codecs/da7219.h" @@ -51,6 +52,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_component *component = codec_dai->component; + struct snd_soc_jack *jack;
dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
@@ -80,6 +82,12 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) return ret; }
+ jack = &cz_jack; + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + da7219_aad_jack_det(component, &cz_jack);
return 0;
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 Acked-by: Alex Deucher alexander.deucher@amd.com --- V2: Correcting the pin to OSCOUT1 from OSCOUT2 V3: Fix error/warnings from kbuild test V4: Fix build errors for platform which do not support CONFIG_COMMON_CLK V5: Review comments by Dan and Sriram V6: Adding COMMON_CLK to Kconfig, moving clk_get to register, var name changes
sound/soc/amd/Kconfig | 2 +- sound/soc/amd/acp-da7219-max98357a.c | 150 +++++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 6 deletions(-)
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 6cbf9cf..8104f8f 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -8,7 +8,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_ADAU7002 - depends on SND_SOC_AMD_ACP && I2C + depends on SND_SOC_AMD_ACP && I2C && COMMON_CLK help This option enables machine driver for DA7219 and MAX9835.
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 1012a80..29847f7 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -30,21 +30,42 @@ #include <sound/soc-dapm.h> #include <sound/jack.h> #include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/acpi.h> +#include <linux/types.h>
#include "../codecs/da7219.h" #include "../codecs/da7219-aad.h"
-#define CZ_PLAT_CLK 24000000 -#define MCLK_RATE 24576000 +#define CZ_PLAT_CLK 25000000 +#define DA7219_PLL_OUT 24576000 #define DUAL_CHANNEL 2
+/* Clock Driving Strength 2 register */ +#define CLKDRVSTR2 0x28 +/* Clock Control 1 register */ +#define MISCCLKCNTL1 0x40 +/* Auxiliary clock1 enable bit */ +#define OSCCLKENB 2 +/* 25Mhz auxiliary output clock freq bit */ +#define OSCOUT1CLK25MHZ 16 + +struct cz_clock { + const char* acp_clk_name; + struct clk_hw acp_clk_hw; + struct clk_lookup *acp_clk_lookup; + struct clk *acp_clk; + void __iomem *res_base; +}; + static struct snd_soc_jack cz_jack; -struct clk *da7219_dai_clk; +static struct clk *da7219_dai_clk; +static struct clk *acpd7219_clk;
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { @@ -64,13 +85,18 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) }
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL, - CZ_PLAT_CLK, MCLK_RATE); + CZ_PLAT_CLK, DA7219_PLL_OUT); if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; }
da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks"); + if (IS_ERR(da7219_dai_clk)) { + dev_err(rtd->dev, "failed to get clock: %ld\n", + PTR_ERR(da7219_dai_clk)); + return PTR_ERR(da7219_dai_clk); + }
ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | @@ -105,13 +131,20 @@ static int cz_da7219_hw_params(struct snd_pcm_substream *substream, return ret; }
+ ret = clk_prepare_enable(acpd7219_clk); + if (ret < 0) + dev_err(rtd->dev, "can't enable oscillator clock %d\n", ret); + return ret; }
static int cz_da7219_hw_free(struct snd_pcm_substream *substream) { + clk_disable_unprepare(da7219_dai_clk);
+ clk_disable_unprepare(acpd7219_clk); + return 0; }
@@ -244,6 +277,112 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) .num_controls = ARRAY_SIZE(cz_mc_controls), };
+static int acpd7219_clk_enable(struct clk_hw *hw) +{ + u32 reg_val; + struct cz_clock *cz_clock = + container_of(hw, struct cz_clock, acp_clk_hw); + void __iomem *base = cz_clock->res_base; + + reg_val = readl(base + MISCCLKCNTL1); + reg_val &= ~(0x1 << OSCCLKENB); + writel(reg_val, base + MISCCLKCNTL1); + reg_val = readl(base + CLKDRVSTR2); + reg_val |= (0x1 << OSCOUT1CLK25MHZ); + writel(reg_val, base + CLKDRVSTR2); + + return 0; +} + +static void acpd7219_clk_disable(struct clk_hw *hw) +{ + u32 reg_val; + struct cz_clock *cz_clock = + container_of(hw, struct cz_clock, acp_clk_hw); + void __iomem *base = cz_clock->res_base; + + reg_val = readl(base + MISCCLKCNTL1); + reg_val |= (0x1 << OSCCLKENB); + writel(reg_val, base + MISCCLKCNTL1); +} + +static int acpd7219_clk_is_enabled(struct clk_hw *hw) +{ + u32 reg_val; + struct cz_clock *cz_clock = + container_of(hw, struct cz_clock, acp_clk_hw); + void __iomem *base = cz_clock->res_base; + + reg_val = readl(base + MISCCLKCNTL1); + + return !(reg_val & OSCCLKENB); +} + +static const struct clk_ops acpd7219_clk_ops = { + .enable = acpd7219_clk_enable, + .disable = acpd7219_clk_disable, + .is_enabled = acpd7219_clk_is_enabled, +}; + +static int register_acpd7219_clocks(struct platform_device *pdev) +{ + struct clk *acp_clk; + struct clk_lookup *acp_clk_lookup; + struct cz_clock *cz_clock_obj; + struct resource *res; + struct device *dev = &pdev->dev; + static const struct clk_init_data init = { + .name = "acpd7219-clk", + .ops = &acpd7219_clk_ops, + }; + + cz_clock_obj = kzalloc(sizeof(struct cz_clock), GFP_KERNEL); + if (!cz_clock_obj) + return -ENOMEM; + + cz_clock_obj->acp_clk_name = "acpd7219-clk"; + + cz_clock_obj->acp_clk_hw.init = &init; + + acp_clk = devm_clk_register(dev, &cz_clock_obj->acp_clk_hw); + if (IS_ERR(acp_clk)) + { + dev_err(dev, "Failed to register DAI clocks: %ld\n", + PTR_ERR(acp_clk)); + return PTR_ERR(acp_clk); + } + cz_clock_obj->acp_clk = acp_clk; + + acp_clk_lookup = clkdev_create(acp_clk, cz_clock_obj->acp_clk_name, + "%s", dev_name(dev)); + if (!acp_clk_lookup) + dev_warn(dev, "Failed to create DAI clkdev"); + else + cz_clock_obj->acp_clk_lookup = acp_clk_lookup; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get misc io resource.\n"); + clkdev_drop(acp_clk_lookup); + return -EINVAL; + } + cz_clock_obj->res_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!cz_clock_obj->res_base) { + clkdev_drop(acp_clk_lookup); + return -ENOMEM; + } + + acpd7219_clk = devm_clk_get(dev, "acpd7219-clk"); + if (IS_ERR(acpd7219_clk)) { + dev_err(dev, "failed to get acpd7219-clk: %ld\n", + PTR_ERR(acpd7219_clk)); + return PTR_ERR(acpd7219_clk); + } + + return 0; +} + static int cz_probe(struct platform_device *pdev) { int ret; @@ -259,7 +398,8 @@ static int cz_probe(struct platform_device *pdev) cz_card.name, ret); return ret; } - return 0; + + return register_acpd7219_clocks(pdev); }
static const struct acpi_device_id cz_audio_acpi_match[] = {
On 09 April 2018 10:20, Akshu Agrawal wrote:
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 Acked-by: Alex Deucher alexander.deucher@amd.com
V2: Correcting the pin to OSCOUT1 from OSCOUT2 V3: Fix error/warnings from kbuild test V4: Fix build errors for platform which do not support CONFIG_COMMON_CLK V5: Review comments by Dan and Sriram V6: Adding COMMON_CLK to Kconfig, moving clk_get to register, var name changes
sound/soc/amd/Kconfig | 2 +- sound/soc/amd/acp-da7219-max98357a.c | 150 +++++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 6 deletions(-)
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 6cbf9cf..8104f8f 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -8,7 +8,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_ADAU7002
- depends on SND_SOC_AMD_ACP && I2C
- depends on SND_SOC_AMD_ACP && I2C && COMMON_CLK help This option enables machine driver for DA7219 and MAX9835.
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp- da7219-max98357a.c index 1012a80..29847f7 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -30,21 +30,42 @@ #include <sound/soc-dapm.h> #include <sound/jack.h> #include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/acpi.h> +#include <linux/types.h>
#include "../codecs/da7219.h" #include "../codecs/da7219-aad.h"
-#define CZ_PLAT_CLK 24000000 -#define MCLK_RATE 24576000 +#define CZ_PLAT_CLK 25000000 +#define DA7219_PLL_OUT 24576000 #define DUAL_CHANNEL 2
+/* Clock Driving Strength 2 register */ +#define CLKDRVSTR2 0x28 +/* Clock Control 1 register */ +#define MISCCLKCNTL1 0x40 +/* Auxiliary clock1 enable bit */ +#define OSCCLKENB 2 +/* 25Mhz auxiliary output clock freq bit */ +#define OSCOUT1CLK25MHZ 16
+struct cz_clock {
- const char* acp_clk_name;
- struct clk_hw acp_clk_hw;
- struct clk_lookup *acp_clk_lookup;
- struct clk *acp_clk;
- void __iomem *res_base;
+};
static struct snd_soc_jack cz_jack; -struct clk *da7219_dai_clk; +static struct clk *da7219_dai_clk; +static struct clk *acpd7219_clk;
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { @@ -64,13 +85,18 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) }
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL,
CZ_PLAT_CLK, MCLK_RATE);
CZ_PLAT_CLK, DA7219_PLL_OUT);
For the PLL_OUT value you should use one of the following:
- For 48KHz family of SRs: 'DA7219_PLL_FREQ_OUT_98304' - For 44.1KHz family of SRs: 'DA7219_PLL_FREQ_OUT_90316'
These will ensure correct calculations in the codec's set_pll() function for the sampling rate family you require.
On 4/9/2018 5:58 PM, Adam Thomson wrote:
On 09 April 2018 10:20, Akshu Agrawal wrote:
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 Acked-by: Alex Deucher alexander.deucher@amd.com
V2: Correcting the pin to OSCOUT1 from OSCOUT2 V3: Fix error/warnings from kbuild test V4: Fix build errors for platform which do not support CONFIG_COMMON_CLK V5: Review comments by Dan and Sriram V6: Adding COMMON_CLK to Kconfig, moving clk_get to register, var name changes
sound/soc/amd/Kconfig | 2 +- sound/soc/amd/acp-da7219-max98357a.c | 150 +++++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 6 deletions(-)
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 6cbf9cf..8104f8f 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -8,7 +8,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_ADAU7002
- depends on SND_SOC_AMD_ACP && I2C
- depends on SND_SOC_AMD_ACP && I2C && COMMON_CLK help This option enables machine driver for DA7219 and MAX9835.
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp- da7219-max98357a.c index 1012a80..29847f7 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -30,21 +30,42 @@ #include <sound/soc-dapm.h> #include <sound/jack.h> #include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/acpi.h> +#include <linux/types.h>
#include "../codecs/da7219.h" #include "../codecs/da7219-aad.h"
-#define CZ_PLAT_CLK 24000000 -#define MCLK_RATE 24576000 +#define CZ_PLAT_CLK 25000000 +#define DA7219_PLL_OUT 24576000 #define DUAL_CHANNEL 2
+/* Clock Driving Strength 2 register */ +#define CLKDRVSTR2 0x28 +/* Clock Control 1 register */ +#define MISCCLKCNTL1 0x40 +/* Auxiliary clock1 enable bit */ +#define OSCCLKENB 2 +/* 25Mhz auxiliary output clock freq bit */ +#define OSCOUT1CLK25MHZ 16
+struct cz_clock {
- const char* acp_clk_name;
- struct clk_hw acp_clk_hw;
- struct clk_lookup *acp_clk_lookup;
- struct clk *acp_clk;
- void __iomem *res_base;
+};
- static struct snd_soc_jack cz_jack;
-struct clk *da7219_dai_clk; +static struct clk *da7219_dai_clk; +static struct clk *acpd7219_clk;
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { @@ -64,13 +85,18 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) }
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL,
CZ_PLAT_CLK, MCLK_RATE);
CZ_PLAT_CLK, DA7219_PLL_OUT);
For the PLL_OUT value you should use one of the following:
- For 48KHz family of SRs: 'DA7219_PLL_FREQ_OUT_98304'
- For 44.1KHz family of SRs: 'DA7219_PLL_FREQ_OUT_90316'
These will ensure correct calculations in the codec's set_pll() function for the sampling rate family you require.
Changing to DA7219_PLL_FREQ_OUT_98304 as we are using 48Khz SR.
Thanks, Akshu
On Mon, Apr 9, 2018 at 3:20 AM Akshu Agrawal akshu.agrawal@amd.com wrote:
Adds headset button support.
TEST=Tested Volume UP/Down, Play/Pause functionality
Signed-off-by: Akshu Agrawal akshu.agrawal@amd.com
v2: Changed KEY_MEDIA to KEY_PLAYPAUSE
sound/soc/amd/acp-da7219-max98357a.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/sound/soc/amd/acp-da7219-max98357a.c
b/sound/soc/amd/acp-da7219-max98357a.c
index b205c78..1012a80 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -33,6 +33,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/input.h> #include <linux/acpi.h>
#include "../codecs/da7219.h" @@ -51,6 +52,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime
*rtd)
struct snd_soc_card *card = rtd->card; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_component *component = codec_dai->component;
struct snd_soc_jack *jack;
dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
@@ -80,6 +82,12 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime
*rtd)
return ret; }
jack = &cz_jack;
Hmm, this seems a bit unnecessary. For all of these, can you just do:
snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
da7219_aad_jack_det(component, &cz_jack);
return 0;
-- 1.9.1
On 4/9/2018 11:02 PM, Daniel Kurtz wrote:
On Mon, Apr 9, 2018 at 3:20 AM Akshu Agrawal akshu.agrawal@amd.com wrote:
Adds headset button support.
TEST=Tested Volume UP/Down, Play/Pause functionality
Signed-off-by: Akshu Agrawal akshu.agrawal@amd.com
v2: Changed KEY_MEDIA to KEY_PLAYPAUSE
sound/soc/amd/acp-da7219-max98357a.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/sound/soc/amd/acp-da7219-max98357a.c
b/sound/soc/amd/acp-da7219-max98357a.c
index b205c78..1012a80 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -33,6 +33,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/input.h> #include <linux/acpi.h>
#include "../codecs/da7219.h" @@ -51,6 +52,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime
*rtd)
struct snd_soc_card *card = rtd->card; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_component *component = codec_dai->component;
struct snd_soc_jack *jack;
dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
@@ -80,6 +82,12 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime
*rtd)
return ret; }
jack = &cz_jack;
Hmm, this seems a bit unnecessary. For all of these, can you just do:
snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
Accepted.
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
da7219_aad_jack_det(component, &cz_jack);
return 0;
-- 1.9.1
Thanks, Akshu
participants (4)
-
Adam Thomson
-
Agrawal, Akshu
-
Akshu Agrawal
-
Daniel Kurtz