diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-1046-Enhances-the-Hifiberry-DAC-driver-for-Hifiberry-AMP1.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-1046-Enhances-the-Hifiberry-DAC-driver-for-Hifiberry-AMP1.patch | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-1046-Enhances-the-Hifiberry-DAC-driver-for-Hifiberry-AMP1.patch b/target/linux/bcm27xx/patches-5.4/950-1046-Enhances-the-Hifiberry-DAC-driver-for-Hifiberry-AMP1.patch new file mode 100644 index 0000000000..4e15285220 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-1046-Enhances-the-Hifiberry-DAC-driver-for-Hifiberry-AMP1.patch @@ -0,0 +1,238 @@ +From 0b584f3c2808dba8dc9e74a628b65008302804a5 Mon Sep 17 00:00:00 2001 +From: Joerg Schambacher <joerg@i2audio.com> +Date: Fri, 29 Jan 2021 16:16:39 +0100 +Subject: [PATCH] Enhances the Hifiberry DAC+ driver for Hifiberry + AMP100 support + +Adds the necessary GPIO handling and ALSA mixer extensions. +Also fixes a problem with the PLL/CLK control when switching sample rates. +Thanks to Clive Messer for the support! + +Signed-off-by: Joerg Schambacher <joerg@hifiberry.com> +--- + sound/soc/bcm/hifiberry_dacplus.c | 124 ++++++++++++++++++++++++++---- + 1 file changed, 111 insertions(+), 13 deletions(-) + +--- a/sound/soc/bcm/hifiberry_dacplus.c ++++ b/sound/soc/bcm/hifiberry_dacplus.c +@@ -1,10 +1,10 @@ + /* +- * ASoC Driver for HiFiBerry DAC+ / DAC Pro ++ * ASoC Driver for HiFiBerry DAC+ / DAC Pro / AMP100 + * + * Author: Daniel Matuschek, Stuart MacLean <stuart@hifiberry.com> + * Copyright 2014-2015 + * based on code by Florian Meier <florian.meier@koalo.de> +- * Headphone added by Joerg Schambacher, joerg@i2audio.com ++ * Headphone/AMP100 Joerg Schambacher <joerg@hifiberry.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -17,6 +17,8 @@ + */ + + #include <linux/module.h> ++#include <linux/gpio/consumer.h> ++#include <../drivers/gpio/gpiolib.h> + #include <linux/platform_device.h> + #include <linux/kernel.h> + #include <linux/clk.h> +@@ -53,6 +55,47 @@ static bool slave; + static bool snd_rpi_hifiberry_is_dacpro; + static bool digital_gain_0db_limit = true; + static bool leds_off; ++static bool auto_mute; ++static int mute_ext_ctl; ++static int mute_ext; ++static struct gpio_desc *snd_mute_gpio; ++static struct gpio_desc *snd_reset_gpio; ++static struct snd_soc_card snd_rpi_hifiberry_dacplus; ++ ++static int snd_rpi_hifiberry_dacplus_mute_set(int mute) ++{ ++ gpiod_set_value_cansleep(snd_mute_gpio, mute); ++ return 1; ++} ++ ++static int snd_rpi_hifiberry_dacplus_mute_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.integer.value[0] = mute_ext; ++ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_dacplus_mute_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ if (mute_ext == ucontrol->value.integer.value[0]) ++ return 0; ++ ++ mute_ext = ucontrol->value.integer.value[0]; ++ ++ return snd_rpi_hifiberry_dacplus_mute_set(mute_ext); ++} ++ ++static const char * const mute_text[] = {"Play", "Mute"}; ++static const struct soc_enum hb_dacplus_opt_mute_enum = ++ SOC_ENUM_SINGLE_EXT(2, mute_text); ++ ++static const struct snd_kcontrol_new hb_dacplus_opt_mute_controls[] = { ++ SOC_ENUM_EXT("Mute(ext)", hb_dacplus_opt_mute_enum, ++ snd_rpi_hifiberry_dacplus_mute_get, ++ snd_rpi_hifiberry_dacplus_mute_put), ++}; + + static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_component *component, + int clk_id) +@@ -68,6 +111,7 @@ static void snd_rpi_hifiberry_dacplus_se + snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x04); + break; + } ++ usleep_range(2000, 2100); + } + + static void snd_rpi_hifiberry_dacplus_clk_gpio(struct snd_soc_component *component) +@@ -85,13 +129,6 @@ static bool snd_rpi_hifiberry_dacplus_is + return (!(sck & 0x40)); + } + +-static bool snd_rpi_hifiberry_dacplus_is_sclk_sleep( +- struct snd_soc_component *component) +-{ +- msleep(2); +- return snd_rpi_hifiberry_dacplus_is_sclk(component); +-} +- + static bool snd_rpi_hifiberry_dacplus_is_pro_card(struct snd_soc_component *component) + { + bool isClk44EN, isClk48En, isNoClk; +@@ -99,13 +136,13 @@ static bool snd_rpi_hifiberry_dacplus_is + snd_rpi_hifiberry_dacplus_clk_gpio(component); + + snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_CLK44EN); +- isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk_sleep(component); ++ isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk(component); + + snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_NOCLOCK); +- isNoClk = snd_rpi_hifiberry_dacplus_is_sclk_sleep(component); ++ isNoClk = snd_rpi_hifiberry_dacplus_is_sclk(component); + + snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_CLK48EN); +- isClk48En = snd_rpi_hifiberry_dacplus_is_sclk_sleep(component); ++ isClk48En = snd_rpi_hifiberry_dacplus_is_sclk(component); + + return (isClk44EN && isClk48En && !isNoClk); + } +@@ -149,6 +186,7 @@ static int snd_rpi_hifiberry_dacplus_ini + { + struct snd_soc_component *component = rtd->codec_dai->component; + struct pcm512x_priv *priv; ++ struct snd_soc_card *card = &snd_rpi_hifiberry_dacplus; + + if (slave) + snd_rpi_hifiberry_is_dacpro = false; +@@ -187,6 +225,20 @@ static int snd_rpi_hifiberry_dacplus_ini + if (ret < 0) + dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); + } ++ if (snd_reset_gpio) { ++ gpiod_set_value_cansleep(snd_reset_gpio, 0); ++ msleep(1); ++ gpiod_set_value_cansleep(snd_reset_gpio, 1); ++ msleep(1); ++ gpiod_set_value_cansleep(snd_reset_gpio, 0); ++ } ++ ++ if (mute_ext_ctl) ++ snd_soc_add_card_controls(card, hb_dacplus_opt_mute_controls, ++ ARRAY_SIZE(hb_dacplus_opt_mute_controls)); ++ ++ if (snd_mute_gpio) ++ gpiod_set_value_cansleep(snd_mute_gpio, mute_ext); + + return 0; + } +@@ -254,6 +306,8 @@ static int snd_rpi_hifiberry_dacplus_sta + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = rtd->codec_dai->component; + ++ if (auto_mute) ++ gpiod_set_value_cansleep(snd_mute_gpio, 0); + if (leds_off) + return 0; + snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); +@@ -267,6 +321,8 @@ static void snd_rpi_hifiberry_dacplus_sh + struct snd_soc_component *component = rtd->codec_dai->component; + + snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); ++ if (auto_mute) ++ gpiod_set_value_cansleep(snd_mute_gpio, 1); + } + + /* machine stream operations */ +@@ -342,6 +398,8 @@ static int snd_rpi_hifiberry_dacplus_pro + struct device_node *tpa_node; + struct property *tpa_prop; + struct of_changeset ocs; ++ struct property *pp; ++ int tmp; + + /* probe for head phone amp */ + ret = hb_hp_detect(); +@@ -396,6 +454,39 @@ static int snd_rpi_hifiberry_dacplus_pro + "hifiberry-dacplus,slave"); + leds_off = of_property_read_bool(pdev->dev.of_node, + "hifiberry-dacplus,leds_off"); ++ auto_mute = of_property_read_bool(pdev->dev.of_node, ++ "hifiberry-dacplus,auto_mute"); ++ ++ /* ++ * check for HW MUTE as defined in DT-overlay ++ * active high, therefore default to HIGH to MUTE ++ */ ++ snd_mute_gpio = devm_gpiod_get_optional(&pdev->dev, ++ "mute", GPIOD_OUT_HIGH); ++ if (IS_ERR(snd_mute_gpio)) { ++ dev_err(&pdev->dev, "Can't allocate GPIO (HW-MUTE)"); ++ return PTR_ERR(snd_mute_gpio); ++ } ++ ++ /* add ALSA control if requested in DT-overlay (AMP100) */ ++ pp = of_find_property(pdev->dev.of_node, ++ "hifiberry-dacplus,mute_ext_ctl", &tmp); ++ if (pp) { ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "hifiberry-dacplus,mute_ext_ctl", &mute_ext)) { ++ /* ALSA control will be used */ ++ mute_ext_ctl = 1; ++ } ++ } ++ ++ /* check for HW RESET (AMP100) */ ++ snd_reset_gpio = devm_gpiod_get_optional(&pdev->dev, ++ "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(snd_reset_gpio)) { ++ dev_err(&pdev->dev, "Can't allocate GPIO (HW-RESET)"); ++ return PTR_ERR(snd_reset_gpio); ++ } ++ + } + + ret = devm_snd_soc_register_card(&pdev->dev, +@@ -403,7 +494,14 @@ static int snd_rpi_hifiberry_dacplus_pro + if (ret && ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "snd_soc_register_card() failed: %d\n", ret); +- ++ if (!ret) { ++ if (snd_mute_gpio) ++ dev_info(&pdev->dev, "GPIO%i for HW-MUTE selected", ++ gpio_chip_hwgpio(snd_mute_gpio)); ++ if (snd_reset_gpio) ++ dev_info(&pdev->dev, "GPIO%i for HW-RESET selected", ++ gpio_chip_hwgpio(snd_reset_gpio)); ++ } + return ret; + } + |