Skip to content

Commit

Permalink
Merge remote-tracking branches 'asoc/topic/dpcm', 'asoc/topic/dt', 'a…
Browse files Browse the repository at this point in the history
…soc/topic/dwc' and 'asoc/topic/fsl' into asoc-next
  • Loading branch information
Mark Brown committed Jul 24, 2016
5 parents 3ceeda1 + 0946497 + add3873 + 57072ae + 121a015 commit dd0111d
Show file tree
Hide file tree
Showing 12 changed files with 580 additions and 134 deletions.
4 changes: 4 additions & 0 deletions Documentation/devicetree/bindings/sound/designware-i2s.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Required properties:
one for receive.
- dma-names : "tx" for the transmit channel, "rx" for the receive channel.

Optional properties:
- interrupts: The interrupt line number for the I2S controller. Add this
parameter if the I2S controller that you are using does not support DMA.

For more details on the 'dma', 'dma-names', 'clock' and 'clock-names'
properties please check:
* resource-names.txt
Expand Down
2 changes: 1 addition & 1 deletion Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Required properties:
* DMIC (stands for Digital Microphone Jack)

Note: The "Mic Jack" and "AMIC" are redundant while
coexsiting in order to support the old bindings
coexisting in order to support the old bindings
of wm8962 and sgtl5000.

Optional properties:
Expand Down
2 changes: 1 addition & 1 deletion Documentation/sound/alsa/soc/machine.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ASoC Machine Driver

The ASoC machine (or board) driver is the code that glues together all the
component drivers (e.g. codecs, platforms and DAIs). It also describes the
relationships between each componnent which include audio paths, GPIOs,
relationships between each component which include audio paths, GPIOs,
interrupts, clocking, jacks and voltage regulators.

The machine driver can contain codec and platform specific code. It registers
Expand Down
5 changes: 4 additions & 1 deletion include/sound/soc-dapm.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ struct snd_soc_dapm_context;
struct regulator;
struct snd_soc_dapm_widget_list;
struct snd_soc_dapm_update;
enum snd_soc_dapm_direction;

int dapm_regulator_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
Expand Down Expand Up @@ -454,7 +455,9 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card);

/* dapm path query */
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list);
struct snd_soc_dapm_widget_list **list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
enum snd_soc_dapm_direction));

struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
struct snd_kcontrol *kcontrol);
Expand Down
9 changes: 9 additions & 0 deletions sound/soc/dwc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,13 @@ config SND_DESIGNWARE_I2S
Synopsys desigwnware I2S device. The device supports upto
maximum of 8 channels each for play and record.

config SND_DESIGNWARE_PCM
tristate "PCM PIO extension for I2S driver"
depends on SND_DESIGNWARE_I2S
help
Say Y, M or N if you want to add a custom ALSA extension that registers
a PCM and uses PIO to transfer data.

This functionality is specially suited for I2S devices that don't have
DMA support.

4 changes: 3 additions & 1 deletion sound/soc/dwc/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# SYNOPSYS Platform Support
obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o

ifdef CONFIG_SND_DESIGNWARE_PCM
obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_pcm.o
endif
231 changes: 115 additions & 116 deletions sound/soc/dwc/designware_i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,90 +24,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>

/* common register for all channel */
#define IER 0x000
#define IRER 0x004
#define ITER 0x008
#define CER 0x00C
#define CCR 0x010
#define RXFFR 0x014
#define TXFFR 0x018

/* I2STxRxRegisters for all channels */
#define LRBR_LTHR(x) (0x40 * x + 0x020)
#define RRBR_RTHR(x) (0x40 * x + 0x024)
#define RER(x) (0x40 * x + 0x028)
#define TER(x) (0x40 * x + 0x02C)
#define RCR(x) (0x40 * x + 0x030)
#define TCR(x) (0x40 * x + 0x034)
#define ISR(x) (0x40 * x + 0x038)
#define IMR(x) (0x40 * x + 0x03C)
#define ROR(x) (0x40 * x + 0x040)
#define TOR(x) (0x40 * x + 0x044)
#define RFCR(x) (0x40 * x + 0x048)
#define TFCR(x) (0x40 * x + 0x04C)
#define RFF(x) (0x40 * x + 0x050)
#define TFF(x) (0x40 * x + 0x054)

/* I2SCOMPRegisters */
#define I2S_COMP_PARAM_2 0x01F0
#define I2S_COMP_PARAM_1 0x01F4
#define I2S_COMP_VERSION 0x01F8
#define I2S_COMP_TYPE 0x01FC

/*
* Component parameter register fields - define the I2S block's
* configuration.
*/
#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25)
#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22)
#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19)
#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16)
#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9)
#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7)
#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6)
#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5)
#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4)
#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2)
#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)

#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10)
#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7)
#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3)
#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0)

/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
#define COMP_MAX_WORDSIZE (1 << 3)
#define COMP_MAX_DATA_WIDTH (1 << 2)

#define MAX_CHANNEL_NUM 8
#define MIN_CHANNEL_NUM 2

union dw_i2s_snd_dma_data {
struct i2s_dma_data pd;
struct snd_dmaengine_dai_dma_data dt;
};

