[alsa-devel] [PATCH] CA0106 SPI DAC powerdown

Trent Piepho xyzzy at speakeasy.org
Wed Jul 25 03:15:50 CEST 2007


Here are three patches for the CA0106 SPI DAC.

The first patch powers down the DACs (other than the front dac) when they
are not in use.  The front dac serves as the output for the line-in
pass-through / capture feedback, so it's not powered down.

The second patch adds a bunch of symbol names for SPI registers, and
converts the existing init sequence from magic numbers to these names.  The
sequence doesn't change at all.

The third patch cleans up some unnecessary repeated sets to the same
registers in the SPI init sequence.

Of course, I have tested all these patches and they work.
-------------- next part --------------
# HG changeset patch
# User Trent Piepho <xyzzy at speakeasy.org>
# Date 1185320510 25200
# Node ID f5c5a75ae9d670bfc0bd1c5fe444075e64a96313
# Parent  c2078875c0cd21816516b5cbb1f2e404aca512bb
ca0106: power down SPI DAC channels when not in use

For cards with an SPI DAC (SB Live 24-bit / Audigy SE), power down channels
0-2 when not in use.  They are powered up on PCM open and down again on PCM
close.  Channel 4 (== Front) is not powered down, as it is used for capture
feedback.  Powering it down would effectively kill line in pass-through.

Signed-off-by: Trent Piepho <xyzzy at speakeasy.org>

diff -r c2078875c0cd -r f5c5a75ae9d6 pci/ca0106/ca0106.h
--- a/pci/ca0106/ca0106.h	Tue Jul 24 12:10:34 2007 +0200
+++ b/pci/ca0106/ca0106.h	Tue Jul 24 16:41:50 2007 -0700
@@ -587,7 +587,7 @@
 #define SPI_DACD0_BIT		1
 #define SPI_DACD1_BIT		2
 #define SPI_DACD2_BIT		3
-#define SPI_DACD4_BIT		1
+#define SPI_DACD4_BIT		0	/* datasheet error says it's 1 */
 
 #define SPI_PWRDNALL_REG	10	/* power down everything */
 #define SPI_PWRDNALL_BIT	4
diff -r c2078875c0cd -r f5c5a75ae9d6 pci/ca0106/ca0106_main.c
--- a/pci/ca0106/ca0106_main.c	Tue Jul 24 12:10:34 2007 +0200
+++ b/pci/ca0106/ca0106_main.c	Tue Jul 24 16:41:50 2007 -0700
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James at superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.24
+ *  Version: 0.0.25
  *
  *  FEATURES currently supported:
  *    Front, Rear and Center/LFE.
@@ -81,6 +81,8 @@
  *    Implement support for Line-in capture on SB Live 24bit.
  *  0.0.24
  *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
+ *  0.0.25
+ *    Powerdown SPI DAC channels when not in use
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-ca0106 kernel module.
@@ -458,6 +460,19 @@ static void snd_ca0106_pcm_free_substrea
 	kfree(runtime->private_data);
 }
 
+static const int spi_dacd_reg[] = {
+	[PCM_FRONT_CHANNEL]	= SPI_DACD4_REG,
+	[PCM_REAR_CHANNEL]	= SPI_DACD0_REG,
+	[PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG,
+	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_REG,
+};
+static const int spi_dacd_bit[] = {
+	[PCM_FRONT_CHANNEL]	= 1<<SPI_DACD4_BIT,
+	[PCM_REAR_CHANNEL]	= 1<<SPI_DACD0_BIT,
+	[PCM_CENTER_LFE_CHANNEL]= 1<<SPI_DACD2_BIT,
+	[PCM_UNKNOWN_CHANNEL]	= 1<<SPI_DACD1_BIT,
+};
+
 /* open_playback callback */
 static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
 						int channel_id)
@@ -492,6 +507,16 @@ static int snd_ca0106_pcm_open_playback_
                 return err;
 	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
                 return err;
+
+	if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) {
+		const int reg = spi_dacd_reg[channel_id];
+
+		/* Power up dac */
+		chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id];
+		err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+		if (err < 0)
+			return err;
+	}
 	return 0;
 }
 
@@ -502,6 +527,14 @@ static int snd_ca0106_pcm_close_playback
 	struct snd_pcm_runtime *runtime = substream->runtime;
         struct snd_ca0106_pcm *epcm = runtime->private_data;
 	chip->playback_channels[epcm->channel_id].use = 0;
+
+	if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
+		const int reg = spi_dacd_reg[epcm->channel_id];
+
+		/* Power down DAC */
+		chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id];
+		snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+	}
 	/* FIXME: maybe zero others */
 	return 0;
 }
