Commit 69bfca0e authored by Liam Girdwood's avatar Liam Girdwood Committed by Linus Torvalds

[PATCH] OSS Support for AC97 low power codecs

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: default avatarLiam Girdwood <liam.girdwood@wolfsonmicro.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent dd26caf1
......@@ -323,6 +323,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,
......
......@@ -71,6 +71,7 @@ static int wolfson_init03(struct ac97_codec * codec);
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 = { wolfson_init03, NULL, NULL };
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},
......@@ -798,6 +801,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.
*/
......@@ -811,34 +817,17 @@ int ac97_probe_codec(struct ac97_codec *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;
......@@ -850,9 +839,34 @@ int ac97_probe_codec(struct ac97_codec *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 */
......@@ -921,11 +935,6 @@ static int ac97_init_mixer(struct ac97_codec *codec)
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];
......@@ -936,6 +945,11 @@ static int ac97_init_mixer(struct ac97_codec *codec)
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
* it but its useless beyond that.
......@@ -1091,6 +1105,19 @@ static int wolfson_init11(struct ac97_codec * codec)
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment