All of lore.kernel.org
 help / color / mirror / Atom feed
From: Barry Song <21cnbao@gmail.com>
To: broonie@kernel.org, lgirdwood@gmail.com, alsa-devel@alsa-project.org
Cc: Rongjun Ying <Rongjun.Ying@csr.com>,
	Workgroup.Linux@csr.com, linux-arm-kernel@lists.infradead.org,
	Barry Song <Baohua.Song@csr.com>
Subject: [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
Date: Fri, 19 Jul 2013 19:07:18 +0800	[thread overview]
Message-ID: <1374232042-26088-3-git-send-email-Baohua.Song@csr.com> (raw)
In-Reply-To: <1374232042-26088-1-git-send-email-Baohua.Song@csr.com>

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>
---
 sound/soc/sirf/Kconfig      |   3 +
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-audio.h | 266 ++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-i2s.c   | 411 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 682 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 3678aed..d98acd4 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_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..514c815
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.h
@@ -0,0 +1,266 @@
+/*
+ * 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
+
+#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..d0f81f9
--- /dev/null
+++ b/sound/soc/sirf/sirf-i2s.c
@@ -0,0 +1,411 @@
+/*
+ * 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/clk.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "sirf-pcm.h"
+#include "sirf-audio.h"
+
+struct sirf_i2s {
+	void __iomem		*base;
+	struct clk		*clk;
+	struct pwm_device	*mclk_pwm;
+	u32			i2s_ctrl;
+	spinlock_t		lock;
+	int			master_mode;
+};
+
+static struct sirf_pcm_dma_data sirf_i2s_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	}
+};
+
+static int sirf_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(dai->dev);
+	snd_soc_dai_set_dma_data(dai, substream,
+		&sirf_i2s_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static void sirf_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	clk_disable_unprepare(si2s->clk);
+}
+
+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);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_TX_ENABLE | I2S_DOUT_OE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				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);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_RX_ENABLE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				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_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+
+	if (runtime->channels == 2)
+		ctrl &= ~I2S_SIX_CHANNELS;
+	else
+		ctrl |= I2S_SIX_CHANNELS;
+
+	writel(ctrl, si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	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;
+
+	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 ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+		ctrl |= I2S_SLAVE_MODE;
+		writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+		si2s->master_mode = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		si2s->master_mode = 1;
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	/* 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;
+}
+
+struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
+	.startup	= sirf_i2s_startup,
+	.shutdown	= sirf_i2s_shutdown,
+	.trigger	= sirf_i2s_trigger,
+	.prepare	= sirf_i2s_prepare,
+	.hw_params	= sirf_i2s_hw_params,
+	.set_fmt	= sirf_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sirf_i2s_dai = {
+	.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
+static int sirf_i2s_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	clk_disable_unprepare(si2s->clk);
+
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	return 0;
+}
+
+static int sirf_i2s_resume(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(&pdev->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);
+	writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_INT_MSK);
+	writel(0, si2s->base + AUDIO_CTRL_RXFIFO_INT_MSK);
+
+	return 0;
+}
+#else
+#define sirf_usp_pcm_suspend NULL
+#define sirf_usp_pcm_resume NULL
+#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;
+
+	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;
+	}
+	sirf_i2s_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_i2s_dai_dma_data[1].dma_req = 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;
+	}
+
+	/* i2s bus uses an internal PWM to generate MCLK */
+	si2s->mclk_pwm = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->mclk_pwm)) {
+		dev_err(&pdev->dev, "unable to request PWM\n");
+		ret = PTR_ERR(si2s->mclk_pwm);
+		goto err_clk_put;
+	}
+
+	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_clk_put;
+	}
+
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(si2s->clk);
+err:
+	return ret;
+}
+
+static int sirf_i2s_remove(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	pwm_disable(si2s->mclk_pwm);
+	snd_soc_unregister_component(&pdev->dev);
+	clk_disable_unprepare(si2s->clk);
+
+	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 struct platform_driver sirf_i2s_driver = {
+	.driver = {
+		.name = "sirf-i2s",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_i2s_of_match,
+	},
+	.probe = sirf_i2s_probe,
+	.remove = sirf_i2s_remove,
+	.suspend = sirf_i2s_suspend,
+	.resume = sirf_i2s_resume,
+};
+
+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

WARNING: multiple messages have this Message-ID (diff)
From: 21cnbao@gmail.com (Barry Song)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver
Date: Fri, 19 Jul 2013 19:07:18 +0800	[thread overview]
Message-ID: <1374232042-26088-3-git-send-email-Baohua.Song@csr.com> (raw)
In-Reply-To: <1374232042-26088-1-git-send-email-Baohua.Song@csr.com>

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>
---
 sound/soc/sirf/Kconfig      |   3 +
 sound/soc/sirf/Makefile     |   2 +
 sound/soc/sirf/sirf-audio.h | 266 ++++++++++++++++++++++++++++
 sound/soc/sirf/sirf-i2s.c   | 411 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 682 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 3678aed..d98acd4 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_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..514c815
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.h
@@ -0,0 +1,266 @@
+/*
+ * 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
+
+#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..d0f81f9
--- /dev/null
+++ b/sound/soc/sirf/sirf-i2s.c
@@ -0,0 +1,411 @@
+/*
+ * 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/clk.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "sirf-pcm.h"
+#include "sirf-audio.h"
+
+struct sirf_i2s {
+	void __iomem		*base;
+	struct clk		*clk;
+	struct pwm_device	*mclk_pwm;
+	u32			i2s_ctrl;
+	spinlock_t		lock;
+	int			master_mode;
+};
+
+static struct sirf_pcm_dma_data sirf_i2s_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	}
+};
+
+static int sirf_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(dai->dev);
+	snd_soc_dai_set_dma_data(dai, substream,
+		&sirf_i2s_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static void sirf_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	clk_disable_unprepare(si2s->clk);
+}
+
+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);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_EXT_TXFIFO1_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_TX_ENABLE | I2S_DOUT_OE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				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);
+			mdelay(1);
+			writel(AUDIO_FIFO_START,
+				si2s->base + AUDIO_CTRL_RXFIFO_OP);
+			mdelay(1);
+
+			writel(readl(si2s->base+AUDIO_CTRL_I2S_TX_RX_EN)
+				| I2S_RX_ENABLE |
+				(si2s->master_mode == 1 ? I2S_MCLK_EN : 0),
+				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_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sirf_i2s *si2s = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+
+	if (runtime->channels == 2)
+		ctrl &= ~I2S_SIX_CHANNELS;
+	else
+		ctrl |= I2S_SIX_CHANNELS;
+
+	writel(ctrl, si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	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;
+
+	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 ctrl;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl = readl(si2s->base + AUDIO_CTRL_I2S_CTRL);
+		ctrl |= I2S_SLAVE_MODE;
+		writel(ctrl, si2s->base + AUDIO_CTRL_I2S_CTRL);
+		si2s->master_mode = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		si2s->master_mode = 1;
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	/* 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;
+}
+
+struct snd_soc_dai_ops sirfsoc_i2s_dai_ops = {
+	.startup	= sirf_i2s_startup,
+	.shutdown	= sirf_i2s_shutdown,
+	.trigger	= sirf_i2s_trigger,
+	.prepare	= sirf_i2s_prepare,
+	.hw_params	= sirf_i2s_hw_params,
+	.set_fmt	= sirf_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sirf_i2s_dai = {
+	.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
+static int sirf_i2s_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	si2s->i2s_ctrl = readl(si2s->base+AUDIO_CTRL_I2S_CTRL);
+
+	clk_disable_unprepare(si2s->clk);
+
+	if (si2s->master_mode)
+		pwm_disable(si2s->mclk_pwm);
+	return 0;
+}
+
+static int sirf_i2s_resume(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+	if (si2s->master_mode)
+		pwm_enable(si2s->mclk_pwm);
+	clk_prepare_enable(si2s->clk);
+
+	device_reset(&pdev->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);
+	writel(0, si2s->base + AUDIO_CTRL_EXT_TXFIFO1_INT_MSK);
+	writel(0, si2s->base + AUDIO_CTRL_RXFIFO_INT_MSK);
+
+	return 0;
+}
+#else
+#define sirf_usp_pcm_suspend NULL
+#define sirf_usp_pcm_resume NULL
+#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;
+
+	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;
+	}
+	sirf_i2s_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_i2s_dai_dma_data[1].dma_req = 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;
+	}
+
+	/* i2s bus uses an internal PWM to generate MCLK */
+	si2s->mclk_pwm = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(si2s->mclk_pwm)) {
+		dev_err(&pdev->dev, "unable to request PWM\n");
+		ret = PTR_ERR(si2s->mclk_pwm);
+		goto err_clk_put;
+	}
+
+	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_clk_put;
+	}
+
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(si2s->clk);
+err:
+	return ret;
+}
+
+static int sirf_i2s_remove(struct platform_device *pdev)
+{
+	struct sirf_i2s *si2s = platform_get_drvdata(pdev);
+
+	pwm_disable(si2s->mclk_pwm);
+	snd_soc_unregister_component(&pdev->dev);
+	clk_disable_unprepare(si2s->clk);
+
+	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 struct platform_driver sirf_i2s_driver = {
+	.driver = {
+		.name = "sirf-i2s",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_i2s_of_match,
+	},
+	.probe = sirf_i2s_probe,
+	.remove = sirf_i2s_remove,
+	.suspend = sirf_i2s_suspend,
+	.resume = sirf_i2s_resume,
+};
+
+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

  parent reply	other threads:[~2013-07-19 11:09 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-07-19 11:07 [PATCH 0/6] ASoC: add CSR SiRFSoC sound drivers Barry Song
2013-07-19 11:07 ` Barry Song
2013-07-19 11:07 ` [PATCH 1/6] ASoC: sirf: add sirf platform driver which provides DMA Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 15:08   ` Lars-Peter Clausen
2013-07-19 15:08     ` [alsa-devel] " Lars-Peter Clausen
2013-07-19 16:12   ` Mark Brown
2013-07-19 16:12     ` Mark Brown
2013-07-22  0:06     ` Barry Song
2013-07-22  0:06       ` Barry Song
2013-07-19 11:07 ` Barry Song [this message]
2013-07-19 11:07   ` [PATCH 2/6] ASoC: sirf: add I2S CPU DAI driver Barry Song
2013-07-19 16:52   ` Mark Brown
2013-07-19 16:52     ` Mark Brown
2013-07-22  9:07     ` Barry Song
2013-07-22  9:07       ` Barry Song
2013-07-19 11:07 ` [PATCH 3/6] ASoC: usp-pcm: add CPU DAI driver for PCM simulated from USP Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 18:13   ` Mark Brown
2013-07-19 18:13     ` Mark Brown
2013-07-25  9:32     ` Barry Song
2013-07-25  9:32       ` Barry Song
2013-07-25 10:24       ` Mark Brown
2013-07-25 10:24         ` Mark Brown
2013-07-19 11:07 ` [PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 18:35   ` Mark Brown
2013-07-19 18:35     ` Mark Brown
2013-07-25  9:11     ` Barry Song
2013-07-25  9:11       ` Barry Song
2013-07-19 11:07 ` [PATCH 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec Barry Song
2013-07-19 11:07   ` Barry Song
2013-07-19 18:51   ` Mark Brown
2013-07-19 18:51     ` Mark Brown
2013-07-25  8:57     ` Barry Song
2013-07-25  8:57       ` Barry Song
2013-07-19 11:07 ` [PATCH 6/6] arm: prima2: defconfig: enable sound components Barry Song
2013-07-19 11:07   ` Barry Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1374232042-26088-3-git-send-email-Baohua.Song@csr.com \
    --to=21cnbao@gmail.com \
    --cc=Baohua.Song@csr.com \
    --cc=Rongjun.Ying@csr.com \
    --cc=Workgroup.Linux@csr.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.