Skip to content

Commit

Permalink
ASoC: add driver for new HiFiBerry ADC only board(s)
Browse files Browse the repository at this point in the history
Adds the driver for the soon to be released first ADC only board.
It includes the same ADC controls as used by the DAC+ADC Pro driver.

Signed-off-by: j-schambacher <[email protected]>
  • Loading branch information
j-schambacher authored and pelwell committed Aug 27, 2024
1 parent 778f1e9 commit 255788c
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 0 deletions.
7 changes: 7 additions & 0 deletions sound/soc/bcm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ 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.

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 = snd_soc_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(snd_soc_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");

0 comments on commit 255788c

Please sign in to comment.