[PATCH 4/6] ASoC: sirf-soc-inner: add drivers for both CPU and Codec DAIs

Barry Song 21cnbao at gmail.com
Fri Jul 19 07:07:20 EDT 2013


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

there is an internal codec embedded in the SiRF SoC. this is not
a typical user scenerios of ASoC. but we can still get benefit by
sharing platform DMA codes instead of implementing a pure ALSA
driver.
This driver adds DAI drivers for this internal codec.

The features of  Internal Codec Controller include:
Support two channel 16-bit resolution playback with fix 48KHz sample rate
Support two channel 16-bit resolution record with fix 48KHz sample rate
Use dedicated Internal Codec TXFIFO and Internal Codec RXFIFO
Supports two DMA channels for Internal Codec TXFIFO and Internal Codec RXFIFO

Signed-off-by: Rongjun Ying <Rongjun.Ying at csr.com>
Signed-off-by: Barry Song <Baohua.Song at csr.com>
---
 sound/soc/sirf/Kconfig          |   3 +
 sound/soc/sirf/Makefile         |   2 +
 sound/soc/sirf/sirf-soc-inner.c | 653 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 658 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-soc-inner.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 3606614..60b8857 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -6,5 +6,8 @@ config SND_SIRF_SOC
 config SND_SOC_SIRF_I2S
 	tristate
 
+config SND_SIRF_SOC_INNER
+	tristate
+
 config SND_SOC_SIRF_USP
 	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 630c9be..8517c67 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,7 +1,9 @@
 snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-soc-inner-objs := sirf-soc-inner.o
 snd-soc-sirf-i2s-objs := sirf-i2s.o
 snd-soc-sirf-usp-objs := sirf-usp.o
 
 obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SIRF_SOC_INNER) += snd-soc-sirf-soc-inner.o
 obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
 obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-soc-inner.c b/sound/soc/sirf/sirf-soc-inner.c