struct dw_i2s_dev {
void __iomem *i2s_base;
struct clk *clk;
int active;
unsigned int capability;
unsigned int quirks;
unsigned int i2s_reg_comp1;
unsigned int i2s_reg_comp2;
struct device *dev;
u32 ccr;
u32 xfer_resolution;
u32 fifo_th;

/* data related to DMA transfers b/w i2s and DMAC */
union dw_i2s_snd_dma_data play_dma_data;
union dw_i2s_snd_dma_data capture_dma_data;
struct i2s_clk_config_data config;
int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
};
#include "local.h"

static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
{
Expand Down Expand Up @@ -145,51 +62,115 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
}
}

static void i2s_start(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream)
static inline void i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream,
int chan_nr)
{
u32 i, irq;

if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
}
} else {
for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
}
}
}

static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream,
int chan_nr)
{
struct i2s_clk_config_data *config = &dev->config;
u32 i, irq;
i2s_write_reg(dev->i2s_base, IER, 1);

if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < (config->chan_nr / 2); i++) {
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
}
i2s_write_reg(dev->i2s_base, ITER, 1);
} else {
for (i = 0; i < (config->chan_nr / 2); i++) {
for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
}
i2s_write_reg(dev->i2s_base, IRER, 1);
}
}

static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
{
struct dw_i2s_dev *dev = dev_id;
bool irq_valid = false;
u32 isr[4];
int i;

for (i = 0; i < 4; i++)
isr[i] = i2s_read_reg(dev->i2s_base, ISR(i));

i2s_clear_irqs(dev, SNDRV_PCM_STREAM_PLAYBACK);
i2s_clear_irqs(dev, SNDRV_PCM_STREAM_CAPTURE);

for (i = 0; i < 4; i++) {
/*
* Check if TX fifo is empty. If empty fill FIFO with samples
* NOTE: Only two channels supported
*/
if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) {
dw_pcm_push_tx(dev);
irq_valid = true;
}

/* Data available. Record mode not supported in PIO mode */
if (isr[i] & ISR_RXDA)
irq_valid = true;

/* Error Handling: TX */
if (isr[i] & ISR_TXFO) {
dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i);
irq_valid = true;
}

/* Error Handling: TX */
if (isr[i] & ISR_RXFO) {
dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i);
irq_valid = true;
}
}

if (irq_valid)
return IRQ_HANDLED;
else
return IRQ_NONE;
}

static void i2s_start(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream)
{
struct i2s_clk_config_data *config = &dev->config;

i2s_write_reg(dev->i2s_base, IER, 1);
i2s_enable_irqs(dev, substream->stream, config->chan_nr);

if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
i2s_write_reg(dev->i2s_base, ITER, 1);
else
i2s_write_reg(dev->i2s_base, IRER, 1);

i2s_write_reg(dev->i2s_base, CER, 1);
}

static void i2s_stop(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream)
{
u32 i = 0, irq;

i2s_clear_irqs(dev, substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
i2s_write_reg(dev->i2s_base, ITER, 0);

for (i = 0; i < 4; i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
}
} else {
else
i2s_write_reg(dev->i2s_base, IRER, 0);

for (i = 0; i < 4; i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
}
}
i2s_disable_irqs(dev, substream->stream, 8);

if (!dev->active) {
i2s_write_reg(dev->i2s_base, CER, 0);
Expand Down Expand Up @@ -223,7 +204,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,

static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
{
u32 ch_reg, irq;
u32 ch_reg;
struct i2s_clk_config_data *config = &dev->config;


Expand All @@ -235,16 +216,12 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
dev->fifo_th - 1);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
} else {
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
dev->fifo_th - 1);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
}

Expand Down Expand Up @@ -278,7 +255,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
break;

default:
dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
dev_err(dev->dev, "designware-i2s: unsupported PCM fmt");
return -EINVAL;
}

Expand Down Expand Up @@ -626,7 +603,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
const struct i2s_platform_data *pdata = pdev->dev.platform_data;
struct dw_i2s_dev *dev;
struct resource *res;
int ret;
int ret, irq;
struct snd_soc_dai_driver *dw_i2s_dai;
const char *clk_id;

Expand All @@ -651,6 +628,16 @@ static int dw_i2s_probe(struct platform_device *pdev)

dev->dev = &pdev->dev;

irq = platform_get_irq(pdev, 0);
if (irq >= 0) {
ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
pdev->name, dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
}

dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
if (pdata) {
Expand Down Expand Up @@ -697,12 +684,24 @@ static int dw_i2s_probe(struct platform_device *pdev)

if (!pdata) {
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
if (ret == -EPROBE_DEFER) {
dev_err(&pdev->dev,
"failed to register PCM, deferring probe\n");
return ret;
} else if (ret) {
dev_err(&pdev->dev,
"Could not register PCM: %d\n", ret);
goto err_clk_disable;
"Could not register DMA PCM: %d\n"
"falling back to PIO mode\n", ret);
ret = dw_pcm_register(pdev);
if (ret) {
dev_err(&pdev->dev,
"Could not register PIO PCM: %d\n",
ret);
goto err_clk_disable;
}
}
}

pm_runtime_enable(&pdev->dev);
return 0;

Expand Down
Loading

0 comments on commit dd0111d

Please sign in to comment.