Title: [6617] trunk/sound/soc: 1.
Revision
6617
Author
bhsong
Date
2009-06-09 04:17:35 -0500 (Tue, 09 Jun 2009)

Log Message

1. add controls: dac and adc switches/ADC High Pass Filter/DAC Deemphasis enum
2. add DAC/ADC/PLL power management and DAC mute controls for decrease pops

Modified Paths

Diff

Modified: trunk/sound/soc/blackfin/bf5xx-ad1938.c (6616 => 6617)


--- trunk/sound/soc/blackfin/bf5xx-ad1938.c	2009-06-09 06:42:00 UTC (rev 6616)
+++ trunk/sound/soc/blackfin/bf5xx-ad1938.c	2009-06-09 09:17:35 UTC (rev 6617)
@@ -60,26 +60,41 @@
 	return 0;
 }
 
+static int bf5xx_ad1938_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return snd_soc_dai_set_pll(codec_dai, 0, 0, 0);
+}
+
 static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+		struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 	int ret = 0;
 
 	/* set cpu DAI configuration */
 	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_SPORT_TDM |
-		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
+	/* set codec DAI pll */
+	ret = snd_soc_dai_set_pll(codec_dai, 0, 12288000, 48000*512);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
-
 static struct snd_soc_ops bf5xx_ad1938_ops = {
 	.startup = bf5xx_ad1938_startup,
 	.hw_params = bf5xx_ad1938_hw_params,
+	.hw_free = bf5xx_ad1938_hw_free,
 };
 
 static struct snd_soc_dai_link bf5xx_ad1938_dai = {

Modified: trunk/sound/soc/codecs/ad1938.c (6616 => 6617)


--- trunk/sound/soc/codecs/ad1938.c	2009-06-09 06:42:00 UTC (rev 6616)
+++ trunk/sound/soc/codecs/ad1938.c	2009-06-09 09:17:35 UTC (rev 6617)
@@ -25,35 +25,54 @@
 #include <linux/spi/spi.h>
 #include "ad1938.h"
 
-struct snd_soc_dai ad1938_dai = {
-	.name = "AD1938",
-	.playback = {
-		.stream_name = "Playback",
-		.channels_min = 2,
-		.channels_max = 8,
-		.rates = SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S32_LE, },
-	.capture = {
-		.stream_name = "Capture",
-		.channels_min = 2,
-		.channels_max = 4,
-		.rates = SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S32_LE, },
+struct snd_soc_device *ad1938_socdev;
+
+/* struct to flag whether adc and dac need power to work */
+struct ad1938_pwr_sta {
+	int adc_pwr;
+	int dac_pwr;
 };
-EXPORT_SYMBOL_GPL(ad1938_dai);
 
-struct snd_soc_device *ad1938_socdev;
+/* dac de-emphasis enum control */
+static const char *ad1938_deemp[] = {"flat", "48kHz", "44.1kHz", "32kHz"};
 
-/* DAC volume controls */
+static const struct soc_enum ad1938_enum[] = {
+	SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp),
+};
+
+/* AD1938 volume/mute/de-emphasis etc. controls */
 static const struct snd_kcontrol_new ad1938_snd_controls[] = {
-	SOC_SINGLE("DAC L1", AD1938_DAC_L1_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC R1", AD1938_DAC_R1_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC L2", AD1938_DAC_L2_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC R2", AD1938_DAC_R2_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC L3", AD1938_DAC_L3_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC R3", AD1938_DAC_R3_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC L4", AD1938_DAC_L4_VOL, 0, 0xFF, 1),
-	SOC_SINGLE("DAC R4", AD1938_DAC_R4_VOL, 0, 0xFF, 1),
+	/* DAC volume control */
+	SOC_SINGLE("DAC L1 Volume", AD1938_DAC_L1_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC R1 Volume", AD1938_DAC_R1_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC L2 Volume", AD1938_DAC_L2_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC R2 Volume", AD1938_DAC_R2_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC L3 Volume", AD1938_DAC_L3_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC R3 Volume", AD1938_DAC_R3_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC L4 Volume", AD1938_DAC_L4_VOL, 0, 0xFF, 1),
+	SOC_SINGLE("DAC R4 Volume", AD1938_DAC_R4_VOL, 0, 0xFF, 1),
+
+	/* DAC mute control */
+	SOC_SINGLE("DAC L1 Switch", AD1938_DAC_CHNL_MUTE, 0, 1, 1),
+	SOC_SINGLE("DAC R1 Switch", AD1938_DAC_CHNL_MUTE, 1, 1, 1),
+	SOC_SINGLE("DAC L2 Switch", AD1938_DAC_CHNL_MUTE, 2, 1, 1),
+	SOC_SINGLE("DAC R2 Switch", AD1938_DAC_CHNL_MUTE, 3, 1, 1),
+	SOC_SINGLE("DAC L3 Switch", AD1938_DAC_CHNL_MUTE, 4, 1, 1),
+	SOC_SINGLE("DAC R3 Switch", AD1938_DAC_CHNL_MUTE, 5, 1, 1),
+	SOC_SINGLE("DAC L4 Switch", AD1938_DAC_CHNL_MUTE, 6, 1, 1),
+	SOC_SINGLE("DAC R4 Switch", AD1938_DAC_CHNL_MUTE, 7, 1, 1),
+
+	/* ADC mute control */
+	SOC_SINGLE("ADC L1 Switch", AD1938_ADC_CTRL0, ADC0_MUTE, 1, 1),
+	SOC_SINGLE("ADC R1 Switch", AD1938_ADC_CTRL0, ADC1_MUTE, 1, 1),
+	SOC_SINGLE("ADC L2 Switch", AD1938_ADC_CTRL0, ADC2_MUTE, 1, 1),
+	SOC_SINGLE("ADC R2 Switch", AD1938_ADC_CTRL0, ADC3_MUTE, 1, 1),
+
+	/* ADC high-pass filter */
+	SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0, ADC_HIGHPASS_FILTER, 1, 0),
+
+	/* DAC de-emphasis */
+	SOC_ENUM("Playback Deemphasis", ad1938_enum[0]),
 };
 
 /* add non dapm controls */
