All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-27 22:37 ` Barry Song
  0 siblings, 0 replies; 14+ messages in thread
From: Barry Song @ 2013-10-27 22:37 UTC (permalink / raw)
  To: broonie, lgirdwood
  Cc: Rongjun Ying, alsa-devel, workgroup.linux, linux-arm-kernel, Barry Song

From: Rongjun Ying <Rongjun.Ying@csr.com>

This patch adds I2S DAI driver for SiRFprima2 and SiRFatlas6. The hardware supports:
I2S bus specification compliant (Released by Philips Semiconductors in June, 1996)
Supports I2S master and I2S slave mode
Provides MCLK to I2S CODEC in I2S master mode
Supports 16-bit resolution playback
Supports 16-bit resolution record
Supports 2 channel record
Supports 2 channel and 6 channel mode playback
Frame length, left channel length and right channel length are all programmable
Supports two DMA channels for TXFIFO and RXFIFO
Supports floating mode (x_ac97_dout can be configured as input)

headfile sound/soc/sirf/sirf-audio.h will be shard by all I2S and soc-inner
DAIs.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 -v2:
 runtime pm enabled;
 fix wrong device_reset path in v1;
 other minor fixes according to Mark's comments

 sound/soc/sirf/Kconfig      |   3 +
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-audio.h | 268 +++++++++++++++++++++++++
 sound/soc/sirf/sirf-i2s.c   | 464 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 737 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-audio.h
 create mode 100644 sound/soc/sirf/sirf-i2s.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 1637089..5064cfc 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -2,3 +2,6 @@ config SND_SIRF_SOC
 	tristate "Platform DMA driver for the SiRF SoC chips"
 	depends on ARCH_SIRF && SND_SOC
 	select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_SIRF_I2S
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index f268b83..9f754fe 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,3 +1,5 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-i2s-objs := sirf-i2s.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
diff --git a/sound/soc/sirf/sirf-audio.h b/sound/soc/sirf/sirf-audio.h
new file mode 100644
index 0000000..b6fdf06
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.h
@@ -0,0 +1,268 @@
+/*
+ * SiRF inner codec controllers define
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_INNER_AUDIO_CTRL_H
+#define _SIRF_INNER_AUDIO_CTRL_H
+
+#define AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK     0x3F
+#define AUDIO_CTRL_TX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_TX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_TX_FIFO_HC_OFFSET    20
+
+#define TX_FIFO_SC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK     0x0F
+#define AUDIO_CTRL_RX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_RX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_RX_FIFO_HC_OFFSET    20
+
+#define RX_FIFO_SC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_MODE_SEL			(0x0000)
+#define AUDIO_CTRL_AC97_CTRL			(0x0004)
+#define AUDIO_CTRL_AC97_CMD			(0x0008)
+#define AUDIO_CTRL_AC97_OP_STATUS		(0x000C)
+#define AUDIO_CTRL_AC97_RD_CODEC_REG		(0x0010)
+#define AUDIO_CTRL_TXSLOT_EN			(0x0014)
+#define AUDIO_CTRL_RXSLOT_EN			(0x0018)
+#define AUDIO_CTRL_AC97_AUX_SLOT_EN		(0x001c)
+#define AUDIO_CTRL_I2S_CTRL			(0x0020)
+#define AUDIO_CTRL_I2S_TX_RX_EN			(0x0024)
+
+#define AUDIO_CTRL_EXT_TXFIFO1_OP		(0x0040)
+#define AUDIO_CTRL_EXT_TXFIFO1_LEV_CHK		(0x0044)
+#define AUDIO_CTRL_EXT_TXFIFO1_STS		(0x0048)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT		(0x004C)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT_MSK		(0x0050)
+
+#define AUDIO_CTRL_EXT_TXFIFO2_OP		(0x0054)
+#define AUDIO_CTRL_EXT_TXFIFO2_LEV_CHK		(0x0058)
+#define AUDIO_CTRL_EXT_TXFIFO2_STS		(0x005C)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT		(0x0060)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT_MSK		(0x0064)
+
+#define AUDIO_CTRL_EXT_TXFIFO3_OP		(0x0068)
+#define AUDIO_CTRL_EXT_TXFIFO3_LEV_CHK		(0x006C)
+#define AUDIO_CTRL_EXT_TXFIFO3_STS		(0x0070)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT		(0x0074)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT_MSK		(0x0078)
+
+#define AUDIO_CTRL_EXT_TXFIFO4_OP		(0x007C)
+#define AUDIO_CTRL_EXT_TXFIFO4_LEV_CHK		(0x0080)
+#define AUDIO_CTRL_EXT_TXFIFO4_STS		(0x0084)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT		(0x0088)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT_MSK		(0x008C)
+
+#define AUDIO_CTRL_EXT_TXFIFO5_OP		(0x0090)
+#define AUDIO_CTRL_EXT_TXFIFO5_LEV_CHK		(0x0094)
+#define AUDIO_CTRL_EXT_TXFIFO5_STS		(0x0098)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT		(0x009C)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT_MSK		(0x00A0)
+
+#define AUDIO_CTRL_EXT_TXFIFO6_OP		(0x00A4)
+#define AUDIO_CTRL_EXT_TXFIFO6_LEV_CHK		(0x00A8)
+#define AUDIO_CTRL_EXT_TXFIFO6_STS		(0x00AC)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT		(0x00B0)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT_MSK		(0x00B4)
+
+#define AUDIO_CTRL_RXFIFO_OP			(0x00B8)
+#define AUDIO_CTRL_RXFIFO_LEV_CHK		(0x00BC)
+#define AUDIO_CTRL_RXFIFO_STS			(0x00C0)
+#define AUDIO_CTRL_RXFIFO_INT			(0x00C4)
+#define AUDIO_CTRL_RXFIFO_INT_MSK		(0x00C8)
+
+#define AUDIO_CTRL_AUXFIFO_OP			(0x00CC)
+#define AUDIO_CTRL_AUXFIFO_LEV_CHK		(0x00D0)
+#define AUDIO_CTRL_AUXFIFO_STS			(0x00D4)
+#define AUDIO_CTRL_AUXFIFO_INT			(0x00D8)
+#define AUDIO_CTRL_AUXFIFO_INT_MSK		(0x00DC)
+
+#define AUDIO_IC_CODEC_PWR			(0x00E0)
+#define AUDIO_IC_CODEC_CTRL0			(0x00E4)
+#define AUDIO_IC_CODEC_CTRL1			(0x00E8)
+#define AUDIO_IC_CODEC_CTRL2			(0x00EC)
+#define AUDIO_IC_CODEC_CTRL3			(0x00F0)
+
+#define AUDIO_CTRL_IC_CODEC_TX_CTRL		(0x00F4)
+#define AUDIO_CTRL_IC_CODEC_RX_CTRL		(0x00F8)
+
+#define AUDIO_CTRL_IC_TXFIFO_OP			(0x00FC)
+#define AUDIO_CTRL_IC_TXFIFO_LEV_CHK		(0x0100)
+#define AUDIO_CTRL_IC_TXFIFO_STS		(0x0104)
+#define AUDIO_CTRL_IC_TXFIFO_INT		(0x0108)
+#define AUDIO_CTRL_IC_TXFIFO_INT_MSK		(0x010C)
+
+#define AUDIO_CTRL_IC_RXFIFO_OP			(0x0110)
+#define AUDIO_CTRL_IC_RXFIFO_LEV_CHK		(0x0114)
+#define AUDIO_CTRL_IC_RXFIFO_STS		(0x0118)
+#define AUDIO_CTRL_IC_RXFIFO_INT		(0x011C)
+#define AUDIO_CTRL_IC_RXFIFO_INT_MSK		(0x0120)
+
+#define I2S_MODE				(1<<0)
+#define AC97_TX_SLOT3_WIDTH_MASK		(3<<1)
+#define AC97_TX_SLOT4_WIDTH_MASK		(3<<3)
+#define AC97_TX_SLOT6_WIDTH_MASK		(3<<5)
+#define AC97_TX_SLOT7_WIDTH_MASK		(3<<7)
+#define AC97_TX_SLOT8_WIDTH_MASK		(3<<9)
+#define AC97_TX_SLOT9_WIDTH_MASK		(3<<11)
+#define AC97_FIFO_SYNC_MASK			(3<<13)
+#define AC97_FIFO_SYNC_ALL			(1<<13)
+
+#define SYNC_START				(1<<0)
+#define AC97_START				(1<<1)
+#define WARM_WAKEUP				(1<<2)
+
+#define AC97_CMD_TYPE_MASK			(1<<7)
+#define AC97_CMD_ADDR_MASK			(0x7F)
+#define AC97_CMD_TYPE_WRITE			(0<<7)
+#define AC97_CMD_TYPE_READ			(1<<7)
+
+#define CMD_ISSUE_BIT				(1<<0)
+#define	RD_CMD_FINISH_BIT			(1<<1)
+#define	CODEC_READY_BIT				(1<<2)
+
+#define	AC97_RDBACK_ADDR_BITS			(0x7F)
+#define	AC97_RDBACK_DATA_BITS			(0xFF<<16)
+
+#define AC97_TX_SLOT3_EN			(1<<0)
+#define AC97_TX_SLOT4_EN			(1<<1)
+#define AC97_TX_SLOT6_EN			(1<<2)
+#define AC97_TX_SLOT7_EN			(1<<3)
+#define AC97_TX_SLOT8_EN			(1<<4)
+#define AC97_TX_SLOT9_EN			(1<<5)
+
+#define AC97_RX_SLOT3_EN			(1<<0)
+#define AC97_RX_SLOT4_EN			(1<<1)
+#define AC97_RX_SLOT5_EN			(1<<2)
+#define AC97_RX_SLOT6_EN			(1<<3)
+#define AC97_RX_SLOT7_EN			(1<<4)
+#define AC97_RX_SLOT8_EN			(1<<5)
+#define AC97_RX_SLOT9_EN			(1<<6)
+#define AC97_RX_SLOT10_EN			(1<<7)
+#define AC97_RX_SLOT11_EN			(1<<8)
+#define AC97_RX_SLOT12_EN			(1<<9)
+
+#define AC97_AUX_SLOT3_EN			(1<<0)
+#define AC97_AUX_SLOT4_EN			(1<<1)
+#define AC97_AUX_SLOT5_EN			(1<<2)
+#define AC97_AUX_SLOT6_EN			(1<<3)
+#define AC97_AUX_SLOT7_EN			(1<<4)
+#define AC97_AUX_SLOT8_EN			(1<<5)
+#define AC97_AUX_SLOT9_EN			(1<<6)
+#define AC97_AUX_SLOT10_EN			(1<<7)
+#define AC97_AUX_SLOT11_EN			(1<<8)
+#define AC97_AUX_SLOT12_EN			(1<<9)
+
+#define I2S_LOOP_BACK				(1<<3)
+#define	I2S_MCLK_DIV_SHIFT			15
+#define I2S_MCLK_DIV_MASK			(0x1FF<<I2S_MCLK_DIV_SHIFT)
+#define I2S_BITCLK_DIV_SHIFT			24
+#define I2S_BITCLK_DIV_MASK			(0xFF<<I2S_BITCLK_DIV_SHIFT)
+
+#define I2S_MCLK_EN				(1<<2)
+#define I2S_REF_CLK_SEL_EXT			(1<<3)
+#define I2S_DOUT_OE				(1<<4)
+#define i2s_R2X_LP_TO_TX0			(1<<30)
+#define i2s_R2X_LP_TO_TX1			(2<<30)
+#define i2s_R2X_LP_TO_TX2			(3<<30)
+
+#define AUDIO_FIFO_START		(1 << 0)
+#define AUDIO_FIFO_RESET		(1 << 1)
+
+#define AUDIO_FIFO_FULL			(1 << 0)
+#define AUDIO_FIFO_EMPTY		(1 << 1)
+#define AUDIO_FIFO_OFLOW		(1 << 2)
+#define AUDIO_FIFO_UFLOW		(1 << 3)
+
+#define I2S_RX_ENABLE			(1 << 0)
+#define I2S_TX_ENABLE			(1 << 1)
+
+/* Codec I2S Control Register defines */
+#define I2S_SLAVE_MODE			(1 << 0)
+#define I2S_SIX_CHANNELS		(1 << 1)
+#define I2S_L_CHAN_LEN_MASK		(0x1f << 4)
+#define I2S_FRAME_LEN_MASK		(0x3f << 9)
+
+#define AC97_WRITE_FRAME_VALID    0X10
+#define AC97_WRITE_SLOT1_VALID    0X08
+#define AC97_WRITE_SLOT2_VALID    0X04
+#define AC97_WRITE_SLOT3_VALID    0X02
+#define AC97_WRITE_SLOT4_VALID    0X01
+
+#define IC_TX_ENABLE		(0x03)
+#define IC_RX_ENABLE		(0x03)
+
+#define MICBIASEN		(1 << 3)
+
+#define IC_RDACEN		(1 << 0)
+#define IC_LDACEN		(1 << 1)
+#define IC_HSREN		(1 << 2)
+#define IC_HSLEN		(1 << 3)
+#define IC_SPEN			(1 << 4)
+#define IC_CPEN			(1 << 5)
+
+#define IC_HPRSELR		(1 << 6)
+#define IC_HPLSELR		(1 << 7)
+#define IC_HPRSELL		(1 << 8)
+#define IC_HPLSELL		(1 << 9)
+#define IC_SPSELR		(1 << 10)
+#define IC_SPSELL		(1 << 11)
+
+#define IC_MONOR		(1 << 12)
+#define IC_MONOL		(1 << 13)
+
+#define IC_RXOSRSEL		(1 << 28)
+#define IC_CPFREQ		(1 << 29)
+#define IC_HSINVEN		(1 << 30)
+
+#define IC_MICINREN		(1 << 0)
+#define IC_MICINLEN		(1 << 1)
+#define IC_MICIN1SEL		(1 << 2)
+#define IC_MICIN2SEL		(1 << 3)
+#define IC_MICDIFSEL		(1 << 4)
+#define	IC_LINEIN1SEL		(1 << 5)
+#define	IC_LINEIN2SEL		(1 << 6)
+#define	IC_RADCEN		(1 << 7)
+#define	IC_LADCEN		(1 << 8)
+#define	IC_ALM			(1 << 9)
+
+#define IC_DIGMICEN             (1 << 22)
+#define IC_DIGMICFREQ           (1 << 23)
+#define IC_ADC14B_12            (1 << 24)
+#define IC_FIRDAC_HSL_EN        (1 << 25)
+#define IC_FIRDAC_HSR_EN        (1 << 26)
+#define IC_FIRDAC_LOUT_EN       (1 << 27)
+#define IC_POR                  (1 << 28)
+#define IC_CODEC_CLK_EN         (1 << 29)
+#define IC_HP_3DB_BOOST         (1 << 30)
+
+#define IC_ADC_LEFT_GAIN_SHIFT	16
+#define IC_ADC_RIGHT_GAIN_SHIFT 10
+#define IC_ADC_GAIN_MASK	0x3F
+#define IC_MIC_MAX_GAIN		0x39
+
+#define IC_RXPGAR_MASK		0x3F
+#define IC_RXPGAR_SHIFT		14
+#define IC_RXPGAL_MASK		0x3F
+#define IC_RXPGAL_SHIFT		21
+#define IC_RXPGAR		0x7B
+#define IC_RXPGAL		0x7B
+
+#define SIRF_I2S_EXT_CLK	0x0
+#define SIRF_I2S_PWM_CLK	0x1
+#endif /*__SIRF_INNER_AUDIO_CTRL_H*/
diff --git a/sound/soc/sirf/sirf-i2s.c b/sound/soc/sirf/sirf-i2s.c
new file mode 100644
index 0000000..ae4102f
--- /dev/null
+++ b/sound/soc/sirf/sirf-i2s.c
@@ -0,0 +1,464 @@
+/*
+ * SiRF I2S driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "sirf-audio.h"
+
+struct sirf_i2s {
+	void __iomem		*base;
+	struct clk		*clk;
+	u32			i2s_ctrl;
+	u32			i2s_ctrl_tx_rx_en;
+	spinlock_t		lock;
+	struct platform_device	*sirf_pcm_pdev;
+};
+
+static struct snd_dmaengine_dai_dma_data dma_data[2];
+
+static int sirf_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+	dai->playback_dma_data = &dma_data[0];
+	dai->capture_dma_data = &dma_data[1];
+	return 0;
+}
+
+static int sirf_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pm_runtime_get_sync(dai->dev);
+	return 0;
+}
+
+static void sirf_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pm_runtime_put(dai->dev);
+}
+
+static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_TX_ENABLE | I2S_DOUT_OE,
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+		} else {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_RX_ENABLE,
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_TX_ENABLE | I2S_MCLK_EN),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+		} else {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_RX_ENABLE | I2S_MCLK_EN),
+				si2s->base+AUDIO_CTRL_I2S_TX_RX_EN);
+
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_RXFIFO_OP);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sirf_i2s_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	u32 left_len, frame_len;
+	int channels = params_channels(params);
+
+	/*
+	 * SiRFSoC I2S controller only support 2 and 6 channells output.
+	 * I2S_SIX_CHANNELS bit clear: select 2 channels mode.
+	 * I2S_SIX_CHANNELS bit set: select 6 channels mode.
+	 */
+	switch (channels) {
+	case 2:
+		i2s_ctrl &= ~I2S_SIX_CHANNELS;
+		break;
+	case 6:
+		i2s_ctrl |= I2S_SIX_CHANNELS;
+		break;
+	default:
+		dev_err(dai->dev, "%d channels unsupported\n", channels);
+		return -EINVAL;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		left_len = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		left_len = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		left_len = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		left_len = 32;
+		break;
+	default:
+		dev_err(dai->dev, "Format unsupported\n");
+		return -EINVAL;
+	}
+
+	frame_len = left_len * 2;
+	i2s_ctrl &= ~(I2S_L_CHAN_LEN_MASK | I2S_FRAME_LEN_MASK);
+	/* Fill the actual len - 1 */
+	i2s_ctrl |= ((frame_len - 1) << 9) | ((left_len - 1) << 4)
+		| (0 << 15) | (3 << 24);
+	writel(i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+	return 0;
+}
+
+static int sirf_i2s_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 i2s_ctrl, i2s_tx_rx_ctrl;
+
+	i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	i2s_tx_rx_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		i2s_ctrl |= I2S_SLAVE_MODE;
+		i2s_tx_rx_ctrl &= ~I2S_MCLK_EN;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s_ctrl &= ~I2S_SLAVE_MODE;
+		i2s_tx_rx_ctrl |= I2S_MCLK_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+	writel(i2s_tx_rx_ctrl, si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		writel(readl(si2s->base + AUDIO_CTRL_MODE_SEL)
+			| I2S_MODE,
+			si2s->base + AUDIO_CTRL_MODE_SEL);
+		break;
+	default:
+		dev_err(dai->dev, "Only I2S format supported\n");
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		dev_err(dai->dev, " Only normal bit clock, normal frame clock supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sirf_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 val;
+	u32 bclk_div_coefficient;
+
+	if (div < 2 || div % 2) {
+		dev_err(dai->dev, "BITCLK divider must greater than 1,"
+			"And must is a multiple of 2\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Calculate the divider coefficient of I2S reference
+	 * clock frequency divider.
+	 */
+	bclk_div_coefficient = div / 2 - 1;
+
+	if (bclk_div_coefficient >= (1 << 9)) {
+		dev_err(dai->dev, "The BITCLK divider(%d) must less than "
+			"%d.\n", div, (1 << 9) * 2);
+		return -EINVAL;
+	}
+
+	val = readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+	switch (div_id) {
+	case SIRF_I2S_EXT_CLK:
+		val |= I2S_REF_CLK_SEL_EXT;
+		break;
+	case SIRF_I2S_PWM_CLK:
+		val &= ~I2S_REF_CLK_SEL_EXT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(val, si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+	val = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	val |= (bclk_div_coefficient << 24);
+	/*
+	 * MCLK coefficient must set to 0, means
+	 * divide-by-two from reference clock.
+	 */
+	val &= ~(((1 << 10) - 1) << 15);
+	writel(val, si2s->base + AUDIO_CTRL_I2S_CTRL);
+
+	return 0;
+}
+
+struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
+	.startup	= sirf_i2s_startup,
+	.shutdown	= sirf_i2s_shutdown,
+	.trigger	= sirf_i2s_trigger,
+	.hw_params	= sirf_i2s_hw_params,
+	.set_fmt	= sirf_i2s_set_dai_fmt,
+	.set_clkdiv	= sirf_i2s_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver sirf_i2s_dai = {
+	.probe = sirf_i2s_dai_probe,
+	.name		= "sirf-i2s",
+	.id			= 0,
+	.playback = {
+		.stream_name = "SiRF I2S Playback",
+		.channels_min = 2,
+		.channels_max = 6,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.stream_name = "SiRF I2S Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &sirfsoc_i2s_dai_ops,
+};
+#ifdef CONFIG_PM_RUNTIME
+static int sirf_i2s_runtime_suspend(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+	clk_disable_unprepare(si2s->clk);
+
+	return 0;
+}
+
+static int sirf_i2s_runtime_resume(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+	clk_prepare_enable(si2s->clk);
+	device_reset(dev);
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_i2s_suspend(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+
+	if (!pm_runtime_status_suspended(dev)) {
+		si2s->i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+		si2s->i2s_ctrl_tx_rx_en =
+			readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+		sirf_i2s_runtime_suspend(dev);
+	}
+	return 0;
+}
+
+static int sirf_i2s_resume(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+	if (!pm_runtime_status_suspended(dev)) {
+		sirf_i2s_runtime_resume(dev);
+		writel(readl(si2s->base + AUDIO_CTRL_MODE_SEL)
+				| I2S_MODE,
+				si2s->base + AUDIO_CTRL_MODE_SEL);
+		writel(si2s->i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+		/*Restore MCLK enable and reference clock select bits.*/
+		writel(si2s->i2s_ctrl_tx_rx_en &
+			(I2S_MCLK_EN | I2S_REF_CLK_SEL_EXT),
+			si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+		writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_INT_MSK);
+		writel(0, si2s->base + AUDIO_CTRL_RXFIFO_INT_MSK);
+	}
+
+	return 0;
+}
+#endif
+
+static const struct snd_soc_component_driver sirf_i2s_component = {
+	.name       = "sirf-i2s",
+};
+
+static int sirf_i2s_probe(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s;
+	u32 rx_dma_ch, tx_dma_ch;
+	int ret;
+	struct resource mem_res;
+
+	si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
+			GFP_KERNEL);
+	if (!si2s)
+		return -ENOMEM;
+
+	si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
+			0, NULL, 0);
+	if (IS_ERR(si2s->sirf_pcm_pdev))
+		return PTR_ERR(si2s->sirf_pcm_pdev);
+
+	platform_set_drvdata(pdev, si2s);
+
+	spin_lock_init(&si2s->lock);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
+		return ret;
+	}
+
+	dma_data[0].filter_data = (void *)tx_dma_ch;
+	dma_data[1].filter_data = (void *)rx_dma_ch;
+
+	ret = of_address_to_resource(pdev->dev.of_node, 0, &mem_res);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to get i2s memory resource.\n");
+		return ret;
+	}
+	si2s->base = devm_ioremap(&pdev->dev, mem_res.start,
+		resource_size(&mem_res));
+	if (!si2s->base)
+		return -ENOMEM;
+
+	si2s->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		ret = PTR_ERR(si2s->clk);
+		goto err;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_i2s_component,
+			&sirf_i2s_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	return 0;
+
+err:
+	return ret;
+}
+
+static int sirf_i2s_remove(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_component(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	platform_device_unregister(si2s->sirf_pcm_pdev);
+	return 0;
+}
+
+static const struct of_device_id sirf_i2s_of_match[] = {
+	{ .compatible = "sirf,prima2-i2s", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_i2s_of_match);
+
+static const struct dev_pm_ops sirf_i2s_pm_ops = {
+	SET_RUNTIME_PM_OPS(sirf_i2s_runtime_suspend, sirf_i2s_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(sirf_i2s_suspend, sirf_i2s_resume)
+};
+
+static struct platform_driver sirf_i2s_driver = {
+	.driver = {
+		.name = "sirf-i2s",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_i2s_of_match,
+		.pm = &sirf_i2s_pm_ops,
+	},
+	.probe = sirf_i2s_probe,
+	.remove = sirf_i2s_remove,
+};
+
+module_platform_driver(sirf_i2s_driver);
+
+MODULE_DESCRIPTION("SiRF SoC I2S driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-27 22:37 ` Barry Song
  0 siblings, 0 replies; 14+ messages in thread
From: Barry Song @ 2013-10-27 22:37 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.Ying@csr.com>

This patch adds I2S DAI driver for SiRFprima2 and SiRFatlas6. The hardware supports:
I2S bus specification compliant (Released by Philips Semiconductors in June, 1996)
Supports I2S master and I2S slave mode
Provides MCLK to I2S CODEC in I2S master mode
Supports 16-bit resolution playback
Supports 16-bit resolution record
Supports 2 channel record
Supports 2 channel and 6 channel mode playback
Frame length, left channel length and right channel length are all programmable
Supports two DMA channels for TXFIFO and RXFIFO
Supports floating mode (x_ac97_dout can be configured as input)

headfile sound/soc/sirf/sirf-audio.h will be shard by all I2S and soc-inner
DAIs.

Signed-off-by: Rongjun Ying <Rongjun.Ying@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 -v2:
 runtime pm enabled;
 fix wrong device_reset path in v1;
 other minor fixes according to Mark's comments

 sound/soc/sirf/Kconfig      |   3 +
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-audio.h | 268 +++++++++++++++++++++++++
 sound/soc/sirf/sirf-i2s.c   | 464 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 737 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-audio.h
 create mode 100644 sound/soc/sirf/sirf-i2s.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 1637089..5064cfc 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -2,3 +2,6 @@ config SND_SIRF_SOC
 	tristate "Platform DMA driver for the SiRF SoC chips"
 	depends on ARCH_SIRF && SND_SOC
 	select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_SIRF_I2S
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index f268b83..9f754fe 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,3 +1,5 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-i2s-objs := sirf-i2s.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
diff --git a/sound/soc/sirf/sirf-audio.h b/sound/soc/sirf/sirf-audio.h
new file mode 100644
index 0000000..b6fdf06
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.h
@@ -0,0 +1,268 @@
+/*
+ * SiRF inner codec controllers define
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_INNER_AUDIO_CTRL_H
+#define _SIRF_INNER_AUDIO_CTRL_H
+
+#define AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK     0x3F
+#define AUDIO_CTRL_TX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_TX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_TX_FIFO_HC_OFFSET    20
+
+#define TX_FIFO_SC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)           (((x) & AUDIO_CTRL_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_TX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK     0x0F
+#define AUDIO_CTRL_RX_FIFO_SC_OFFSET    0
+#define AUDIO_CTRL_RX_FIFO_LC_OFFSET    10
+#define AUDIO_CTRL_RX_FIFO_HC_OFFSET    20
+
+#define RX_FIFO_SC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)           (((x) & AUDIO_CTRL_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_CTRL_RX_FIFO_HC_OFFSET)
+
+#define AUDIO_CTRL_MODE_SEL			(0x0000)
+#define AUDIO_CTRL_AC97_CTRL			(0x0004)
+#define AUDIO_CTRL_AC97_CMD			(0x0008)
+#define AUDIO_CTRL_AC97_OP_STATUS		(0x000C)
+#define AUDIO_CTRL_AC97_RD_CODEC_REG		(0x0010)
+#define AUDIO_CTRL_TXSLOT_EN			(0x0014)
+#define AUDIO_CTRL_RXSLOT_EN			(0x0018)
+#define AUDIO_CTRL_AC97_AUX_SLOT_EN		(0x001c)
+#define AUDIO_CTRL_I2S_CTRL			(0x0020)
+#define AUDIO_CTRL_I2S_TX_RX_EN			(0x0024)
+
+#define AUDIO_CTRL_EXT_TXFIFO1_OP		(0x0040)
+#define AUDIO_CTRL_EXT_TXFIFO1_LEV_CHK		(0x0044)
+#define AUDIO_CTRL_EXT_TXFIFO1_STS		(0x0048)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT		(0x004C)
+#define AUDIO_CTRL_EXT_TXFIFO1_INT_MSK		(0x0050)
+
+#define AUDIO_CTRL_EXT_TXFIFO2_OP		(0x0054)
+#define AUDIO_CTRL_EXT_TXFIFO2_LEV_CHK		(0x0058)
+#define AUDIO_CTRL_EXT_TXFIFO2_STS		(0x005C)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT		(0x0060)
+#define AUDIO_CTRL_EXT_TXFIFO2_INT_MSK		(0x0064)
+
+#define AUDIO_CTRL_EXT_TXFIFO3_OP		(0x0068)
+#define AUDIO_CTRL_EXT_TXFIFO3_LEV_CHK		(0x006C)
+#define AUDIO_CTRL_EXT_TXFIFO3_STS		(0x0070)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT		(0x0074)
+#define AUDIO_CTRL_EXT_TXFIFO3_INT_MSK		(0x0078)
+
+#define AUDIO_CTRL_EXT_TXFIFO4_OP		(0x007C)
+#define AUDIO_CTRL_EXT_TXFIFO4_LEV_CHK		(0x0080)
+#define AUDIO_CTRL_EXT_TXFIFO4_STS		(0x0084)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT		(0x0088)
+#define AUDIO_CTRL_EXT_TXFIFO4_INT_MSK		(0x008C)
+
+#define AUDIO_CTRL_EXT_TXFIFO5_OP		(0x0090)
+#define AUDIO_CTRL_EXT_TXFIFO5_LEV_CHK		(0x0094)
+#define AUDIO_CTRL_EXT_TXFIFO5_STS		(0x0098)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT		(0x009C)
+#define AUDIO_CTRL_EXT_TXFIFO5_INT_MSK		(0x00A0)
+
+#define AUDIO_CTRL_EXT_TXFIFO6_OP		(0x00A4)
+#define AUDIO_CTRL_EXT_TXFIFO6_LEV_CHK		(0x00A8)
+#define AUDIO_CTRL_EXT_TXFIFO6_STS		(0x00AC)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT		(0x00B0)
+#define AUDIO_CTRL_EXT_TXFIFO6_INT_MSK		(0x00B4)
+
+#define AUDIO_CTRL_RXFIFO_OP			(0x00B8)
+#define AUDIO_CTRL_RXFIFO_LEV_CHK		(0x00BC)
+#define AUDIO_CTRL_RXFIFO_STS			(0x00C0)
+#define AUDIO_CTRL_RXFIFO_INT			(0x00C4)
+#define AUDIO_CTRL_RXFIFO_INT_MSK		(0x00C8)
+
+#define AUDIO_CTRL_AUXFIFO_OP			(0x00CC)
+#define AUDIO_CTRL_AUXFIFO_LEV_CHK		(0x00D0)
+#define AUDIO_CTRL_AUXFIFO_STS			(0x00D4)
+#define AUDIO_CTRL_AUXFIFO_INT			(0x00D8)
+#define AUDIO_CTRL_AUXFIFO_INT_MSK		(0x00DC)
+
+#define AUDIO_IC_CODEC_PWR			(0x00E0)
+#define AUDIO_IC_CODEC_CTRL0			(0x00E4)
+#define AUDIO_IC_CODEC_CTRL1			(0x00E8)
+#define AUDIO_IC_CODEC_CTRL2			(0x00EC)
+#define AUDIO_IC_CODEC_CTRL3			(0x00F0)
+
+#define AUDIO_CTRL_IC_CODEC_TX_CTRL		(0x00F4)
+#define AUDIO_CTRL_IC_CODEC_RX_CTRL		(0x00F8)
+
+#define AUDIO_CTRL_IC_TXFIFO_OP			(0x00FC)
+#define AUDIO_CTRL_IC_TXFIFO_LEV_CHK		(0x0100)
+#define AUDIO_CTRL_IC_TXFIFO_STS		(0x0104)
+#define AUDIO_CTRL_IC_TXFIFO_INT		(0x0108)
+#define AUDIO_CTRL_IC_TXFIFO_INT_MSK		(0x010C)
+
+#define AUDIO_CTRL_IC_RXFIFO_OP			(0x0110)
+#define AUDIO_CTRL_IC_RXFIFO_LEV_CHK		(0x0114)
+#define AUDIO_CTRL_IC_RXFIFO_STS		(0x0118)
+#define AUDIO_CTRL_IC_RXFIFO_INT		(0x011C)
+#define AUDIO_CTRL_IC_RXFIFO_INT_MSK		(0x0120)
+
+#define I2S_MODE				(1<<0)
+#define AC97_TX_SLOT3_WIDTH_MASK		(3<<1)
+#define AC97_TX_SLOT4_WIDTH_MASK		(3<<3)
+#define AC97_TX_SLOT6_WIDTH_MASK		(3<<5)
+#define AC97_TX_SLOT7_WIDTH_MASK		(3<<7)
+#define AC97_TX_SLOT8_WIDTH_MASK		(3<<9)
+#define AC97_TX_SLOT9_WIDTH_MASK		(3<<11)
+#define AC97_FIFO_SYNC_MASK			(3<<13)
+#define AC97_FIFO_SYNC_ALL			(1<<13)
+
+#define SYNC_START				(1<<0)
+#define AC97_START				(1<<1)
+#define WARM_WAKEUP				(1<<2)
+
+#define AC97_CMD_TYPE_MASK			(1<<7)
+#define AC97_CMD_ADDR_MASK			(0x7F)
+#define AC97_CMD_TYPE_WRITE			(0<<7)
+#define AC97_CMD_TYPE_READ			(1<<7)
+
+#define CMD_ISSUE_BIT				(1<<0)
+#define	RD_CMD_FINISH_BIT			(1<<1)
+#define	CODEC_READY_BIT				(1<<2)
+
+#define	AC97_RDBACK_ADDR_BITS			(0x7F)
+#define	AC97_RDBACK_DATA_BITS			(0xFF<<16)
+
+#define AC97_TX_SLOT3_EN			(1<<0)
+#define AC97_TX_SLOT4_EN			(1<<1)
+#define AC97_TX_SLOT6_EN			(1<<2)
+#define AC97_TX_SLOT7_EN			(1<<3)
+#define AC97_TX_SLOT8_EN			(1<<4)
+#define AC97_TX_SLOT9_EN			(1<<5)
+
+#define AC97_RX_SLOT3_EN			(1<<0)
+#define AC97_RX_SLOT4_EN			(1<<1)
+#define AC97_RX_SLOT5_EN			(1<<2)
+#define AC97_RX_SLOT6_EN			(1<<3)
+#define AC97_RX_SLOT7_EN			(1<<4)
+#define AC97_RX_SLOT8_EN			(1<<5)
+#define AC97_RX_SLOT9_EN			(1<<6)
+#define AC97_RX_SLOT10_EN			(1<<7)
+#define AC97_RX_SLOT11_EN			(1<<8)
+#define AC97_RX_SLOT12_EN			(1<<9)
+
+#define AC97_AUX_SLOT3_EN			(1<<0)
+#define AC97_AUX_SLOT4_EN			(1<<1)
+#define AC97_AUX_SLOT5_EN			(1<<2)
+#define AC97_AUX_SLOT6_EN			(1<<3)
+#define AC97_AUX_SLOT7_EN			(1<<4)
+#define AC97_AUX_SLOT8_EN			(1<<5)
+#define AC97_AUX_SLOT9_EN			(1<<6)
+#define AC97_AUX_SLOT10_EN			(1<<7)
+#define AC97_AUX_SLOT11_EN			(1<<8)
+#define AC97_AUX_SLOT12_EN			(1<<9)
+
+#define I2S_LOOP_BACK				(1<<3)
+#define	I2S_MCLK_DIV_SHIFT			15
+#define I2S_MCLK_DIV_MASK			(0x1FF<<I2S_MCLK_DIV_SHIFT)
+#define I2S_BITCLK_DIV_SHIFT			24
+#define I2S_BITCLK_DIV_MASK			(0xFF<<I2S_BITCLK_DIV_SHIFT)
+
+#define I2S_MCLK_EN				(1<<2)
+#define I2S_REF_CLK_SEL_EXT			(1<<3)
+#define I2S_DOUT_OE				(1<<4)
+#define i2s_R2X_LP_TO_TX0			(1<<30)
+#define i2s_R2X_LP_TO_TX1			(2<<30)
+#define i2s_R2X_LP_TO_TX2			(3<<30)
+
+#define AUDIO_FIFO_START		(1 << 0)
+#define AUDIO_FIFO_RESET		(1 << 1)
+
+#define AUDIO_FIFO_FULL			(1 << 0)
+#define AUDIO_FIFO_EMPTY		(1 << 1)
+#define AUDIO_FIFO_OFLOW		(1 << 2)
+#define AUDIO_FIFO_UFLOW		(1 << 3)
+
+#define I2S_RX_ENABLE			(1 << 0)
+#define I2S_TX_ENABLE			(1 << 1)
+
+/* Codec I2S Control Register defines */
+#define I2S_SLAVE_MODE			(1 << 0)
+#define I2S_SIX_CHANNELS		(1 << 1)
+#define I2S_L_CHAN_LEN_MASK		(0x1f << 4)
+#define I2S_FRAME_LEN_MASK		(0x3f << 9)
+
+#define AC97_WRITE_FRAME_VALID    0X10
+#define AC97_WRITE_SLOT1_VALID    0X08
+#define AC97_WRITE_SLOT2_VALID    0X04
+#define AC97_WRITE_SLOT3_VALID    0X02
+#define AC97_WRITE_SLOT4_VALID    0X01
+
+#define IC_TX_ENABLE		(0x03)
+#define IC_RX_ENABLE		(0x03)
+
+#define MICBIASEN		(1 << 3)
+
+#define IC_RDACEN		(1 << 0)
+#define IC_LDACEN		(1 << 1)
+#define IC_HSREN		(1 << 2)
+#define IC_HSLEN		(1 << 3)
+#define IC_SPEN			(1 << 4)
+#define IC_CPEN			(1 << 5)
+
+#define IC_HPRSELR		(1 << 6)
+#define IC_HPLSELR		(1 << 7)
+#define IC_HPRSELL		(1 << 8)
+#define IC_HPLSELL		(1 << 9)
+#define IC_SPSELR		(1 << 10)
+#define IC_SPSELL		(1 << 11)
+
+#define IC_MONOR		(1 << 12)
+#define IC_MONOL		(1 << 13)
+
+#define IC_RXOSRSEL		(1 << 28)
+#define IC_CPFREQ		(1 << 29)
+#define IC_HSINVEN		(1 << 30)
+
+#define IC_MICINREN		(1 << 0)
+#define IC_MICINLEN		(1 << 1)
+#define IC_MICIN1SEL		(1 << 2)
+#define IC_MICIN2SEL		(1 << 3)
+#define IC_MICDIFSEL		(1 << 4)
+#define	IC_LINEIN1SEL		(1 << 5)
+#define	IC_LINEIN2SEL		(1 << 6)
+#define	IC_RADCEN		(1 << 7)
+#define	IC_LADCEN		(1 << 8)
+#define	IC_ALM			(1 << 9)
+
+#define IC_DIGMICEN             (1 << 22)
+#define IC_DIGMICFREQ           (1 << 23)
+#define IC_ADC14B_12            (1 << 24)
+#define IC_FIRDAC_HSL_EN        (1 << 25)
+#define IC_FIRDAC_HSR_EN        (1 << 26)
+#define IC_FIRDAC_LOUT_EN       (1 << 27)
+#define IC_POR                  (1 << 28)
+#define IC_CODEC_CLK_EN         (1 << 29)
+#define IC_HP_3DB_BOOST         (1 << 30)
+
+#define IC_ADC_LEFT_GAIN_SHIFT	16
+#define IC_ADC_RIGHT_GAIN_SHIFT 10
+#define IC_ADC_GAIN_MASK	0x3F
+#define IC_MIC_MAX_GAIN		0x39
+
+#define IC_RXPGAR_MASK		0x3F
+#define IC_RXPGAR_SHIFT		14
+#define IC_RXPGAL_MASK		0x3F
+#define IC_RXPGAL_SHIFT		21
+#define IC_RXPGAR		0x7B
+#define IC_RXPGAL		0x7B
+
+#define SIRF_I2S_EXT_CLK	0x0
+#define SIRF_I2S_PWM_CLK	0x1
+#endif /*__SIRF_INNER_AUDIO_CTRL_H*/
diff --git a/sound/soc/sirf/sirf-i2s.c b/sound/soc/sirf/sirf-i2s.c
new file mode 100644
index 0000000..ae4102f
--- /dev/null
+++ b/sound/soc/sirf/sirf-i2s.c
@@ -0,0 +1,464 @@
+/*
+ * SiRF I2S driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "sirf-audio.h"
+
+struct sirf_i2s {
+	void __iomem		*base;
+	struct clk		*clk;
+	u32			i2s_ctrl;
+	u32			i2s_ctrl_tx_rx_en;
+	spinlock_t		lock;
+	struct platform_device	*sirf_pcm_pdev;
+};
+
+static struct snd_dmaengine_dai_dma_data dma_data[2];
+
+static int sirf_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+	dai->playback_dma_data = &dma_data[0];
+	dai->capture_dma_data = &dma_data[1];
+	return 0;
+}
+
+static int sirf_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pm_runtime_get_sync(dai->dev);
+	return 0;
+}
+
+static void sirf_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pm_runtime_put(dai->dev);
+}
+
+static int sirf_i2s_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_TX_ENABLE | I2S_DOUT_OE,
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+		} else {
+			/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_RESET,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_RX_ENABLE,
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&si2s->lock);
+
+		if (playback) {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_TX_ENABLE | I2S_MCLK_EN),
+				si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+		} else {
+			writel(readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN)
+				& ~(I2S_RX_ENABLE | I2S_MCLK_EN),
+				si2s->base+AUDIO_CTRL_I2S_TX_RX_EN);
+
+			/* First disable the tx/rx, then stop the FIFO */
+			writel(0, si2s->base + AUDIO_CTRL_RXFIFO_OP);
+		}
+
+		spin_unlock(&si2s->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sirf_i2s_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	u32 left_len, frame_len;
+	int channels = params_channels(params);
+
+	/*
+	 * SiRFSoC I2S controller only support 2 and 6 channells output.
+	 * I2S_SIX_CHANNELS bit clear: select 2 channels mode.
+	 * I2S_SIX_CHANNELS bit set: select 6 channels mode.
+	 */
+	switch (channels) {
+	case 2:
+		i2s_ctrl &= ~I2S_SIX_CHANNELS;
+		break;
+	case 6:
+		i2s_ctrl |= I2S_SIX_CHANNELS;
+		break;
+	default:
+		dev_err(dai->dev, "%d channels unsupported\n", channels);
+		return -EINVAL;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		left_len = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		left_len = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		left_len = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		left_len = 32;
+		break;
+	default:
+		dev_err(dai->dev, "Format unsupported\n");
+		return -EINVAL;
+	}
+
+	frame_len = left_len * 2;
+	i2s_ctrl &= ~(I2S_L_CHAN_LEN_MASK | I2S_FRAME_LEN_MASK);
+	/* Fill the actual len - 1 */
+	i2s_ctrl |= ((frame_len - 1) << 9) | ((left_len - 1) << 4)
+		| (0 << 15) | (3 << 24);
+	writel(i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+	return 0;
+}
+
+static int sirf_i2s_set_dai_fmt(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 i2s_ctrl, i2s_tx_rx_ctrl;
+
+	i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	i2s_tx_rx_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		i2s_ctrl |= I2S_SLAVE_MODE;
+		i2s_tx_rx_ctrl &= ~I2S_MCLK_EN;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s_ctrl &= ~I2S_SLAVE_MODE;
+		i2s_tx_rx_ctrl |= I2S_MCLK_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+	writel(i2s_tx_rx_ctrl, si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		writel(readl(si2s->base + AUDIO_CTRL_MODE_SEL)
+			| I2S_MODE,
+			si2s->base + AUDIO_CTRL_MODE_SEL);
+		break;
+	default:
+		dev_err(dai->dev, "Only I2S format supported\n");
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		dev_err(dai->dev, " Only normal bit clock, normal frame clock supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sirf_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 val;
+	u32 bclk_div_coefficient;
+
+	if (div < 2 || div % 2) {
+		dev_err(dai->dev, "BITCLK divider must greater than 1,"
+			"And must is a multiple of 2\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Calculate the divider coefficient of I2S reference
+	 * clock frequency divider.
+	 */
+	bclk_div_coefficient = div / 2 - 1;
+
+	if (bclk_div_coefficient >= (1 << 9)) {
+		dev_err(dai->dev, "The BITCLK divider(%d) must less than "
+			"%d.\n", div, (1 << 9) * 2);
+		return -EINVAL;
+	}
+
+	val = readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+	switch (div_id) {
+	case SIRF_I2S_EXT_CLK:
+		val |= I2S_REF_CLK_SEL_EXT;
+		break;
+	case SIRF_I2S_PWM_CLK:
+		val &= ~I2S_REF_CLK_SEL_EXT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(val, si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+	val = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+	val |= (bclk_div_coefficient << 24);
+	/*
+	 * MCLK coefficient must set to 0, means
+	 * divide-by-two from reference clock.
+	 */
+	val &= ~(((1 << 10) - 1) << 15);
+	writel(val, si2s->base + AUDIO_CTRL_I2S_CTRL);
+
+	return 0;
+}
+
+struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
+	.startup	= sirf_i2s_startup,
+	.shutdown	= sirf_i2s_shutdown,
+	.trigger	= sirf_i2s_trigger,
+	.hw_params	= sirf_i2s_hw_params,
+	.set_fmt	= sirf_i2s_set_dai_fmt,
+	.set_clkdiv	= sirf_i2s_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver sirf_i2s_dai = {
+	.probe = sirf_i2s_dai_probe,
+	.name		= "sirf-i2s",
+	.id			= 0,
+	.playback = {
+		.stream_name = "SiRF I2S Playback",
+		.channels_min = 2,
+		.channels_max = 6,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.stream_name = "SiRF I2S Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &sirfsoc_i2s_dai_ops,
+};
+#ifdef CONFIG_PM_RUNTIME
+static int sirf_i2s_runtime_suspend(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+	clk_disable_unprepare(si2s->clk);
+
+	return 0;
+}
+
+static int sirf_i2s_runtime_resume(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+	clk_prepare_enable(si2s->clk);
+	device_reset(dev);
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_i2s_suspend(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+
+	if (!pm_runtime_status_suspended(dev)) {
+		si2s->i2s_ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+		si2s->i2s_ctrl_tx_rx_en =
+			readl(si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+		sirf_i2s_runtime_suspend(dev);
+	}
+	return 0;
+}
+
+static int sirf_i2s_resume(struct device *dev)
+{
+	struct sirf_i2s *si2s = dev_get_drvdata(dev);
+	if (!pm_runtime_status_suspended(dev)) {
+		sirf_i2s_runtime_resume(dev);
+		writel(readl(si2s->base + AUDIO_CTRL_MODE_SEL)
+				| I2S_MODE,
+				si2s->base + AUDIO_CTRL_MODE_SEL);
+		writel(si2s->i2s_ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+		/*Restore MCLK enable and reference clock select bits.*/
+		writel(si2s->i2s_ctrl_tx_rx_en &
+			(I2S_MCLK_EN | I2S_REF_CLK_SEL_EXT),
+			si2s->base + AUDIO_CTRL_I2S_TX_RX_EN);
+
+		writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_INT_MSK);
+		writel(0, si2s->base + AUDIO_CTRL_RXFIFO_INT_MSK);
+	}
+
+	return 0;
+}
+#endif
+
+static const struct snd_soc_component_driver sirf_i2s_component = {
+	.name       = "sirf-i2s",
+};
+
+static int sirf_i2s_probe(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s;
+	u32 rx_dma_ch, tx_dma_ch;
+	int ret;
+	struct resource mem_res;
+
+	si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
+			GFP_KERNEL);
+	if (!si2s)
+		return -ENOMEM;
+
+	si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
+			0, NULL, 0);
+	if (IS_ERR(si2s->sirf_pcm_pdev))
+		return PTR_ERR(si2s->sirf_pcm_pdev);
+
+	platform_set_drvdata(pdev, si2s);
+
+	spin_lock_init(&si2s->lock);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,i2s-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
+		return ret;
+	}
+
+	dma_data[0].filter_data = (void *)tx_dma_ch;
+	dma_data[1].filter_data = (void *)rx_dma_ch;
+
+	ret = of_address_to_resource(pdev->dev.of_node, 0, &mem_res);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to get i2s memory resource.\n");
+		return ret;
+	}
+	si2s->base = devm_ioremap(&pdev->dev, mem_res.start,
+		resource_size(&mem_res));
+	if (!si2s->base)
+		return -ENOMEM;
+
+	si2s->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		ret = PTR_ERR(si2s->clk);
+		goto err;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_i2s_component,
+			&sirf_i2s_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	return 0;
+
+err:
+	return ret;
+}
+
+static int sirf_i2s_remove(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_component(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	platform_device_unregister(si2s->sirf_pcm_pdev);
+	return 0;
+}
+
+static const struct of_device_id sirf_i2s_of_match[] = {
+	{ .compatible = "sirf,prima2-i2s", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_i2s_of_match);
+
+static const struct dev_pm_ops sirf_i2s_pm_ops = {
+	SET_RUNTIME_PM_OPS(sirf_i2s_runtime_suspend, sirf_i2s_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(sirf_i2s_suspend, sirf_i2s_resume)
+};
+
+static struct platform_driver sirf_i2s_driver = {
+	.driver = {
+		.name = "sirf-i2s",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_i2s_of_match,
+		.pm = &sirf_i2s_pm_ops,
+	},
+	.probe = sirf_i2s_probe,
+	.remove = sirf_i2s_remove,
+};
+
+module_platform_driver(sirf_i2s_driver);
+
+MODULE_DESCRIPTION("SiRF SoC I2S driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-10-27 22:37 ` Barry Song
@ 2013-10-28  3:41   ` Timur Tabi
  -1 siblings, 0 replies; 14+ messages in thread
From: Timur Tabi @ 2013-10-28  3:41 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel mailing list, Liam Girdwood, workgroup.linux,
	Rongjun Ying, broonie, Barry Song, linux-arm-kernel

On Sun, Oct 27, 2013 at 5:37 PM, Barry Song <21cnbao@gmail.com> wrote:
> +       si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
> +                       0, NULL, 0);
> +       if (IS_ERR(si2s->sirf_pcm_pdev))
> +               return PTR_ERR(si2s->sirf_pcm_pdev);
> +
> +       platform_set_drvdata(pdev, si2s);
> +
> +       spin_lock_init(&si2s->lock);
> +
> +       ret = of_property_read_u32(pdev->dev.of_node,
> +                       "sirf,i2s-dma-rx-channel", &rx_dma_ch);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
> +               return ret;
> +       }

Shouldn't you also call platform_device_unregister() before the 'return' here?

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [alsa-devel] [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-28  3:41   ` Timur Tabi
  0 siblings, 0 replies; 14+ messages in thread
From: Timur Tabi @ 2013-10-28  3:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Oct 27, 2013 at 5:37 PM, Barry Song <21cnbao@gmail.com> wrote:
> +       si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
> +                       0, NULL, 0);
> +       if (IS_ERR(si2s->sirf_pcm_pdev))
> +               return PTR_ERR(si2s->sirf_pcm_pdev);
> +
> +       platform_set_drvdata(pdev, si2s);
> +
> +       spin_lock_init(&si2s->lock);
> +
> +       ret = of_property_read_u32(pdev->dev.of_node,
> +                       "sirf,i2s-dma-rx-channel", &rx_dma_ch);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
> +               return ret;
> +       }

Shouldn't you also call platform_device_unregister() before the 'return' here?

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-10-27 22:37 ` Barry Song
@ 2013-10-28  6:48   ` Lars-Peter Clausen
  -1 siblings, 0 replies; 14+ messages in thread
From: Lars-Peter Clausen @ 2013-10-28  6:48 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, workgroup.linux, Rongjun Ying, broonie,
	Barry Song, linux-arm-kernel

On 10/27/2013 11:37 PM, Barry Song wrote:
> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {

static const

Building your driver with sparse (`make C=2 ...`) will tell you these things.

> +	.startup	= sirf_i2s_startup,
> +	.shutdown	= sirf_i2s_shutdown,
> +	.trigger	= sirf_i2s_trigger,
> +	.hw_params	= sirf_i2s_hw_params,
> +	.set_fmt	= sirf_i2s_set_dai_fmt,
> +	.set_clkdiv	= sirf_i2s_set_clkdiv,
> +};
> +
[...]
> +
> +static int sirf_i2s_probe(struct platform_device *pdev)
> +{
> +	struct sirf_i2s *si2s;
> +	u32 rx_dma_ch, tx_dma_ch;
> +	int ret;
> +	struct resource mem_res;
> +
> +	si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
> +			GFP_KERNEL);
> +	if (!si2s)
> +		return -ENOMEM;
> +
> +	si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
> +			0, NULL, 0);
> +	if (IS_ERR(si2s->sirf_pcm_pdev))
> +		return PTR_ERR(si2s->sirf_pcm_pdev);
> +
> +	platform_set_drvdata(pdev, si2s);
> +
> +	spin_lock_init(&si2s->lock);
> +
> +	ret = of_property_read_u32(pdev->dev.of_node,
> +			"sirf,i2s-dma-rx-channel", &rx_dma_ch);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
> +		return ret;
> +	}
> +	ret = of_property_read_u32(pdev->dev.of_node,
> +			"sirf,i2s-dma-tx-channel", &tx_dma_ch);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
> +		return ret;
> +	}
> +
> +	dma_data[0].filter_data = (void *)tx_dma_ch;
> +	dma_data[1].filter_data = (void *)rx_dma_ch;

This should be using the standard DMA OF bindings. This combind with the
recent changes to the generic dmaengine PCM driver, which added support for
querying certain parameters (like max period length) from the dmaengine
driver, will allow you to completely remove your custom PCM code. You'd
basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
here in the I2S driver.

This will require some changes to your dmaengine driver though. You need to
implement the of_xlate callback for the generic DMA bindings and the
device_slave_caps API for being able to query the capabilities from the driver.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [alsa-devel] [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-28  6:48   ` Lars-Peter Clausen
  0 siblings, 0 replies; 14+ messages in thread
From: Lars-Peter Clausen @ 2013-10-28  6:48 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/27/2013 11:37 PM, Barry Song wrote:
> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {

static const

Building your driver with sparse (`make C=2 ...`) will tell you these things.

> +	.startup	= sirf_i2s_startup,
> +	.shutdown	= sirf_i2s_shutdown,
> +	.trigger	= sirf_i2s_trigger,
> +	.hw_params	= sirf_i2s_hw_params,
> +	.set_fmt	= sirf_i2s_set_dai_fmt,
> +	.set_clkdiv	= sirf_i2s_set_clkdiv,
> +};
> +
[...]
> +
> +static int sirf_i2s_probe(struct platform_device *pdev)
> +{
> +	struct sirf_i2s *si2s;
> +	u32 rx_dma_ch, tx_dma_ch;
> +	int ret;
> +	struct resource mem_res;
> +
> +	si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
> +			GFP_KERNEL);
> +	if (!si2s)
> +		return -ENOMEM;
> +
> +	si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
> +			0, NULL, 0);
> +	if (IS_ERR(si2s->sirf_pcm_pdev))
> +		return PTR_ERR(si2s->sirf_pcm_pdev);
> +
> +	platform_set_drvdata(pdev, si2s);
> +
> +	spin_lock_init(&si2s->lock);
> +
> +	ret = of_property_read_u32(pdev->dev.of_node,
> +			"sirf,i2s-dma-rx-channel", &rx_dma_ch);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
> +		return ret;
> +	}
> +	ret = of_property_read_u32(pdev->dev.of_node,
> +			"sirf,i2s-dma-tx-channel", &tx_dma_ch);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
> +		return ret;
> +	}
> +
> +	dma_data[0].filter_data = (void *)tx_dma_ch;
> +	dma_data[1].filter_data = (void *)rx_dma_ch;

This should be using the standard DMA OF bindings. This combind with the
recent changes to the generic dmaengine PCM driver, which added support for
querying certain parameters (like max period length) from the dmaengine
driver, will allow you to completely remove your custom PCM code. You'd
basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
here in the I2S driver.

This will require some changes to your dmaengine driver though. You need to
implement the of_xlate callback for the generic DMA bindings and the
device_slave_caps API for being able to query the capabilities from the driver.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-10-28  6:48   ` [alsa-devel] " Lars-Peter Clausen
@ 2013-10-28  8:32     ` Barry Song
  -1 siblings, 0 replies; 14+ messages in thread
From: Barry Song @ 2013-10-28  8:32 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: alsa-devel, Liam Girdwood, DL-SHA-WorkGroupLinux, Rongjun Ying,
	Mark Brown, Barry Song, linux-arm-kernel

2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
> On 10/27/2013 11:37 PM, Barry Song wrote:
>> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
>
> static const
>
> Building your driver with sparse (`make C=2 ...`) will tell you these things.
>
>> +     .startup        = sirf_i2s_startup,
>> +     .shutdown       = sirf_i2s_shutdown,
>> +     .trigger        = sirf_i2s_trigger,
>> +     .hw_params      = sirf_i2s_hw_params,
>> +     .set_fmt        = sirf_i2s_set_dai_fmt,
>> +     .set_clkdiv     = sirf_i2s_set_clkdiv,
>> +};
>> +
> [...]
>> +
>> +static int sirf_i2s_probe(struct platform_device *pdev)
>> +{
>> +     struct sirf_i2s *si2s;
>> +     u32 rx_dma_ch, tx_dma_ch;
>> +     int ret;
>> +     struct resource mem_res;
>> +
>> +     si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
>> +                     GFP_KERNEL);
>> +     if (!si2s)
>> +             return -ENOMEM;
>> +
>> +     si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
>> +                     0, NULL, 0);
>> +     if (IS_ERR(si2s->sirf_pcm_pdev))
>> +             return PTR_ERR(si2s->sirf_pcm_pdev);
>> +
>> +     platform_set_drvdata(pdev, si2s);
>> +
>> +     spin_lock_init(&si2s->lock);
>> +
>> +     ret = of_property_read_u32(pdev->dev.of_node,
>> +                     "sirf,i2s-dma-rx-channel", &rx_dma_ch);
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
>> +             return ret;
>> +     }
>> +     ret = of_property_read_u32(pdev->dev.of_node,
>> +                     "sirf,i2s-dma-tx-channel", &tx_dma_ch);
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
>> +             return ret;
>> +     }
>> +
>> +     dma_data[0].filter_data = (void *)tx_dma_ch;
>> +     dma_data[1].filter_data = (void *)rx_dma_ch;
>
> This should be using the standard DMA OF bindings. This combind with the
> recent changes to the generic dmaengine PCM driver, which added support for
> querying certain parameters (like max period length) from the dmaengine
> driver, will allow you to completely remove your custom PCM code. You'd
> basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
> here in the I2S driver.
>
> This will require some changes to your dmaengine driver though. You need to
> implement the of_xlate callback for the generic DMA bindings and the
> device_slave_caps API for being able to query the capabilities from the driver.

i am really happy to have that as for clock, gpio, pinctrl, all of
them we have the binding.
here the problem is we can't implement this in this driver as there
are still many drivers depending on the sirf-dma changes.

so our point is keeping the things as is here. and we have another
patchset of dt binding for sirf-dma and all drivers depending sirf-dma
driver.

-barry

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [alsa-devel] [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-28  8:32     ` Barry Song
  0 siblings, 0 replies; 14+ messages in thread
From: Barry Song @ 2013-10-28  8:32 UTC (permalink / raw)
  To: linux-arm-kernel

2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
> On 10/27/2013 11:37 PM, Barry Song wrote:
>> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
>
> static const
>
> Building your driver with sparse (`make C=2 ...`) will tell you these things.
>
>> +     .startup        = sirf_i2s_startup,
>> +     .shutdown       = sirf_i2s_shutdown,
>> +     .trigger        = sirf_i2s_trigger,
>> +     .hw_params      = sirf_i2s_hw_params,
>> +     .set_fmt        = sirf_i2s_set_dai_fmt,
>> +     .set_clkdiv     = sirf_i2s_set_clkdiv,
>> +};
>> +
> [...]
>> +
>> +static int sirf_i2s_probe(struct platform_device *pdev)
>> +{
>> +     struct sirf_i2s *si2s;
>> +     u32 rx_dma_ch, tx_dma_ch;
>> +     int ret;
>> +     struct resource mem_res;
>> +
>> +     si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
>> +                     GFP_KERNEL);
>> +     if (!si2s)
>> +             return -ENOMEM;
>> +
>> +     si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
>> +                     0, NULL, 0);
>> +     if (IS_ERR(si2s->sirf_pcm_pdev))
>> +             return PTR_ERR(si2s->sirf_pcm_pdev);
>> +
>> +     platform_set_drvdata(pdev, si2s);
>> +
>> +     spin_lock_init(&si2s->lock);
>> +
>> +     ret = of_property_read_u32(pdev->dev.of_node,
>> +                     "sirf,i2s-dma-rx-channel", &rx_dma_ch);
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
>> +             return ret;
>> +     }
>> +     ret = of_property_read_u32(pdev->dev.of_node,
>> +                     "sirf,i2s-dma-tx-channel", &tx_dma_ch);
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
>> +             return ret;
>> +     }
>> +
>> +     dma_data[0].filter_data = (void *)tx_dma_ch;
>> +     dma_data[1].filter_data = (void *)rx_dma_ch;
>
> This should be using the standard DMA OF bindings. This combind with the
> recent changes to the generic dmaengine PCM driver, which added support for
> querying certain parameters (like max period length) from the dmaengine
> driver, will allow you to completely remove your custom PCM code. You'd
> basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
> here in the I2S driver.
>
> This will require some changes to your dmaengine driver though. You need to
> implement the of_xlate callback for the generic DMA bindings and the
> device_slave_caps API for being able to query the capabilities from the driver.

i am really happy to have that as for clock, gpio, pinctrl, all of
them we have the binding.
here the problem is we can't implement this in this driver as there
are still many drivers depending on the sirf-dma changes.

so our point is keeping the things as is here. and we have another
patchset of dt binding for sirf-dma and all drivers depending sirf-dma
driver.

-barry

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-10-28  8:32     ` [alsa-devel] " Barry Song
@ 2013-10-28  8:48       ` Lars-Peter Clausen
  -1 siblings, 0 replies; 14+ messages in thread
From: Lars-Peter Clausen @ 2013-10-28  8:48 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, Liam Girdwood, DL-SHA-WorkGroupLinux, Rongjun Ying,
	Mark Brown, Barry Song, linux-arm-kernel

On 10/28/2013 09:32 AM, Barry Song wrote:
> 2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
>> On 10/27/2013 11:37 PM, Barry Song wrote:
>>> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
>>
>> static const
>>
>> Building your driver with sparse (`make C=2 ...`) will tell you these things.
>>
>>> +     .startup        = sirf_i2s_startup,
>>> +     .shutdown       = sirf_i2s_shutdown,
>>> +     .trigger        = sirf_i2s_trigger,
>>> +     .hw_params      = sirf_i2s_hw_params,
>>> +     .set_fmt        = sirf_i2s_set_dai_fmt,
>>> +     .set_clkdiv     = sirf_i2s_set_clkdiv,
>>> +};
>>> +
>> [...]
>>> +
>>> +static int sirf_i2s_probe(struct platform_device *pdev)
>>> +{
>>> +     struct sirf_i2s *si2s;
>>> +     u32 rx_dma_ch, tx_dma_ch;
>>> +     int ret;
>>> +     struct resource mem_res;
>>> +
>>> +     si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
>>> +                     GFP_KERNEL);
>>> +     if (!si2s)
>>> +             return -ENOMEM;
>>> +
>>> +     si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
>>> +                     0, NULL, 0);
>>> +     if (IS_ERR(si2s->sirf_pcm_pdev))
>>> +             return PTR_ERR(si2s->sirf_pcm_pdev);
>>> +
>>> +     platform_set_drvdata(pdev, si2s);
>>> +
>>> +     spin_lock_init(&si2s->lock);
>>> +
>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>> +                     "sirf,i2s-dma-rx-channel", &rx_dma_ch);
>>> +     if (ret < 0) {
>>> +             dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
>>> +             return ret;
>>> +     }
>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>> +                     "sirf,i2s-dma-tx-channel", &tx_dma_ch);
>>> +     if (ret < 0) {
>>> +             dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
>>> +             return ret;
>>> +     }
>>> +
>>> +     dma_data[0].filter_data = (void *)tx_dma_ch;
>>> +     dma_data[1].filter_data = (void *)rx_dma_ch;
>>
>> This should be using the standard DMA OF bindings. This combind with the
>> recent changes to the generic dmaengine PCM driver, which added support for
>> querying certain parameters (like max period length) from the dmaengine
>> driver, will allow you to completely remove your custom PCM code. You'd
>> basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
>> here in the I2S driver.
>>
>> This will require some changes to your dmaengine driver though. You need to
>> implement the of_xlate callback for the generic DMA bindings and the
>> device_slave_caps API for being able to query the capabilities from the driver.
> 
> i am really happy to have that as for clock, gpio, pinctrl, all of
> them we have the binding.
> here the problem is we can't implement this in this driver as there
> are still many drivers depending on the sirf-dma changes.
> 
> so our point is keeping the things as is here. and we have another
> patchset of dt binding for sirf-dma and all drivers depending sirf-dma
> driver.

