diff options
Diffstat (limited to 'kernel/framework/ac97')
-rw-r--r-- | kernel/framework/ac97/.config | 1 | ||||
-rw-r--r-- | kernel/framework/ac97/.params | 20 | ||||
-rw-r--r-- | kernel/framework/ac97/ac97_ext.inc | 1453 | ||||
-rw-r--r-- | kernel/framework/ac97/oss_ac97.c | 1228 |
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; +} |