Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions arch/arm/boot/dts/overlays/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
hd44780-lcd.dtbo \
hdmi-backlight-hwhack-gpio.dtbo \
hifiberry-amp.dtbo \
hifiberry-amp100.dtbo \
hifiberry-dac.dtbo \
hifiberry-dacplus.dtbo \
hifiberry-dacplusadc.dtbo \
Expand Down
30 changes: 29 additions & 1 deletion arch/arm/boot/dts/overlays/README
Original file line number Diff line number Diff line change
Expand Up @@ -1045,8 +1045,36 @@ Load: dtoverlay=hifiberry-amp
Params: <None>


Name: hifiberry-amp100
Info: Configures the HifiBerry AMP100 audio card
Load: dtoverlay=hifiberry-amp100,<param>=<val>
Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec
Digital volume control. Enable with
"dtoverlay=hifiberry-amp100,24db_digital_gain"
(The default behaviour is that the Digital
volume control is limited to a maximum of
0dB. ie. it can attenuate but not provide
gain. For most users, this will be desired
as it will prevent clipping. By appending
the 24dB_digital_gain parameter, the Digital
volume control will allow up to 24dB of
gain. If this parameter is enabled, it is the
responsibility of the user to ensure that
the Digital volume control is set to a value
that does not result in clipping/distortion!)
slave Force DAC+ Pro into slave mode, using Pi as
master for bit clock and frame clock.
leds_off If set to 'true' the onboard indicator LEDs
are switched off at all times.
auto_mute If set to 'true' the amplifier is automatically
muted when the DAC is not playing.
mute_ext_ctl The amplifier's HW mute control is enabled
in ALSA mixer and set to <val>.
Will be overwritten by ALSA user settings.


Name: hifiberry-dac
Info: Configures the HifiBerry DAC audio card
Info: Configures the HifiBerry DAC audio cards
Load: dtoverlay=hifiberry-dac
Params: <None>

Expand Down
64 changes: 64 additions & 0 deletions arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Definitions for HiFiBerry AMP100
/dts-v1/;
/plugin/;

/ {
compatible = "brcm,bcm2835";

fragment@0 {
target-path = "/";
__overlay__ {
dacpro_osc: dacpro_osc {
compatible = "hifiberry,dacpro-clk";
#clock-cells = <0>;
};
};
};

fragment@1 {
target = <&i2s>;
__overlay__ {
status = "okay";
};
};

fragment@2 {
target = <&i2c1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";

pcm5122@4d {
#sound-dai-cells = <0>;
compatible = "ti,pcm5122";
reg = <0x4d>;
clocks = <&dacpro_osc>;
AVDD-supply = <&vdd_3v3_reg>;
DVDD-supply = <&vdd_3v3_reg>;
CPVDD-supply = <&vdd_3v3_reg>;
status = "okay";
};
};
};

fragment@3 {
target = <&sound>;
hifiberry_dacplus: __overlay__ {
compatible = "hifiberry,hifiberry-dacplus";
i2s-controller = <&i2s>;
status = "okay";
mute-gpio = <&gpio 4 0>;
reset-gpio = <&gpio 17 0x11>;
};
};

__overrides__ {
24db_digital_gain =
<&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
mute_ext_ctl = <&hifiberry_dacplus>,"hifiberry-dacplus,mute_ext_ctl:0";
auto_mute = <&hifiberry_dacplus>,"hifiberry-dacplus,auto_mute?";
};
};
124 changes: 111 additions & 13 deletions sound/soc/bcm/hifiberry_dacplus.c
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 <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
Expand All @@ -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>
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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);
}
Expand Down Expand Up @@ -149,6 +186,7 @@ static int snd_rpi_hifiberry_dacplus_init(struct snd_soc_pcm_runtime *rtd)
{
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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 = 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);
Expand All @@ -267,6 +321,8 @@ static void snd_rpi_hifiberry_dacplus_shutdown(
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 */
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}

Expand Down