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

Barry Song 21cnbao at gmail.com
Mon Oct 28 19:05:34 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>
---
 -v2: use DAPM and runtime PM to replace legacy kcontrol

 sound/soc/sirf/Kconfig          |   3 +
 sound/soc/sirf/Makefile         |   2 +
 sound/soc/sirf/sirf-soc-inner.c | 674 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 679 insertions(+)
 create mode 100644 sound/soc/sirf/sirf-soc-inner.c

diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 5e395d3..afa3952 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..fc40105
--- /dev/null
+++ b/sound/soc/sirf/sirf-soc-inner.c
@@ -0,0 +1,674 @@
+/*
+ * 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/pm_runtime.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 <sound/dmaengine_pcm.h>
+
+#include "sirf-audio.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;
+};
+
+struct sirf_soc_inner_audio {
+	void __iomem            *base;
+	struct clk              *clk;
+	spinlock_t              lock;
+	u32			sys_pwrc_reg_base;
+	struct sirf_soc_inner_audio_reg_bits *reg_bits;
+	u32			reg_ctrl0, reg_ctrl1;
+	struct platform_device	*sirf_pcm_pdev;
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_prima2 = {
+	20, 21, 22, 23, 24, 25, 26, 27,
+};
+
+static struct sirf_soc_inner_audio_reg_bits sirf_soc_inner_audio_reg_bits_atlas6 = {
+	22, 23, 24, 25, 26, 27, 28, 29,
+};
+static const char * const input_mode_mux[] = {"Single-ended",
+	"Differential"};
+
+static const struct soc_enum sirf_inner_enum[] = {
+	SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux),
+};
+
+static const struct snd_kcontrol_new sirf_inner_input_mode_control =
+	SOC_DAPM_ENUM("Route", sirf_inner_enum[0]);
+
+static struct snd_kcontrol_new volume_controls_atlas6[] = {
+	SOC_DOUBLE("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0),
+	SOC_DOUBLE("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
+			0x3F, 0),
+};
+
+static struct snd_kcontrol_new 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),
+};
+
+static struct snd_kcontrol_new left_input_path_controls[] = {
+	SOC_DAPM_SINGLE("Line left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0),
+	SOC_DAPM_SINGLE("Mic left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0),
+};
+
+static struct snd_kcontrol_new right_input_path_controls[] = {
+	SOC_DAPM_SINGLE("Line right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0),
+	SOC_DAPM_SINGLE("Mic right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0),
+};
+
+static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0);
+
+static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0);
+
+static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0);
+
+static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0);
+
+static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0);
+
+static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0);
+
+static int adc_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	u32 val;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+#define PWRC_PDN_CTRL_OFFSET    0
+#define AUDIO_POWER_EN_BIT      14
+		/* Enable capture power of codec*/
+		val = sirfsoc_rtc_iobrg_readl(sinner_audio->sys_pwrc_reg_base +
+			PWRC_PDN_CTRL_OFFSET);
+		val |= (1 << AUDIO_POWER_EN_BIT);
+		sirfsoc_rtc_iobrg_writel(val,
+			sinner_audio->sys_pwrc_reg_base + PWRC_PDN_CTRL_OFFSET);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*After enable adc, Delay 200ms to avoid pop noise*/
+		msleep(200);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int hp_amp_left_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	u32 val;
+
+	val = snd_soc_read(codec, AUDIO_IC_CODEC_CTRL1);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		val |= (1 << sinner_audio->reg_bits->firdac_hsl_en_bits);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		val &= ~(1 << sinner_audio->reg_bits->firdac_hsl_en_bits);
+	default:
+		break;
+	}
+	snd_soc_write(codec, AUDIO_IC_CODEC_CTRL1, val);
+	return 0;
+}
+
+static int hp_amp_right_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	u32 val;
+
+	val = snd_soc_read(codec, AUDIO_IC_CODEC_CTRL1);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		val |= (1 << sinner_audio->reg_bits->firdac_hsr_en_bits);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		val &= ~(1 << sinner_audio->reg_bits->firdac_hsr_en_bits);
+	default:
+		break;
+	}
+	snd_soc_write(codec, AUDIO_IC_CODEC_CTRL1, val);
+	return 0;
+}
+
+static int speaker_output_enable_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(codec->dev);
+	u32 val;
+
+	val = snd_soc_read(codec, AUDIO_IC_CODEC_CTRL1);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		val |= (1 << sinner_audio->reg_bits->firdac_lout_en_bits);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		val &= ~(1 << sinner_audio->reg_bits->firdac_lout_en_bits);
+	default:
+		break;
+	}
+	snd_soc_write(codec, AUDIO_IC_CODEC_CTRL1, val);
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget sirf_inner_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0),
+	SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0),
+	SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0,
+			&left_dac_to_hp_left_amp_switch_control),
+	SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0,
+			&left_dac_to_hp_right_amp_switch_control),
+	SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0,
+			&right_dac_to_hp_left_amp_switch_control),
+	SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0,
+			&right_dac_to_hp_right_amp_switch_control),
+	SND_SOC_DAPM_OUT_DRV_E("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
+			NULL, 0, hp_amp_left_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_OUT_DRV_E("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 2, 0,
+			NULL, 0, hp_amp_right_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0,
+			&left_dac_to_speaker_lineout_switch_control),
+	SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0,
+			&right_dac_to_speaker_lineout_switch_control),
+	SND_SOC_DAPM_OUT_DRV_E("Speaker output driver", AUDIO_IC_CODEC_CTRL0, 4, 0,
+			NULL, 0, speaker_output_enable_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_OUTPUT("HPOUTL"),
+	SND_SOC_DAPM_OUTPUT("HPOUTR"),
+	SND_SOC_DAPM_OUTPUT("SPKOUT"),
+
+	SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0,
+			adc_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0,
+			adc_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0,
+		&left_input_path_controls[0],
+		ARRAY_SIZE(left_input_path_controls)),
+	SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0,
+		&right_input_path_controls[0],
+		ARRAY_SIZE(right_input_path_controls)),
+
+	SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0,
+			&sirf_inner_input_mode_control),
+	SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0),
+	SND_SOC_DAPM_INPUT("MICIN1"),
+	SND_SOC_DAPM_INPUT("MICIN2"),
+	SND_SOC_DAPM_INPUT("LINEIN1"),
+	SND_SOC_DAPM_INPUT("LINEIN2"),
+
+	SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0,
+			30, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route sirf_inner_audio_map[] = {
+	{"SPKOUT", NULL, "Speaker output driver"},
+	{"Speaker output driver", NULL, "Left dac to speaker lineout"},
+	{"Speaker output driver", NULL, "Right dac to speaker lineout"},
+	{"Left dac to speaker lineout", "Switch", "DAC left"},
+	{"Right dac to speaker lineout", "Switch", "DAC right"},
+	{"HPOUTL", NULL, "HP amp left driver"},
+	{"HPOUTR", NULL, "HP amp right driver"},
+	{"HP amp left driver", NULL, "Right dac to hp left amp"},
+	{"HP amp right driver", NULL , "Right dac to hp right amp"},
+	{"HP amp left driver", NULL, "Left dac to hp left amp"},
+	{"HP amp right driver", NULL , "Right dac to hp right amp"},
+	{"Right dac to hp left amp", "Switch", "DAC left"},
+	{"Right dac to hp right amp", "Switch", "DAC right"},
+	{"Left dac to hp left amp", "Switch", "DAC left"},
+	{"Left dac to hp right amp", "Switch", "DAC right"},
+	{"DAC left", NULL, "Playback"},
+	{"DAC right", NULL, "Playback"},
+	{"DAC left", NULL, "HSL Phase Opposite"},
+	{"DAC right", NULL, "HSL Phase Opposite"},
+
+	{"Capture", NULL, "ADC left"},
+	{"Capture", NULL, "ADC right"},
+	{"ADC left", NULL, "Left PGA mixer"},
+	{"ADC right", NULL, "Right PGA mixer"},
+	{"Left PGA mixer", "Line left Switch", "LINEIN2"},
+	{"Right PGA mixer", "Line right Switch", "LINEIN1"},
+	{"Left PGA mixer", "Mic left Switch", "MICIN2"},
+	{"Right PGA mixer", "Mic right Switch", "Mic input mode mux"},
+	{"Mic input mode mux", "Single-ended", "MICIN1"},
+	{"Mic input mode mux", "Differential", "MICIN1"},
+};
+
+static int sirf_inner_codec_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	pm_runtime_get_sync(codec->dev);
+	return 0;
+}
+
+static void sirf_inner_codec_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	pm_runtime_put(codec->dev);
+}
+
+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;
+	struct snd_soc_codec *codec = dai->codec;
+	u32 val;
+
+	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) {
+			/*Disconnect HP amp connect to avoid pop noise*/
+			val = snd_soc_read(codec, AUDIO_IC_CODEC_CTRL0);
+			val &= ~(IC_HSLEN | IC_HSREN);
+			snd_soc_write(codec, AUDIO_IC_CODEC_CTRL0, val);
+
+			snd_soc_write(codec, AUDIO_CTRL_IC_TXFIFO_OP, 0x0);
+
+			val = snd_soc_read(codec, AUDIO_CTRL_IC_CODEC_TX_CTRL);
+			val &= ~IC_TX_ENABLE;
+			snd_soc_write(codec, AUDIO_CTRL_IC_CODEC_TX_CTRL, val);
+		} else {
+			val = snd_soc_read(codec, AUDIO_CTRL_IC_CODEC_RX_CTRL);
+			val &= ~IC_RX_ENABLE;
+			snd_soc_write(codec, AUDIO_CTRL_IC_CODEC_RX_CTRL, val);
+		}
+		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) {
+			snd_soc_write(codec, AUDIO_CTRL_IC_TXFIFO_OP, AUDIO_FIFO_RESET);
+			snd_soc_write(codec, AUDIO_CTRL_IC_TXFIFO_INT_MSK, 0);
+			snd_soc_write(codec, AUDIO_CTRL_IC_TXFIFO_OP, 0x0);
+			snd_soc_write(codec, AUDIO_CTRL_IC_TXFIFO_OP,
+				AUDIO_FIFO_START);
+			snd_soc_write(codec, AUDIO_CTRL_IC_CODEC_TX_CTRL,
+				IC_TX_ENABLE);
+			val = snd_soc_read(codec, AUDIO_IC_CODEC_CTRL0);
+			val |= (IC_HSLEN | IC_HSREN);
+			snd_soc_write(codec, AUDIO_IC_CODEC_CTRL0, val);
+		} else {
+			snd_soc_write(codec, AUDIO_CTRL_IC_RXFIFO_OP, AUDIO_FIFO_RESET);
+			/* unmask rx fifo interrupt */
+			snd_soc_write(codec, AUDIO_CTRL_IC_RXFIFO_INT_MSK, 0);
+
+			snd_soc_write(codec, AUDIO_CTRL_IC_RXFIFO_OP, 0x0);
+			/* First start the FIFO, then enable the tx/rx */
+			snd_soc_write(codec, AUDIO_CTRL_IC_RXFIFO_OP,
+					AUDIO_FIFO_START);
+			/* mono capture from dacr*/
+			if (substream->runtime->channels == 1)
+				snd_soc_write(codec,
+					AUDIO_CTRL_IC_CODEC_RX_CTRL, 0x1);
+			else
+				snd_soc_write(codec,
+					AUDIO_CTRL_IC_CODEC_RX_CTRL,
+					IC_RX_ENABLE);
+		}
+		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,
+	.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 = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "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)
+{
+	pm_runtime_enable(codec->dev);
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio"))
+		return snd_soc_add_codec_controls(codec,
+			volume_controls_prima2,
+			ARRAY_SIZE(volume_controls_prima2));
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio"))
+		return snd_soc_add_codec_controls(codec,
+			volume_controls_atlas6,
+			ARRAY_SIZE(volume_controls_atlas6));
+
+	return -EINVAL;
+}
+
+static int sirf_inner_codec_remove(struct snd_soc_codec *codec)
+{
+	pm_runtime_disable(codec->dev);
+	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,
+	.dapm_widgets = sirf_inner_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(sirf_inner_dapm_widgets),
+	.dapm_routes = sirf_inner_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(sirf_inner_audio_map),
+	.idle_bias_off = true,
+};
+
+static struct snd_dmaengine_dai_dma_data dma_data[2];
+
+static int sirf_soc_inner_dai_probe(struct snd_soc_dai *dai)
+{
+	dai->playback_dma_data = &dma_data[0];
+	dai->capture_dma_data = &dma_data[1];
+	return 0;
+}
+
+static struct snd_soc_dai_driver sirf_soc_inner_dai = {
+	.probe = sirf_soc_inner_dai_probe,
+	.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,
+	},
+};
+
+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;
+
+	sinner_audio->sirf_pcm_pdev = platform_device_register_simple("sirf-pcm-audio",
+			1, NULL, 0);
+	if (IS_ERR(sinner_audio->sirf_pcm_pdev))
+		return PTR_ERR(sinner_audio->sirf_pcm_pdev);
+
+	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;
+	}
+
+	dma_data[0].filter_data = (void *)tx_dma_ch;
+	dma_data[1].filter_data = (void *)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;
+	/* Always open charge pump, if not, when the charge pump closed the
+	 * adc will not stable*/
+	writel(readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0) | IC_CPFREQ,
+			sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+
+	spin_lock_init(&sinner_audio->lock);
+	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_RUNTIME
+static int sirf_inner_runtime_suspend(struct device *dev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(dev);
+	u32 val;
+	val = readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	val &= ~(1 << sinner_audio->reg_bits->codec_clk_en_bits);
+	writel(val, sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	return 0;
+}
+
+static int sirf_inner_runtime_resume(struct device *dev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(dev);
+	u32 val;
+	val = readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	val |= (1 << sinner_audio->reg_bits->codec_clk_en_bits);
+	val &= ~(1 << sinner_audio->reg_bits->por_bits);
+	writel(val, 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);
+
+	msleep(20);
+	val |= (1 << sinner_audio->reg_bits->por_bits);
+	writel(val, sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_soc_inner_suspend(struct device *dev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(dev);
+
+	sinner_audio->reg_ctrl0 = readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	sinner_audio->reg_ctrl1 = readl(sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+	sirf_inner_runtime_suspend(dev);
+	clk_disable_unprepare(sinner_audio->clk);
+
+	return 0;
+}
+
+static int sirf_soc_inner_resume(struct device *dev)
+{
+	struct sirf_soc_inner_audio *sinner_audio = dev_get_drvdata(dev);
+
+	clk_prepare_enable(sinner_audio->clk);
+
+	writel(sinner_audio->reg_ctrl0,
+		sinner_audio->base + AUDIO_IC_CODEC_CTRL0);
+	writel(sinner_audio->reg_ctrl1, sinner_audio->base + AUDIO_IC_CODEC_CTRL1);
+
+	if (!pm_runtime_status_suspended(dev))
+		sirf_inner_runtime_resume(dev);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops sirf_inner_pm_ops = {
+	SET_RUNTIME_PM_OPS(sirf_inner_runtime_suspend, sirf_inner_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(sirf_soc_inner_suspend, sirf_soc_inner_resume)
+};
+
+static struct platform_driver sirf_soc_inner_driver = {
+	.driver = {
+		.name = "sirf-soc-inner",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_soc_inner_of_match,
+		.pm = &sirf_inner_pm_ops,
+	},
+	.probe = sirf_soc_inner_probe,
+	.remove = sirf_soc_inner_remove,
+};
+
+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