@@ -1246,7 +1279,7 @@ static unsigned int spi_dac_init[] = {
 	0x0530,
 	0x0602,
 	0x0622,
-	0x1400,
+	0x140e,
 };
 
 static unsigned int i2c_adc_init[][2] = {
-------------- next part --------------
# HG changeset patch
# User Trent Piepho <xyzzy at speakeasy.org>
# Date 1185324418 25200
# Node ID 9d3c78b9b2aeb8df79175747291307d4afce3b9b
# Parent  f5c5a75ae9d670bfc0bd1c5fe444075e64a96313
ca0106: Add more symbol SPI register names and use them

Add more symbol name for SPI register values.  Change the SPI_XXX_BIT defines
from the bit number to a mask.  Saves having to write (1<<SPI_XXX_BIT) all the
time to convert to mask.  We never end up wanting the bit number.

Use all the symbol names for the SPI DAC init sequence.  The sequence is
exactly the same as it was before.

Signed-off-by: Trent Piepho <xyzzy at speakeasy.org>

diff -r f5c5a75ae9d6 -r 9d3c78b9b2ae pci/ca0106/ca0106.h
--- a/pci/ca0106/ca0106.h	Tue Jul 24 16:41:50 2007 -0700
+++ b/pci/ca0106/ca0106.h	Tue Jul 24 17:46:58 2007 -0700
@@ -559,38 +559,89 @@
 #define SPI_REG_MASK	0x1ff	/* 16-bit SPI writes have a 7-bit address */
 #define SPI_REG_SHIFT	9	/* followed by 9 bits of data */
 
+#define SPI_LDA1_REG		0	/* digital attenuation */
+#define SPI_RDA1_REG		1
+#define SPI_LDA2_REG		4
+#define SPI_RDA2_REG		5
+#define SPI_LDA3_REG		6
+#define SPI_RDA3_REG		7
+#define SPI_LDA4_REG		13
+#define SPI_RDA4_REG		14
+#define SPI_MASTDA_REG		8
+
+#define SPI_DA_BIT_UPDATE	(1<<8)	/* update attenuation values */
+#define SPI_DA_BIT_0dB		0xff	/* 0 dB */
+#define SPI_DA_BIT_infdB	0x00	/* inf dB attenuation (mute) */
+
+#define SPI_PL_REG		2
+#define SPI_PL_BIT_L_M		(0<<5)	/* left channel = mute */
+#define SPI_PL_BIT_L_L		(1<<5)	/* left channel = left */
+#define SPI_PL_BIT_L_R		(2<<5)	/* left channel = right */
+#define SPI_PL_BIT_L_C		(3<<5)	/* left channel = (L+R)/2 */
+#define SPI_PL_BIT_R_M		(0<<7)	/* right channel = mute */
+#define SPI_PL_BIT_R_L		(1<<7)	/* right channel = left */
+#define SPI_PL_BIT_R_R		(2<<7)	/* right channel = right */
+#define SPI_PL_BIT_R_C		(3<<7)	/* right channel = (L+R)/2 */
+#define SPI_IZD_REG		2
+#define SPI_IZD_BIT		(1<<4)	/* infinite zero detect */
+
+#define SPI_FMT_REG		3
+#define SPI_FMT_BIT_RJ		(0<<0)	/* right justified mode */
+#define SPI_FMT_BIT_LJ		(1<<0)	/* left justified mode */
+#define SPI_FMT_BIT_I2S		(2<<0)	/* I2S mode */
+#define SPI_FMT_BIT_DSP		(3<<0)	/* DSP Modes A or B */
+#define SPI_LRP_REG		3
+#define SPI_LRP_BIT		(1<<2)	/* invert LRCLK polarity */
+#define SPI_BCP_REG		3
+#define SPI_BCP_BIT		(1<<3)	/* invert BCLK polarity */
+#define SPI_IWL_REG		3
+#define SPI_IWL_BIT_16		(0<<4)	/* 16-bit world length */
+#define SPI_IWL_BIT_20		(1<<4)	/* 20-bit world length */
+#define SPI_IWL_BIT_24		(2<<4)	/* 24-bit world length */
+#define SPI_IWL_BIT_32		(3<<4)	/* 32-bit world length */
+
+#define SPI_MS_REG		10
+#define SPI_MS_BIT		(1<<5)	/* master mode */
+#define SPI_RATE_REG		10	/* only applies in master mode */
+#define SPI_RATE_BIT_128	(0<<6)	/* MCLK = LRCLK * 128 */
+#define SPI_RATE_BIT_192	(1<<6)
+#define SPI_RATE_BIT_256	(2<<6)
+#define SPI_RATE_BIT_384	(3<<6)
+#define SPI_RATE_BIT_512	(4<<6)
+#define SPI_RATE_BIT_768	(5<<6)
+
 /* They really do label the bit for the 4th channel "4" and not "3" */
 #define SPI_DMUTE0_REG		9
 #define SPI_DMUTE1_REG		9
 #define SPI_DMUTE2_REG		9
 #define SPI_DMUTE4_REG		15
-#define SPI_DMUTE0_BIT		3
-#define SPI_DMUTE1_BIT		4
-#define SPI_DMUTE2_BIT		5
-#define SPI_DMUTE4_BIT		2
+#define SPI_DMUTE0_BIT		(1<<3)
+#define SPI_DMUTE1_BIT		(1<<4)
+#define SPI_DMUTE2_BIT		(1<<5)
+#define SPI_DMUTE4_BIT		(1<<2)
 
 #define SPI_PHASE0_REG		3
 #define SPI_PHASE1_REG		3
 #define SPI_PHASE2_REG		3
 #define SPI_PHASE4_REG		15
-#define SPI_PHASE0_BIT		6
-#define SPI_PHASE1_BIT		7
-#define SPI_PHASE2_BIT		8
-#define SPI_PHASE4_BIT		3
+#define SPI_PHASE0_BIT		(1<<6)
+#define SPI_PHASE1_BIT		(1<<7)
+#define SPI_PHASE2_BIT		(1<<8)
+#define SPI_PHASE4_BIT		(1<<3)
 
 #define SPI_PDWN_REG		2	/* power down all DACs */
-#define SPI_PDWN_BIT		2
+#define SPI_PDWN_BIT		(1<<2)
 #define SPI_DACD0_REG		10	/* power down individual DACs */
 #define SPI_DACD1_REG		10
 #define SPI_DACD2_REG		10
 #define SPI_DACD4_REG		15
-#define SPI_DACD0_BIT		1
-#define SPI_DACD1_BIT		2
-#define SPI_DACD2_BIT		3
-#define SPI_DACD4_BIT		0	/* datasheet error says it's 1 */
+#define SPI_DACD0_BIT		(1<<1)
+#define SPI_DACD1_BIT		(1<<2)
+#define SPI_DACD2_BIT		(1<<3)
+#define SPI_DACD4_BIT		(1<<0)	/* datasheet error says it's 1 */
 
 #define SPI_PWRDNALL_REG	10	/* power down everything */
-#define SPI_PWRDNALL_BIT	4
+#define SPI_PWRDNALL_BIT	(1<<4)
 
 #include "ca_midi.h"
 
diff -r f5c5a75ae9d6 -r 9d3c78b9b2ae pci/ca0106/ca0106_main.c
--- a/pci/ca0106/ca0106_main.c	Tue Jul 24 16:41:50 2007 -0700
+++ b/pci/ca0106/ca0106_main.c	Tue Jul 24 17:46:58 2007 -0700
@@ -467,10 +467,10 @@ static const int spi_dacd_reg[] = {
 	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_REG,
 };
 static const int spi_dacd_bit[] = {
-	[PCM_FRONT_CHANNEL]	= 1<<SPI_DACD4_BIT,
-	[PCM_REAR_CHANNEL]	= 1<<SPI_DACD0_BIT,
-	[PCM_CENTER_LFE_CHANNEL]= 1<<SPI_DACD2_BIT,
-	[PCM_UNKNOWN_CHANNEL]	= 1<<SPI_DACD1_BIT,
+	[PCM_FRONT_CHANNEL]	= SPI_DACD4_BIT,
+	[PCM_REAR_CHANNEL]	= SPI_DACD0_BIT,
+	[PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT,
+	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_BIT,
 };
 
 /* open_playback callback */
@@ -1258,28 +1258,29 @@ static int __devinit snd_ca0106_pcm(stru
 	return 0;
 }
 
+#define SPI_REG(reg, value)	(((reg) << SPI_REG_SHIFT) | (value))
 static unsigned int spi_dac_init[] = {
-	0x00ff,
-	0x02ff,
-	0x0400,
-	0x0520,
-	0x0620, /* Set 24 bit. Was 0x0600 */
-	0x08ff,
-	0x0aff,
-	0x0cff,
-	0x0eff,
-	0x10ff,
-	0x1200,
-	0x1400,
-	0x1480,
-	0x1800,
-	0x1aff,
-	0x1cff,
-	0x1e00,
-	0x0530,
-	0x0602,
-	0x0622,
-	0x140e,
+	SPI_REG(SPI_LDA1_REG,	SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
+	SPI_REG(SPI_RDA1_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_PL_REG,	0x00),
+	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R),
+	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_RJ | SPI_IWL_BIT_24),
+	SPI_REG(SPI_LDA2_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA2_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_LDA3_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA3_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_MASTDA_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(9,		0x00),
+	SPI_REG(SPI_MS_REG,	0x00),
+	SPI_REG(SPI_MS_REG,	SPI_RATE_BIT_256),
+	SPI_REG(12,		0x00),
+	SPI_REG(SPI_LDA4_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA4_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(15,		0x00),
+	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
+	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S),
+	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S | SPI_IWL_BIT_24),
+	SPI_REG(SPI_MS_REG,	SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT),
 };
 
 static unsigned int i2c_adc_init[][2] = {
diff -r f5c5a75ae9d6 -r 9d3c78b9b2ae pci/ca0106/ca0106_mixer.c
--- a/pci/ca0106/ca0106_mixer.c	Tue Jul 24 16:41:50 2007 -0700
+++ b/pci/ca0106/ca0106_mixer.c	Tue Jul 24 17:46:58 2007 -0700
@@ -599,7 +599,7 @@ static struct snd_kcontrol_new snd_ca010
 	.info	= spi_mute_info,				\
 	.get	= spi_mute_get,					\
 	.put	= spi_mute_put,					\
-	.private_value = (reg<<SPI_REG_SHIFT) | (1<<bit)	\
+	.private_value = (reg<<SPI_REG_SHIFT) | (bit)		\
 }
 
 static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
-------------- next part --------------
# HG changeset patch
# User Trent Piepho <xyzzy at speakeasy.org>
# Date 1185324880 25200
# Node ID 4d25787f9eb63b4671deaac12705a8c010c6db71
# Parent  9d3c78b9b2aeb8df79175747291307d4afce3b9b
ca0106: remove extra commands in SPI DAC init sequence

The init sequence set a number of registers more than once to different
values.  It's only necessary to set them once to their final values.

It also never actually updated the digital attenuation settings.

Signed-off-by: Trent Piepho <xyzzy at speakeasy.org>

diff -r 9d3c78b9b2ae -r 4d25787f9eb6 pci/ca0106/ca0106_main.c
--- a/pci/ca0106/ca0106_main.c	Tue Jul 24 17:46:58 2007 -0700
+++ b/pci/ca0106/ca0106_main.c	Tue Jul 24 17:54:40 2007 -0700
@@ -1262,25 +1262,19 @@ static unsigned int spi_dac_init[] = {
 static unsigned int spi_dac_init[] = {
 	SPI_REG(SPI_LDA1_REG,	SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
 	SPI_REG(SPI_RDA1_REG,	SPI_DA_BIT_0dB),
-	SPI_REG(SPI_PL_REG,	0x00),
-	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R),
-	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_RJ | SPI_IWL_BIT_24),
+	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
+	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S | SPI_IWL_BIT_24),
 	SPI_REG(SPI_LDA2_REG,	SPI_DA_BIT_0dB),
 	SPI_REG(SPI_RDA2_REG,	SPI_DA_BIT_0dB),
 	SPI_REG(SPI_LDA3_REG,	SPI_DA_BIT_0dB),
 	SPI_REG(SPI_RDA3_REG,	SPI_DA_BIT_0dB),
 	SPI_REG(SPI_MASTDA_REG,	SPI_DA_BIT_0dB),
 	SPI_REG(9,		0x00),
-	SPI_REG(SPI_MS_REG,	0x00),
-	SPI_REG(SPI_MS_REG,	SPI_RATE_BIT_256),
+	SPI_REG(SPI_MS_REG,	SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT),
 	SPI_REG(12,		0x00),
 	SPI_REG(SPI_LDA4_REG,	SPI_DA_BIT_0dB),
-	SPI_REG(SPI_RDA4_REG,	SPI_DA_BIT_0dB),
-	SPI_REG(15,		0x00),
-	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
-	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S),
-	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S | SPI_IWL_BIT_24),
-	SPI_REG(SPI_MS_REG,	SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT),
+	SPI_REG(SPI_RDA4_REG,	SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE),
+	SPI_REG(SPI_DACD4_REG,	0x00),
 };
 
 static unsigned int i2c_adc_init[][2] = {


More information about the Alsa-devel mailing list