diff options
Diffstat (limited to 'kernel/framework/ac97/ac97_ext.inc')
-rw-r--r-- | kernel/framework/ac97/ac97_ext.inc | 1453 |
1 files changed, 1453 insertions, 0 deletions
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); +} |