public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] AC97 support for low power codecs
@ 2005-01-04 11:34 Liam Girdwood
  2005-01-04 15:08 ` Takashi Iwai
  0 siblings, 1 reply; 5+ messages in thread
From: Liam Girdwood @ 2005-01-04 11:34 UTC (permalink / raw)
  To: Alan Cox; +Cc: Jeff Garzik, lkml

[-- Attachment #1: Type: text/plain, Size: 776 bytes --]

Hi Alan,

This is a resend of a patch that has been applied to 2.4.

I've attached a patch against 2.6.10 that checks the codec ID before
doing an AC97 register reset. This allows the kernel to support low
power codecs that are powered down by a reset command. This patch also
fixes some other minor issues.

A similar patch for ALSA will follow soon.

Changes:-

 o Added AC97_DEFAULT_POWER_OFF to ac97_codec_ids[]
 o ac97_probe now checks hardwired codec ID's before sending a reset
 o Added support for WM9713
 o Moved the codec specific inits after the mixer setup as some init
settings were being clobbered.
 o Added extra check so that default_digital_ops doesn't overwrite a
valid codec_ops. (SPDIF)

Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>

Liam

[-- Attachment #2: ac97_codec.diff --]
[-- Type: text/x-patch, Size: 6328 bytes --]

--- a/sound/oss/ac97_codec.c	2004-12-24 21:34:00.000000000 +0000
+++ b/sound/oss/ac97_codec.c	2004-12-08 17:08:42.000000000 +0000
@@ -30,6 +30,9 @@
  **************************************************************************
  *
  * History
+ * Feb 25, 2004 Liam Girdwood
+ *  Added support for codecs that require a warm reset to power up.
+ *  Support for WM9713
  * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
  *	Removed non existant WM9700
  *	Added support for WM9705, WM9708, WM9709, WM9710, WM9711
@@ -70,6 +73,7 @@
 static int wolfson_init04(struct ac97_codec * codec);
 static int wolfson_init05(struct ac97_codec * codec);
 static int wolfson_init11(struct ac97_codec * codec);
+static int wolfson_init13(struct ac97_codec * codec);
 static int tritech_init(struct ac97_codec * codec);
 static int tritech_maestro_init(struct ac97_codec * codec);
 static int sigmatel_9708_init(struct ac97_codec *codec);
@@ -106,6 +110,7 @@
 static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
 static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
 static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
+static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
 static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
 static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
 static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
@@ -167,6 +172,7 @@
 	{0x574D4C05, "Wolfson WM9705/WM9710",   &wolfson_ops05},
 	{0x574D4C09, "Wolfson WM9709",		&null_ops},
 	{0x574D4C12, "Wolfson WM9711/9712",	&wolfson_ops11},
+	{0x574D4C13, "Wolfson WM9713",	&wolfson_ops13, AC97_DEFAULT_POWER_OFF},
 	{0x83847600, "SigmaTel STAC????",	&null_ops},
 	{0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
 	{0x83847605, "SigmaTel STAC9704",	&null_ops},
@@ -793,6 +799,9 @@
  *	Currently codec_wait is used to wait for AC97 codec
  *	reset to complete. 
  *
+ *  Some codecs will power down when a register reset is
+ *  performed. We now check for such codecs.
+ *
  *	Returns 1 (true) on success, or 0 (false) on failure.
  */
  
@@ -806,34 +815,17 @@
 	struct list_head *l;
 	struct ac97_driver *d;
 	
-	/* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should 
-	 * be read zero.
-	 *
-	 * FIXME: is the following comment outdated?  -jgarzik 
-	 * Probing of AC97 in this way is not reliable, it is not even SAFE !!
-	 */
-	codec->codec_write(codec, AC97_RESET, 0L);
-
-	/* also according to spec, we wait for codec-ready state */	
+	/* wait for codec-ready state */	
 	if (codec->codec_wait)
 		codec->codec_wait(codec);
 	else
 		udelay(10);
-
-	if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
-		printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
-		       (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") 
-		       : (codec->id&1 ? "Secondary":  "Primary"));
-		return 0;
-	}
-
-	/* probe for Modem Codec */
-	codec->modem = ac97_check_modem(codec);
-	codec->name = NULL;
-	codec->codec_ops = &default_ops;
-
+	
+	/* will the codec power down if register reset ? */
 	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
 	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+	codec->name = NULL;
+	codec->codec_ops = &null_ops;
 	for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
 		if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
 			codec->type = ac97_codec_ids[i].id;
@@ -845,9 +837,34 @@
 	}
 
 	codec->model = (id1 << 16) | id2;
+	if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
+		/* reset codec and wait for the ready bit before we continue */
+		codec->codec_write(codec, AC97_RESET, 0L);
+		if (codec->codec_wait)
+			codec->codec_wait(codec);
+		else
+			udelay(10);
+	}
 	
+	/* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should 
+	 * be read zero.
+	 *
+	 * FIXME: is the following comment outdated?  -jgarzik 
+	 * Probing of AC97 in this way is not reliable, it is not even SAFE !!
+	 */
+	if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
+		printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
+		       (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") 
+		       : (codec->id&1 ? "Secondary":  "Primary"));
+		return 0;
+	}
+	
+	/* probe for Modem Codec */
+	codec->modem = ac97_check_modem(codec);
+	
+	/* enable SPDIF */
 	f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
-	if(f & 4)
+	if((codec->codec_ops == &null_ops) && (f & 4))
 		codec->codec_ops = &default_digital_ops;
 	
 	/* A device which thinks its a modem but isnt */
@@ -916,11 +933,6 @@
 	codec->recmask_io = ac97_recmask_io;
 	codec->mixer_ioctl = ac97_mixer_ioctl;
 
-	/* codec specific initialization for 4-6 channel output or secondary codec stuff */
-	if (codec->codec_ops->init != NULL) {
-		codec->codec_ops->init(codec);
-	}
-
 	/* initialize mixer channel volumes */
 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
 		struct mixer_defaults *md = &mixer_defaults[i];
@@ -930,6 +942,11 @@
 			continue;
 		ac97_set_mixer(codec, md->mixer, md->value);
 	}
+	
+	/* codec specific initialization for 4-6 channel output or secondary codec stuff */
+	if (codec->codec_ops->init != NULL) {
+		codec->codec_ops->init(codec);
+	}
 
 	/*
 	 *	Volume is MUTE only on this device. We have to initialise
@@ -1086,6 +1103,19 @@
 	return 0;
 }
 
+/* WM9713 */
+static int wolfson_init13(struct ac97_codec * codec)
+{
+	codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);	
+	codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
+	codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
+	codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
+	codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
+	codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
+
+	return 0;
+}
+
 static int tritech_init(struct ac97_codec * codec)
 {
 	codec->codec_write(codec, 0x26, 0x0300);
--- a/include/linux/ac97_codec.h	2004-12-24 21:33:50.000000000 +0000
+++ b/include/linux/ac97_codec.h	2004-12-08 13:28:42.000000000 +0000
@@ -290,6 +290,7 @@
 	
 #define AC97_DELUDED_MODEM	1	/* Audio codec reports its a modem */
 #define AC97_NO_PCM_VOLUME	2	/* Volume control is missing 	   */
+#define AC97_DEFAULT_POWER_OFF 4 	/* Needs warm reset to power up */
 };
 
 extern int ac97_read_proc (char *page_out, char **start, off_t off,

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] AC97 support for low power codecs
  2005-01-04 11:34 [PATCH] AC97 support for low power codecs Liam Girdwood
@ 2005-01-04 15:08 ` Takashi Iwai
  2005-01-04 15:27   ` Liam Girdwood
  0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2005-01-04 15:08 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: Alan Cox, Jeff Garzik, lkml

At Tue, 04 Jan 2005 11:34:10 +0000,
Liam Girdwood wrote:
> 
> I've attached a patch against 2.6.10 that checks the codec ID before
> doing an AC97 register reset. This allows the kernel to support low
> power codecs that are powered down by a reset command. This patch also
> fixes some other minor issues.

Does writing RESET on such a codec must be avoided always?
Or can one power up again by writing some values on POWER register?


Takashi

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] AC97 support for low power codecs
  2005-01-04 15:08 ` Takashi Iwai
