/* * Purpose: AC97 codec support library * * This source file contains common AC97 codec/mixer related code that is * used by all drivers for AC97 based devices. */ /* * * This file is part of Open Sound System. * * Copyright (C) 4Front Technologies 1996-2008. * * This this source file is released under GPL v2 license (no other versions). * See the COPYING file included in the main directory of this source * distribution for the license terms and conditions. * */ #include "oss_config.h" #include "ac97.h" extern int ac97_amplifier; /* From oss_core_options.c */ extern int ac97_recselect; /* From oss_core_options.c */ /* * LINE1 == AUX */ #define MIXER_DEVS (SOUND_MASK_LINE1 | SOUND_MASK_VIDEO | \ SOUND_MASK_MIC | SOUND_MASK_VOLUME | \ SOUND_MASK_LINE | SOUND_MASK_SPEAKER | \ SOUND_MASK_CD | SOUND_MASK_LINE | \ SOUND_MASK_PHONE | SOUND_MASK_MONO | \ SOUND_MASK_PCM | SOUND_MASK_IGAIN) #define EXTRA_DEVS (SOUND_MASK_REARVOL | SOUND_MASK_CENTERVOL | \ SOUND_MASK_SIDEVOL) #define REC_DEVS (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \ SOUND_MASK_PHONE | SOUND_MASK_CD | \ SOUND_MASK_LINE | SOUND_MASK_VOLUME | \ SOUND_MASK_MONO | SOUND_MASK_VIDEO ) #define STEREO_DEVS ((MIXER_DEVS|EXTRA_DEVS) & ~(SOUND_MASK_MIC| \ SOUND_MASK_SPEAKER|SOUND_MASK_BASS| \ SOUND_MASK_TREBLE | SOUND_MASK_MONO)) static int default_mixer_levels[32] = { 0x4b4b, /* Master Volume */ 0x3232, /* Bass */ 0x3232, /* Treble */ 0x4b4b, /* FM */ 0x4b4b, /* PCM */ 0x8000, /* PC Speaker */ 0x2020, /* Ext Line */ 0x0000, /* Mic */ 0x4b4b, /* CD */ 0x0000, /* Recording monitor */ 0x4b4b, /* Second PCM */ 0x4b4b, /* Recording level */ 0x4b4b, /* Input gain */ 0x4b4b, /* Output gain */ 0x2020, /* Line1 */ 0x2020, /* Line2 */ 0x1515, /* Line3 (usually line in) */ 0x0101, /* Digital1 */ 0x0000, /* Digital2 */ 0x0000, /* Digital3 */ 0x0000, /* Phone In */ 0x4b4b, /* Mono/Phone out */ 0x0000, /* Video */ 0x0000, /* Radio */ 0x0000, /* Depth */ 0x4b4b, /* Rear */ 0x4b4b, /* Center */ 0x4b4b /* Surround */ }; static int nr_ac97 = 0; extern int ac97_amplifier, ac97_recselect; static unsigned short codec_read (ac97_devc * devc, int reg) { return devc->read (devc->host_parms, reg) & 0xffff; } static int codec_write (ac97_devc * devc, int reg, unsigned short data) { return devc->write (devc->host_parms, reg, data); } static int ac97_set_recmask (ac97_devc * devc, int pmask) { int mask = pmask & devc->recmask; int sel = 0; mask = mask & (mask ^ devc->recdevs); /* Find out the changed bits */ if (!mask) /* No change */ return devc->recdevs; devc->recdevs = mask; switch (mask) { case SOUND_MASK_MIC: sel = 0; break; case SOUND_MASK_CD: sel = 1; break; case SOUND_MASK_VIDEO: sel = 2; break; case SOUND_MASK_LINE1: sel = 3; break; /* AUX */ case SOUND_MASK_LINE: sel = 4; break; case SOUND_MASK_VOLUME: sel = 5; break; case SOUND_MASK_MONO: sel = 6; break; case SOUND_MASK_PHONE: sel = 7; break; default: /* Unknown choise. Default to mic */ devc->recdevs = SOUND_MASK_MIC; sel = 0; } codec_write (devc, 0x1a, sel | (sel << 8)); return devc->levels[31] = devc->recdevs; } static int ac97_mixer_get (ac97_devc * devc, int dev) { if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES) return OSS_EINVAL; if (!((1 << dev) & devc->devmask)) return OSS_EINVAL; return devc->levels[dev]; } static unsigned int mix_scale (int left, int right, int bits) { int reverse = 0, mute = 0; if (bits < 0) { bits = -bits; reverse = 1; } if ((left | right) == 0) /* Mute */ mute = 0x8000; if (reverse) { left = 100 - left; right = 100 - right; } left = 100 - mix_cvt[left]; right = 100 - mix_cvt[right]; return mute | ((left * ((1 << bits) - 1) / 100) << 8) | (right * ((1 << bits) - 1) / 100); } int ac97_mixer_set (ac97_devc * devc, int dev, int value) { int left, right, lvl; if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES) return OSS_EINVAL; if (!((1 << dev) & devc->devmask)) return OSS_EINVAL; if (!((1 << dev) & STEREO_DEVS)) { lvl = value & 0xff; if (lvl > 100) lvl = 100; left = right = lvl; value = lvl | (lvl << 8); } else { left = value & 0xff; right = (value >> 8) & 0xff; if (left > 100) left = 100; if (right > 100) right = 100; value = left | (right << 8); } switch (dev) { case SOUND_MIXER_VOLUME: if (!(devc->devmask & SOUND_MASK_ALTPCM)) codec_write (devc, 0x04, mix_scale (left, right, devc->mastervol_bits)); codec_write (devc, 0x02, mix_scale (left, right, devc->mastervol_bits)); break; case SOUND_MIXER_TREBLE: codec_write (devc, 0x08, mix_scale (left, devc->levels[SOUND_MIXER_TREBLE] & 0xff, 4)); break; case SOUND_MIXER_BASS: codec_write (devc, 0x08, mix_scale (devc->levels[SOUND_MIXER_BASS] & 0xff, left, 4)); break; case SOUND_MIXER_DEPTH: codec_write (devc, 0x22, (mix_cvt[left] * ((1 << devc->enh_bits) - 1)) / 100); break; case SOUND_MIXER_SPEAKER: codec_write (devc, 0x0a, mix_scale (0, left, 5) & 0x801F); break; case SOUND_MIXER_PHONE: codec_write (devc, 0x0c, mix_scale (0, left, devc->mastervol_bits)); break; case SOUND_MIXER_MONO: codec_write (devc, 0x06, mix_scale (0, left, devc->mastervol_bits)); break; case SOUND_MIXER_MIC: codec_write (devc, 0x0e, (mix_scale (0, left, devc->mastervol_bits) & ~0x40) | devc->micboost); break; case SOUND_MIXER_LINE: codec_write (devc, 0x10, mix_scale (left, right, 5)); break; case SOUND_MIXER_CD: codec_write (devc, 0x12, mix_scale (left, right, 5)); break; case SOUND_MIXER_VIDEO: codec_write (devc, 0x14, mix_scale (left, right, 5)); break; case SOUND_MIXER_LINE1: codec_write (devc, 0x16, mix_scale (left, right, 5)); break; case SOUND_MIXER_PCM: codec_write (devc, 0x18, mix_scale (left, right, devc->pcmvol_bits)); break; case SOUND_MIXER_IGAIN: codec_write (devc, 0x1c, mix_scale (left, right, -4)); break; case SOUND_MIXER_ALTPCM: codec_write (devc, 0x04, mix_scale (left, right, devc->mastervol_bits)); #if 0 codec_write (devc, 0x36, mix_scale (left, right, devc->mastervol_bits)); codec_write (devc, 0x38, mix_scale (left, right, devc->mastervol_bits)); #endif break; case SOUND_MIXER_REARVOL: codec_write (devc, 0x38, mix_scale (left, right, devc->rearvol_bits)); break; case SOUND_MIXER_CENTERVOL: codec_write (devc, 0x36, mix_scale (left, right, devc->centervol_bits)); break; #if 1 /* AC97 2.4 specified mid-surround channel */ case SOUND_MIXER_SIDEVOL: codec_write (devc, 0x1c, mix_scale (left, right, devc->sidevol_bits)); break; #endif default: return OSS_EINVAL; } return devc->levels[dev] = value; } #include "ac97_ext.inc" static void ac97_mixer_reset (ac97_devc * devc) { int i; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if ((1 << i) & devc->devmask) ac97_mixer_set (devc, i, devc->levels[i]); devc->recmask = REC_DEVS & devc->devmask; if (devc->levels[31] == 0) devc->levels[31] = SOUND_MASK_LINE; ac97_set_recmask (devc, devc->levels[31]); } /* * Remove a set of unused controls (mask) from the list of supported * controls. */ void ac97_remove_control (ac97_devc * devc, int mask, int level) { int i; if (!devc->is_ok) return; devc->devmask &= ~(mask); devc->recmask = REC_DEVS & devc->devmask; devc->recdevs &= devc->recmask; if (devc->recmask == 0) { ac97_set_recmask (devc, SOUND_MASK_LINE); } for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mask & (1 << i)) ac97_mixer_set (devc, i, level); } /* * Override a volume control with application defined handler. Before that * set the AC97 volume to the given level. */ void ac97_override_control (ac97_devc * devc, int ctrl, ac97_ext_ioctl func, int level) { int dev = devc->mixer_dev; int oldlevel; if (!devc->is_ok) return; if (ctrl < 0 || ctrl >= SOUND_MIXER_NRDEVICES) { cmn_err (CE_WARN, "ac97: Invalid override for %d\n", ctrl); return; } oldlevel = ac97_mixer_get (devc, ctrl); ac97_mixer_set (devc, ctrl, level); devc->overrides[ctrl] = func; mixer_devs[dev]->ignore_mask |= (1 << ctrl); func (devc->mixer_dev, -1, MIXER_WRITE (ctrl), oldlevel); } /*ARGSUSED*/ static int find_current_recsrc (ac97_devc * devc) { return SOUND_MIXER_IGAIN; } static int find_current_monsrc (ac97_devc * devc) { int i; for (i = 0; i < 32; i++) { if (devc->recdevs & (1 << i)) return i; } return SOUND_MIXER_MIC; } static int call_override_func (ac97_devc * devc, int audiodev, unsigned int cmd, int val) { int ret, ctrl; if (!devc->is_ok) return OSS_EIO; ctrl = cmd & 0xff; if (ctrl < 0 || ctrl >= SOUND_MIXER_NRDEVICES || devc->overrides[ctrl] == NULL) return OSS_EINVAL; ret = devc->overrides[ctrl] (devc->mixer_dev, audiodev, cmd, val); if (ret < 0) return ret; return devc->levels[ctrl] = ret; } static int ac97_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) { ac97_devc *devc = mixer_devs[dev]->devc; int ctrl; /* * Handle some special cases first */ switch (cmd) { case SOUND_MIXER_WRITE_RECGAIN: { int chn, val; chn = find_current_recsrc (devc); val = *arg; return *arg = (ac97_mixer_set (devc, chn, val)); } break; case SOUND_MIXER_READ_RECGAIN: { int chn; chn = find_current_recsrc (devc); return *arg = (ac97_mixer_get (devc, chn)); } break; case SOUND_MIXER_WRITE_MONGAIN: { int chn, val; chn = find_current_monsrc (devc); val = *arg; return *arg = (ac97_mixer_set (devc, chn, val)); } break; case SOUND_MIXER_READ_MONGAIN: { int chn; chn = find_current_monsrc (devc); return *arg = (ac97_mixer_get (devc, chn)); } break; /* enable/disable 20db Mic Boost */ case SOUND_MIXER_PRIVATE1: { int value, tmp; value = *arg; if (value != 0 && value != 1) return OSS_EINVAL; tmp = codec_read (devc, 0x0e); if (value) devc->micboost = 0x40; else devc->micboost = 0x00; codec_write (devc, 0x0e, (tmp & ~0x40) | devc->micboost); return *arg = (value); } break; } if (((cmd >> 8) & 0xff) == 'M') /* Set control */ { int val; if (IOC_IS_OUTPUT (cmd)) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: if (ac97_recselect) return *arg = (SOUND_MASK_MIC); val = *arg; return *arg = (ac97_set_recmask (devc, val)); break; default: if ((cmd & 0xff) > SOUND_MIXER_NRDEVICES) return OSS_EINVAL; val = *arg; ctrl = cmd & 0xff; if (ctrl >= 0 && ctrl < SOUND_MIXER_NRDEVICES) if (devc->overrides[ctrl] != NULL) return *arg = call_override_func (devc, audiodev, cmd, val); return *arg = (ac97_mixer_set (devc, cmd & 0xff, val)); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: if (ac97_recselect) return *arg = (SOUND_MASK_MIC); return *arg = (devc->recdevs); break; case SOUND_MIXER_DEVMASK: return *arg = (devc->devmask); break; case SOUND_MIXER_STEREODEVS: return *arg = (STEREO_DEVS); break; case SOUND_MIXER_RECMASK: if (ac97_recselect) return *arg = (0); return *arg = (devc->recmask); break; case SOUND_MIXER_CAPS: return *arg = (SOUND_CAP_EXCL_INPUT); break; default: ctrl = cmd & 0xff; if (ctrl >= 0 && ctrl < SOUND_MIXER_NRDEVICES) if (devc->overrides[cmd & 0xff] != NULL) return *arg = call_override_func (devc, audiodev, cmd, 0); return *arg = (ac97_mixer_get (devc, cmd & 0xff)); } } else { return OSS_EINVAL; } } static mixer_driver_t ac97_mixer_driver = { ac97_mixer_ioctl }; int ac97_install (ac97_devc * devc, char *host_name, ac97_readfunc_t readfn, ac97_writefunc_t writefn, void *hostparms, oss_device_t * osdev) { return ac97_install_full (devc, host_name, readfn, writefn, hostparms, osdev, 0); } int ac97_install_full (ac97_devc * devc, char *host_name, ac97_readfunc_t readfn, ac97_writefunc_t writefn, void *hostparms, oss_device_t * osdev, int flags) { int my_mixer; int tmp, tmp2; unsigned int id, mask; char tmp_name[100]; memset (devc, 0, sizeof (ac97_devc)); devc->osdev = osdev; devc->host_parms = hostparms; devc->read = readfn; devc->is_ok = 0; devc->write = writefn; strcpy (devc->name, "AC97"); if (codec_write (devc, 0x00, 0x00) < 0) /* reset */ return OSS_EIO; codec_write (devc, 0x26, 0x00); /* Power up */ oss_udelay (1000); if (ac97_amplifier != -1) tmp = ac97_amplifier; else tmp = !(flags & AC97_INVERTED); if (tmp) codec_write (devc, 0x26, codec_read (devc, 0x26) & ~0x8000); /* Power up (external amplifier powered up) */ else codec_write (devc, 0x26, codec_read (devc, 0x26) | 0x8000); /* Power up (external amplifier powered down) */ codec_write (devc, 0x20, 0x00); /* General Purpose */ tmp = codec_read (devc, 0x7c); if (tmp >= 0xffff) { cmn_err (CE_WARN, "AC97 Codec/mixer chip doesn't seem to be alive.\n"); return OSS_EIO; } tmp2 = codec_read (devc, 0x7e); id = ((tmp & 0xffff) << 16) | (tmp2 & 0xffff); DDB (cmn_err (CE_CONT, "AC97 codec ID=0x%08x ('%c%c%c%x')\n", id, (tmp >> 8) & 0xff, tmp & 0xff, (tmp2 >> 8) & 0xff, tmp2 & 0xff)); devc->ac97_id = codec_read (devc, 0x00); devc->enh_3d = (devc->ac97_id >> 10) & 0x1f; devc->devmask = MIXER_DEVS; devc->fixed_rate = 0; devc->var_rate_support = (codec_read (devc, 0x28) & 0x1) ? 1 : 0; devc->spdifout_support = (codec_read (devc, 0x28) & 0x4) ? 1 : 0; devc->mixer_ext = 0; devc->playrate_support = PLAY_2CHAN; if (codec_read (devc, 0x28) & 0x1C0) devc->playrate_support = PLAY_6CHAN; else if (codec_read (devc, 0x28) & 0x80) devc->playrate_support = PLAY_4CHAN; devc->enh_bits = 4; devc->micboost = 0x40; devc->pcmvol_bits = 5; devc->rearvol_bits = 5; devc->sidevol_bits = 5; devc->centervol_bits = 5; devc->auxvol_bits = 5; devc->extmixlevels[CENTER_VOL] = 0x0404; devc->extmixlevels[REAR_VOL] = 0x0404; devc->extmixlevels[SIDE_VOL] = 0x0404; /* need to detect all the Cirrus Logic variations! */ if ((id & 0xffff0000) == 0x43520000) mask = 0xFFFFFFF0; else mask = 0xFFFFFFFF; switch (id & mask) { case 0: strcpy (devc->name, "Unknown"); break; case 0x414b4d00: strcpy (devc->name, "AK4540"); break; case 0x83847600: strcpy (devc->name, "STAC9700"); break; case 0xc250c250: case 0x83847601: strcpy (devc->name, "STAC9701"); break; case 0x83847609: strcpy (devc->name, "STAC9721"); break; case 0x83847604: strcpy (devc->name, "STAC9704"); break; case 0x83847605: strcpy (devc->name, "STAC9705"); break; case 0x83847608: strcpy (devc->name, "STAC9708"); devc->devmask |= SOUND_MASK_ALTPCM; codec_write (devc, 0x6E, 8); /* codec_read (devc, 0x6E) & ~0x8); non-inverted pphase */ devc->enh_bits = 2; break; case 0x83847644: strcpy (devc->name, "STAC9744"); break; case 0x83847650: strcpy (devc->name, "STAC9750"); devc->spdifout_support = STAC_SPDIFOUT; /* SPDIF output on ACR */ devc->enh_bits = 3; break; case 0x83847652: strcpy (devc->name, "STAC9752"); devc->spdifout_support = STAC_SPDIFOUT; /* SPDIF output on ACR */ devc->enh_bits = 3; break; case 0x83847656: strcpy (devc->name, "STAC9756"); devc->spdifout_support = STAC_SPDIFOUT; /* SPDIF output on ACR */ devc->enh_bits = 3; break; case 0x83847666: strcpy (devc->name, "STAC9766"); devc->enh_bits = 3; devc->spdifout_support = STAC_SPDIFOUT; /* SPDIF output on ACR */ break; case 0x83847658: strcpy (devc->name, "STAC9758"); devc->enh_bits = 3; devc->spdifout_support = STAC_SPDIFOUT; /* SPDIF output on ACR */ devc->mixer_ext = STAC9758_MIXER_EXT; break; case 0x54524108: case 0x54524128: strcpy (devc->name, "TR28028"); devc->devmask |= SOUND_MASK_ALTPCM; break; case 0x54524103: case 0x54524123: strcpy (devc->name, "TR28023"); break; case 0x454d4328: strcpy (devc->name, "EM28028"); codec_write (devc, 0x2a, (codec_read (devc, 0x2a) & ~3800) | 0xE0); devc->devmask |= SOUND_MASK_ALTPCM; break; case 0x43585428: case 0x43585429: strcpy (devc->name, "CX20468"); devc->spdifout_support = CX_SPDIFOUT; break; case 0x43525900: strcpy (devc->name, "CS4297"); break; case 0x43525910: strcpy (devc->name, "CS4297A"); devc->spdifout_support = CS_SPDIFOUT; break; case 0x43525920: strcpy (devc->name, "CS4294"); break; case 0x43525930: strcpy (devc->name, "CS4299"); devc->spdifout_support = CS_SPDIFOUT; break; case 0x43525970: strcpy (devc->name, "CS4202"); devc->spdifout_support = CS_SPDIFOUT; break; case 0x43525950: strcpy (devc->name, "CS4205"); devc->spdifout_support = CS_SPDIFOUT; break; case 0x4144303: /* ADS3 */ strcpy (devc->name, "AD1819B"); break; case 0x41445340: /* ADS40 */ strcpy (devc->name, "AD1881"); break; case 0x41445348: /* ADS48 */ strcpy (devc->name, "AD1881A"); break; case 0x41445360: /* ADS60 */ strcpy (devc->name, "AD1885"); break; case 0x41445361: /* ADS61 */ strcpy (devc->name, "AD1886"); /* Jack sense */ codec_write (devc, 0x72, (codec_read (devc, 0x72) & (~0xEF)) | 0x10); devc->spdifout_support = AD_SPDIFOUT; break; case 0x41445362: /* ADS62 */ strcpy (devc->name, "AD1887"); break; case 0x41445368: /* ADS62 */ strcpy (devc->name, "AD1888"); devc->spdifout_support = AD_SPDIFOUT; devc->mixer_ext = AD1980_MIXER_EXT; codec_write (devc, 0x76, 0xC420); devc->centervol_bits = 6; devc->rearvol_bits = 6; codec_write (devc, 0x04, 0x0808); break; case 0x41445370: /* ADS70 */ strcpy (devc->name, "AD1980"); devc->spdifout_support = AD_SPDIFOUT; devc->mixer_ext = AD1980_MIXER_EXT; codec_write (devc, 0x76, 0xC420); break; case 0x41445372: /* ADS72 */ strcpy (devc->name, "AD1981"); devc->spdifout_support = AD_SPDIFOUT; /* set jacksense to mute line if headphone is plugged */ if (flags & AC97_FORCE_SENSE) /* XXX */ codec_write (devc, 0x72, (codec_read (devc, 0x72) & (~0xe00)) | 0x400); break; case 0x41445374: /* ADS74 */ strcpy (devc->name, "AD1981B"); devc->spdifout_support = AD_SPDIFOUT; /* set jacksense to mute line if headphone is plugged */ if (flags & AC97_FORCE_SENSE) codec_write (devc, 0x72, (codec_read (devc, 0x72) | 0x0800)); break; case 0x41445375: /* ADS74 */ strcpy (devc->name, "AD1985"); devc->spdifout_support = AD_SPDIFOUT; devc->mixer_ext = AD1980_MIXER_EXT; if (flags & AC97_FORCE_SENSE) /* XXX */ codec_write (devc, 0x72, (codec_read (devc, 0x72) & (~0xe00)) | 0x400); codec_write (devc, 0x76, 0xC420); break; case 0x574d4c00: strcpy (devc->name, "WM9701A"); /* www.wolfson.co.uk */ break; case 0x574d4c03: strcpy (devc->name, "WM9703"); break; case 0x574d4c04: strcpy (devc->name, "WM9704"); devc->mixer_ext = WM9704_MIXER_EXT; codec_write (devc, 0x5A, codec_read (devc, 0x5A) | 0x80); /*enable I2S */ break; case 0x45838308: strcpy (devc->name, "ES19XX"); break; case 0x49434511: /* IcEnsemble ICE1232 (VIA1611A) */ strcpy (devc->name, "ICE1232/VT1611A"); break; case 0x56494161: /* VIA1612A */ case 0x56494170: /* VIA1612A */ strcpy (devc->name, "VT1612A"); if (codec_read (devc, 0x28) & 0x04) devc->spdifout_support = VIA_SPDIFOUT; devc->mixer_ext = VIA1616_MIXER_EXT; codec_write (devc, 0x2a, codec_read (devc, 0x2a) & ~0x3800); codec_write (devc, 0x5a, 0x0230); break; case 0x49434551: /* IcEnsemble ICE1232 (VIA1616) */ strcpy (devc->name, "VT1616"); /* Enable S/PDIF mixer extensions only if S/PDIF is physically present */ if (codec_read (devc, 0x28) & 0x04) devc->spdifout_support = VIA_SPDIFOUT; devc->mixer_ext = VIA1616_MIXER_EXT; devc->mixer_ext = VIA1616_MIXER_EXT; codec_write (devc, 0x2a, codec_read (devc, 0x2a) & ~0x3800); codec_write (devc, 0x5a, 0x0230); break; case 0x49434552: /* IcEnsemble ICE1232 (VIA1616A) */ strcpy (devc->name, "VT1616A"); devc->spdifout_support = VIA_SPDIFOUT; devc->mixer_ext = VIA1616_MIXER_EXT; break; case 0x56494182: /* VIA1618 */ strcpy (devc->name, "VT1618"); devc->spdifout_support = VIA_SPDIFOUT; devc->mixer_ext = VIA1616_MIXER_EXT; break; case 0x414c4326: strcpy (devc->name, "ALC100"); devc->enh_bits = 2; break; case 0x414c4710: strcpy (devc->name, "ALC200P"); devc->enh_bits = 2; break; case 0x414c4740: strcpy (devc->name, "ALC202"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; devc->enh_bits = 2; break; case 0x414c4770: strcpy (devc->name, "ALC203"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; devc->enh_bits = 2; break; case 0x000f8384: strcpy (devc->name, "EV1938"); /* Creative/Ectiva */ break; case 0x414c4750: case 0x414c4752: strcpy (devc->name, "ALC250"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; devc->spdifin_support = ALC_SPDIFIN; devc->enh_bits = 2; break; case 0x414c4720: strcpy (devc->name, "ALC650"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; /* devc->spdifin_support = ALC_SPDIFIN; */ devc->mixer_ext = ALC650_MIXER_EXT; devc->enh_bits = 2; break; case 0x414c4760: case 0x414c4761: strcpy (devc->name, "ALC655"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; devc->spdifin_support = ALC_SPDIFIN; devc->mixer_ext = ALC650_MIXER_EXT; devc->enh_bits = 2; break; case 0x414c4780: strcpy (devc->name, "ALC658"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; devc->spdifin_support = ALC_SPDIFIN; devc->mixer_ext = ALC650_MIXER_EXT; devc->enh_bits = 2; break; case 0x414c4790: strcpy (devc->name, "ALC850"); /* www.realtek.com.tw */ devc->spdifout_support = ALC_SPDIFOUT; devc->spdifin_support = ALC_SPDIFIN; devc->mixer_ext = ALC650_MIXER_EXT; devc->enh_bits = 2; break; case 0x434d4941: strcpy (devc->name, "CMI9738"); devc->devmask |= SOUND_MASK_ALTPCM; break; case 0x434d4961: strcpy (devc->name, "CMI9739"); devc->spdifout_support = CMI_SPDIFOUT; devc->spdifin_support = CMI_SPDIFIN; devc->mixer_ext = CMI9739_MIXER_EXT; break; case 0x434d4983: strcpy (devc->name, "CMI9761A"); devc->spdifout_support = CMI_SPDIFOUT; devc->spdifin_support = CMI_SPDIFIN; devc->mixer_ext = CMI9739_MIXER_EXT; break; case 0x434d4969: strcpy (devc->name, "CMI9780"); #if 0 devc->spdifout_support = CMI_SPDIFOUT; devc->spdifin_support = CMI_SPDIFIN; devc->mixer_ext = CMI9780_MIXER_EXT; devc->playrate_support == PLAY_8CHAN; #endif break; case 0x594d4800: strcpy (devc->name, "YMF743"); break; case 0x594d4803: strcpy (devc->name, "YMF753"); devc->spdifout_support = YMF_SPDIFOUT; codec_write (devc, 0x66, codec_read (devc, 0x66) | 0x9); /* set TX8 + 3AWE */ break; default: sprintf (devc->name, "0x%04x%04x", tmp, tmp2); } DDB (cmn_err (CE_CONT, "Detected AC97 codec: %s\n", devc->name)); DDB (cmn_err (CE_CONT, "AC97 codec capabilities %x\n", devc->ac97_id)); DDB (cmn_err (CE_CONT, "Dedicated Mic PCM in channel %d\n", !!(devc->ac97_id & 0x0001))); DDB (cmn_err (CE_CONT, "Modem Line Codec support %d\n", !!(devc->ac97_id & 0x0002))); DDB (cmn_err (CE_CONT, "Bass&Treble control %d\n", !!(devc->ac97_id & 0x0004))); DDB (cmn_err (CE_CONT, "Simulated stereo %d\n", !!(devc->ac97_id & 0x0008))); DDB (cmn_err (CE_CONT, "Headphone out support %d\n", !!(devc->ac97_id & 0x0010))); DDB (cmn_err (CE_CONT, "Loudness support %d\n", !!(devc->ac97_id & 0x0020))); DDB (cmn_err (CE_CONT, "18bit DAC resolution %d\n", !!(devc->ac97_id & 0x0040))); DDB (cmn_err (CE_CONT, "20bit DAC resolution %d\n", !!(devc->ac97_id & 0x0080))); DDB (cmn_err (CE_CONT, "18bit ADC resolution %d\n", !!(devc->ac97_id & 0x0100))); DDB (cmn_err (CE_CONT, "20bit ADC resolution %d\n", !!(devc->ac97_id & 0x0200))); DDB (cmn_err (CE_CONT, "3D enhancement technique: %x\n", devc->enh_3d)); #if 1 { int ext_status = codec_read (devc, 0x28); DDB (cmn_err (CE_CONT, "AC97 v2.1 multi slot support %d\n", !!(ext_status & 0x0200))); DDB (cmn_err (CE_CONT, "AC97 v2.1 surround DAC support 0x%x\n", (ext_status >> 6) & 0x07)); } #endif if (devc->fixed_rate) { int tmp3; /* Turn off variable samling rate support */ tmp3 = codec_read (devc, 0x2a) & ~0x0001; codec_write (devc, 0x2a, tmp3); } if (devc->ac97_id & 0x0004) devc->devmask |= SOUND_MASK_BASS | SOUND_MASK_TREBLE; if (devc->enh_3d != 0) { devc->devmask |= SOUND_MASK_DEPTH; codec_write (devc, 0x20, codec_read (devc, 0x20) | 0x2000); /* Turn on 3D */ } /* * Detect if the codec supports 5 or 6 bits in the master control register. */ codec_write (devc, 0x02, 0x20); if ((codec_read (devc, 0x02) & 0x1f) == 0x1f) devc->mastervol_bits = 5; else devc->mastervol_bits = 6; #if 0 codec_write (devc, 0x18, 0x20); if ((codec_read (devc, 0x18) & 0x1f) == 0x1f) devc->pcmvol_bits = 5; else devc->pcmvol_bits = 6; #endif if (devc->playrate_support == PLAY_8CHAN) devc->devmask |= SOUND_MASK_REARVOL | SOUND_MASK_CENTERVOL | SOUND_MASK_SIDEVOL; if (devc->playrate_support == PLAY_6CHAN) devc->devmask |= SOUND_MASK_REARVOL | SOUND_MASK_CENTERVOL; if (devc->playrate_support == PLAY_4CHAN) devc->devmask |= SOUND_MASK_REARVOL; sprintf (tmp_name, "%s (%s)", host_name, devc->name); if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, osdev, osdev, tmp_name, &ac97_mixer_driver, sizeof (mixer_driver_t), devc)) >= 0) { mixer_devs[my_mixer]->hw_devc = hostparms; mixer_devs[my_mixer]->priority = 2; /* Possible default mixer candidate */ devc->recdevs = 0; devc->mixer_dev = my_mixer; devc->is_ok = 1; sprintf (tmp_name, "%s_%d", devc->name, nr_ac97++); devc->levels = load_mixer_volumes (tmp_name, default_mixer_levels, 1); ac97_mixer_reset (devc); /* AD1888 seems to keep muting the headphone out */ if ((id & mask) == 0x41445368) { codec_write (devc, 0x04, codec_read (devc, 0x04) & ~0x8000); codec_write (devc, 0x1c, codec_read (devc, 0x1c) & ~0x8000); } /* set PC speaker to mute causes humming on STAC97xx AC97s */ codec_write (devc, 0x0a, 0x8000); } if ((devc->mixer_ext) || (devc->spdifout_support) || (devc->spdifin_support) || ac97_recselect) { mixer_ext_set_init_fn (devc->mixer_dev, ac97mixer_ext_init, 60); } return my_mixer; } int ac97_init_ext (int dev, ac97_devc * devc, mixer_create_controls_t func, int nextra) { if (dev < 0) return OSS_EIO; if ((devc->mixer_ext) || (devc->spdifout_support) || (devc->spdifin_support) || ac97_recselect) nextra += 50; devc->client_mixer_init = func; return mixer_ext_set_init_fn (dev, ac97mixer_ext_init, nextra); } int ac97_varrate (ac97_devc * devc) { int ext_status; if ((devc->fixed_rate) || !devc->is_ok) return 0; ext_status = codec_read (devc, 0x28); if (ext_status & 0x0001) { devc->var_rate_support = 1; } return devc->var_rate_support; } int ac97_playrate (ac97_devc * devc, int srate) { if (!devc->is_ok) return 0; if ((codec_read (devc, 0x7c) == 0x4144) && (codec_read (devc, 0x7e) == 0x5303)) { /* AD 18191B */ codec_write (devc, 0x74, 0x1901); codec_write (devc, 0x76, 0x0404); codec_write (devc, 0x7A, srate); /*use sr1 for play */ return 1; } if ((codec_read (devc, 0x7c) == 0x4144) && ((codec_read (devc, 0x7e) == 0x5348) || (codec_read (devc, 0x7e) == 0x5360) || (codec_read (devc, 0x7e) == 0x5361) || (codec_read (devc, 0x7e) == 0x5362))) { /* AD1881/AD1886/AD1887/AD1891 */ codec_write (devc, 0x76, 0x0404); codec_write (devc, 0x2a, codec_read (devc, 0x2a) | 0x01); codec_write (devc, 0x2c, srate); /* use sr1 for play */ return 1; } if (devc->playrate_support == PLAY_6CHAN) /* set front/rear/lfe/c/s rate */ { codec_write (devc, 0x2c, srate); codec_write (devc, 0x2e, srate); codec_write (devc, 0x30, srate); codec_write (devc, 0x2a, codec_read (devc, 0x2a) | 0x01); return 1; } codec_write (devc, 0x2c, srate); codec_write (devc, 0x2a, codec_read (devc, 0x2a) | 0x01); return 1; } int ac97_recrate (ac97_devc * devc, int srate) { if (!devc->is_ok) return 0; if ((codec_read (devc, 0x7c) == 0x4144) && (codec_read (devc, 0x7e) == 0x5303)) { /* AD 1819B */ codec_write (devc, 0x74, 0x1901); codec_write (devc, 0x76, 0x0404); codec_write (devc, 0x78, srate); /*use sr0 for rec */ return 1; } if ((codec_read (devc, 0x7c) == 0x4144) && ((codec_read (devc, 0x7e) == 0x5348) || (codec_read (devc, 0x7e) == 0x5360) || (codec_read (devc, 0x7e) == 0x5361) || (codec_read (devc, 0x7e) == 0x5362))) { /* AD1881/AD886/AD1887/AD1891 */ codec_write (devc, 0x76, 0x0404); codec_write (devc, 0x2a, codec_read (devc, 0x2a) | 0x01); codec_write (devc, 0x32, srate); /* use sr0 for rec */ return 1; } codec_write (devc, 0x32, srate); codec_write (devc, 0x2a, codec_read (devc, 0x2a) | 0x01); return 1; }