@@ -71,7 +90,109 @@
 	return 0;
 }
 
+/* dai_ops.digital_mute entry */
+static int ad1938_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
 
+	if (!mute)
+		codec->write(codec, AD1938_DAC_CHNL_MUTE, 0);
+	else
+		codec->write(codec, AD1938_DAC_CHNL_MUTE, 0xff);
+
+	return 0;
+}
+
+/* dai_ops.set_pll entry */
+static int ad1938_set_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int pll_reg;
+
+	if (freq_out) {
+		pll_reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
+		pll_reg &= ~PLL_POWERDOWN;
+	} else {
+		pll_reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
+		pll_reg |= PLL_POWERDOWN;
+	}
+	codec->write(codec, AD1938_PLL_CLK_CTRL0, pll_reg);
+
+	return 0;
+}
+
+/* ops.prepare entry */
+static int ad1938_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ad1938_pwr_sta *pwr_sta = codec->private_data;
+	int pwr_reg;
+
+	/* set active */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* If not poweron adc, dac can't work */
+		pwr_reg = codec->read(codec, AD1938_ADC_CTRL0);
+		if (pwr_reg & ADC_POWERDOWN)
+			codec->write(codec, AD1938_ADC_CTRL0, pwr_reg & ~ADC_POWERDOWN);
+
+		/* poweron dac */
+		pwr_reg = codec->read(codec, AD1938_DAC_CTRL0);
+		pwr_reg &= ~DAC_POWERDOWN;
+		codec->write(codec, AD1938_DAC_CTRL0, pwr_reg);
+
+		pwr_sta->dac_pwr = 1;
+	} else {
+		/* poweron adc */
+		pwr_reg = codec->read(codec, AD1938_ADC_CTRL0);
+		pwr_reg &= ~ADC_POWERDOWN;
+		codec->write(codec, AD1938_ADC_CTRL0, pwr_reg);
+
+		pwr_sta->adc_pwr = 1;
+	}
+
+	return 0;
+}
+
+
+/* ops.shutdown entry */
+static void ad1938_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ad1938_pwr_sta *pwr_sta = codec->private_data;
+	int pwr_reg;
+
+	/* deactivate */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* poweroff dac */
+		pwr_reg = codec->read(codec, AD1938_DAC_CTRL0);
+		pwr_reg |= DAC_POWERDOWN;
+		codec->write(codec, AD1938_DAC_CTRL0, pwr_reg);
+
+		pwr_sta->dac_pwr = 0;
+
+		/* after poweroff dac, if adc is not opened, poweroff it too */
+		if (pwr_sta->adc_pwr == 0) {
+			pwr_reg = codec->read(codec, AD1938_ADC_CTRL0);
+			pwr_reg |= ADC_POWERDOWN;
+			codec->write(codec, AD1938_ADC_CTRL0, pwr_reg);
+		}
+	} else {
+		/* if dac is still working, can't shutdown adc */
+		if (pwr_sta->dac_pwr == 0) {
+			pwr_reg = codec->read(codec, AD1938_ADC_CTRL0);
+			pwr_reg |= ADC_POWERDOWN;
+			codec->write(codec, AD1938_ADC_CTRL0, pwr_reg);
+		}
+
+		pwr_sta->adc_pwr = 0;
+	}
+}
+
 /*
  * interface to read/write ad1938 register
  */