new file mode 100644
index 0000000..96bf59b
--- /dev/null
+++ b/sound/soc/sirf/sirf-soc-inner.c
@@ -0,0 +1,653 @@
+/*
+ * SiRF inner audio codec driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.*
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc/sirfsoc_rtciobrg.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "sirf-audio.h"
+#include "sirf-pcm.h"
+
+struct sirf_soc_inner_audio_reg_bits {
+	u32 dig_mic_en_bits;
+	u32 dig_mic_freq_bits;
+	u32 adc14b_12_bits;
+	u32 firdac_hsl_en_bits;
+	u32 firdac_hsr_en_bits;
+	u32 firdac_lout_en_bits;
+	u32 por_bits;
+	u32 codec_clk_en_bits;
+	u32 hp_3db_boost_bits;
+	u32 adc_left_gain_shift;
+	u32 adc_right_gain_shift;
+	u32 adc_gain_mask;
+	u32 mic_max_gain;
+};
+
+struct sirf_soc_inner_audio {
+	void __iomem            *base;
+	unsigned int            playing;
+	struct clk              *clk;
+	spinlock_t              lock;
+	u32			sys_pwrc_reg_base;
+	struct sirf_soc_inner_audio_reg_bits *reg_bits;
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_prima2 = {
+	20, 21, 22, 23, 24, 25, 26, 27, 28, 15, 10, 0x1f, 0x19,
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_atlas6 = {
+	22, 23, 24, 25, 26, 27, 28, 29, 30, 16, 10, 0x3f, 0x39,
+};
+
+static int sirf_inner_control(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol,
+		int get, char *name)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_card *card = codec->card;
+	int i;
+	for (i = 0; i < card->num_controls; i++) {
+		if (!strcmp(card->controls[i].name, name)) {
+			if (card->controls[i].get && get)
+				return card->controls[i].get(kcontrol, ucontrol);
+			else if (card->controls[i].put && !get)
+				return card->controls[i].put(kcontrol, ucontrol);
+		}
+	}
+	return 0;
+}
+
+static int sirf_inner_snd_speaker_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	sirf_inner_control(kcontrol, ucontrol, 1, "Speaker Out");
+	return 0;
+}
+
+static int sirf_inner_snd_speaker_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+
+	spin_lock(&sinner_audio->lock);
+	sirf_inner_control(kcontrol, ucontrol, 0, "Speaker Out");
+
+	if (ucontrol->value.integer.value[0]) {
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_RDACEN | IC_SPSELR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+				(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
+					IC_SPEN), sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	} else {
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+					& ~(IC_SPEN | IC_SPSELR)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				& ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	}
+
+	spin_unlock(&sinner_audio->lock);
+
+	return 0;
+}
+
+static int sirf_inner_snd_headphone_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	sirf_inner_control(kcontrol, ucontrol, 1, "Headphone Out");
+	return 0;
+}
+
+static int sirf_inner_snd_headphone_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+
+	spin_lock(&sinner_audio->lock);
+	sirf_inner_control(kcontrol, ucontrol, 0, "Headphone Out");
+	if (ucontrol->value.integer.value[0])
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_HSLEN | IC_HSREN | IC_HPRSELR
+				| IC_HPLSELL,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	else
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_HSLEN | IC_HSREN | IC_HPRSELR
+					| IC_HPLSELL),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	spin_unlock(&sinner_audio->lock);
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_sirf_inner_volume_controls_atlas6[] = {
+	SOC_DOUBLE("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
+			0x3F, 0),
+	SOC_DOUBLE("Capture Switch", AUDIO_IC_CODEC_CTRL1, 0, 1,
+			1, 0),
+	SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, sirf_inner_snd_speaker_get,
+			sirf_inner_snd_speaker_set),
+	SOC_SINGLE_BOOL_EXT("Headphone Switch", 0, sirf_inner_snd_headphone_get,
+			sirf_inner_snd_headphone_set),
+};
+
+static struct snd_kcontrol_new snd_sirf_inner_volume_controls_prima2[] = {
+	SOC_DOUBLE("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
+			0x1F, 0),
+	SOC_DOUBLE("Capture Switch", AUDIO_IC_CODEC_CTRL1, 0, 1,
+			1, 0),
+	SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, sirf_inner_snd_speaker_get,
+			sirf_inner_snd_speaker_set),
+	SOC_SINGLE_BOOL_EXT("Headphone Switch", 0, sirf_inner_snd_headphone_get,
+			sirf_inner_snd_headphone_set),
+};
+
+static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
+	u32 adc_gain_mask = sinner_audio->reg_bits->adc_gain_mask;
+	u32 adc_left_gain_shift = sinner_audio->reg_bits->adc_left_gain_shift;
+	u32 adc_right_gain_shift = sinner_audio->reg_bits->adc_right_gain_shift;
+	u32 mic_max_gain = sinner_audio->reg_bits->mic_max_gain;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+					| (1 << sinner_audio->reg_bits->codec_clk_en_bits) |
+					(1 << sinner_audio->reg_bits->por_bits)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_HSINVEN)
+				& ~IC_MONOR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		msleep(50);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_RDACEN |
+				IC_LDACEN | IC_HPRSELR | IC_HPLSELL,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+					(1 << sinner_audio->reg_bits->firdac_hsl_en_bits) |
+					(1 << sinner_audio->reg_bits->firdac_hsr_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		usleep_range(300, 1000);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+					| IC_HSREN | IC_HSLEN),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		/* avoid break noise when sound loud, set RX gain -1dB */
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) &
+				~((IC_RXPGAR_MASK << IC_RXPGAR_SHIFT) |
+					(IC_RXPGAL_MASK << IC_RXPGAL_SHIFT)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) |
+				((IC_RXPGAR << IC_RXPGAR_SHIFT) |
+				 (IC_RXPGAL << IC_RXPGAL_SHIFT)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+#define PWRC_PDN_CTRL_OFFSET    0
+#define AUDIO_POWER_EN_BIT      14
+		sirfsoc_rtc_iobrg_writel(sirfsoc_rtc_iobrg_readl(
+			(sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET))
+			| (1 << AUDIO_POWER_EN_BIT),
+			sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) |
+			(1 << sinner_audio->reg_bits->codec_clk_en_bits) |
+			(1 << sinner_audio->reg_bits->por_bits),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		msleep(50);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_PWR) |
+			MICBIASEN, sinner_audio->base + AUDIO_IC_CODEC_PWR);
+		usleep_range(300, 1000);
+
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+			| IC_MICINREN | IC_MICINLEN,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		usleep_range(100, 200);
+		writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) | IC_RADCEN |
+			IC_LADCEN, sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+		usleep_range(100, 200);
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) | IC_MICIN1SEL
+			| IC_MICDIFSEL) & (~IC_MICIN2SEL),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+		writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1) &
+			~(adc_gain_mask << adc_left_gain_shift) &
+			~(adc_gain_mask << adc_right_gain_shift)) |
+			(mic_max_gain << adc_left_gain_shift) |
+			(mic_max_gain << adc_right_gain_shift),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	}
+	return 0;
+}
+
+static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+}
+
+static int sirf_inner_codec_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static int sirf_inner_codec_trigger(struct snd_pcm_substream *substream,
+		int cmd,
+		struct snd_soc_dai *dai)
+{
+	struct sirf_soc_inner_audio *sinner_audio = snd_soc_dai_get_drvdata(dai);
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock(&sinner_audio->lock);
+		if (playback) {
+			writel(readl(sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL)
+				& ~IC_TX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			sinner_audio->playing = false;
+			writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_SPEN | IC_SPSELR | IC_HSLEN)),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				& ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				& ~(IC_HSLEN | IC_HSREN | IC_HPRSELR | IC_HPLSELL),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+		} else {
+			writel(readl(sinner_audio->base + AUDIO_CTRL_IC_CODEC_RX_CTRL)
+				& ~IC_RX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_RX_CTRL);
+		}
+		spin_unlock(&sinner_audio->lock);
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock(&sinner_audio->lock);
+		if (playback) {
+			writel(0, sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_INT_MSK);
+			writel(AUDIO_FIFO_START,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+			writel(IC_TX_ENABLE,
+				sinner_audio->base + AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			sinner_audio->playing = true;
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_RDACEN | IC_SPSELR,
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				| (1 << sinner_audio->reg_bits->firdac_lout_en_bits),
+				sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_SPEN, sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+			writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0)
+				| IC_HSLEN | IC_HSREN
+				| IC_HPRSELR | IC_HPLSELL,
+				sinner_audio->base
+				+ AUDIO_IC_CODEC_CTRL0);
+		} else {
+			/* unmask rx fifo interrupt */
+			writel(0, sinner_audio->base
+				+ AUDIO_CTRL_IC_RXFIFO_INT_MSK);
+
+				/* First start the FIFO, then enable the tx/rx */
+			writel(AUDIO_FIFO_START, sinner_audio->base
+				+ AUDIO_CTRL_IC_RXFIFO_OP);
+			/* mono capture from dacr*/
+			if (substream->runtime->channels == 1)
+				writel(0x01, sinner_audio->base
+					+ AUDIO_CTRL_IC_CODEC_RX_CTRL);
+			else
+				writel(IC_RX_ENABLE, sinner_audio->base
+					+ AUDIO_CTRL_IC_CODEC_RX_CTRL);
+		}
+		spin_unlock(&sinner_audio->lock);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+struct snd_soc_dai_ops sirf_inner_codec_dai_ops = {
+	.startup = sirf_inner_codec_startup,
+	.hw_params = sirf_inner_codec_hw_params,
+	.shutdown = sirf_inner_codec_shutdown,
+	.trigger = sirf_inner_codec_trigger,
+};
+
+struct snd_soc_dai_driver sirf_inner_codec_dai = {
+	.name = "sirf-soc-inner",
+	.playback = {
+		.stream_name = "Audio Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "Audio Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_inner_codec_dai_ops,
+};
+EXPORT_SYMBOL_GPL(sirf_inner_codec_dai);
+
+static int sirf_inner_codec_probe(struct snd_soc_codec *codec)
+{
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio"))
+		return snd_soc_add_codec_controls(codec,
+			snd_sirf_inner_volume_controls_prima2,
+			ARRAY_SIZE(snd_sirf_inner_volume_controls_prima2));
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio"))
+		return snd_soc_add_codec_controls(codec,
+			snd_sirf_inner_volume_controls_atlas6,
+			ARRAY_SIZE(snd_sirf_inner_volume_controls_atlas6));
+
+	return -EINVAL;
+}
+
+static int sirf_inner_codec_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static unsigned int sirf_inner_codec_reg_read(struct snd_soc_codec *codec,
+		unsigned int reg)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	return readl(sinner_audio->base + reg);
+}
+
+static int sirf_inner_codec_reg_write(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int val)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	writel(val, sinner_audio->base + reg);
+	return 0;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_device_sirf_inner_codec = {
+	.probe = sirf_inner_codec_probe,
+	.remove = sirf_inner_codec_remove,
+	.read = sirf_inner_codec_reg_read,
+	.write = sirf_inner_codec_reg_write,
+};
+
+static struct sirf_pcm_dma_data sirf_soc_inner_dai_dma_data[2] = {
+	{
+		.name = "Audio Playback",
+	}, {
+		.name = "Audio Capture",
+	},
+};
+
+static int sirf_soc_inner_dai_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	snd_soc_dai_set_dma_data(dai, substream,
+			&sirf_soc_inner_dai_dma_data[substream->stream]);
+	return 0;
+}
+
+static int sirf_soc_inner_dai_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct sirf_soc_inner_audio *sinner_audio =
+			snd_soc_dai_get_drvdata(dai);
+	if (playback) {
+		writel(AUDIO_FIFO_RESET,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+
+		writel(0x00,
+				sinner_audio->base + AUDIO_CTRL_IC_TXFIFO_OP);
+	} else {
+		writel(AUDIO_FIFO_RESET,
+				sinner_audio->base + AUDIO_CTRL_IC_RXFIFO_OP);
+
+		writel(0x00,
+				sinner_audio->base + AUDIO_CTRL_IC_RXFIFO_OP);
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_soc_inner_dai_ops = {
+	.startup        = sirf_soc_inner_dai_startup,
+	.hw_params      = sirf_soc_inner_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver sirf_soc_inner_dai = {
+	.name		= "sirf-soc-inner",
+	.id			= 0,
+	.playback = {
+		.stream_name = "inner Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "inner Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_soc_inner_dai_ops,
+};
+
+static const struct snd_soc_component_driver sirf_soc_inner_component = {
+	.name		= "sirf-soc-inner",
+};
+
+static const struct of_device_id sirf_soc_inner_of_match[] = {
+	{ .compatible = "sirf,prima2-audio", .data = &sirf_soc_inner_audio_reg_bits_prima2 },
+	{ .compatible = "sirf,atlas6-audio", .data = &sirf_soc_inner_audio_reg_bits_atlas6 },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_soc_inner_of_match);
+
+static int sirf_soc_inner_probe(struct platform_device *pdev)
+{
+	int ret;
+	u32 rx_dma_ch, tx_dma_ch;
+	struct sirf_soc_inner_audio *sinner_audio;
+	struct resource *mem_res;
+	struct device_node *dn = NULL;
+	const struct of_device_id *match;
+
+	match = of_match_node(sirf_soc_inner_of_match, pdev->dev.of_node);
+
+	sinner_audio = devm_kzalloc(&pdev->dev,
+		sizeof(struct sirf_soc_inner_audio), GFP_KERNEL);
+	if (!sinner_audio)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, sinner_audio);
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,inner-audio-dma-rx-channel", &rx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to audio capture dma channel\n");
+		return ret;
+	}
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"sirf,inner-audio-dma-tx-channel", &tx_dma_ch);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to audio playback dma channel\n");
+		return ret;
+	}
+	sirf_soc_inner_dai_dma_data[0].dma_req = tx_dma_ch;
+	sirf_soc_inner_dai_dma_data[1].dma_req = rx_dma_ch;
+
+	dn = of_find_compatible_node(dn, NULL, "sirf,prima2-pwrc");
+	if (!dn) {
+		dev_err(&pdev->dev, "Failed to get sirf,prima2-pwrc  node!\n");
+		return -ENODEV;
+	}
+
+	ret = of_property_read_u32(dn, "reg", &sinner_audio->sys_pwrc_reg_base);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed tp get pwrc register base address\n");
+		return -EINVAL;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	sinner_audio->base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (sinner_audio->base == NULL)
+		return -ENOMEM;
+
+	sinner_audio->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(sinner_audio->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		return PTR_ERR(sinner_audio->clk);
+	}
+	clk_prepare_enable(sinner_audio->clk);
+
+	ret = snd_soc_register_component(&pdev->dev, &sirf_soc_inner_component,
+		&sirf_soc_inner_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+		goto err_clk_put;
+	}
+
+	ret = snd_soc_register_codec(&(pdev->dev),
+			&soc_codec_device_sirf_inner_codec,
+			&sirf_inner_codec_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
+		goto err_com_unreg;
+	}
+
+	sinner_audio->reg_bits = (struct sirf_soc_inner_audio_reg_bits *)match->data;
+
+	spin_lock_init(&sinner_audio->lock);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+				| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+			| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	return 0;
+
+err_com_unreg:
+	snd_soc_unregister_component(&pdev->dev);
+err_clk_put:
+	clk_disable_unprepare(sinner_audio->clk);
+	return ret;
+}
+
+static int sirf_soc_inner_remove(struct platform_device *pdev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sinner_audio->clk);
+	snd_soc_unregister_codec(&(pdev->dev));
+	snd_soc_unregister_component(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int sirf_soc_inner_suspend(struct platform_device *pdev,
+		pm_message_t msg)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sinner_audio->clk);
+	return 0;
+}
+
+static int sirf_soc_inner_resume(struct platform_device *pdev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(sinner_audio->clk);
+
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+		| (1 << sinner_audio->reg_bits->codec_clk_en_bits)),
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel((readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1)
+		| (1 << sinner_audio->reg_bits->adc14b_12_bits)),
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPEN,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	return 0;
+}
+#else
+#define sirf_soc_inner_suspend NULL
+#define sirf_soc_inner_resume NULL
+#endif
+
+static struct platform_driver sirf_soc_inner_driver = {
+	.driver = {
+		.name = "sirf-soc-inner",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_soc_inner_of_match,
+	},
+	.probe = sirf_soc_inner_probe,
+	.remove = sirf_soc_inner_remove,
+	.suspend = sirf_soc_inner_suspend,
+	.resume = sirf_soc_inner_resume,
+};
+
+module_platform_driver(sirf_soc_inner_driver);
+
+MODULE_DESCRIPTION("SiRF SoC inner bus and codec driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying at csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3




More information about the linux-arm-kernel mailing list