summaryrefslogtreecommitdiff
path: root/kernel/framework/ac97
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/ac97')
-rw-r--r--kernel/framework/ac97/.config1
-rw-r--r--kernel/framework/ac97/.params20
-rw-r--r--kernel/framework/ac97/ac97_ext.inc1453
-rw-r--r--kernel/framework/ac97/oss_ac97.c1228
4 files changed, 2702 insertions, 0 deletions
diff --git a/kernel/framework/ac97/.config b/kernel/framework/ac97/.config
new file mode 100644
index 0000000..e44f78f
--- /dev/null
+++ b/kernel/framework/ac97/.config
@@ -0,0 +1 @@
+targetcpu=any
diff --git a/kernel/framework/ac97/.params b/kernel/framework/ac97/.params
new file mode 100644
index 0000000..93a4f87
--- /dev/null
+++ b/kernel/framework/ac97/.params
@@ -0,0 +1,20 @@
+int ac97_amplifier=-1;
+/*
+ * AC97 codecs have a control output for an external audio output amplifier
+ * (necessary in some situations such as laptops). In some cases there may be
+ * need to turn this amplifier off.
+ *
+ * ac97_amplifier=-1 enables autodetection (default).
+ * ac97_amplifier=1 always enables the output amplifier stage.
+ * ac97_amplifier=0 disables it.
+ */
+
+int ac97_recselect=0;
+/*
+ * AC97 codecs support separate recording source selections for left and
+ * right channels. This feature can be enabled but that may cause some
+ * compatibility problems with certain applications.
+ *
+ * ac97_recselect=0 (default) selects common source selection for both channels.
+ * ac97_recselect=1 enables independent source selections.
+ */
diff --git a/kernel/framework/ac97/ac97_ext.inc b/kernel/framework/ac97/ac97_ext.inc
new file mode 100644
index 0000000..425dee7
--- /dev/null
+++ b/kernel/framework/ac97/ac97_ext.inc
@@ -0,0 +1,1453 @@
+
+/*
+ *
+ * Purpose: AC97 chip specific Mixer extension handlers
+ *
+ * This file is included from ac97.c and it implements codec specific
+ * extensions such as S/PDIF control.
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+extern int ac97_recselect;
+
+void
+ac97_enable_spdif (ac97_devc * devc)
+{
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ codec_write (devc, 0x68, codec_read (devc, 0x68) | 0x8000);
+ break;
+ case CX_SPDIFOUT:
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) | 0x08);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ codec_write (devc, 0x2a,
+ (codec_read (devc, 0x2a) & 0xffc3) | 0x4 | devc->
+ spdif_slot);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+ac97_disable_spdif (ac97_devc * devc)
+{
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ codec_write (devc, 0x68, codec_read (devc, 0x68) & ~0x8000);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ codec_write (devc, 0x2a, (codec_read (devc, 0x2a) & 0xffc3) & ~0x4);
+ break;
+ case CX_SPDIFOUT:
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) & ~0x8);
+ break;
+ default:
+ break;
+ }
+}
+
+/********************AC97 SPDIFIN Control *************************/
+int
+ac97_spdifin_ctl (int dev, int ctrl, unsigned int cmd, int value)
+{
+ ac97_devc *devc = mixer_devs[dev]->devc;
+
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ value = 0;
+ switch (ctrl)
+ {
+ case SPDIFIN_ENABLE: /* Enable/disable SPDIF IN */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x7A) & 0x8000) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x6c) & 0x1) ? 1 : 0;
+ break;
+ case SPDIFIN_PRO: /* Pro */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0x1) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0x1) ? 1 : 0;
+ break;
+ case SPDIFIN_AUDIO: /* /Audio */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0x2) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0x1) ? 1 : 0;
+ break;
+ case SPDIFIN_COPY: /* Copy */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0x4) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0x4) ? 1 : 0;
+ break;
+ case SPDIFIN_PREEMPH: /* Pre */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0x38) >> 3;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0x38) >> 3;
+ break;
+ case SPDIFIN_MODE: /* Mode */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0xc0) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0xc0) ? 1 : 0;
+ break;
+ case SPDIFIN_CATEGORY: /* Category */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0x7f00) >> 8;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0x7f00) >> 8;
+ break;
+ case SPDIFIN_GENERATION: /* Level */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x60) & 0x8000) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x68) & 0x8000) ? 1 : 0;
+ break;
+ case SPDIFIN_SOURCE: /* Source */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = codec_read (devc, 0x62) & 0xf;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = codec_read (devc, 0x6a) & 0xf;
+ break;
+ case SPDIFIN_CHAN: /* Channel */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x62) & 0xf0) >> 4;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x6a) & 0xf0) >> 4;
+ break;
+ case SPDIFIN_RATE: /* Sampling Rate */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x62) & 0xf00) >> 8;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x6a) & 0xf00) >> 8;
+ switch (value)
+ {
+ case 0:
+ value = 44100;
+ break;
+ case 2:
+ value = 48000;
+ break;
+ case 3:
+ value = 32000;
+ break;
+ }
+ break;
+ case SPDIFIN_CLOCK: /* Clock Accuracy */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x62) & 0x3000) >> 12;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = (codec_read (devc, 0x6a) & 0x3000) >> 12;
+ break;
+ case SPDIFIN_SIGNAL: /* Lock */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x62) & 0x4000) ? 1 : 0;
+ break;
+ case SPDIFIN_VBIT: /* VBit */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = (codec_read (devc, 0x62) & 0x8000) ? 1 : 0;
+ break;
+
+ case SPDIFIN_MON: /* SPDIF Input Mon */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ value = codec_read (devc, 0x6A) & (1 << 13) ? 1 : 0;
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ value = codec_read (devc, 0x6c) & 0x8 ? 1 : 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ switch (ctrl)
+ {
+ case SPDIFIN_ENABLE: /* SPDIF Record Enable */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ {
+ if (value)
+ {
+ /* Set 0x7a enable SPDIF Input */
+ /* set 0x6a: PCM Input from SPDIFIN */
+ codec_write (devc, 0x7a, codec_read (devc, 0x7a) | 0x2);
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x8000);
+ }
+ else
+ {
+ codec_write (devc, 0x7a, codec_read (devc, 0x7a) & ~0x2);
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x8000);
+ }
+ }
+
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ {
+ if (value)
+ {
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) | 1);
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) | 0x10);
+ }
+ else
+ {
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) & ~1);
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) & ~0x10);
+ }
+ }
+ break;
+
+ case SPDIFIN_MON: /* SPDIF input monitor on analog DAC */
+ if (devc->spdifin_support == ALC_SPDIFIN)
+ {
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | (1 << 13));
+ else
+ codec_write (devc, 0x6a,
+ codec_read (devc, 0x6a) & ~(1 << 13));
+ }
+
+ if (devc->spdifin_support == CMI_SPDIFIN)
+ {
+ if (value)
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) | 0x8);
+ else
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) & ~0x8);
+ }
+ break;
+
+ }
+ }
+ return value;
+}
+
+static int
+spdifin_ext_init (int dev)
+{
+ int group, err;
+
+ if ((group = mixer_ext_create_group_flags (dev, 0, "SPDIN", MIXF_FLAT)) < 0)
+ return group;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_ENABLE, ac97_spdifin_ctl,
+ MIXT_ONOFF, "SPDIN_Enable", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_MON, ac97_spdifin_ctl,
+ MIXT_ONOFF, "SPDIN_Monitor", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+#if 1
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_PRO, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Pro", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_AUDIO, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Audio", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_COPY, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Copy", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_PREEMPH,
+ ac97_spdifin_ctl, MIXT_VALUE,
+ "SPDIN_Pre-emph", 1, MIXF_READABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_MODE, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Mode", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_CATEGORY,
+ ac97_spdifin_ctl, MIXT_VALUE,
+ "SPDIN_CATEGORY", 1, MIXF_READABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_GENERATION,
+ ac97_spdifin_ctl, MIXT_VALUE,
+ "SPDIN_GenLevel", 1, MIXF_READABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_SOURCE, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Source", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_CHAN, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Channel", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_RATE, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Rate", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_CLOCK, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Clock", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_SIGNAL, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_Signal", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFIN_VBIT, ac97_spdifin_ctl,
+ MIXT_VALUE, "SPDIN_VBit", 1,
+ MIXF_READABLE)) < 0)
+ return err;
+#endif
+ return 0;
+}
+
+/********************AC97 SPDIFOUT Control *************************/
+int
+ac97_spdifout_ctl (int dev, int ctrl, unsigned int cmd, int value)
+{
+ int tmp = 0;
+ static int flag;
+ ac97_devc *devc = mixer_devs[dev]->devc;
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ value = 0;
+ switch (ctrl)
+ {
+ case SPDIFOUT_ENABLE: /* SPDIF OUT */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ value = (codec_read (devc, 0x68) & 0x8000) ? 1 : 0;
+ break;
+ case CX_SPDIFOUT:
+ value = (codec_read (devc, 0x5c) & 0x08) ? 1 : 0;
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ value = (codec_read (devc, 0x2a) & 0x4) ? 1 : 0;
+ break;
+ }
+ break;
+
+ case SPDIFOUT_PRO: /* Consumer/PRO */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ value = (codec_read (devc, 0x68) & 0x1) ? 1 : 0;
+ break;
+ case CX_SPDIFOUT:
+ value = (codec_read (devc, 0x5c) & 0x1) ? 1 : 0;
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ value = (codec_read (devc, 0x3a) & 0x1) ? 1 : 0;
+ break;
+ }
+ break;
+
+ case SPDIFOUT_AUDIO: /* PCM/AC3 */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ value = (codec_read (devc, 0x68) & 0x2) ? 1 : 0;
+ break;
+ case CX_SPDIFOUT:
+ value = (codec_read (devc, 0x5c) & 0x2) ? 1 : 0;
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ value = (codec_read (devc, 0x3a) & 0x2) ? 1 : 0;
+ break;
+ }
+ break;
+
+ case SPDIFOUT_COPY: /* Copy Prot */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ value = (codec_read (devc, 0x68) & 0x4) ? 1 : 0;
+ break;
+ case CX_SPDIFOUT:
+ value = (codec_read (devc, 0x5c) & 0x4) ? 1 : 0;
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ value = (codec_read (devc, 0x3a) & 0x4) ? 1 : 0;
+ break;
+ }
+ break;
+
+ case SPDIFOUT_PREEMPH: /* Pre emphasis */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ value = codec_read (devc, 0x68) & 0x8 ? 1 : 0;
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ value = (codec_read (devc, 0x3a) & 0x8) ? 1 : 0;
+ break;
+ }
+ break;
+
+ case SPDIFOUT_RATE: /* Sampling Rate */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ {
+ tmp = codec_read (devc, 0x68) & 0x1000;
+ switch (tmp)
+ {
+ case 0x1000:
+ value = 0;
+ break;
+ case 0x0000:
+ value = 1;
+ break;
+ default:
+ cmn_err (CE_NOTE, "unsupported SPDIF F/S rate\n");
+ break;
+ }
+
+ }
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ {
+ tmp = codec_read (devc, 0x3a) & 0x3000;
+ switch (tmp)
+ {
+ case 0x2000:
+ value = 0;
+ break;
+ case 0x0000:
+ value = 1;
+ break;
+ case 0x3000:
+ value = 2;
+ break;
+ default:
+ cmn_err (CE_NOTE, "unsupported SPDIF F/S rate\n");
+ value = 0;
+ break;
+ }
+ }
+ break;
+ }
+ break;
+
+ case SPDIFOUT_VBIT: /* V Bit */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ value = codec_read (devc, 0x68) & 0x4000 ? 1 : 0;
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ value = codec_read (devc, 0x3a) & 0x8000 ? 1 : 0;
+ break;
+ }
+ break;
+
+ case SPDIFOUT_ADC: /* Analog In to SPDIF Out */
+ switch (devc->spdifout_support)
+ {
+ case ALC_SPDIFOUT:
+ value = codec_read (devc, 0x6a) & (1 << 12) ? 1 : 0;
+ break;
+
+ case AD_SPDIFOUT:
+ value = codec_read (devc, 0x74) & 0x4 ? 1 : 0;
+ break;
+
+ case CS_SPDIFOUT:
+ value = codec_read (devc, 0x5e) & (1 << 11) ? 1 : 0;
+ break;
+
+ case STAC_SPDIFOUT:
+ value = codec_read (devc, 0x6a) & 0x2 ? 1 : 0;
+ break;
+
+ case YMF_SPDIFOUT:
+ value = codec_read (devc, 0x66) & 0x2 ? 1 : 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ ac97_disable_spdif (devc);
+ switch (ctrl)
+ {
+ case SPDIFOUT_ENABLE: /* Enable SPDIF OUT */
+ if (value)
+ {
+ ac97_enable_spdif (devc);
+ flag = 1;
+ }
+ else
+ {
+ ac97_disable_spdif (devc);
+ flag = 0;
+ }
+ break;
+
+ case SPDIFOUT_PRO: /* consumer/pro audio */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x68, codec_read (devc, 0x68) | 1);
+ else
+ codec_write (devc, 0x68, codec_read (devc, 0x68) & ~1);
+ break;
+ case CX_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) | 1);
+ else
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) & ~1);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) | 1);
+ else
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) & ~1);
+ break;
+ }
+ break;
+
+ case SPDIFOUT_AUDIO: /* PCM/AC3 */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x68, codec_read (devc, 0x68) | 0x2);
+ else
+ codec_write (devc, 0x68, codec_read (devc, 0x68) & ~2);
+ break;
+ case CX_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) | 0x2);
+ else
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) & ~2);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) | 0x2);
+ else
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) & ~2);
+ break;
+ }
+ break;
+
+ case SPDIFOUT_COPY: /* copy prot */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x68, codec_read (devc, 0x68) | 4);
+ else
+ codec_write (devc, 0x68, codec_read (devc, 0x68) & ~4);
+ break;
+ case CX_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) | 4);
+ else
+ codec_write (devc, 0x5c, codec_read (devc, 0x5c) & ~4);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) | 4);
+ else
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) & ~4);
+ break;
+ }
+ break;
+
+ case SPDIFOUT_PREEMPH: /* preemphasis */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x68, codec_read (devc, 0x68) | 8);
+ else
+ codec_write (devc, 0x68, codec_read (devc, 0x68) & ~8);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) | 8);
+ else
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) & ~8);
+ break;
+ }
+ break;
+
+ case SPDIFOUT_RATE: /* Frequency */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ switch (value)
+ {
+ case 0:
+ codec_write (devc, 0x68,
+ (codec_read (devc, 0x68) & 0xEFFF) | 0x1000);
+ break;
+ case 1:
+ codec_write (devc, 0x68,
+ (codec_read (devc, 0x68) & 0xEFFF) | 0);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ switch (value)
+ {
+ case 0: /* 48000 */
+ codec_write (devc, 0x3a,
+ (codec_read (devc, 0x3a) & 0xcfff) | 0x2000);
+ break;
+ case 1: /* 44100 */
+ codec_write (devc, 0x3a,
+ (codec_read (devc, 0x3a) & 0xcfff) | 0x0);
+ break;
+ case 2: /* 32000 */
+ codec_write (devc, 0x3a,
+ (codec_read (devc, 0x3a) & 0xcfff) | 0x3000);
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ break;
+
+ case SPDIFOUT_VBIT: /* V Bit */
+ switch (devc->spdifout_support)
+ {
+ case CS_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x68, codec_read (devc, 0x68) | 0x4000);
+ else
+ codec_write (devc, 0x68, codec_read (devc, 0x68) & ~0x4000);
+ break;
+ case AD_SPDIFOUT:
+ case ALC_SPDIFOUT:
+ case VIA_SPDIFOUT:
+ case STAC_SPDIFOUT:
+ case CMI_SPDIFOUT:
+ case YMF_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) | 0x8000);
+ else
+ codec_write (devc, 0x3a, codec_read (devc, 0x3a) & ~0x8000);
+ break;
+ }
+ break;
+
+ case SPDIFOUT_ADC: /* Analog In to SPDIF Out */
+ switch (devc->spdifout_support)
+ {
+ case ALC_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | (1 << 12));
+ else
+ codec_write (devc, 0x6a,
+ codec_read (devc, 0x6a) & ~(1 << 12));
+ break;
+
+ case AD_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x74, codec_read (devc, 0x74) | 0x4);
+ else
+ codec_write (devc, 0x74, codec_read (devc, 0x74) & ~0x4);
+ break;
+
+ case CS_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x5e, codec_read (devc, 0x5e) | (1 << 11));
+ else
+ codec_write (devc, 0x5e,
+ codec_read (devc, 0x5e) & ~(1 << 11));
+ break;
+
+ case STAC_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x2);
+ else
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x2);
+ break;
+
+ case CMI_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) | 0x2);
+ else
+ codec_write (devc, 0x6c, codec_read (devc, 0x6c) & ~0x2);
+ break;
+
+ case YMF_SPDIFOUT:
+ if (value)
+ codec_write (devc, 0x66, codec_read (devc, 0x66) | 0x2);
+ else
+ codec_write (devc, 0x66, codec_read (devc, 0x66) & ~0x2);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (flag)
+ ac97_enable_spdif (devc);
+ }
+
+ return value;
+}
+
+static int
+spdifout_ext_init (int dev)
+{
+ int group, err;
+ if ((group =
+ mixer_ext_create_group_flags (dev, 0, "SPDOUT", MIXF_FLAT)) < 0)
+ return group;
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_ENABLE,
+ ac97_spdifout_ctl, MIXT_ONOFF,
+ "SPDOUT_ENABLE", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_ADC,
+ ac97_spdifout_ctl, MIXT_ONOFF,
+ "SPDOUT_ADC/DAC", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_PRO,
+ ac97_spdifout_ctl, MIXT_ENUM,
+ "SPDOUT_Pro", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_AUDIO,
+ ac97_spdifout_ctl, MIXT_ENUM,
+ "SPDOUT_Audio", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_COPY,
+ ac97_spdifout_ctl, MIXT_ONOFF,
+ "SPDOUT_Copy", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_PREEMPH,
+ ac97_spdifout_ctl, MIXT_ONOFF,
+ "SPDOUT_Pre-emph", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_RATE,
+ ac97_spdifout_ctl, MIXT_ENUM,
+ "SPDOUT_Rate", 3,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPDIFOUT_VBIT,
+ ac97_spdifout_ctl, MIXT_ONOFF,
+ "SPDOUT_VBit", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+
+ return 0;
+}
+
+/**********************Mixer Extensions ********************/
+int
+ac97_mixext_ctl (int dev, int ctrl, unsigned int cmd, int value)
+{
+ int left, right;
+ ac97_devc *devc = mixer_devs[dev]->devc;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ value = 0;
+ switch (ctrl)
+ {
+ case VOL_CENTER: /* Left line in to output connection */
+ value = devc->extmixlevels[CENTER_VOL]; /* LFE/Center */
+ break;
+
+ case VOL_REAR:
+ value = devc->extmixlevels[REAR_VOL]; /* Rear/Surround */
+ break;
+
+ case VOL_SIDE:
+ value = devc->extmixlevels[SIDE_VOL]; /* Side Surround */
+ break;
+
+ case REAR2LINE: /* Rear to Line In */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ value = (codec_read (devc, 0x6A) & 0x200) ? 1 : 0;
+ if (devc->mixer_ext == CMI9739_MIXER_EXT)
+ value = (codec_read (devc, 0x64) & 0x400) ? 1 : 0;
+ if (devc->mixer_ext == STAC9758_MIXER_EXT)
+ value = (codec_read (devc, 0x64) & 0x50) ? 1 : 0;
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ value = (codec_read (devc, 0x76) & (1 << 11)) ? 0 : 1;
+ break;
+
+ case CENTER2MIC: /* Center to Mic */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ value = (codec_read (devc, 0x6A) & 0x400) ? 1 : 0;
+ if (devc->mixer_ext == CMI9739_MIXER_EXT)
+ value = (codec_read (devc, 0x64) & 0x1000) ? 1 : 0;
+ if (devc->mixer_ext == STAC9758_MIXER_EXT)
+ value = (codec_read (devc, 0x64) & 0x0C) ? 1 : 0;
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ value = (codec_read (devc, 0x76) & (1 << 12)) ? 0 : 1;
+ break;
+
+ case SPREAD: /* Duplicate front on lfe/rear */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ value = (codec_read (devc, 0x6A) & 0x1) ? 1 : 0;
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ value = (codec_read (devc, 0x76) & 0x80) ? 1 : 0;
+ if (devc->mixer_ext == VIA1616_MIXER_EXT)
+ value = (codec_read (devc, 0x5a) & 0x8000) ? 1 : 0;
+ break;
+
+ case MICBOOST: /* 30db Mic boost */
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ value = (codec_read (devc, 0x76) & 0x2) ? 1 : 0;
+ if (devc->mixer_ext == CMI9739_MIXER_EXT)
+ value = (codec_read (devc, 0x64) & 0x1) ? 1 : 0;
+ break;
+
+ case DOWNMIX_LFE: /* Downmix LFE to Front */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ value = (codec_read (devc, 0x6A) & 0x4) ? 1 : 0;
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ value = (codec_read (devc, 0x76) & 0x100) ? 1 : 0;
+ if (devc->mixer_ext == VIA1616_MIXER_EXT)
+ value = (codec_read (devc, 0x5a) & 0x1000) ? 1 : 0;
+ break;
+
+ case DOWNMIX_REAR: /* Downmix Rear to Front */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ value = (codec_read (devc, 0x6A) & 0x2) ? 1 : 0;
+ if (devc->mixer_ext == VIA1616_MIXER_EXT)
+ value = (codec_read (devc, 0x5a) & 0x800) ? 1 : 0;
+ break;
+ }
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ switch (ctrl)
+ {
+ case VOL_CENTER: /* Center/LFE volume */
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ value = left | (right << 8);
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ codec_write (devc, 0x66,
+ mix_scale (left, right, devc->centervol_bits));
+ devc->extmixlevels[CENTER_VOL] = value;
+ break;
+
+ case VOL_REAR: /* Rear/Surround volume */
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ value = left | (right << 8);
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ codec_write (devc, 0x64,
+ mix_scale (left, right, devc->rearvol_bits));
+ devc->extmixlevels[REAR_VOL] = value;
+ break;
+
+ case VOL_SIDE: /* Side Surround volume */
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ value = left | (right << 8);
+ if (devc->mixer_ext == CMI9780_MIXER_EXT)
+ codec_write (devc, 0x60,
+ mix_scale (left, right, devc->sidevol_bits));
+ devc->extmixlevels[SIDE_VOL] = value;
+ break;
+
+ case REAR2LINE: /* Surround to Line In */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x200);
+ else
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x200);
+ }
+
+ if ((devc->mixer_ext == CMI9739_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT))
+ {
+ if (value)
+ codec_write (devc, 0x64, codec_read (devc, 0x64) | 0x400);
+ else
+ codec_write (devc, 0x64, codec_read (devc, 0x64) & ~0x400);
+ }
+
+ if (devc->mixer_ext == STAC9758_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x64, codec_read (devc, 0x64) | 0x50);
+ else
+ codec_write (devc, 0x64, codec_read (devc, 0x64) & ~0x50);
+ }
+
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x76,
+ codec_read (devc, 0x76) & ~(1 << 11));
+ else
+ codec_write (devc, 0x76, codec_read (devc, 0x76) | (1 << 11));
+ }
+
+ break;
+
+ case CENTER2MIC: /* Center/LFE to Mic In */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x400);
+ else
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x400);
+ }
+
+ if ((devc->mixer_ext == CMI9739_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT))
+ {
+ if (value)
+ codec_write (devc, 0x64, codec_read (devc, 0x64) | 0x1000);
+ else
+ codec_write (devc, 0x64, codec_read (devc, 0x64) & ~0x1000);
+ }
+
+ if (devc->mixer_ext == STAC9758_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x64, codec_read (devc, 0x64) | 0xC);
+ else
+ codec_write (devc, 0x64, codec_read (devc, 0x64) & ~0xC);
+ }
+
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x76,
+ codec_read (devc, 0x76) & ~(1 << 12));
+ else
+ codec_write (devc, 0x76, codec_read (devc, 0x76) | (1 << 12));
+ }
+ break;
+
+ case SPREAD: /* 4 Speaker output - LF/RF duplicate on Rear */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ {
+ if (value)
+ {
+ /* enable Line-In jack to play Surround out */
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x200);
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 1);
+ }
+ else
+ {
+ /* disable Line-In jack to play Surround out */
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x200);
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~1);
+ }
+ }
+
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x76, codec_read (devc, 0x76) | 0x80);
+ else
+ codec_write (devc, 0x76, codec_read (devc, 0x76) & ~0x80);
+ }
+
+ if (devc->mixer_ext == VIA1616_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x5a, codec_read (devc, 0x5a) | 0x8000);
+ else
+ codec_write (devc, 0x5a, codec_read (devc, 0x5a) & ~0x8000);
+ }
+ break;
+
+ case MICBOOST:
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x76, codec_read (devc, 0x76) | 0x2);
+ else
+ codec_write (devc, 0x76, codec_read (devc, 0x76) & ~0x2);
+ }
+
+ if ((devc->mixer_ext == CMI9739_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT))
+ {
+ if (value)
+ codec_write (devc, 0x64, codec_read (devc, 0x64) | 0x1);
+ else
+ codec_write (devc, 0x64, codec_read (devc, 0x64) & ~0x1);
+ }
+ break;
+
+ case DOWNMIX_LFE: /* Downmix LFE to Front */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x4);
+ else
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x4);
+ }
+
+ if (devc->mixer_ext == AD1980_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x76, codec_read (devc, 0x76) | 0x300);
+ else
+ codec_write (devc, 0x76, codec_read (devc, 0x76) & ~0x300);
+ }
+
+ if (devc->mixer_ext == VIA1616_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x5a, codec_read (devc, 0x5a) | 0x1000);
+ else
+ codec_write (devc, 0x5a, codec_read (devc, 0x5a) & ~0x1000);
+ }
+ break;
+
+ case DOWNMIX_REAR: /* Downmix Rear to Front */
+ if (devc->mixer_ext == ALC650_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) | 0x2);
+ else
+ codec_write (devc, 0x6a, codec_read (devc, 0x6a) & ~0x2);
+ }
+
+ if (devc->mixer_ext == VIA1616_MIXER_EXT)
+ {
+ if (value)
+ codec_write (devc, 0x5a, codec_read (devc, 0x5a) | 0x800);
+ else
+ codec_write (devc, 0x5a, codec_read (devc, 0x5a) & ~0x800);
+ }
+ break;
+ }
+ }
+ return value;
+}
+
+static int
+ac97_micboost_ctl (int dev, int ctrl, unsigned int cmd, int value)
+{
+ ac97_devc *devc = mixer_devs[dev]->devc;
+ int tmp;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ return !!devc->micboost;
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ value = !!value;
+
+ tmp = codec_read (devc, 0x0e);
+
+ if (value)
+ devc->micboost = 0x40;
+ else
+ devc->micboost = 0x00;
+
+ codec_write (devc, 0x0e, (tmp & ~0x40) | devc->micboost);
+
+ return value;
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+ac97_recselect_ctl (int dev, int ctrl, unsigned int cmd, int value)
+{
+ int tmp;
+ ac97_devc *devc = mixer_devs[dev]->devc;
+
+ if (ctrl < 0 || ctrl > 1)
+ return OSS_EINVAL;
+
+ tmp = codec_read (devc, 0x1a);
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ return (tmp >> (ctrl * 8)) & 0x07;
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ if (value < 0 || value > 7)
+ return OSS_EINVAL;
+
+ tmp &= ~(0x7 << (ctrl * 8));
+ tmp |= (value << (ctrl * 8));
+ codec_write (devc, 0x1a, tmp);
+ return value;
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+ac97_mixext_init (int dev)
+{
+ ac97_devc *devc = mixer_devs[dev]->devc;
+ unsigned int mixflags = 0;
+
+ int group, err, ctl;
+
+ if ((ctl =
+ mixer_ext_create_control (dev, 0, 0,
+ ac97_micboost_ctl, MIXT_ONOFF,
+ "micboost", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+
+ if (ac97_recselect)
+ {
+ if ((group =
+ mixer_ext_create_group_flags (dev, 0, "AC97_RECSEL",
+ MIXF_FLAT)) < 0)
+ return group;
+
+ if ((ctl =
+ mixer_ext_create_control (dev, group, 1,
+ ac97_recselect_ctl, MIXT_ENUM,
+ "LEFT", 8,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ mixer_ext_set_strings (dev, ctl,
+ "MIC CD VIDEO AUX LINE STEREOMIX MONOMIX PHONE",
+ 0);
+
+ if ((ctl =
+ mixer_ext_create_control (dev, group, 0,
+ ac97_recselect_ctl, MIXT_ENUM,
+ "RIGHT", 8,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ mixer_ext_set_strings (dev, ctl,
+ "MIC CD VIDEO AUX LINE STEREOMIX MONOMIX PHONE",
+ 0);
+ }
+
+/*
+ * Don't use flat groups with AC97 chips that have slider controls (ALC650 at this moment)
+ */
+ if (devc->mixer_ext != ALC650_MIXER_EXT)
+ mixflags = MIXF_FLAT;
+
+ if ((group =
+ mixer_ext_create_group_flags (dev, 0, "AC97_MIXEXT", mixflags)) < 0)
+ return group;
+
+ if ((devc->mixer_ext == ALC650_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT))
+ {
+ if ((err =
+ mixer_ext_create_control (dev, group, VOL_CENTER,
+ ac97_mixext_ctl, MIXT_STEREOSLIDER,
+ "CENTERVOL", 100,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ ac97_mixext_ctl (dev, VOL_CENTER, SNDCTL_MIX_WRITE, 100 | (100 << 8));
+
+ if ((err =
+ mixer_ext_create_control (dev, group, VOL_REAR, ac97_mixext_ctl,
+ MIXT_STEREOSLIDER, "REARVOL", 100,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ ac97_mixext_ctl (dev, VOL_REAR, SNDCTL_MIX_WRITE, 100 | (100 << 8));
+
+ if ((err =
+ mixer_ext_create_control (dev, group, VOL_SIDE, ac97_mixext_ctl,
+ MIXT_STEREOSLIDER, "SIDEVOL", 100,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ ac97_mixext_ctl (dev, VOL_SIDE, SNDCTL_MIX_WRITE, 100 | (100 << 8));
+ }
+
+
+ if ((devc->mixer_ext == CMI9739_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT)
+ || (devc->mixer_ext == ALC650_MIXER_EXT)
+ || (devc->mixer_ext == AD1980_MIXER_EXT)
+ || (devc->mixer_ext == STAC9758_MIXER_EXT))
+ {
+ if ((err =
+ mixer_ext_create_control (dev, group, REAR2LINE,
+ ac97_mixext_ctl, MIXT_ONOFF,
+ "REAR2LINEJACK", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+
+ if ((devc->mixer_ext == CMI9739_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT)
+ || (devc->mixer_ext == ALC650_MIXER_EXT)
+ || (devc->mixer_ext == AD1980_MIXER_EXT)
+ || (devc->mixer_ext == STAC9758_MIXER_EXT))
+ {
+
+ if ((err =
+ mixer_ext_create_control (dev, group, CENTER2MIC,
+ ac97_mixext_ctl, MIXT_ONOFF,
+ "CENTER2MICJACK", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+
+ if ((devc->mixer_ext == AD1980_MIXER_EXT)
+ || (devc->mixer_ext == VIA1616_MIXER_EXT)
+ || (devc->mixer_ext == ALC650_MIXER_EXT))
+ {
+
+ if ((err =
+ mixer_ext_create_control (dev, group, SPREAD,
+ ac97_mixext_ctl, MIXT_ENUM,
+ "SPKMODE", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+
+ if ((devc->mixer_ext == AD1980_MIXER_EXT)
+ || (devc->mixer_ext == VIA1616_MIXER_EXT)
+ || (devc->mixer_ext == ALC650_MIXER_EXT))
+ {
+ if ((err =
+ mixer_ext_create_control (dev, group, DOWNMIX_LFE,
+ ac97_mixext_ctl, MIXT_ONOFF,
+ "MIX-LFE2FRONT", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+
+ if ((devc->mixer_ext == VIA1616_MIXER_EXT)
+ || (devc->mixer_ext == ALC650_MIXER_EXT))
+ {
+ if ((err =
+ mixer_ext_create_control (dev, group, DOWNMIX_REAR,
+ ac97_mixext_ctl, MIXT_ONOFF,
+ "MIX-REAR2FRONT", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+
+ if ((devc->mixer_ext == AD1980_MIXER_EXT)
+ || (devc->mixer_ext == CMI9739_MIXER_EXT)
+ || (devc->mixer_ext == CMI9780_MIXER_EXT))
+ {
+ if ((err =
+ mixer_ext_create_control (dev, group, MICBOOST,
+ ac97_mixext_ctl, MIXT_ONOFF,
+ "MICBOOST", 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ }
+ return 0;
+}
+
+/********************END of Mixer Extensions **************/
+
+/* AC97 Mixer Extensions */
+
+static int
+ac97mixer_ext_init (int dev)
+{
+ ac97_devc *devc = mixer_devs[dev]->devc;
+ int err;
+
+ if (devc->client_mixer_init != NULL)
+ if ((err = devc->client_mixer_init (dev)) < 0)
+ return err;
+
+ if ((devc->mixer_ext > 0) || ac97_recselect)
+ if ((err = ac97_mixext_init (dev)) < 0)
+ return err;
+
+ if (devc->spdifout_support)
+ if ((err = spdifout_ext_init (dev)) < 0)
+ return err;
+
+ if (devc->spdifin_support)
+ if ((err = spdifin_ext_init (dev)) < 0)
+ return err;
+ return 0;
+}
+
+void
+ac97_spdif_setup (int dev, int speed, int bits)
+{
+ int val;
+
+ val = ac97_spdifout_ctl (dev, SPDIFOUT_RATE, SNDCTL_MIX_READ, 0);
+
+ mixer_devs[dev]->modify_counter++;
+
+ switch (speed)
+ {
+ case 48000:
+ if (val != 0)
+ ac97_spdifout_ctl (dev, SPDIFOUT_RATE, SNDCTL_MIX_WRITE, 0);
+ break;
+
+ case 44100:
+ if (val != 1)
+ ac97_spdifout_ctl (dev, SPDIFOUT_RATE, SNDCTL_MIX_WRITE, 1);
+ break;
+
+ case 32000:
+ if (val != 2)
+ ac97_spdifout_ctl (dev, SPDIFOUT_RATE, SNDCTL_MIX_WRITE, 2);
+ break;
+
+ default:
+ break;
+ }
+
+ if (bits == AFMT_AC3)
+ ac97_spdifout_ctl (dev, SPDIFOUT_AUDIO, SNDCTL_MIX_WRITE, 1);
+ else
+ ac97_spdifout_ctl (dev, SPDIFOUT_AUDIO, SNDCTL_MIX_WRITE, 0);
+}
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;
+}