From: Liam Girdwood This is a resend of a patch that has been applied to 2.4. The low power codec functionality has also now been included in ALSA. It 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. Changes:- - Added AC97_DEFAULT_POWER_OFF to ac97_codec_ids[] - ac97_probe now checks hardwired codec ID's before sending a reset - Added support for WM9713 - Moved the codec specific inits after the mixer setup as some init - tings were being clobbered. - Added extra check so that default_digital_ops doesn't overwrite a valid codec_ops. (SPDIF) Signed-off-by: Liam Girdwood Signed-off-by: Andrew Morton --- 25-akpm/include/linux/ac97_codec.h | 1 25-akpm/sound/oss/ac97_codec.c | 83 ++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 28 deletions(-) diff -puN include/linux/ac97_codec.h~oss-support-for-ac97-low-power-codecs include/linux/ac97_codec.h --- 25/include/linux/ac97_codec.h~oss-support-for-ac97-low-power-codecs 2005-01-26 17:21:36.626477128 -0800 +++ 25-akpm/include/linux/ac97_codec.h 2005-01-26 17:21:36.635475760 -0800 @@ -290,6 +290,7 @@ struct ac97_ops #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, diff -puN sound/oss/ac97_codec.c~oss-support-for-ac97-low-power-codecs sound/oss/ac97_codec.c --- 25/sound/oss/ac97_codec.c~oss-support-for-ac97-low-power-codecs 2005-01-26 17:21:36.628476824 -0800 +++ 25-akpm/sound/oss/ac97_codec.c 2005-01-26 17:21:36.634475912 -0800 @@ -71,6 +71,7 @@ static int wolfson_init03(struct ac97_co 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); @@ -107,6 +108,7 @@ static struct ac97_ops wolfson_ops03 = { 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 }; @@ -171,6 +173,7 @@ static const struct { {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}, @@ -797,6 +800,9 @@ EXPORT_SYMBOL(ac97_release_codec); * 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. */ @@ -810,34 +816,17 @@ int ac97_probe_codec(struct ac97_codec * 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; @@ -849,9 +838,34 @@ int ac97_probe_codec(struct ac97_codec * } 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 */ @@ -920,11 +934,6 @@ static int ac97_init_mixer(struct ac97_c 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]; @@ -934,6 +943,11 @@ static int ac97_init_mixer(struct ac97_c 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 @@ -1090,6 +1104,19 @@ static int wolfson_init11(struct ac97_co 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); _