summaryrefslogtreecommitdiff
path: root/kernel/framework/ac97/oss_ac97.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
commit1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch)
tree4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/framework/ac97/oss_ac97.c
downloadoss4-1058def8e7827e56ce4a70afb4aeacb5dc44148f.tar.gz
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/framework/ac97/oss_ac97.c')
-rw-r--r--kernel/framework/ac97/oss_ac97.c1228
1 files changed, 1228 insertions, 0 deletions
diff --git a/kernel/framework/ac97/oss_ac97.c b/kernel/framework/ac97/oss_ac97.c
new file mode 100644
index 0000000..8cab4e0
--- /dev/null
+++ b/kernel/framework/ac97/oss_ac97.c
@@ -0,0 +1,1228 @@
+/*
+ * 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;
+}