Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASoC: Add support for the HiFiBerry ADC sound card #6318

Merged
merged 4 commits into from
Aug 22, 2024
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 @@ -85,6 +85,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
hd44780-i2c-lcd.dtbo \
hd44780-lcd.dtbo \
hdmi-backlight-hwhack-gpio.dtbo \
hifiberry-adc.dtbo \
hifiberry-amp.dtbo \
hifiberry-amp100.dtbo \
hifiberry-amp3.dtbo \
Expand Down
7 changes: 7 additions & 0 deletions arch/arm/boot/dts/overlays/README
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,13 @@ Params: gpio_pin GPIO pin used (default 17)
expects a high to switch it on.


Name: hifiberry-adc
Info: Configures the HifiBerry ADC audio card
Load: dtoverlay=hifiberry-adc,<param>=<val>
Params: leds_off If set to 'true' the onboard indicator LED
is switched off at all times.


Name: hifiberry-amp
Info: Configures the HifiBerry Amp and Amp+ audio cards
Load: dtoverlay=hifiberry-amp
Expand Down
45 changes: 45 additions & 0 deletions arch/arm/boot/dts/overlays/hifiberry-adc-overlay.dts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0
// Definitions for HiFiBerry ADC, no onboard clocks
/dts-v1/;
/plugin/;

/ {
compatible = "brcm,bcm2835";

fragment@0 {
target = <&i2s_clk_producer>;
__overlay__ {
status = "okay";
};
};

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

hb_adc: pcm186x@4a {
#sound-dai-cells = <0>;
compatible = "ti,pcm1863";
reg = <0x4a>;
status = "okay";
};
};
};

fragment@2 {
target = <&sound>;
hifiberry_adc: __overlay__ {
compatible = "hifiberry,hifiberry-adc";
audio-codec = <&hb_adc>;
i2s-controller = <&i2s_clk_producer>;
status = "okay";
};
};

__overrides__ {
leds_off = <&hifiberry_adc>,"hifiberry-adc,leds_off?";
};
};
1 change: 1 addition & 0 deletions arch/arm/configs/bcm2709_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,7 @@ CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_CHIPDIP_DAC=m
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
Expand Down
1 change: 1 addition & 0 deletions arch/arm/configs/bcm2711_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_CHIPDIP_DAC=m
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
Expand Down
1 change: 1 addition & 0 deletions arch/arm/configs/bcmrpi_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,7 @@ CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_CHIPDIP_DAC=m
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/configs/bcm2711_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@ CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_CHIPDIP_DAC=m
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/configs/bcm2712_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_CHIPDIP_DAC=m
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/configs/bcmrpi3_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,7 @@ CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_CHIPDIP_DAC=m
CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
Expand Down
8 changes: 8 additions & 0 deletions sound/soc/bcm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
help
Say Y or M if you want to add support for voiceHAT soundcard.

config SND_BCM2708_SOC_HIFIBERRY_ADC
tristate "Support for HifiBerry ADC"
select SND_SOC_PCM186X_I2C
select SND_RPI_HIFIBERRY_ADC
help
Say Y or M if you want to add support for HifiBerry ADC.
Use this module for HiFiBerry's ADC-only sound cards

config SND_BCM2708_SOC_HIFIBERRY_DAC
tristate "Support for HifiBerry DAC and DAC8X"
select SND_SOC_PCM5102A
Expand Down
2 changes: 2 additions & 0 deletions sound/soc/bcm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o

# BCM2708 Machine Support
snd-soc-hifiberry-adc-objs := hifiberry_adc.o
snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
snd-soc-hifiberry-dacplushd-objs := hifiberry_dacplushd.o
snd-soc-hifiberry-dacplusadc-objs := hifiberry_dacplusadc.o
Expand Down Expand Up @@ -51,6 +52,7 @@ snd-soc-chipdip-dac-objs := chipdip-dac.o
snd-soc-dacberry400-objs := dacberry400.o

obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC) += snd-soc-hifiberry-adc.o
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD) += snd-soc-hifiberry-dacplushd.o
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC) += snd-soc-hifiberry-dacplusadc.o
Expand Down
174 changes: 174 additions & 0 deletions sound/soc/bcm/hifiberry_adc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ASoC Driver for HiFiBerry ADC
*
* Author: Joerg Schambacher <[email protected]>
* Copyright 2024
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/tlv.h>

#include "../codecs/pcm186x.h"
#include "hifiberry_adc_controls.h"

static bool leds_off;

static int pcm1863_add_controls(struct snd_soc_component *component)
{
snd_soc_add_component_controls(component,
pcm1863_snd_controls_card,
ARRAY_SIZE(pcm1863_snd_controls_card));
return 0;
}

static int snd_rpi_hifiberry_adc_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct snd_soc_component *adc = codec_dai->component;
int ret;

ret = pcm1863_add_controls(adc);
if (ret < 0)
dev_warn(rtd->dev, "Failed to add pcm1863 controls: %d\n",
ret);

codec_dai->driver->capture.rates =
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000;

/* set GPIO2 to output, GPIO3 input */
snd_soc_component_write(adc, PCM186X_GPIO3_2_CTRL, 0x00);
snd_soc_component_write(adc, PCM186X_GPIO3_2_DIR_CTRL, 0x04);
if (leds_off)
snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x00);
else
snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x40);

return 0;
}

static int snd_rpi_hifiberry_adc_hw_params(
struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int channels = params_channels(params);
int width = snd_pcm_format_width(params_format(params));

/* Using powers of 2 allows for an integer clock divisor */
width = width <= 16 ? 16 : 32;

ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), channels * width);
return ret;
}

/* machine stream operations */
static const struct snd_soc_ops snd_rpi_hifiberry_adc_ops = {
.hw_params = snd_rpi_hifiberry_adc_hw_params,
};

SND_SOC_DAILINK_DEFS(hifi,
DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("pcm186x.1-004a", "pcm1863-aif")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));

static struct snd_soc_dai_link snd_rpi_hifiberry_adc_dai[] = {
{
.name = "HiFiBerry ADC",
.stream_name = "HiFiBerry ADC HiFi",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &snd_rpi_hifiberry_adc_ops,
.init = snd_rpi_hifiberry_adc_init,
SND_SOC_DAILINK_REG(hifi),
},
};

/* audio machine driver */
static struct snd_soc_card snd_rpi_hifiberry_adc = {
.name = "snd_rpi_hifiberry_adc",
.driver_name = "HifiberryAdc",
.owner = THIS_MODULE,
.dai_link = snd_rpi_hifiberry_adc_dai,
.num_links = ARRAY_SIZE(snd_rpi_hifiberry_adc_dai),
};

static int snd_rpi_hifiberry_adc_probe(struct platform_device *pdev)
{
int ret = 0, i = 0;
struct snd_soc_card *card = &snd_rpi_hifiberry_adc;

snd_rpi_hifiberry_adc.dev = &pdev->dev;
if (pdev->dev.of_node) {
struct device_node *i2s_node;
struct snd_soc_dai_link *dai;

dai = &snd_rpi_hifiberry_adc_dai[0];
i2s_node = of_parse_phandle(pdev->dev.of_node,
"i2s-controller", 0);
if (i2s_node) {
for (i = 0; i < card->num_links; i++) {
dai->cpus->dai_name = NULL;
dai->cpus->of_node = i2s_node;
dai->platforms->name = NULL;
dai->platforms->of_node = i2s_node;
}
}
}
leds_off = of_property_read_bool(pdev->dev.of_node,
"hifiberry-adc,leds_off");
ret = snd_soc_register_card(&snd_rpi_hifiberry_adc);
if (ret && ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"snd_soc_register_card() failed: %d\n", ret);

return ret;
}

static const struct of_device_id snd_rpi_hifiberry_adc_of_match[] = {
{ .compatible = "hifiberry,hifiberry-adc", },
{},
};

MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_adc_of_match);

static struct platform_driver snd_rpi_hifiberry_adc_driver = {
.driver = {
.name = "snd-rpi-hifiberry-adc",
.owner = THIS_MODULE,
.of_match_table = snd_rpi_hifiberry_adc_of_match,
},
.probe = snd_rpi_hifiberry_adc_probe,
};

module_platform_driver(snd_rpi_hifiberry_adc_driver);

MODULE_AUTHOR("Joerg Schambacher <[email protected]>");
MODULE_DESCRIPTION("ASoC Driver for HiFiBerry ADC");
MODULE_LICENSE("GPL");
Loading