I don't think this is a good idea. Adding of_xlate support to the DMA driver
is a 10 line patch at most. And there is no compile time dependency between
the DMA driver changes and using the generic DMA bindings in this driver, so
this driver does not have to wait for the changes to the DMA driver to be
merged first.

I'm fine with having the device_slave_caps stuff added in a later patch, but
adding custom DMA bindings at this point is in my opinion a no-go.
Especially if you consider that DT bindings are considered ABI by some people.

Btw. the DT bindings documentation for this driver seems to be missing.

- Lars

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [alsa-devel] [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-28  8:48       ` Lars-Peter Clausen
  0 siblings, 0 replies; 14+ messages in thread
From: Lars-Peter Clausen @ 2013-10-28  8:48 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/28/2013 09:32 AM, Barry Song wrote:
> 2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
>> On 10/27/2013 11:37 PM, Barry Song wrote:
>>> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
>>
>> static const
>>
>> Building your driver with sparse (`make C=2 ...`) will tell you these things.
>>
>>> +     .startup        = sirf_i2s_startup,
>>> +     .shutdown       = sirf_i2s_shutdown,
>>> +     .trigger        = sirf_i2s_trigger,
>>> +     .hw_params      = sirf_i2s_hw_params,
>>> +     .set_fmt        = sirf_i2s_set_dai_fmt,
>>> +     .set_clkdiv     = sirf_i2s_set_clkdiv,
>>> +};
>>> +
>> [...]
>>> +
>>> +static int sirf_i2s_probe(struct platform_device *pdev)
>>> +{
>>> +     struct sirf_i2s *si2s;
>>> +     u32 rx_dma_ch, tx_dma_ch;
>>> +     int ret;
>>> +     struct resource mem_res;
>>> +
>>> +     si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
>>> +                     GFP_KERNEL);
>>> +     if (!si2s)
>>> +             return -ENOMEM;
>>> +
>>> +     si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
>>> +                     0, NULL, 0);
>>> +     if (IS_ERR(si2s->sirf_pcm_pdev))
>>> +             return PTR_ERR(si2s->sirf_pcm_pdev);
>>> +
>>> +     platform_set_drvdata(pdev, si2s);
>>> +
>>> +     spin_lock_init(&si2s->lock);
>>> +
>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>> +                     "sirf,i2s-dma-rx-channel", &rx_dma_ch);
>>> +     if (ret < 0) {
>>> +             dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
>>> +             return ret;
>>> +     }
>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>> +                     "sirf,i2s-dma-tx-channel", &tx_dma_ch);
>>> +     if (ret < 0) {
>>> +             dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
>>> +             return ret;
>>> +     }
>>> +
>>> +     dma_data[0].filter_data = (void *)tx_dma_ch;
>>> +     dma_data[1].filter_data = (void *)rx_dma_ch;
>>
>> This should be using the standard DMA OF bindings. This combind with the
>> recent changes to the generic dmaengine PCM driver, which added support for
>> querying certain parameters (like max period length) from the dmaengine
>> driver, will allow you to completely remove your custom PCM code. You'd
>> basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
>> here in the I2S driver.
>>
>> This will require some changes to your dmaengine driver though. You need to
>> implement the of_xlate callback for the generic DMA bindings and the
>> device_slave_caps API for being able to query the capabilities from the driver.
> 
> i am really happy to have that as for clock, gpio, pinctrl, all of
> them we have the binding.
> here the problem is we can't implement this in this driver as there
> are still many drivers depending on the sirf-dma changes.
> 
> so our point is keeping the things as is here. and we have another
> patchset of dt binding for sirf-dma and all drivers depending sirf-dma
> driver.

I don't think this is a good idea. Adding of_xlate support to the DMA driver
is a 10 line patch at most. And there is no compile time dependency between
the DMA driver changes and using the generic DMA bindings in this driver, so
this driver does not have to wait for the changes to the DMA driver to be
merged first.

I'm fine with having the device_slave_caps stuff added in a later patch, but
adding custom DMA bindings at this point is in my opinion a no-go.
Especially if you consider that DT bindings are considered ABI by some people.

Btw. the DT bindings documentation for this driver seems to be missing.

- Lars

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-10-28  8:48       ` [alsa-devel] " Lars-Peter Clausen
@ 2013-10-28  8:58         ` Barry Song
  -1 siblings, 0 replies; 14+ messages in thread
From: Barry Song @ 2013-10-28  8:58 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: alsa-devel, Liam Girdwood, DL-SHA-WorkGroupLinux, Rongjun Ying,
	Mark Brown, Barry Song, linux-arm-kernel

2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
> On 10/28/2013 09:32 AM, Barry Song wrote:
>> 2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
>>> On 10/27/2013 11:37 PM, Barry Song wrote:
>>>> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
>>>
>>> static const
>>>
>>> Building your driver with sparse (`make C=2 ...`) will tell you these things.
>>>
>>>> +     .startup        = sirf_i2s_startup,
>>>> +     .shutdown       = sirf_i2s_shutdown,
>>>> +     .trigger        = sirf_i2s_trigger,
>>>> +     .hw_params      = sirf_i2s_hw_params,
>>>> +     .set_fmt        = sirf_i2s_set_dai_fmt,
>>>> +     .set_clkdiv     = sirf_i2s_set_clkdiv,
>>>> +};
>>>> +
>>> [...]
>>>> +
>>>> +static int sirf_i2s_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct sirf_i2s *si2s;
>>>> +     u32 rx_dma_ch, tx_dma_ch;
>>>> +     int ret;
>>>> +     struct resource mem_res;
>>>> +
>>>> +     si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
>>>> +                     GFP_KERNEL);
>>>> +     if (!si2s)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
>>>> +                     0, NULL, 0);
>>>> +     if (IS_ERR(si2s->sirf_pcm_pdev))
>>>> +             return PTR_ERR(si2s->sirf_pcm_pdev);
>>>> +
>>>> +     platform_set_drvdata(pdev, si2s);
>>>> +
>>>> +     spin_lock_init(&si2s->lock);
>>>> +
>>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>>> +                     "sirf,i2s-dma-rx-channel", &rx_dma_ch);
>>>> +     if (ret < 0) {
>>>> +             dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
>>>> +             return ret;
>>>> +     }
>>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>>> +                     "sirf,i2s-dma-tx-channel", &tx_dma_ch);
>>>> +     if (ret < 0) {
>>>> +             dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
>>>> +             return ret;
>>>> +     }
>>>> +
>>>> +     dma_data[0].filter_data = (void *)tx_dma_ch;
>>>> +     dma_data[1].filter_data = (void *)rx_dma_ch;
>>>
>>> This should be using the standard DMA OF bindings. This combind with the
>>> recent changes to the generic dmaengine PCM driver, which added support for
>>> querying certain parameters (like max period length) from the dmaengine
>>> driver, will allow you to completely remove your custom PCM code. You'd
>>> basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
>>> here in the I2S driver.
>>>
>>> This will require some changes to your dmaengine driver though. You need to
>>> implement the of_xlate callback for the generic DMA bindings and the
>>> device_slave_caps API for being able to query the capabilities from the driver.
>>
>> i am really happy to have that as for clock, gpio, pinctrl, all of
>> them we have the binding.
>> here the problem is we can't implement this in this driver as there
>> are still many drivers depending on the sirf-dma changes.
>>
>> so our point is keeping the things as is here. and we have another
>> patchset of dt binding for sirf-dma and all drivers depending sirf-dma
>> driver.
>
> I don't think this is a good idea. Adding of_xlate support to the DMA driver
> is a 10 line patch at most. And there is no compile time dependency between
> the DMA driver changes and using the generic DMA bindings in this driver, so
> this driver does not have to wait for the changes to the DMA driver to be
> merged first.

if we don't wait dma driver and other drivers using sirf-dma to be
merged before merging sound, it is ok to me to have of_xlate support
at first.

>
> I'm fine with having the device_slave_caps stuff added in a later patch, but
> adding custom DMA bindings at this point is in my opinion a no-go.
> Especially if you consider that DT bindings are considered ABI by some people.
>
> Btw. the DT bindings documentation for this driver seems to be missing.

fine. take it into v3 then.

>
> - Lars
>

-barry

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [alsa-devel] [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-28  8:58         ` Barry Song
  0 siblings, 0 replies; 14+ messages in thread
From: Barry Song @ 2013-10-28  8:58 UTC (permalink / raw)
  To: linux-arm-kernel

2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
> On 10/28/2013 09:32 AM, Barry Song wrote:
>> 2013/10/28 Lars-Peter Clausen <lars@metafoo.de>:
>>> On 10/27/2013 11:37 PM, Barry Song wrote:
>>>> +struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
>>>
>>> static const
>>>
>>> Building your driver with sparse (`make C=2 ...`) will tell you these things.
>>>
>>>> +     .startup        = sirf_i2s_startup,
>>>> +     .shutdown       = sirf_i2s_shutdown,
>>>> +     .trigger        = sirf_i2s_trigger,
>>>> +     .hw_params      = sirf_i2s_hw_params,
>>>> +     .set_fmt        = sirf_i2s_set_dai_fmt,
>>>> +     .set_clkdiv     = sirf_i2s_set_clkdiv,
>>>> +};
>>>> +
>>> [...]
>>>> +
>>>> +static int sirf_i2s_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct sirf_i2s *si2s;
>>>> +     u32 rx_dma_ch, tx_dma_ch;
>>>> +     int ret;
>>>> +     struct resource mem_res;
>>>> +
>>>> +     si2s = devm_kzalloc(&pdev->dev, sizeof(struct sirf_i2s),
>>>> +                     GFP_KERNEL);
>>>> +     if (!si2s)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     si2s->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
>>>> +                     0, NULL, 0);
>>>> +     if (IS_ERR(si2s->sirf_pcm_pdev))
>>>> +             return PTR_ERR(si2s->sirf_pcm_pdev);
>>>> +
>>>> +     platform_set_drvdata(pdev, si2s);
>>>> +
>>>> +     spin_lock_init(&si2s->lock);
>>>> +
>>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>>> +                     "sirf,i2s-dma-rx-channel", &rx_dma_ch);
>>>> +     if (ret < 0) {
>>>> +             dev_err(&pdev->dev, "Unable to USP0 rx dma channel\n");
>>>> +             return ret;
>>>> +     }
>>>> +     ret = of_property_read_u32(pdev->dev.of_node,
>>>> +                     "sirf,i2s-dma-tx-channel", &tx_dma_ch);
>>>> +     if (ret < 0) {
>>>> +             dev_err(&pdev->dev, "Unable to USP0 tx dma channel\n");
>>>> +             return ret;
>>>> +     }
>>>> +
>>>> +     dma_data[0].filter_data = (void *)tx_dma_ch;
>>>> +     dma_data[1].filter_data = (void *)rx_dma_ch;
>>>
>>> This should be using the standard DMA OF bindings. This combind with the
>>> recent changes to the generic dmaengine PCM driver, which added support for
>>> querying certain parameters (like max period length) from the dmaengine
>>> driver, will allow you to completely remove your custom PCM code. You'd
>>> basically just need to call "snd_dmaengine_pcm_register(&pdev->dev, NULL)"
>>> here in the I2S driver.
>>>
>>> This will require some changes to your dmaengine driver though. You need to
>>> implement the of_xlate callback for the generic DMA bindings and the
>>> device_slave_caps API for being able to query the capabilities from the driver.
>>
>> i am really happy to have that as for clock, gpio, pinctrl, all of
>> them we have the binding.
>> here the problem is we can't implement this in this driver as there
>> are still many drivers depending on the sirf-dma changes.
>>
>> so our point is keeping the things as is here. and we have another
>> patchset of dt binding for sirf-dma and all drivers depending sirf-dma
>> driver.
>
> I don't think this is a good idea. Adding of_xlate support to the DMA driver
> is a 10 line patch at most. And there is no compile time dependency between
> the DMA driver changes and using the generic DMA bindings in this driver, so
> this driver does not have to wait for the changes to the DMA driver to be
> merged first.

