X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sound%2Foss%2Fac97_codec.c;h=602db497929abd70d048d1d5215b0d869651ab5f;hb=refs%2Fheads%2Fvserver;hp=c883411dacaf8f05c5ee52cff7becdeef5e60c70;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c index c883411da..602db4979 100644 --- a/sound/oss/ac97_codec.c +++ b/sound/oss/ac97_codec.c @@ -52,8 +52,10 @@ #include #include #include +#include #include #include +#include #define CODEC_ID_BUFSZ 14 @@ -70,6 +72,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); @@ -106,6 +109,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 }; @@ -128,6 +132,9 @@ static const struct { {0x41445348, "Analog Devices AD1881A", &null_ops}, {0x41445360, "Analog Devices AD1885", &default_ops}, {0x41445361, "Analog Devices AD1886", &ad1886_ops}, + {0x41445370, "Analog Devices AD1981", &null_ops}, + {0x41445372, "Analog Devices AD1981A", &null_ops}, + {0x41445374, "Analog Devices AD1981B", &null_ops}, {0x41445460, "Analog Devices AD1885", &default_ops}, {0x41445461, "Analog Devices AD1886", &ad1886_ops}, {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, @@ -149,6 +156,7 @@ static const struct { {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x43585430, "CXT48", &default_ops, AC97_DELUDED_MODEM }, {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM }, {0x44543031, "Diamond Technology DT0893", &default_ops}, {0x45838308, "ESS Allegro ES1988", &null_ops}, @@ -167,12 +175,14 @@ 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}, {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops}, {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops}, {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops}, + {0x83847652, "SigmaTel STAC9752/53", &default_ops}, {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops}, {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops}, {0x83847684, "SigmaTel STAC9783/84?", &null_ops}, @@ -294,7 +304,7 @@ static const unsigned int ac97_oss_rm[] = { static LIST_HEAD(codecs); static LIST_HEAD(codec_drivers); -static DECLARE_MUTEX(codec_sem); +static DEFINE_MUTEX(codec_mutex); /* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows about that given mixer, and should be holding a spinlock for the card */ @@ -759,9 +769,9 @@ void ac97_release_codec(struct ac97_codec *codec) { /* Remove from the list first, we don't want to be "rediscovered" */ - down(&codec_sem); + mutex_lock(&codec_mutex); list_del(&codec->list); - up(&codec_sem); + mutex_unlock(&codec_mutex); /* * The driver needs to deal with internal * locking to avoid accidents here. @@ -793,6 +803,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. */ @@ -806,34 +819,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; @@ -845,9 +841,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 */ @@ -868,7 +889,7 @@ int ac97_probe_codec(struct ac97_codec *codec) * callbacks. */ - down(&codec_sem); + mutex_lock(&codec_mutex); list_add(&codec->list, &codecs); list_for_each(l, &codec_drivers) { @@ -882,7 +903,7 @@ int ac97_probe_codec(struct ac97_codec *codec) } } - up(&codec_sem); + mutex_unlock(&codec_mutex); return 1; } @@ -916,11 +937,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]; @@ -931,6 +947,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. @@ -1086,6 +1107,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); @@ -1365,93 +1399,91 @@ unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) EXPORT_SYMBOL(ac97_set_adc_rate); -int ac97_save_state(struct ac97_codec *codec) -{ - return 0; -} - -EXPORT_SYMBOL(ac97_save_state); - -int ac97_restore_state(struct ac97_codec *codec) +static int swap_headphone(int remove_master) { - int i; - unsigned int left, right, val; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!supported_mixer(codec, i)) - continue; + struct list_head *l; + struct ac97_codec *c; + + if (remove_master) { + mutex_lock(&codec_mutex); + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if (supported_mixer(c, SOUND_MIXER_PHONEOUT)) + c->supported_mixers &= ~SOUND_MASK_PHONEOUT; + } + mutex_unlock(&codec_mutex); + } else + ac97_hw[SOUND_MIXER_PHONEOUT].offset = AC97_MASTER_VOL_STEREO; - val = codec->mixer_state[i]; - right = val >> 8; - left = val & 0xff; - codec->write_mixer(codec, i, left, right); - } + /* Scale values already match */ + ac97_hw[SOUND_MIXER_VOLUME].offset = AC97_MASTER_VOL_MONO; return 0; } -EXPORT_SYMBOL(ac97_restore_state); - -/** - * ac97_register_driver - register a codec helper - * @driver: Driver handler - * - * Register a handler for codecs matching the codec id. The handler - * attach function is called for all present codecs and will be - * called when new codecs are discovered. - */ - -int ac97_register_driver(struct ac97_driver *driver) +static int apply_quirk(int quirk) { - struct list_head *l; - struct ac97_codec *c; - - down(&codec_sem); - INIT_LIST_HEAD(&driver->list); - list_add(&driver->list, &codec_drivers); - - list_for_each(l, &codecs) - { - c = list_entry(l, struct ac97_codec, list); - if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask)) - continue; - if(driver->probe(c, driver)) - continue; - c->driver = driver; + switch (quirk) { + case AC97_TUNE_NONE: + return 0; + case AC97_TUNE_HP_ONLY: + return swap_headphone(1); + case AC97_TUNE_SWAP_HP: + return swap_headphone(0); + case AC97_TUNE_SWAP_SURROUND: + return -ENOSYS; /* not yet implemented */ + case AC97_TUNE_AD_SHARING: + return -ENOSYS; /* not yet implemented */ + case AC97_TUNE_ALC_JACK: + return -ENOSYS; /* not yet implemented */ } - up(&codec_sem); - return 0; + return -EINVAL; } -EXPORT_SYMBOL_GPL(ac97_register_driver); - /** - * ac97_unregister_driver - unregister a codec helper - * @driver: Driver handler + * ac97_tune_hardware - tune up the hardware + * @pdev: pci_dev pointer + * @quirk: quirk list + * @override: explicit quirk value (overrides if not AC97_TUNE_DEFAULT) + * + * Do some workaround for each pci device, such as renaming of the + * headphone (true line-out) control as "Master". + * The quirk-list must be terminated with a zero-filled entry. * - * Unregister a handler for codecs matching the codec id. The handler - * remove function is called for all matching codecs. + * Returns zero if successful, or a negative error code on failure. */ - -void ac97_unregister_driver(struct ac97_driver *driver) + +int ac97_tune_hardware(struct pci_dev *pdev, struct ac97_quirk *quirk, int override) { - struct list_head *l; - struct ac97_codec *c; - - down(&codec_sem); - list_del_init(&driver->list); + int result; - list_for_each(l, &codecs) - { - c = list_entry(l, struct ac97_codec, list); - if (c->driver == driver) { - driver->remove(c, driver); - c->driver = NULL; + if (!quirk) + return -EINVAL; + + if (override != AC97_TUNE_DEFAULT) { + result = apply_quirk(override); + if (result < 0) + printk(KERN_ERR "applying quirk type %d failed (%d)\n", override, result); + return result; + } + + for (; quirk->vendor; quirk++) { + if (quirk->vendor != pdev->subsystem_vendor) + continue; + if ((! quirk->mask && quirk->device == pdev->subsystem_device) || + quirk->device == (quirk->mask & pdev->subsystem_device)) { +#ifdef DEBUG + printk("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, pdev->subsystem_device); +#endif + result = apply_quirk(quirk->type); + if (result < 0) + printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result); + return result; } } - - up(&codec_sem); + return 0; } -EXPORT_SYMBOL_GPL(ac97_unregister_driver); - +EXPORT_SYMBOL_GPL(ac97_tune_hardware); + MODULE_LICENSE("GPL");