[alsa-devel] AT32AP7000 (AVR32) + WM8510

Geoffrey Wossum geoffrey at pager.net
Tue May 27 17:16:56 CEST 2008


On Monday 26 May 2008 04:45:24 am Mark Brown wrote:
> On Fri, May 23, 2008 at 05:56:00PM -0500, Geoffrey Wossum wrote:

> The frame clock rate generated by the WM8510 will be 1/256 of the system
> clock, which should be configured to 256 times the sample rate using the
> PLL and dividers.

Ah, so FRAME is not affected by BCLKDIV.  I was misled by figure 31 on page 54 
of the WM8510 datasheet, which gives the impression that BCLKDIV affects 
FRAME as well as BCLK.  I might give the WM8510 a try as the master again, 
since I can get the sample rates closer with it.


> > I had to change the WM8510's format to say that it wanted big endian. 
> > Should the CODEC (and platform and machine) be setup just to say that it
> > uses SNDRV_PCM_FMTBIT_S16 instead of SNDRV_PCM_FMTBIT_S16_LE/BE?  Looks
> > from sound/pcm.h this should just do the Right Thing.
>
> Yes, it does look reasonable - I'll investigate.

I've tried it out, and it seems to do exactly the right thing.  Only problem 
is that there is no auto-endian constant for SNDRV_PCM_FMTBIT_S24_3LE/3BE.  I 
don't know enough to know if this is an oversight or a technical issue.

It seems like the other CODEC drivers will eventually need to use the 
auto-endian constants, especially if big endian microcontrollers like the 
AVR32 catch on.

I'm attaching my final patch to sound/soc/codecs/wm8510.c (again, this is 
against a 2.6.24 kernel).  I noticed that there seems to be a misprint in the 
WM8510 datasheet.  Register 0x32 (SPKMIX) seems to have a different default 
value than what is in the datasheet.  Also, the driver never configured CLKSEL 
to run the CODEC from PLL.  I changed wm8510_set_dai_pll() to automatically 
set CLKSEL when the PLL is started, and to clear CLKSEL when the PLL is 
turned off.  It doesn't seem like anyone would ever configure the PLL if they 
didn't want to use the PLL.

Thanks!
---
Geoffrey

Index: linux-2.6.24.3.atmel.3/sound/soc/codecs/wm8510.c
===================================================================
--- linux-2.6.24.3.atmel.3.orig/sound/soc/codecs/wm8510.c
+++ linux-2.6.24.3.atmel.3/sound/soc/codecs/wm8510.c
@@ -71,7 +71,7 @@ static const u16 wm8510_reg[WM8510_CACHE
     0x0008, 0x000c, 0x0093, 0x00e9,
     0x0000, 0x0000, 0x0000, 0x0000,
     0x0003, 0x0010, 0x0000, 0x0000,
-    0x0000, 0x0002, 0x0000, 0x0000,
+    0x0000, 0x0002, 0x0001, 0x0000,
     0x0000, 0x0000, 0x0039, 0x0000,
     0x0000,
 };
@@ -186,7 +186,7 @@ SOC_SINGLE("Speaker Playback Volume", WM
 SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
 
 SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST,  8, 1, 0),
-SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
 };
 
 /* add non dapm controls */
@@ -209,14 +209,14 @@ static int wm8510_add_controls(struct sn
 static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
 SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
 SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
 };
 
 /* Mono Output Mixer */
 static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
 SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
 SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 1),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
 };
 
 /* AUX Input boost vol */
@@ -251,7 +251,7 @@ SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_
 	&wm8510_mono_mixer_controls[0],
 	ARRAY_SIZE(wm8510_mono_mixer_controls)),
 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
-SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
 SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
 SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
 SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
@@ -384,6 +384,11 @@ static int wm8510_set_dai_pll(struct snd
 	u16 reg;
 
 	if (freq_in == 0 || freq_out == 0) {
+                /* Clock CODEC directly from MCLK */
+                reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+                wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+
+                /* Turn off PLL */
 		reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
 		wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
 		return 0;
@@ -391,12 +396,17 @@ static int wm8510_set_dai_pll(struct snd
 
 	pll_factors(freq_out*8, freq_in);
 
+        /* Set PLL parameters, enable PLL */
 	wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
 	wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
-	wm8510_write(codec, WM8510_PLLK1, (pll_div.k >> 9) && 0x1ff);
-	wm8510_write(codec, WM8510_PLLK1, pll_div.k && 0x1ff);
+	wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+	wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
 	reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
 	wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+
+        /* Run CODEC from PLL instead of MCLK */
+        reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+        wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
 	return 0;
 
 }
@@ -412,23 +422,23 @@ static int wm8510_set_dai_clkdiv(struct 
 
 	switch (div_id) {
 	case WM8510_OPCLKDIV:
-		reg = wm8510_read_reg_cache(codec, WM8510_GPIO & 0x1cf);
+                reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
 		wm8510_write(codec, WM8510_GPIO, reg | div);
 		break;
 	case WM8510_MCLKDIV:
-		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK & 0x1f);
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
 		wm8510_write(codec, WM8510_CLOCK, reg | div);
 		break;
 	case WM8510_ADCCLK:
-		reg = wm8510_read_reg_cache(codec, WM8510_ADC & 0x1f7);
+		reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
 		wm8510_write(codec, WM8510_ADC, reg | div);
 		break;
 	case WM8510_DACCLK:
-		reg = wm8510_read_reg_cache(codec, WM8510_DAC & 0x1f7);
+		reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
 		wm8510_write(codec, WM8510_DAC, reg | div);
 		break;
 	case WM8510_BCLKDIV:
-		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK & 0x1e3);
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
 		wm8510_write(codec, WM8510_CLOCK, reg | div);
 		break;
 	default:
@@ -506,15 +516,15 @@ static int wm8510_pcm_hw_params(struct s
 
 	/* bit size */
 	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16:
 		break;
 	case SNDRV_PCM_FORMAT_S20_3LE:
 		iface |= 0x0020;
 		break;
-	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24:
 		iface |= 0x0040;
 		break;
-	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32:
 		iface |= 0x0060;
 		break;
 	}
@@ -573,7 +583,9 @@ static int wm8510_dapm_event(struct snd_
 		break;
 	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
 		/* everything off except vref/vmid, dac mute, inactive */
-
+                wm8510_write(codec, WM8510_POWER1, 0x00f);
+		wm8510_write(codec, WM8510_POWER2, 0x0);
+		wm8510_write(codec, WM8510_POWER3, 0x0);
 		break;
 	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
 		/* everything off, dac mute, inactive */
@@ -590,21 +602,21 @@ static int wm8510_dapm_event(struct snd_
 		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
 		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
 
-#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-	SNDRV_PCM_FMTBIT_S24_LE)
+#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24)
 
 struct snd_soc_codec_dai wm8510_dai = {
 	.name = "WM8510 HiFi",
 	.playback = {
 		.stream_name = "Playback",
-		.channels_min = 1,
-		.channels_max = 1,
+		.channels_min = 2,
+		.channels_max = 2,
 		.rates = WM8510_RATES,
 		.formats = WM8510_FORMATS,},
 	.capture = {
 		.stream_name = "Capture",
-		.channels_min = 1,
-		.channels_max = 1,
+		.channels_min = 2,
+		.channels_max = 2,
 		.rates = WM8510_RATES,
 		.formats = WM8510_FORMATS,},
 	.ops = {



More information about the Alsa-devel mailing list