if we don't wait dma driver and other drivers using sirf-dma to be
merged before merging sound, it is ok to me to have of_xlate support
at first.

>
> I'm fine with having the device_slave_caps stuff added in a later patch, but
> adding custom DMA bindings at this point is in my opinion a no-go.
> Especially if you consider that DT bindings are considered ABI by some people.
>
> Btw. the DT bindings documentation for this driver seems to be missing.

fine. take it into v3 then.

>
> - Lars
>

-barry

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
  2013-10-27 22:37 ` Barry Song
@ 2013-10-28 16:21   ` Mark Brown
  -1 siblings, 0 replies; 14+ messages in thread
From: Mark Brown @ 2013-10-28 16:21 UTC (permalink / raw)
  To: Barry Song
  Cc: alsa-devel, lgirdwood, workgroup.linux, Rongjun Ying, Barry Song,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 2253 bytes --]

On Mon, Oct 28, 2013 at 06:37:04AM +0800, Barry Song wrote:

> +static int sirf_i2s_startup(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +	pm_runtime_get_sync(dai->dev);
> +	return 0;
> +}

This appears to be duplicating functionality in the core?  It already
holds a runtime PM reference on the device while it's active.

> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		i2s_ctrl |= I2S_SLAVE_MODE;
> +		i2s_tx_rx_ctrl &= ~I2S_MCLK_EN;
> +		break;
> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		i2s_ctrl &= ~I2S_SLAVE_MODE;
> +		i2s_tx_rx_ctrl |= I2S_MCLK_EN;
> +		break;

