[alsa-devel] [PATCH] ak4642 codec updates
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
This series has some updates to the ak4642 codec driver like ALC controls, improved sysclk support for the ak4648.
Sascha
---------------------------------------------------------------- Sascha Hauer (6): ASoC: ak4642: Fix typo zoro -> zero ASoC: ak4642: Add ALC controls ASoC: ak4642: Add driver data and driver private struct ASoC: ak4642: Add support for extended sysclk frequencies of the ak4648 ASoC: ak4642: Implement Microphone in pathes ASoC: ak4642: Add enable gpio support
Documentation/devicetree/bindings/sound/ak4642.txt | 3 + sound/soc/codecs/ak4642.c | 230 +++++++++++++++++++-- 2 files changed, 221 insertions(+), 12 deletions(-)
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/codecs/ak4642.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 92655cc..3373a9c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -98,7 +98,7 @@ #define MGAIN0 (1 << 0) /* MIC amp gain*/
/* TIMER */ -#define ZTM(param) ((param & 0x3) << 4) /* ALC Zoro Crossing TimeOut */ +#define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */ #define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2))
/* ALC_CTL1 */
![](https://secure.gravatar.com/avatar/d930951cb00393ecf9c3db3a56d78fa9.jpg?s=120&d=mm&r=g)
On Wed, May 14, 2014 at 09:37:33AM +0200, Sascha Hauer wrote:
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de
Applied, thanks.
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
ALC and ALC Zero crossing detection has been enabled unconditionally. Add controls for this.
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/codecs/ak4642.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 3373a9c..90d2d93 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -148,6 +148,8 @@ static const struct snd_kcontrol_new ak4642_snd_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC, 0, 0xFF, 1, out_tlv), + SOC_SINGLE("ALC Capture Switch", ALC_CTL1, 5, 1, 0), + SOC_SINGLE("ALC Capture ZC Switch", ALC_CTL1, 4, 1, 1), };
static const struct snd_kcontrol_new ak4642_headphone_control =
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
Currently unused, this is done to let the driver distinguish between the different supported codec types in later patches.
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/codecs/ak4642.c | 51 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 11 deletions(-)
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 90d2d93..b568692 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -134,6 +134,14 @@ /* MD_CTL4 */ #define DACH (1 << 0)
+struct ak4642_drvdata { + const struct regmap_config *regmap_config; +}; + +struct ak4642_priv { + const struct ak4642_drvdata *drvdata; +}; + /* * Playback Volume (table 39) * @@ -507,30 +515,51 @@ static const struct regmap_config ak4648_regmap = { .num_reg_defaults = ARRAY_SIZE(ak4648_reg), };
+static const struct ak4642_drvdata ak4642_drvdata = { + .regmap_config = &ak4642_regmap, +}; + +static const struct ak4642_drvdata ak4643_drvdata = { + .regmap_config = &ak4642_regmap, +}; + +static const struct ak4642_drvdata ak4648_drvdata = { + .regmap_config = &ak4648_regmap, +}; + static 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; - const struct regmap_config *regmap_config = NULL; + const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; + struct ak4642_priv *priv;
if (np) { const struct of_device_id *of_id;
of_id = of_match_device(ak4642_of_match, &i2c->dev); if (of_id) - regmap_config = of_id->data; + drvdata = of_id->data; } else { - regmap_config = (const struct regmap_config *)id->driver_data; + drvdata = (const struct ak4642_drvdata *)id->driver_data; }
- if (!regmap_config) { + if (!drvdata) { dev_err(&i2c->dev, "Unknown device type\n"); return -EINVAL; }
- regmap = devm_regmap_init_i2c(i2c, regmap_config); + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->drvdata = drvdata; + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap);
@@ -545,17 +574,17 @@ static int ak4642_i2c_remove(struct i2c_client *client) }
static struct of_device_id ak4642_of_match[] = { - { .compatible = "asahi-kasei,ak4642", .data = &ak4642_regmap}, - { .compatible = "asahi-kasei,ak4643", .data = &ak4642_regmap}, - { .compatible = "asahi-kasei,ak4648", .data = &ak4648_regmap}, + { .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata}, + { .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata}, + { .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata}, {}, }; MODULE_DEVICE_TABLE(of, ak4642_of_match);
static const struct i2c_device_id ak4642_i2c_id[] = { - { "ak4642", (kernel_ulong_t)&ak4642_regmap }, - { "ak4643", (kernel_ulong_t)&ak4642_regmap }, - { "ak4648", (kernel_ulong_t)&ak4648_regmap }, + { "ak4642", (kernel_ulong_t)&ak4642_drvdata }, + { "ak4643", (kernel_ulong_t)&ak4643_drvdata }, + { "ak4648", (kernel_ulong_t)&ak4648_drvdata }, { } }; MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
![](https://secure.gravatar.com/avatar/d930951cb00393ecf9c3db3a56d78fa9.jpg?s=120&d=mm&r=g)
On Wed, May 14, 2014 at 09:37:35AM +0200, Sascha Hauer wrote:
Currently unused, this is done to let the driver distinguish between the different supported codec types in later patches.
Applied, thanks.
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
Additionally to the ak4642 pll frequencies the ak4648 also supports 13MHz, 19.2MHz and 26MHz. This adds support for these frequencies.
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/codecs/ak4642.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index b568692..3ba4c0f 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -136,6 +136,7 @@
struct ak4642_drvdata { const struct regmap_config *regmap_config; + int extended_frequencies; };
struct ak4642_priv { @@ -297,7 +298,9 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); u8 pll; + int extended_freq = 0;
switch (freq) { case 11289600: @@ -318,9 +321,25 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai, case 27000000: pll = PLL3 | PLL2 | PLL0; break; + case 19200000: + pll = PLL3; + extended_freq = 1; + break; + case 13000000: + pll = PLL3 | PLL2 | PLL1; + extended_freq = 1; + break; + case 26000000: + pll = PLL3 | PLL2 | PLL1 | PLL0; + extended_freq = 1; + break; default: return -EINVAL; } + + if (extended_freq && !priv->drvdata->extended_frequencies) + return -EINVAL; + snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
return 0; @@ -525,6 +544,7 @@ static const struct ak4642_drvdata ak4643_drvdata = {
static const struct ak4642_drvdata ak4648_drvdata = { .regmap_config = &ak4648_regmap, + .extended_frequencies = 1, };
static struct of_device_id ak4642_of_match[];
![](https://secure.gravatar.com/avatar/d930951cb00393ecf9c3db3a56d78fa9.jpg?s=120&d=mm&r=g)
On Wed, May 14, 2014 at 09:37:36AM +0200, Sascha Hauer wrote:
Additionally to the ak4642 pll frequencies the ak4648 also supports 13MHz, 19.2MHz and 26MHz. This adds support for these frequencies.
Applied, thanks. However...
- case 19200000:
pll = PLL3;
extended_freq = 1;
break;
- case 13000000:
pll = PLL3 | PLL2 | PLL1;
extended_freq = 1;
break;
- case 26000000:
...the sorting here is a bit odd - we do 19.2MHz then 13MHz then 26MHz.
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/codecs/ak4642.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+)
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 3ba4c0f..6292a6a 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -153,6 +153,81 @@ struct ak4642_priv { */ static const DECLARE_TLV_DB_SCALE(out_tlv, -11550, 50, 1);
+static const char * const ak4642_micl_strings[] = { "LIN1", "LIN2", "LIN3", "LIN4" }; +static const char * const ak4642_micr_strings[] = { "RIN1", "RIN2", "RIN3", "RIN4" }; + +static const struct soc_enum ak4642_micl_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4642_micl_strings), + ak4642_micl_strings); + +static const struct soc_enum ak4642_micr_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4642_micr_strings), + ak4642_micr_strings); + +static int ak4642_micl_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + u32 val, inl; + + val = snd_soc_read(codec, PW_MGMT3); + + inl = (val >> 1) & 0x1; + inl |= (val >> 5) & 0x2; + + ucontrol->value.integer.value[0] = inl; + + return 0; +} + +static int ak4642_micl_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + int val, inl; + + val = ucontrol->value.integer.value[0]; + + inl = (val & 0x1) << 1; + inl |= (val & 0x2) << 5; + + snd_soc_update_bits(codec, PW_MGMT3, (1 << 1) | (1 << 6), inl); + + return 1; +} + +static int ak4642_micr_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + u32 val, inr; + + val = snd_soc_read(codec, PW_MGMT3); + + inr = (val >> 2) & 0x1; + inr |= (val >> 6) & 0x2; + + ucontrol->value.integer.value[0] = inr; + + return 0; +} + +static int ak4642_micr_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + int val, inr; + + val = ucontrol->value.integer.value[0]; + + inr = (val & 0x1) << 2; + inr |= (val & 0x2) << 6; + + snd_soc_update_bits(codec, PW_MGMT3, (1 << 2) | (1 << 7), inr); + + return 1; +} + static const struct snd_kcontrol_new ak4642_snd_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC, @@ -168,6 +243,16 @@ static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = { SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0), };
+static const struct snd_kcontrol_new ak4642_micl_controls[] = { + SOC_ENUM_EXT("Mic Left Capture Route", ak4642_micl_enum, + ak4642_micl_enum_get, ak4642_micl_enum_put), +}; + +static const struct snd_kcontrol_new ak4642_micr_controls[] = { + SOC_ENUM_EXT("Mic Right Capture Route", ak4642_micr_enum, + ak4642_micr_enum_get, ak4642_micr_enum_put), +}; + static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
/* Outputs */ @@ -188,6 +273,22 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
/* DAC */ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", PW_MGMT1, 2, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("Mic Left Capture Route", + SND_SOC_NOPM, 0, 0, ak4642_micl_controls), + SND_SOC_DAPM_MUX("Mic Right Capture Route", + SND_SOC_NOPM, 0, 0, ak4642_micr_controls), + + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + SND_SOC_DAPM_INPUT("LIN3"), + SND_SOC_DAPM_INPUT("LIN4"), + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + SND_SOC_DAPM_INPUT("RIN3"), + SND_SOC_DAPM_INPUT("RIN4"), };
static const struct snd_soc_dapm_route ak4642_intercon[] = { @@ -205,6 +306,19 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = { {"DACH", NULL, "DAC"},
{"LINEOUT Mixer", "DACL", "DAC"}, + + {"Mic Left Capture Route", "LIN1", "LIN1"}, + {"Mic Left Capture Route", NULL, "LIN2"}, + {"Mic Left Capture Route", NULL, "LIN3"}, + {"Mic Left Capture Route", NULL, "LIN4"}, + + {"Mic Right Capture Route", "RIN1", "RIN1"}, + {"Mic Right Capture Route", NULL, "RIN2"}, + {"Mic Right Capture Route", NULL, "RIN3"}, + {"Mic Right Capture Route", NULL, "RIN4"}, + + {"ADC", NULL, "Mic Right Capture Route"}, + {"ADC", NULL, "Mic Left Capture Route"}, };
/*
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
On Wed, May 14, 2014 at 09:37:37AM +0200, Sascha Hauer wrote:
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de
sound/soc/codecs/ak4642.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+)
I'll rework this patch. I just realized that it is only valid for the ak4648. The ak4642 does not have that many microphone in sources.
Sasca
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 3ba4c0f..6292a6a 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -153,6 +153,81 @@ struct ak4642_priv { */ static const DECLARE_TLV_DB_SCALE(out_tlv, -11550, 50, 1);
+static const char * const ak4642_micl_strings[] = { "LIN1", "LIN2", "LIN3", "LIN4" }; +static const char * const ak4642_micr_strings[] = { "RIN1", "RIN2", "RIN3", "RIN4" };
+static const struct soc_enum ak4642_micl_enum =
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4642_micl_strings),
ak4642_micl_strings);
+static const struct soc_enum ak4642_micr_enum =
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4642_micr_strings),
ak4642_micr_strings);
+static int ak4642_micl_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- u32 val, inl;
- val = snd_soc_read(codec, PW_MGMT3);
- inl = (val >> 1) & 0x1;
- inl |= (val >> 5) & 0x2;
- ucontrol->value.integer.value[0] = inl;
- return 0;
+}
+static int ak4642_micl_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- int val, inl;
- val = ucontrol->value.integer.value[0];
- inl = (val & 0x1) << 1;
- inl |= (val & 0x2) << 5;
- snd_soc_update_bits(codec, PW_MGMT3, (1 << 1) | (1 << 6), inl);
- return 1;
+}
+static int ak4642_micr_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- u32 val, inr;
- val = snd_soc_read(codec, PW_MGMT3);
- inr = (val >> 2) & 0x1;
- inr |= (val >> 6) & 0x2;
- ucontrol->value.integer.value[0] = inr;
- return 0;
+}
+static int ak4642_micr_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- int val, inr;
- val = ucontrol->value.integer.value[0];
- inr = (val & 0x1) << 2;
- inr |= (val & 0x2) << 6;
- snd_soc_update_bits(codec, PW_MGMT3, (1 << 2) | (1 << 7), inr);
- return 1;
+}
static const struct snd_kcontrol_new ak4642_snd_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC, @@ -168,6 +243,16 @@ static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = { SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0), };
+static const struct snd_kcontrol_new ak4642_micl_controls[] = {
- SOC_ENUM_EXT("Mic Left Capture Route", ak4642_micl_enum,
ak4642_micl_enum_get, ak4642_micl_enum_put),
+};
+static const struct snd_kcontrol_new ak4642_micr_controls[] = {
- SOC_ENUM_EXT("Mic Right Capture Route", ak4642_micr_enum,
ak4642_micr_enum_get, ak4642_micr_enum_put),
+};
static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
/* Outputs */ @@ -188,6 +273,22 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
/* DAC */ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", PW_MGMT1, 2, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_MUX("Mic Left Capture Route",
SND_SOC_NOPM, 0, 0, ak4642_micl_controls),
- SND_SOC_DAPM_MUX("Mic Right Capture Route",
SND_SOC_NOPM, 0, 0, ak4642_micr_controls),
- SND_SOC_DAPM_INPUT("LIN1"),
- SND_SOC_DAPM_INPUT("LIN2"),
- SND_SOC_DAPM_INPUT("LIN3"),
- SND_SOC_DAPM_INPUT("LIN4"),
- SND_SOC_DAPM_INPUT("RIN1"),
- SND_SOC_DAPM_INPUT("RIN2"),
- SND_SOC_DAPM_INPUT("RIN3"),
- SND_SOC_DAPM_INPUT("RIN4"),
};
static const struct snd_soc_dapm_route ak4642_intercon[] = { @@ -205,6 +306,19 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = { {"DACH", NULL, "DAC"},
{"LINEOUT Mixer", "DACL", "DAC"},
- {"Mic Left Capture Route", "LIN1", "LIN1"},
- {"Mic Left Capture Route", NULL, "LIN2"},
- {"Mic Left Capture Route", NULL, "LIN3"},
- {"Mic Left Capture Route", NULL, "LIN4"},
- {"Mic Right Capture Route", "RIN1", "RIN1"},
- {"Mic Right Capture Route", NULL, "RIN2"},
- {"Mic Right Capture Route", NULL, "RIN3"},
- {"Mic Right Capture Route", NULL, "RIN4"},
- {"ADC", NULL, "Mic Right Capture Route"},
- {"ADC", NULL, "Mic Left Capture Route"},
};
/*
2.0.0.rc0
![](https://secure.gravatar.com/avatar/8c9bfd9a73890a51c50c84b788616356.jpg?s=120&d=mm&r=g)
Signed-off-by: Sascha Hauer s.hauer@pengutronix.de --- Documentation/devicetree/bindings/sound/ak4642.txt | 3 ++ sound/soc/codecs/ak4642.c | 41 ++++++++++++++++++++++ 2 files changed, 44 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt index 623d4e7..21bc8ef 100644 --- a/Documentation/devicetree/bindings/sound/ak4642.txt +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -7,6 +7,9 @@ Required properties: - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648" - reg : The chip select number on the I2C bus
+Optional properties: + - enable-gpios : contains a GPIO used to enable the codec + Example:
&i2c { diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 6292a6a..67b8c25 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -29,6 +29,7 @@ #include <linux/of_device.h> #include <linux/module.h> #include <linux/regmap.h> +#include <linux/gpio/consumer.h> #include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -141,6 +142,7 @@ struct ak4642_drvdata {
struct ak4642_priv { const struct ak4642_drvdata *drvdata; + struct gpio_desc *pdn_gpio; };
/* @@ -596,9 +598,23 @@ static struct snd_soc_dai_driver ak4642_dai = { .symmetric_rates = 1, };
+static int ak4642_suspend(struct snd_soc_codec *codec) +{ + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (priv->pdn_gpio) + gpiod_set_value(priv->pdn_gpio, 0); + + return 0; +} + static int ak4642_resume(struct snd_soc_codec *codec) { struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (priv->pdn_gpio) + gpiod_set_value(priv->pdn_gpio, 1);
regcache_mark_dirty(regmap); regcache_sync(regmap); @@ -608,6 +624,15 @@ static int ak4642_resume(struct snd_soc_codec *codec)
static int ak4642_probe(struct snd_soc_codec *codec) { + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + if (priv->pdn_gpio) { + ret = gpiod_direction_output(priv->pdn_gpio, 1); + if (ret) + return ret; + } + ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; @@ -615,13 +640,20 @@ static int ak4642_probe(struct snd_soc_codec *codec)
static int ak4642_remove(struct snd_soc_codec *codec) { + struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec); + ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF); + + if (priv->pdn_gpio) + gpiod_set_value(priv->pdn_gpio, 0); + return 0; }
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { .probe = ak4642_probe, .remove = ak4642_remove, + .suspend = ak4642_suspend, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, .controls = ak4642_snd_controls, @@ -669,6 +701,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, const struct ak4642_drvdata *drvdata = NULL; struct regmap *regmap; struct ak4642_priv *priv; + struct gpio_desc *gpio;
if (np) { const struct of_device_id *of_id; @@ -691,6 +724,14 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
priv->drvdata = drvdata;
+ gpio = devm_gpiod_get(&i2c->dev, "enable"); + if (IS_ERR(gpio)) { + if (PTR_ERR(gpio) != -ENOENT) + return PTR_ERR(gpio); + } else { + priv->pdn_gpio = gpio; + } + i2c_set_clientdata(i2c, priv);
regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config);
participants (2)
-
Mark Brown
-
Sascha Hauer