-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
Showing
1 changed file
with
111 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
/* | ||
* ASoC Driver for HiFiBerry DAC+ / DAC Pro | ||
* ASoC Driver for HiFiBerry DAC+ / DAC Pro / AMP100 | ||
* | ||
* Author: Daniel Matuschek, Stuart MacLean <[email protected]> | ||
* Copyright 2014-2015 | ||
* based on code by Florian Meier <[email protected]> | ||
* 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_select_clk(struct snd_soc_component *compo | |
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,27 +129,20 @@ static bool snd_rpi_hifiberry_dacplus_is_sclk(struct snd_soc_component *componen | |
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; | ||
|
||
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_init(struct snd_soc_pcm_runtime *rtd) | |
{ | ||
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->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_init(struct snd_soc_pcm_runtime *rtd) | |
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_startup( | |
struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->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_shutdown( | |
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->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_probe(struct platform_device *pdev) | |
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,14 +454,54 @@ static int snd_rpi_hifiberry_dacplus_probe(struct platform_device *pdev) | |
"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, | ||
&snd_rpi_hifiberry_dacplus); | ||
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; | ||
} | ||
|
||
|