Why is I2S_MCLK_EN being controlled here?  This looks like a clock
outside the actual DAI and it's possible someone might wnat to use the
AP to generate MCLK but still have the CODEC drive the frame and bit
clocks on the bus.

> +	default:
> +		dev_err(dai->dev, " Only normal bit clock, normal frame clock supported\n");
> +		return -EINVAL;

Extra space at the start of the log message.

> +static int sirf_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +	u32 val;
> +	u32 bclk_div_coefficient;
> +
> +	if (div < 2 || div % 2) {
> +		dev_err(dai->dev, "BITCLK divider must greater than 1,"
> +			"And must is a multiple of 2\n");
> +		return -EINVAL;
> +	}

Why must the user manually configure the bitclock divider?  I would
expect the driver to be able to configure this automatically.

> +static struct snd_soc_dai_driver sirf_i2s_dai = {
> +	.probe = sirf_i2s_dai_probe,
> +	.name		= "sirf-i2s",
> +	.id			= 0,

The indentation here is pretty inconsistent.

> +static int sirf_i2s_runtime_resume(struct device *dev)
> +{
> +	struct sirf_i2s *si2s = dev_get_drvdata(dev);
> +	clk_prepare_enable(si2s->clk);
> +	device_reset(dev);
> +	return 0;

You should be checking the return value here.

> +	ret = snd_soc_register_component(&pdev->dev, &sirf_i2s_component,
> +			&sirf_i2s_dai, 1);

devm_snd_soc_register_component().

> +	if (ret) {
> +		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
> +		goto err;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);

You need to enable runtime PM prior to registering otherwise something
may try to use the driver prior to it being enabled.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver
@ 2013-10-28 16:21   ` Mark Brown
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Brown @ 2013-10-28 16:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Oct 28, 2013 at 06:37:04AM +0800, Barry Song wrote:

> +static int sirf_i2s_startup(struct snd_pcm_substream *substream,
> +		struct snd_soc_dai *dai)
> +{
> +	pm_runtime_get_sync(dai->dev);
> +	return 0;
> +}

This appears to be duplicating functionality in the core?  It already
holds a runtime PM reference on the device while it's active.

> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		i2s_ctrl |= I2S_SLAVE_MODE;
> +		i2s_tx_rx_ctrl &= ~I2S_MCLK_EN;
> +		break;
> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		i2s_ctrl &= ~I2S_SLAVE_MODE;
> +		i2s_tx_rx_ctrl |= I2S_MCLK_EN;
> +		break;

Why is I2S_MCLK_EN being controlled here?  This looks like a clock
outside the actual DAI and it's possible someone might wnat to use the
AP to generate MCLK but still have the CODEC drive the frame and bit
clocks on the bus.

> +	default:
> +		dev_err(dai->dev, " Only normal bit clock, normal frame clock supported\n");
> +		return -EINVAL;

Extra space at the start of the log message.

> +static int sirf_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
> +{
> +	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
> +	u32 val;
> +	u32 bclk_div_coefficient;
> +
> +	if (div < 2 || div % 2) {
> +		dev_err(dai->dev, "BITCLK divider must greater than 1,"
> +			"And must is a multiple of 2\n");
> +		return -EINVAL;
> +	}

Why must the user manually configure the bitclock divider?  I would
expect the driver to be able to configure this automatically.

> +static struct snd_soc_dai_driver sirf_i2s_dai = {
> +	.probe = sirf_i2s_dai_probe,
> +	.name		= "sirf-i2s",
> +	.id			= 0,

The indentation here is pretty inconsistent.

> +static int sirf_i2s_runtime_resume(struct device *dev)
> +{
> +	struct sirf_i2s *si2s = dev_get_drvdata(dev);
> +	clk_prepare_enable(si2s->clk);
> +	device_reset(dev);
> +	return 0;

You should be checking the return value here.

> +	ret = snd_soc_register_component(&pdev->dev, &sirf_i2s_component,
> +			&sirf_i2s_dai, 1);

devm_snd_soc_register_component().

> +	if (ret) {
> +		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
> +		goto err;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);

You need to enable runtime PM prior to registering otherwise something
may try to use the driver prior to it being enabled.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20131028/66c112eb/attachment.sig>

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2013-10-28 16:21 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-27 22:37 [PATCH v2 2/6] ASoC: sirf: add I2S CPU DAI driver Barry Song
2013-10-27 22:37 ` Barry Song
2013-10-28  3:41 ` Timur Tabi
2013-10-28  3:41   ` [alsa-devel] " Timur Tabi
2013-10-28  6:48 ` Lars-Peter Clausen
2013-10-28  6:48   ` [alsa-devel] " Lars-Peter Clausen
2013-10-28  8:32   ` Barry Song
2013-10-28  8:32     ` [alsa-devel] " Barry Song
2013-10-28  8:48     ` Lars-Peter Clausen
2013-10-28  8:48       ` [alsa-devel] " Lars-Peter Clausen
2013-10-28  8:58       ` Barry Song
2013-10-28  8:58         ` [alsa-devel] " Barry Song
2013-10-28 16:21 ` Mark Brown
2013-10-28 16:21   ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.