@@ -166,6 +287,32 @@
 	spi_unregister_driver(&ad1938_spi_driver);
 }
 
+/* codec DAI instance */
+struct snd_soc_dai ad1938_dai = {
+	.name = "AD1938",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE, },
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 4,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE, },
+	.ops = {
+		.prepare = ad1938_pcm_prepare,
+		.shutdown = ad1938_pcm_shutdown,
+	},
+	.dai_ops = {
+		.digital_mute = ad1938_mute,
+		.set_pll = ad1938_set_pll,
+	},
+};
+EXPORT_SYMBOL_GPL(ad1938_dai);
+
 static int ad1938_soc_probe(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -173,7 +320,7 @@
 	int ret = 0;
 
 	/* codec alloc and init */
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	codec = kzalloc(sizeof(struct snd_soc_codec) + sizeof(struct ad1938_pwr_sta), GFP_KERNEL);
 	if (codec == NULL)
 		return -ENOMEM;
 	mutex_init(&codec->mutex);
@@ -183,6 +330,7 @@
 	codec->num_dai = 1;
 	codec->write = ad1938_reg_write;
 	codec->read = ad1938_reg_read;
+	codec->private_data = codec + 1;
 	socdev->codec = codec;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
@@ -210,18 +358,20 @@
 	}
 
 	/* default setting for ad1938: 8 channel AUX ADC mode, 16bit, 48000Hz */
-	codec->write(codec, AD1938_DAC_CTRL0, 0x40);
-	codec->write(codec, AD1938_DAC_CTRL1, 0x84);
-	codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
-	codec->write(codec, AD1938_ADC_CTRL0, 0x32);
-	codec->write(codec, AD1938_ADC_CTRL1, 0x43);
-	codec->write(codec, AD1938_ADC_CTRL2, 0x6f);
-	codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9C);
+	codec->write(codec, AD1938_DAC_CTRL0, 0x41); /* sample rate:32/44.1/48kHz, sata delay=1, tdm mode */
+	codec->write(codec, AD1938_DAC_CTRL1, 0x84); /* invert bclk, 256bclk/frame, latch in mid */
+	codec->write(codec, AD1938_DAC_CTRL2, 0x1A); /* de-emphasis: 48kHz */
+	codec->write(codec, AD1938_ADC_CTRL0, 0x33); /* high-pass filter enable */
+	codec->write(codec, AD1938_ADC_CTRL1, 0x43); /* sata delay=1, adc aux mode */
+	codec->write(codec, AD1938_ADC_CTRL2, 0x6F); /* left high, driver on rising edge */
+	codec->write(codec, AD1938_DAC_CHNL_MUTE, 0xFF); /* mute all dac channels */
+	codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9C); /* pll input:mclki/xi, master clock rate:512*fs */
 	codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
 
 	/* register controls for ad1938 */
 	ad1938_add_controls(codec);
 
+	printk(KERN_INFO "Analog Devices AD1938 codec registered\n");
 	return ret;
 
 register_err:

Modified: trunk/sound/soc/codecs/ad1938.h (6616 => 6617)


--- trunk/sound/soc/codecs/ad1938.h	2009-06-09 06:42:00 UTC (rev 6616)
+++ trunk/sound/soc/codecs/ad1938.h	2009-06-09 09:17:35 UTC (rev 6617)
@@ -30,8 +30,10 @@
 #define __AD1938_H__
 
 #define AD1938_PLL_CLK_CTRL0    0
+#define PLL_POWERDOWN           0x01
 #define AD1938_PLL_CLK_CTRL1    1
 #define AD1938_DAC_CTRL0        2
+#define DAC_POWERDOWN           0x01
 #define AD1938_DAC_CTRL1        3
 #define AD1938_DAC_CTRL2        4
 #define AD1938_DAC_CHNL_MUTE    5
@@ -44,6 +46,8 @@
 #define AD1938_DAC_L4_VOL       12
 #define AD1938_DAC_R4_VOL       13
 #define AD1938_ADC_CTRL0        14
+#define ADC_POWERDOWN           0x01
+#define ADC_HIGHPASS_FILTER	1
 #define ADC0_MUTE 		2
 #define ADC1_MUTE 		3
 #define ADC2_MUTE 		4
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to