@ 2005-01-04 15:27   ` Liam Girdwood
  2005-01-04 15:47     ` Takashi Iwai
  0 siblings, 1 reply; 5+ messages in thread
From: Liam Girdwood @ 2005-01-04 15:27 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Alan Cox, Jeff Garzik, lkml

On Tue, 2005-01-04 at 15:08, Takashi Iwai wrote:
> 
> Does writing RESET on such a codec must be avoided always?
> Or can one power up again by writing some values on POWER register?
> 

Writing RESET causes an AC97 codec to return to it's default state. On
some codecs this is state is "power down" and therefore undesirable
during a call to probe.

The only way to wake such a codec is via an AC97 warm reset.

Liam
  


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] AC97 support for low power codecs
  2005-01-04 15:27   ` Liam Girdwood
@ 2005-01-04 15:47     ` Takashi Iwai
  2005-01-04 16:24       ` Liam Girdwood
  0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2005-01-04 15:47 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: Alan Cox, Jeff Garzik, lkml

At Tue, 04 Jan 2005 15:27:02 +0000,
Liam Girdwood wrote:
> 
> On Tue, 2005-01-04 at 15:08, Takashi Iwai wrote:
> > 
> > Does writing RESET on such a codec must be avoided always?
> > Or can one power up again by writing some values on POWER register?
> > 
> 
> Writing RESET causes an AC97 codec to return to it's default state. On
> some codecs this is state is "power down" and therefore undesirable
> during a call to probe.
> 
> The only way to wake such a codec is via an AC97 warm reset.

Understood.  Thanks for explanation.

I have also a patch for ALSA about this as attached below.  It's
to the very latest ALSA CVS version (and untested, of course ;)


Takashi


--- linux/include/sound/ac97_codec.h	4 Jan 2005 03:27:54 -0000	1.61
+++ linux/include/sound/ac97_codec.h	4 Jan 2005 14:43:24 -0000
@@ -366,7 +366,9 @@
 #define AC97_DOUBLE_RATE	(1<<5)	/* supports double rate playback */
 #define AC97_HAS_NO_MASTER_VOL	(1<<6)	/* no Master volume */
 #define AC97_HAS_NO_PCM_VOL	(1<<7)	/* no PCM volume */
+#define AC97_DEFAULT_POWER_OFF	(1<<8)	/* no RESET write */
+#define AC97_MODEM_PATCH	(1<<9)	/* modem patch */
 
 /* rates indexes */
 #define AC97_RATES_FRONT_DAC	0
 #define CONFIG_SND_DATE ""
--- linux/sound/pci/ac97/ac97_codec.c	4 Jan 2005 11:08:31 -0000	1.159
+++ linux/sound/pci/ac97/ac97_codec.c	4 Jan 2005 15:07:17 -0000
@@ -55,127 +55,128 @@
 	unsigned int mask;
 	const char *name;
 	int (*patch)(ac97_t *ac97);
-	int (*mpatch)(ac97_t *ac97);
+	unsigned int flags;
 } ac97_codec_id_t;
 
 static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = {
-{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },
-{ 0x41445300, 0xffffff00, "Analog Devices",	NULL,	NULL },
-{ 0x414c4300, 0xffffff00, "Realtek",		NULL,	NULL },
-{ 0x414c4700, 0xffffff00, "Realtek",		NULL,	NULL },
-{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL,	NULL },
-{ 0x43525900, 0xffffff00, "Cirrus Logic",	NULL,	NULL },
-{ 0x43585400, 0xffffff00, "Conexant",           NULL,	NULL },
-{ 0x44543000, 0xffffff00, "Diamond Technology", NULL,	NULL },
-{ 0x454d4300, 0xffffff00, "eMicro",		NULL,	NULL },
-{ 0x45838300, 0xffffff00, "ESS Technology",	NULL,	NULL },
-{ 0x48525300, 0xffffff00, "Intersil",		NULL,	NULL },
-{ 0x49434500, 0xffffff00, "ICEnsemble",		NULL,	NULL },
-{ 0x49544500, 0xffffff00, "ITE Tech.Inc",	NULL,	NULL },
-{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL },
-{ 0x50534300, 0xffffff00, "Philips",		NULL,	NULL },
-{ 0x53494c00, 0xffffff00, "Silicon Laboratory",	NULL,	NULL },
-{ 0x54524100, 0xffffff00, "TriTech",		NULL,	NULL },
-{ 0x54584e00, 0xffffff00, "Texas Instruments",	NULL,	NULL },
-{ 0x56494100, 0xffffff00, "VIA Technologies",   NULL,	NULL },
-{ 0x57454300, 0xffffff00, "Winbond",		NULL,	NULL },
-{ 0x574d4c00, 0xffffff00, "Wolfson",		NULL,	NULL },
-{ 0x594d4800, 0xffffff00, "Yamaha",		NULL,	NULL },
-{ 0x83847600, 0xffffff00, "SigmaTel",		NULL,	NULL },
-{ 0,	      0, 	  NULL,			NULL,	NULL }
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei" },
+{ 0x41445300, 0xffffff00, "Analog Devices" },
+{ 0x414c4300, 0xffffff00, "Realtek" },
+{ 0x414c4700, 0xffffff00, "Realtek" },
+{ 0x434d4900, 0xffffff00, "C-Media Electronics" },
+{ 0x43525900, 0xffffff00, "Cirrus Logic" },
+{ 0x43585400, 0xffffff00, "Conexant" },
+{ 0x44543000, 0xffffff00, "Diamond Technology" },
+{ 0x454d4300, 0xffffff00, "eMicro" },
+{ 0x45838300, 0xffffff00, "ESS Technology" },
+{ 0x48525300, 0xffffff00, "Intersil" },
+{ 0x49434500, 0xffffff00, "ICEnsemble" },
+{ 0x49544500, 0xffffff00, "ITE Tech.Inc" },
+{ 0x4e534300, 0xffffff00, "National Semiconductor" },
+{ 0x50534300, 0xffffff00, "Philips" },
+{ 0x53494c00, 0xffffff00, "Silicon Laboratory" },
+{ 0x54524100, 0xffffff00, "TriTech" },
+{ 0x54584e00, 0xffffff00, "Texas Instruments" },
+{ 0x56494100, 0xffffff00, "VIA Technologies" },
+{ 0x57454300, 0xffffff00, "Winbond" },
+{ 0x574d4c00, 0xffffff00, "Wolfson" },
+{ 0x594d4800, 0xffffff00, "Yamaha" },
+{ 0x83847600, 0xffffff00, "SigmaTel" },
+{ 0 } /* terminator */
 };
 
 static const ac97_codec_id_t snd_ac97_codec_ids[] = {
-{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL },
-{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL },
-{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL },
-{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL },
-{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },
-{ 0x41445303, 0xffffffff, "AD1819",		patch_ad1819,	NULL },
-{ 0x41445340, 0xffffffff, "AD1881",		patch_ad1881,	NULL },
-{ 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881,	NULL },
-{ 0x41445360, 0xffffffff, "AD1885",		patch_ad1885,	NULL },
-{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1886,	NULL },
-{ 0x41445362, 0xffffffff, "AD1887",		patch_ad1881,	NULL },
-{ 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881,	NULL },
-{ 0x41445368, 0xffffffff, "AD1888",		patch_ad1888,	NULL },
-{ 0x41445370, 0xffffffff, "AD1980",		patch_ad1980,	NULL },
-{ 0x41445372, 0xffffffff, "AD1981A",		patch_ad1981a,	NULL },
-{ 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b,	NULL },
-{ 0x41445375, 0xffffffff, "AD1985",		patch_ad1985,	NULL },
-{ 0x414c4300, 0xffffff00, "ALC100/100P", 	NULL,		NULL },
-{ 0x414c4710, 0xfffffff0, "ALC200/200P",	NULL,		NULL },
-{ 0x414c4721, 0xffffffff, "ALC650D",		NULL,	NULL }, /* already patched */
-{ 0x414c4722, 0xffffffff, "ALC650E",		NULL,	NULL }, /* already patched */
-{ 0x414c4723, 0xffffffff, "ALC650F",		NULL,	NULL }, /* already patched */
-{ 0x414c4720, 0xfffffff0, "ALC650",		patch_alc650,	NULL },
-{ 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655,	NULL },
-{ 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655,	NULL },
-{ 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850,	NULL },
-{ 0x414c4730, 0xffffffff, "ALC101",		NULL,		NULL },
-{ 0x414c4740, 0xfffffff0, "ALC202",		NULL,		NULL },
-{ 0x414c4750, 0xfffffff0, "ALC250",		NULL,		NULL },
-{ 0x414c4770, 0xfffffff0, "ALC203",		NULL,		NULL },
-{ 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738,	NULL },
-{ 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739,	NULL },
-{ 0x434d4978, 0xffffffff, "CMI9761",		patch_cm9761,	NULL },
-{ 0x434d4982, 0xffffffff, "CMI9761",		patch_cm9761,	NULL },
-{ 0x434d4983, 0xffffffff, "CMI9761",		patch_cm9761,	NULL },
-{ 0x43525900, 0xfffffff8, "CS4297",		NULL,		NULL },
-{ 0x43525910, 0xfffffff8, "CS4297A",		patch_cirrus_spdif,	NULL },
-{ 0x43525920, 0xfffffff8, "CS4298",		patch_cirrus_spdif,		NULL },
-{ 0x43525928, 0xfffffff8, "CS4294",		NULL,		NULL },
-{ 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299,	NULL },
-{ 0x43525948, 0xfffffff8, "CS4201",		NULL,		NULL },
-{ 0x43525958, 0xfffffff8, "CS4205",		patch_cirrus_spdif,	NULL },
-{ 0x43525960, 0xfffffff8, "CS4291",		NULL,		NULL },
-{ 0x43525970, 0xfffffff8, "CS4202",		NULL,		NULL },
-{ 0x43585421, 0xffffffff, "HSD11246",		NULL,		NULL },	// SmartMC II
-{ 0x43585428, 0xfffffff8, "Cx20468",		patch_conexant,	NULL }, // SmartAMC fixme: the mask might be different
-{ 0x44543031, 0xfffffff0, "DT0398",		NULL,		NULL },
-{ 0x454d4328, 0xffffffff, "28028",		NULL,		NULL },  // same as TR28028?
-{ 0x45838308, 0xffffffff, "ESS1988",		NULL,		NULL },
-{ 0x48525300, 0xffffff00, "HMP9701",		NULL,		NULL },
-{ 0x49434501, 0xffffffff, "ICE1230",		NULL,		NULL },
-{ 0x49434511, 0xffffffff, "ICE1232",		NULL,		NULL }, // alias VIA VT1611A?
-{ 0x49434514, 0xffffffff, "ICE1232A",		NULL,		NULL },
-{ 0x49434551, 0xffffffff, "VT1616", 		patch_vt1616,	NULL }, 
-{ 0x49434552, 0xffffffff, "VT1616i",		patch_vt1616,	NULL }, // VT1616 compatible (chipset integrated)
-{ 0x49544520, 0xffffffff, "IT2226E",		NULL,		NULL },
-{ 0x49544561, 0xffffffff, "IT2646E",		patch_it2646,	NULL },
-{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48",	NULL,		NULL }, // only guess --jk
-{ 0x4e534331, 0xffffffff, "LM4549",		NULL,		NULL },
-{ 0x4e534350, 0xffffffff, "LM4550",		NULL,		NULL },
-{ 0x50534304, 0xffffffff, "UCB1400",		NULL,		NULL },
-{ 0x53494c20, 0xffffffe0, "Si3036/8",		NULL,		mpatch_si3036 },
-{ 0x54524102, 0xffffffff, "TR28022",		NULL,		NULL },
-{ 0x54524106, 0xffffffff, "TR28026",		NULL,		NULL },
-{ 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028,	NULL }, // added by xin jin [07/09/99]
-{ 0x54524123, 0xffffffff, "TR28602",		NULL,		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
-{ 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL,		NULL },
-{ 0x56494161, 0xffffffff, "VIA1612A",		NULL,		NULL }, // modified ICE1232 with S/PDIF
-{ 0x57454301, 0xffffffff, "W83971D",		NULL,		NULL },
-{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL,		NULL },
-{ 0x574d4C03, 0xffffffff, "WM9703/WM9707/WM9708/WM9717", patch_wolfson03, NULL},
-{ 0x574d4C04, 0xffffffff, "WM9704M/WM9704Q",	patch_wolfson04, NULL},
-{ 0x574d4C05, 0xffffffff, "WM9705/WM9710",	patch_wolfson05, NULL},
-{ 0x574d4C09, 0xffffffff, "WM9709",		NULL,		NULL},
-{ 0x574d4C12, 0xffffffff, "WM9711/WM9712",	patch_wolfson11, NULL},
-{ 0x594d4800, 0xffffffff, "YMF743",		NULL,		NULL },
-{ 0x594d4802, 0xffffffff, "YMF752",		NULL,		NULL },
-{ 0x594d4803, 0xffffffff, "YMF753",		patch_yamaha_ymf753,	NULL },
-{ 0x83847600, 0xffffffff, "STAC9700/83/84",	patch_sigmatel_stac9700,	NULL },
-{ 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL,		NULL },
-{ 0x83847605, 0xffffffff, "STAC9704",		NULL,		NULL },
-{ 0x83847608, 0xffffffff, "STAC9708/11",	patch_sigmatel_stac9708,	NULL },
-{ 0x83847609, 0xffffffff, "STAC9721/23",	patch_sigmatel_stac9721,	NULL },
-{ 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744,	NULL },
-{ 0x83847650, 0xffffffff, "STAC9750/51",	NULL,		NULL },	// patch?
-{ 0x83847652, 0xffffffff, "STAC9752/53",	NULL,		NULL }, // patch?
-{ 0x83847656, 0xffffffff, "STAC9756/57",	patch_sigmatel_stac9756,	NULL },
-{ 0x83847658, 0xffffffff, "STAC9758/59",	patch_sigmatel_stac9758,	NULL },
-{ 0x83847666, 0xffffffff, "STAC9766/67",	NULL,		NULL }, // patch?
-{ 0, 	      0,	  NULL,			NULL,		NULL }
+{ 0x414b4d00, 0xffffffff, "AK4540",		NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542",		NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543",		NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545",		NULL },
+{ 0x41445303, 0xffffffff, "AD1819",		patch_ad1819 },
+{ 0x41445340, 0xffffffff, "AD1881",		patch_ad1881 },
+{ 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881 },
+{ 0x41445360, 0xffffffff, "AD1885",		patch_ad1885 },
+{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1886 },
+{ 0x41445362, 0xffffffff, "AD1887",		patch_ad1881 },
+{ 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881 },
+{ 0x41445368, 0xffffffff, "AD1888",		patch_ad1888 },
+{ 0x41445370, 0xffffffff, "AD1980",		patch_ad1980 },
+{ 0x41445372, 0xffffffff, "AD1981A",		patch_ad1981a },
+{ 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b },
+{ 0x41445375, 0xffffffff, "AD1985",		patch_ad1985 },
+{ 0x414c4300, 0xffffff00, "ALC100/100P", 	NULL },
+{ 0x414c4710, 0xfffffff0, "ALC200/200P",	NULL },
+{ 0x414c4721, 0xffffffff, "ALC650D",		NULL }, /* already patched */
+{ 0x414c4722, 0xffffffff, "ALC650E",		NULL }, /* already patched */
+{ 0x414c4723, 0xffffffff, "ALC650F",		NULL }, /* already patched */
+{ 0x414c4720, 0xfffffff0, "ALC650",		patch_alc650 },
+{ 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655 },
+{ 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655 },
+{ 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850 },
+{ 0x414c4730, 0xffffffff, "ALC101",		NULL },
+{ 0x414c4740, 0xfffffff0, "ALC202",		NULL },
+{ 0x414c4750, 0xfffffff0, "ALC250",		NULL },
+{ 0x414c4770, 0xfffffff0, "ALC203",		NULL },
+{ 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738 },
+{ 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739 },
+{ 0x434d4978, 0xffffffff, "CMI9761",		patch_cm9761 },
+{ 0x434d4982, 0xffffffff, "CMI9761",		patch_cm9761 },
+{ 0x434d4983, 0xffffffff, "CMI9761",		patch_cm9761 },
+{ 0x43525900, 0xfffffff8, "CS4297",		NULL },
+{ 0x43525910, 0xfffffff8, "CS4297A",		patch_cirrus_spdif },
+{ 0x43525920, 0xfffffff8, "CS4298",		patch_cirrus_spdif },
+{ 0x43525928, 0xfffffff8, "CS4294",		NULL },
+{ 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299 },
+{ 0x43525948, 0xfffffff8, "CS4201",		NULL },
+{ 0x43525958, 0xfffffff8, "CS4205",		patch_cirrus_spdif },
+{ 0x43525960, 0xfffffff8, "CS4291",		NULL },
+{ 0x43525970, 0xfffffff8, "CS4202",		NULL },
+{ 0x43585421, 0xffffffff, "HSD11246",		NULL },	// SmartMC II
+{ 0x43585428, 0xfffffff8, "Cx20468",		patch_conexant }, // SmartAMC fixme: the mask might be different
+{ 0x44543031, 0xfffffff0, "DT0398",		NULL },
+{ 0x454d4328, 0xffffffff, "28028",		NULL },  // same as TR28028?
+{ 0x45838308, 0xffffffff, "ESS1988",		NULL },
+{ 0x48525300, 0xffffff00, "HMP9701",		NULL },
+{ 0x49434501, 0xffffffff, "ICE1230",		NULL },
+{ 0x49434511, 0xffffffff, "ICE1232",		NULL }, // alias VIA VT1611A?
+{ 0x49434514, 0xffffffff, "ICE1232A",		NULL },
+{ 0x49434551, 0xffffffff, "VT1616", 		patch_vt1616 }, 
+{ 0x49434552, 0xffffffff, "VT1616i",		patch_vt1616 }, // VT1616 compatible (chipset integrated)
+{ 0x49544520, 0xffffffff, "IT2226E",		NULL },
+{ 0x49544561, 0xffffffff, "IT2646E",		patch_it2646 },
+{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48",	NULL }, // only guess --jk
+{ 0x4e534331, 0xffffffff, "LM4549",		NULL },
+{ 0x4e534350, 0xffffffff, "LM4550",		NULL },
+{ 0x50534304, 0xffffffff, "UCB1400",		NULL },
+{ 0x53494c20, 0xffffffe0, "Si3036/8",		mpatch_si3036,		AC97_MODEM_PATCH },
+{ 0x54524102, 0xffffffff, "TR28022",		NULL },
+{ 0x54524106, 0xffffffff, "TR28026",		NULL },
+{ 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028 }, // added by xin jin [07/09/99]
+{ 0x54524123, 0xffffffff, "TR28602",		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
+{ 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL },
+{ 0x56494161, 0xffffffff, "VIA1612A",		NULL }, // modified ICE1232 with S/PDIF
+{ 0x57454301, 0xffffffff, "W83971D",		NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL },
+{ 0x574d4c03, 0xffffffff, "WM9703/WM9707/WM9708/WM9717", patch_wolfson03 },
+{ 0x574d4c04, 0xffffffff, "WM9704M/WM9704Q",	patch_wolfson04 },
+{ 0x574d4c05, 0xffffffff, "WM9705/WM9710",	patch_wolfson05 },
+{ 0x574d4c09, 0xffffffff, "WM9709",		NULL },
+{ 0x574d4c12, 0xffffffff, "WM9711/WM9712",	patch_wolfson11 },
+{ 0x574d4c13, 0xffffffff, "WM9713",		patch_wolfson13,	AC97_DEFAULT_POWER_OFF }, /* warm start */
+{ 0x594d4800, 0xffffffff, "YMF743",		NULL },
+{ 0x594d4802, 0xffffffff, "YMF752",		NULL },
+{ 0x594d4803, 0xffffffff, "YMF753",		patch_yamaha_ymf753 },
+{ 0x83847600, 0xffffffff, "STAC9700/83/84",	patch_sigmatel_stac9700 },
+{ 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL },
+{ 0x83847605, 0xffffffff, "STAC9704",		NULL },
+{ 0x83847608, 0xffffffff, "STAC9708/11",	patch_sigmatel_stac9708 },
+{ 0x83847609, 0xffffffff, "STAC9721/23",	patch_sigmatel_stac9721 },
+{ 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744 },
+{ 0x83847650, 0xffffffff, "STAC9750/51",	NULL },	// patch?
+{ 0x83847652, 0xffffffff, "STAC9752/53",	NULL }, // patch?
+{ 0x83847656, 0xffffffff, "STAC9756/57",	patch_sigmatel_stac9756 },
+{ 0x83847658, 0xffffffff, "STAC9758/59",	patch_sigmatel_stac9758 },
+{ 0x83847666, 0xffffffff, "STAC9766/67",	NULL }, // patch?
+{ 0 } /* terminator */
 };
 
 const char *snd_ac97_stereo_enhancements[] =
@@ -1663,6 +1664,18 @@
 	return result;
 }
 
+/* look for the codec id table matching with the given id */
+static const ac97_codec_id_t *look_for_codec_id(const ac97_codec_id_t *table,
+						unsigned int id)
+{
+	const ac97_codec_id_t *pid;
+
+	for (pid = table; pid->id; pid++)
+		if (pid->id == (id & pid->mask))
+			return pid;
+	return NULL;
+}
+
 void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem)
 {
 	const ac97_codec_id_t *pid;
@@ -1671,35 +1684,30 @@
 		printable(id >> 24),
 		printable(id >> 16),
 		printable(id >> 8));
-	for (pid = snd_ac97_codec_id_vendors; pid->id; pid++)
-		if (pid->id == (id & pid->mask)) {
-			strcpy(name, pid->name);
-			if (ac97) {
-				if (!modem && pid->patch)
-					pid->patch(ac97);
-				else if (modem && pid->mpatch)
-					pid->mpatch(ac97);
-			} 
-			goto __vendor_ok;
-		}
-	return;
+	pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
+	if (! pid)
+		return;
 
-      __vendor_ok:
-	for (pid = snd_ac97_codec_ids; pid->id; pid++)
-		if (pid->id == (id & pid->mask)) {
-			strcat(name, " ");
-			strcat(name, pid->name);
-			if (pid->mask != 0xffffffff)
-				sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
-			if (ac97) {
-				if (!modem && pid->patch)
-					pid->patch(ac97);
-				else if (modem && pid->mpatch)
-					pid->mpatch(ac97);
-			}
-			return;
+	strcpy(name, pid->name);
+	if (ac97 && pid->patch) {
+		if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+		    (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+			pid->patch(ac97);
+	} 
+
+	pid = look_for_codec_id(snd_ac97_codec_ids, id);
+	if (pid) {
+		strcat(name, " ");
+		strcat(name, pid->name);
+		if (pid->mask != 0xffffffff)
+			sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+		if (ac97 && pid->patch) {
+			if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+			    (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+				pid->patch(ac97);
 		}
-	sprintf(name + strlen(name), " id %x", id & 0xff);
+	} else
+		sprintf(name + strlen(name), " id %x", id & 0xff);
 }
 
 /**
@@ -1838,6 +1846,7 @@
 	char name[64];
 	unsigned long end_time;
 	unsigned int reg;
+	const ac97_codec_id_t *pid;
 	static snd_device_ops_t ops = {
 		.dev_free =	snd_ac97_dev_free,
 	};
@@ -1888,6 +1897,14 @@
 		goto __access_ok;
 	}
 
+	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if (ac97->id && ac97->id != (unsigned int)-1) {
+		pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+		if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF))
+			goto __access_ok;
+	}
+
 	snd_ac97_write(ac97, AC97_RESET, 0);	/* reset to defaults */
 	if (bus->ops->wait)
 		bus->ops->wait(ac97);
@@ -1914,6 +1931,9 @@
 		snd_ac97_free(ac97);
 		return -EIO;
 	}
+	pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+	if (pid)
+		ac97->flags |= pid->flags;
 	
 	/* test for AC'97 */
 	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
@@ -1952,10 +1972,12 @@
 	if (ac97_is_audio(ac97)) {
 		/* nothing should be in powerdown mode */
 		snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
-		snd_ac97_write_cache(ac97, AC97_RESET, 0);		/* reset to defaults */
-		udelay(100);
+		if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+			snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
+			udelay(100);
+			snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
+		}
 		/* nothing should be in powerdown mode */
-		snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
 		snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
 		end_time = jiffies + (HZ / 10);
 		do {
@@ -2217,9 +2239,11 @@
 	}
 
 	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
-	snd_ac97_write(ac97, AC97_RESET, 0);
-	udelay(100);
-	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+	if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+		snd_ac97_write(ac97, AC97_RESET, 0);
+		udelay(100);
+		snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+	}
 	snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
 
 	snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
--- linux/sound/pci/ac97/ac97_patch.c	4 Jan 2005 10:58:12 -0000	1.67
+++ linux/sound/pci/ac97/ac97_patch.c	4 Jan 2005 14:51:36 -0000
@@ -305,6 +305,24 @@
 	return 0;
 }
 
+int patch_wolfson13(ac97_t * ac97)
+{
+	/* WM9713 */
+	static unsigned short inits[] = {
+		AC97_REC_GAIN,		0x00a0,
+		AC97_EXTENDED_MID,	0xda00,
+		AC97_EXTENDED_MSTATUS,	0x3810,
+		AC97_PHONE,		0x0808,
+		AC97_PC_BEEP,		0x0808,
+	};
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(inits); i += 2)
+		snd_ac97_write_cache(ac97, inits[0], inits[1]);
+
+	return 0;
+}
+
 /*
  * Tritech codec
  */
--- linux/sound/pci/ac97/ac97_patch.h	29 Sep 2004 09:44:11 -0000	1.17
+++ linux/sound/pci/ac97/ac97_patch.h	4 Jan 2005 14:43:57 -0000
@@ -28,6 +28,7 @@
 int patch_wolfson04(ac97_t * ac97);
 int patch_wolfson05(ac97_t * ac97);
 int patch_wolfson11(ac97_t * ac97);
+int patch_wolfson13(ac97_t * ac97);
 int patch_tritech_tr28028(ac97_t * ac97);
 int patch_sigmatel_stac9700(ac97_t * ac97);
 int patch_sigmatel_stac9708(ac97_t * ac97);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] AC97 support for low power codecs
  2005-01-04 15:47     ` Takashi Iwai
@ 2005-01-04 16:24       ` Liam Girdwood
  0 siblings, 0 replies; 5+ messages in thread
From: Liam Girdwood @ 2005-01-04 16:24 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Alan Cox, Jeff Garzik, lkml

On Tue, 2005-01-04 at 15:47, Takashi Iwai wrote:
> 
> I have also a patch for ALSA about this as attached below.  It's
> to the very latest ALSA CVS version (and untested, of course ;)
> 

Thanks.

I'll be able to test in a week or two, as I'm using an ARM based system
for my main dev platform.

Liam


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2005-01-04 16:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-01-04 11:34 [PATCH] AC97 support for low power codecs Liam Girdwood
2005-01-04 15:08 ` Takashi Iwai
2005-01-04 15:27   ` Liam Girdwood
2005-01-04 15:47     ` Takashi Iwai
2005-01-04 16:24       ` Liam Girdwood

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox