Skip to content

I2S: switch to bcm2835 #1163

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

Merged
merged 10 commits into from
Oct 25, 2015
5 changes: 2 additions & 3 deletions arch/arm/boot/dts/bcm2708_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,11 @@
};

i2s: i2s@7e203000 {
compatible = "brcm,bcm2708-i2s";
compatible = "brcm,bcm2835-i2s";
reg = <0x7e203000 0x24>,
<0x7e101098 0x08>;

//dmas = <&dma 2>,
// <&dma 3>;
dmas = <&dma 2>, <&dma 3>;
dma-names = "tx", "rx";
status = "disabled";
};
Expand Down
1 change: 1 addition & 0 deletions arch/arm/configs/bcm2709_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ CONFIG_SND_USB_CAIAQ=m
CONFIG_SND_USB_CAIAQ_INPUT=y
CONFIG_SND_USB_6FIRE=m
CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=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 @@ -828,6 +828,7 @@ CONFIG_SND_USB_CAIAQ=m
CONFIG_SND_USB_CAIAQ_INPUT=y
CONFIG_SND_USB_6FIRE=m
CONFIG_SND_SOC=m
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_I2S=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
Expand Down
4 changes: 2 additions & 2 deletions sound/soc/bcm/Kconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
config SND_BCM2835_SOC_I2S
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
depends on ARCH_BCM2835 || COMPILE_TEST
depends on ARCH_BCM2835 || MACH_BCM2708 || MACH_BCM2709 || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Expand Down Expand Up @@ -70,7 +70,7 @@ config SND_BCM2708_SOC_IQAUDIO_DAC

config SND_BCM2708_SOC_RASPIDAC3
tristate "Support for RaspiDAC Rev.3x"
depends on SND_BCM2708_SOC_I2S
depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
select SND_SOC_PCM512x_I2C
select SND_SOC_TPA6130A2
help
Expand Down
90 changes: 69 additions & 21 deletions sound/soc/bcm/bcm2835-i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/of_address.h>

#include <sound/core.h>
#include <sound/pcm.h>
Expand Down Expand Up @@ -158,10 +159,6 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
#define BCM2835_I2S_INT_RXR BIT(1)
#define BCM2835_I2S_INT_TXW BIT(0)

/* I2S DMA interface */
/* FIXME: Needs IOMMU support */
#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000)

/* General device struct */
struct bcm2835_i2s_dev {
struct device *dev;
Expand Down Expand Up @@ -343,11 +340,15 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
data_length = 16;
bclk_ratio = 40;
bclk_ratio = 50;
break;
case SNDRV_PCM_FORMAT_S24_LE:
data_length = 24;
bclk_ratio = 50;
break;
case SNDRV_PCM_FORMAT_S32_LE:
data_length = 32;
bclk_ratio = 80;
bclk_ratio = 100;
break;
default:
return -EINVAL;
Expand Down Expand Up @@ -410,20 +411,30 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
divf = dividend & BCM2835_CLK_DIVF_MASK;
}

/* Set clock divider */
regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
| BCM2835_CLK_DIVI(divi)
| BCM2835_CLK_DIVF(divf));

/* Setup clock, but don't start it yet */
regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
| BCM2835_CLK_MASH(mash)
| BCM2835_CLK_SRC(clk_src));
/* Clock should only be set up here if CPU is clock master */
switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
/* Set clock divider */
regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG,
BCM2835_CLK_PASSWD
| BCM2835_CLK_DIVI(divi)
| BCM2835_CLK_DIVF(divf));

/* Setup clock, but don't start it yet */
regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
BCM2835_CLK_PASSWD
| BCM2835_CLK_MASH(mash)
| BCM2835_CLK_SRC(clk_src));
break;
default:
break;
}

/* Setup the frame format */
format = BCM2835_I2S_CHEN;

if (data_length > 24)
if (data_length >= 24)
format |= BCM2835_I2S_CHWEX;

format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
Expand Down Expand Up @@ -714,13 +725,15 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S32_LE
},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S32_LE
},
.ops = &bcm2835_i2s_dai_ops,
Expand Down Expand Up @@ -769,6 +782,7 @@ static const struct regmap_config bcm2835_regmap_config[] = {
.precious_reg = bcm2835_i2s_precious_reg,
.volatile_reg = bcm2835_i2s_volatile_reg,
.cache_type = REGCACHE_RBTREE,
.name = "i2s",
},
{
.reg_bits = 32,
Expand All @@ -777,20 +791,54 @@ static const struct regmap_config bcm2835_regmap_config[] = {
.max_register = BCM2835_CLK_PCMDIV_REG,
.volatile_reg = bcm2835_clk_volatile_reg,
.cache_type = REGCACHE_RBTREE,
.name = "clk",
},
};

static const struct snd_soc_component_driver bcm2835_i2s_component = {
.name = "bcm2835-i2s-comp",
};

static struct snd_pcm_hardware bcm2835_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
.period_bytes_max = 64 * PAGE_SIZE,
.periods_min = 2,
.periods_max = 255,
.buffer_bytes_max = 128 * PAGE_SIZE,
};

static const struct snd_dmaengine_pcm_config bcm2835_dmaengine_pcm_config = {
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.pcm_hardware = &bcm2835_pcm_hardware,
.prealloc_buffer_size = 256 * PAGE_SIZE,
};

static int bcm2835_i2s_probe(struct platform_device *pdev)
{
struct bcm2835_i2s_dev *dev;
int i;
int ret;
struct regmap *regmap[2];
struct resource *mem[2];
const __be32 *addr;
dma_addr_t dma_reg_base;

addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
if (!addr) {
dev_err(&pdev->dev, "could not get DMA-register address\n");
return -ENODEV;
}
dma_reg_base = be32_to_cpup(addr);

if (of_property_read_bool(pdev->dev.of_node, "brcm,enable-mmap"))
bcm2835_pcm_hardware.info |=
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID;

/* Request both ioareas */
for (i = 0; i <= 1; i++) {
Expand All @@ -817,12 +865,10 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)

/* Set the DMA address */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
(dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
+ BCM2835_VCMMU_SHIFT;
dma_reg_base + BCM2835_I2S_FIFO_A_REG;

dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
(dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
+ BCM2835_VCMMU_SHIFT;
dma_reg_base + BCM2835_I2S_FIFO_A_REG;

/* Set the bus width */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
Expand All @@ -848,7 +894,9 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
return ret;
}

ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
&bcm2835_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_COMPAT);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
return ret;
Expand Down