summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0413-IQaudIO-auto-mute-for-AMP-and-DigiAMP.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-4.4/0413-IQaudIO-auto-mute-for-AMP-and-DigiAMP.patch')
-rw-r--r--target/linux/brcm2708/patches-4.4/0413-IQaudIO-auto-mute-for-AMP-and-DigiAMP.patch272
1 files changed, 272 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.4/0413-IQaudIO-auto-mute-for-AMP-and-DigiAMP.patch b/target/linux/brcm2708/patches-4.4/0413-IQaudIO-auto-mute-for-AMP-and-DigiAMP.patch
new file mode 100644
index 0000000000..1e6f65abd5
--- /dev/null
+++ b/target/linux/brcm2708/patches-4.4/0413-IQaudIO-auto-mute-for-AMP-and-DigiAMP.patch
@@ -0,0 +1,272 @@
+From c5368041bdadf28f7081deb6e11c9aaafe05377f Mon Sep 17 00:00:00 2001
+From: DigitalDreamtime <clive.messer@digitaldreamtime.co.uk>
+Date: Mon, 9 May 2016 20:38:08 +0100
+Subject: [PATCH 413/423] IQaudIO: auto-mute for AMP+ and DigiAMP+
+
+IQAudIO amplifier mute via GPIO22. Add dt params for "one-shot" unmute
+and auto mute.
+
+Revision 2, auto mute implementing HiassofT suggestion to mute/unmute
+using set_bias_level, rather than startup/shutdown....
+"By default DAPM waits 5 seconds (pmdown_time) before shutting down
+playback streams so a close/stop immediately followed by open/start
+doesn't trigger an amp mute+unmute."
+
+Tested on both AMP+ (via DAC+) and DigiAMP+, with both options...
+
+dtoverlay=iqaudio-dacplus,unmute_amp
+ "one-shot" unmute when kernel module loads.
+
+dtoverlay=iqaudio-dacplus,auto_mute_amp
+ Unmute amp when ALSA device opened by a client. Mute, with 5 second delay
+ when ALSA device closed. (Re-opening the device within the 5 second close
+ window, will cancel mute.)
+
+Revision 4, using gpiod.
+
+Revision 5, clean-up formatting before adding mute code.
+ - Convert tab plus 4 space formatting to 2x tab
+ - Remove '// NOT USED' commented code
+
+Revision 6, don't attempt to "one-shot" unmute amp, unless card is
+successfully registered.
+
+Signed-off-by: DigitalDreamtime <clive.messer@digitaldreamtime.co.uk>
+---
+ arch/arm/boot/dts/overlays/README | 4 +
+ .../boot/dts/overlays/iqaudio-dacplus-overlay.dts | 7 +-
+ sound/soc/bcm/iqaudio-dac.c | 144 ++++++++++++++++-----
+ 3 files changed, 124 insertions(+), 31 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -540,6 +540,10 @@ Params: 24db_digital_gain Allow ga
+ responsibility of the user to ensure that
+ the Digital volume control is set to a value
+ that does not result in clipping/distortion!)
++ auto_mute_amp If specified, unmute/mute the IQaudIO amp when
++ starting/stopping audio playback.
++ unmute_amp If specified, unmute the IQaudIO amp once when
++ the DAC driver module loads.
+
+
+ Name: justboom-dac
+--- a/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
+@@ -30,14 +30,17 @@
+
+ fragment@2 {
+ target = <&sound>;
+- frag2: __overlay__ {
++ iqaudio_dac: __overlay__ {
+ compatible = "iqaudio,iqaudio-dac";
+ i2s-controller = <&i2s>;
++ mute-gpios = <&gpio 22 0>;
+ status = "okay";
+ };
+ };
+
+ __overrides__ {
+- 24db_digital_gain = <&frag2>,"iqaudio,24db_digital_gain?";
++ 24db_digital_gain = <&iqaudio_dac>,"iqaudio,24db_digital_gain?";
++ auto_mute_amp = <&iqaudio_dac>,"iqaudio-dac,auto-mute-amp?";
++ unmute_amp = <&iqaudio_dac>,"iqaudio-dac,unmute-amp?";
+ };
+ };
+--- a/sound/soc/bcm/iqaudio-dac.c
++++ b/sound/soc/bcm/iqaudio-dac.c
+@@ -15,6 +15,7 @@
+ */
+
+ #include <linux/module.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/platform_device.h>
+
+ #include <sound/core.h>
+@@ -25,6 +26,8 @@
+
+ static bool digital_gain_0db_limit = true;
+
++static struct gpio_desc *mute_gpio;
++
+ static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd)
+ {
+ if (digital_gain_0db_limit)
+@@ -41,11 +44,9 @@ static int snd_rpi_iqaudio_dac_init(stru
+ }
+
+ static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream,
+- struct snd_pcm_hw_params *params)
++ struct snd_pcm_hw_params *params)
+ {
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+-// NOT USED struct snd_soc_dai *codec_dai = rtd->codec_dai;
+-// NOT USED struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ unsigned int sample_bits =
+@@ -54,6 +55,56 @@ static int snd_rpi_iqaudio_dac_hw_params
+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
+ }
+
++static void snd_rpi_iqaudio_gpio_mute(struct snd_soc_card *card)
++{
++ if (mute_gpio) {
++ dev_info(card->dev, "%s: muting amp using GPIO22\n",
++ __func__);
++ gpiod_set_value_cansleep(mute_gpio, 0);
++ }
++}
++
++static void snd_rpi_iqaudio_gpio_unmute(struct snd_soc_card *card)
++{
++ if (mute_gpio) {
++ dev_info(card->dev, "%s: un-muting amp using GPIO22\n",
++ __func__);
++ gpiod_set_value_cansleep(mute_gpio, 1);
++ }
++}
++
++static int snd_rpi_iqaudio_set_bias_level(struct snd_soc_card *card,
++ struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
++{
++ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
++
++ if (dapm->dev != codec_dai->dev)
++ return 0;
++
++ switch (level) {
++ case SND_SOC_BIAS_PREPARE:
++ if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
++ break;
++
++ /* UNMUTE AMP */
++ snd_rpi_iqaudio_gpio_unmute(card);
++
++ break;
++ case SND_SOC_BIAS_STANDBY:
++ if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
++ break;
++
++ /* MUTE AMP */
++ snd_rpi_iqaudio_gpio_mute(card);
++
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
+ /* machine stream operations */
+ static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = {
+ .hw_params = snd_rpi_iqaudio_dac_hw_params,
+@@ -82,46 +133,81 @@ static struct snd_soc_card snd_rpi_iqaud
+ static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev)
+ {
+ int ret = 0;
++ bool gpio_unmute = false;
+
+ snd_rpi_iqaudio_dac.dev = &pdev->dev;
+
+ if (pdev->dev.of_node) {
+- struct device_node *i2s_node;
+- struct snd_soc_card *card = &snd_rpi_iqaudio_dac;
+- struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_dac_dai[0];
+- i2s_node = of_parse_phandle(pdev->dev.of_node,
+- "i2s-controller", 0);
+-
+- if (i2s_node) {
+- dai->cpu_dai_name = NULL;
+- dai->cpu_of_node = i2s_node;
+- dai->platform_name = NULL;
+- dai->platform_of_node = i2s_node;
+- }
+-
+- digital_gain_0db_limit = !of_property_read_bool(pdev->dev.of_node,
+- "iqaudio,24db_digital_gain");
+- if (of_property_read_string(pdev->dev.of_node, "card_name",
+- &card->name))
+- card->name = "IQaudIODAC";
+- if (of_property_read_string(pdev->dev.of_node, "dai_name",
+- &dai->name))
+- dai->name = "IQaudIO DAC";
+- if (of_property_read_string(pdev->dev.of_node, "dai_stream_name",
+- &dai->stream_name))
+- dai->stream_name = "IQaudIO DAC HiFi";
++ struct device_node *i2s_node;
++ struct snd_soc_card *card = &snd_rpi_iqaudio_dac;
++ struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_dac_dai[0];
++ bool auto_gpio_mute = false;
++
++ i2s_node = of_parse_phandle(pdev->dev.of_node,
++ "i2s-controller", 0);
++ if (i2s_node) {
++ dai->cpu_dai_name = NULL;
++ dai->cpu_of_node = i2s_node;
++ dai->platform_name = NULL;
++ dai->platform_of_node = i2s_node;
++ }
++
++ digital_gain_0db_limit = !of_property_read_bool(
++ pdev->dev.of_node, "iqaudio,24db_digital_gain");
++
++ if (of_property_read_string(pdev->dev.of_node, "card_name",
++ &card->name))
++ card->name = "IQaudIODAC";
++
++ if (of_property_read_string(pdev->dev.of_node, "dai_name",
++ &dai->name))
++ dai->name = "IQaudIO DAC";
++
++ if (of_property_read_string(pdev->dev.of_node,
++ "dai_stream_name", &dai->stream_name))
++ dai->stream_name = "IQaudIO DAC HiFi";
++
++ /* gpio_unmute - one time unmute amp using GPIO */
++ gpio_unmute = of_property_read_bool(pdev->dev.of_node,
++ "iqaudio-dac,unmute-amp");
++
++ /* auto_gpio_mute - mute/unmute amp using GPIO */
++ auto_gpio_mute = of_property_read_bool(pdev->dev.of_node,
++ "iqaudio-dac,auto-mute-amp");
++
++ if (auto_gpio_mute || gpio_unmute) {
++ mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(mute_gpio)) {
++ ret = PTR_ERR(mute_gpio);
++ dev_err(&pdev->dev,
++ "Failed to get mute gpio: %d\n", ret);
++ return ret;
++ }
++
++ if (auto_gpio_mute && mute_gpio)
++ snd_rpi_iqaudio_dac.set_bias_level =
++ snd_rpi_iqaudio_set_bias_level;
++ }
+ }
+
+ ret = snd_soc_register_card(&snd_rpi_iqaudio_dac);
+- if (ret)
++ if (ret) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card() failed: %d\n", ret);
++ return ret;
++ }
++
++ if (gpio_unmute && mute_gpio)
++ snd_rpi_iqaudio_gpio_unmute(&snd_rpi_iqaudio_dac);
+
+- return ret;
++ return 0;
+ }
+
+ static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev)
+ {
++ snd_rpi_iqaudio_gpio_mute(&snd_rpi_iqaudio_dac);
++
+ return snd_soc_unregister_card(&snd_rpi_iqaudio_dac);
+ }
+