ALSA: ctxfi: Add support for Creative Titanium HD
authorHarry Butterworth <heb1001@gmail.com>
Sat, 11 Jun 2011 08:02:06 +0000 (16:02 +0800)
committerTakashi Iwai <tiwai@suse.de>
Tue, 14 Jun 2011 05:32:29 +0000 (07:32 +0200)
Initialise model-specific DAC and ADC parts.
Add controls for output and mic source selection.
Rename some mixer controls according to ControlNames.txt.
Remove Playback switches for Line-in and IEC958-in - these
were controlling the input mute/unmute which affected
capture too.  Use the capture switches to control the
input mute/unmute instead - it's less confusing.
Initialise the WM8775 to invert the left-right clock
to swap the left and right channels of the mic and aux
input.

Signed-off-by: Harry Butterworth <heb1001@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/linux/pci_ids.h
sound/pci/ctxfi/ct20k2reg.h
sound/pci/ctxfi/ctatc.c
sound/pci/ctxfi/ctatc.h
sound/pci/ctxfi/ctdaio.c
sound/pci/ctxfi/ctdaio.h
sound/pci/ctxfi/cthardware.h
sound/pci/ctxfi/cthw20k1.c
sound/pci/ctxfi/cthw20k2.c
sound/pci/ctxfi/ctmixer.c
sound/pci/ctxfi/xfi.c

index a311008af5e1538c262990459fb2daefe7d8dddb..f23f8bf02b048d8ec3085e4e2f8b9420d9a2f17d 100644 (file)
 #define PCI_SUBDEVICE_ID_CREATIVE_SB08801      0x0041
 #define PCI_SUBDEVICE_ID_CREATIVE_SB08802      0x0042
 #define PCI_SUBDEVICE_ID_CREATIVE_SB08803      0x0043
+#define PCI_SUBDEVICE_ID_CREATIVE_SB1270       0x0062
 #define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX      0x6000
 
 #define PCI_VENDOR_ID_ECTIVA           0x1102 /* duplicate: CREATIVE */
index e0394e3996e851dda9b6abc6d22f52d81512a3b8..ca501ba03d64c97cf006c47f5693fb6bde3ca202 100644 (file)
@@ -55,6 +55,7 @@
 /* GPIO Registers */
 #define GPIO_DATA           0x1B7020
 #define GPIO_CTRL           0x1B7024
+#define GPIO_EXT_DATA       0x1B70A0
 
 /* Virtual memory registers */
 #define VMEM_PTPAL          0x1C6300 /* 0x1C6300 + (16 * Chn) */
index 13f33c0719d34bda8ca71b203a568fbae6745b8c..952fd94c266608e7bd8a202dd1879b2363ad08fe 100644 (file)
@@ -30,7 +30,6 @@
 #include <sound/asoundef.h>
 
 #define MONO_SUM_SCALE 0x19a8  /* 2^(-0.5) in 14-bit floating format */
-#define DAIONUM                7
 #define MAX_MULTI_CHN  8
 
 #define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
@@ -53,6 +52,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
 static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
                      "SB0760", CTSB0760),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270,
+                     "SB1270", CTSB1270),
        SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
                      "SB0880", CTSB0880),
        SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
@@ -75,6 +76,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
        [CTSB0760]      = "SB076x",
        [CTHENDRIX]     = "Hendrix",
        [CTSB0880]      = "SB0880",
+       [CTSB1270]      = "SB1270",
        [CT20K2_UNKNOWN] = "Unknown",
 };
 
@@ -459,12 +461,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
                                apcm->substream->runtime->rate);
        *n_srcc = 0;
 
-       if (1 == atc->msr) {
+       if (1 == atc->msr) { /* FIXME: do we really need SRC here if pitch==1 */
                *n_srcc = apcm->substream->runtime->channels;
                conf[0].pitch = pitch;
                conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
                conf[0].vo = 1;
-       } else if (2 == atc->msr) {
+       } else if (2 <= atc->msr) {
                if (0x8000000 < pitch) {
                        /* Need two-stage SRCs, SRCIMPs and
                         * AMIXERs for converting format */
@@ -977,6 +979,55 @@ static int atc_have_digit_io_switch(struct ct_atc *atc)
        return hw->have_digit_io_switch(hw);
 }
 
+static int atc_have_dedicated_mic(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->have_dedicated_mic(hw);
+}
+
+static int atc_have_output_switch(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->have_output_switch(hw);
+}
+
+static int atc_output_switch_get(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->output_switch_get(hw);
+}
+
+static int atc_output_switch_put(struct ct_atc *atc, int position)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->output_switch_put(hw, position);
+}
+
+static int atc_have_mic_source_switch(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->have_mic_source_switch(hw);
+}
+
+static int atc_mic_source_switch_get(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->mic_source_switch_get(hw);
+}
+
+static int atc_mic_source_switch_put(struct ct_atc *atc, int position)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->mic_source_switch_put(hw, position);
+}
+
 static int atc_select_digit_io(struct ct_atc *atc)
 {
        struct hw *hw = atc->hw;
@@ -1045,6 +1096,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
        return atc_daio_unmute(atc, state, LINEIM);
 }
 
+static int atc_mic_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, MIC);
+}
+
 static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
 {
        return atc_daio_unmute(atc, state, SPDIFOO);
@@ -1331,17 +1387,20 @@ static int atc_get_resources(struct ct_atc *atc)
        struct srcimp_mgr *srcimp_mgr;
        struct sum_desc sum_dsc = {0};
        struct sum_mgr *sum_mgr;
-       int err, i;
+       int err, i, num_srcs, num_daios;
+
+       num_daios = ((atc->model == CTSB1270) ? 8 : 7);
+       num_srcs = ((atc->model == CTSB1270) ? 6 : 4);
 
-       atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+       atc->daios = kzalloc(sizeof(void *)*num_daios, GFP_KERNEL);
        if (!atc->daios)
                return -ENOMEM;
 
-       atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+       atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
        if (!atc->srcs)
                return -ENOMEM;
 
-       atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+       atc->srcimps = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
        if (!atc->srcimps)
                return -ENOMEM;
 
@@ -1351,8 +1410,9 @@ static int atc_get_resources(struct ct_atc *atc)
 
        daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
        da_desc.msr = atc->msr;
-       for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
-               da_desc.type = i;
+       for (i = 0, atc->n_daio = 0; i < num_daios; i++) {
+               da_desc.type = (atc->model != CTSB073X) ? i :
+                            ((i == SPDIFIO) ? SPDIFI1 : i);
                err = daio_mgr->get_daio(daio_mgr, &da_desc,
                                        (struct daio **)&atc->daios[i]);
                if (err) {
@@ -1362,23 +1422,12 @@ static int atc_get_resources(struct ct_atc *atc)
                }
                atc->n_daio++;
        }
-       if (atc->model == CTSB073X)
-               da_desc.type = SPDIFI1;
-       else
-               da_desc.type = SPDIFIO;
-       err = daio_mgr->get_daio(daio_mgr, &da_desc,
-                               (struct daio **)&atc->daios[i]);
-       if (err) {
-               printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n");
-               return err;
-       }
-       atc->n_daio++;
 
        src_mgr = atc->rsc_mgrs[SRC];
        src_dsc.multi = 1;
        src_dsc.msr = atc->msr;
        src_dsc.mode = ARCRW;
-       for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+       for (i = 0, atc->n_src = 0; i < num_srcs; i++) {
                err = src_mgr->get_src(src_mgr, &src_dsc,
                                        (struct src **)&atc->srcs[i]);
                if (err)
@@ -1388,8 +1437,8 @@ static int atc_get_resources(struct ct_atc *atc)
        }
 
        srcimp_mgr = atc->rsc_mgrs[SRCIMP];
-       srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
-       for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+       srcimp_dsc.msr = 8;
+       for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) {
                err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
                                        (struct srcimp **)&atc->srcimps[i]);
                if (err)
@@ -1397,15 +1446,6 @@ static int atc_get_resources(struct ct_atc *atc)
 
                atc->n_srcimp++;
        }
-       srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
-       for (i = 0; i < (2*1); i++) {
-               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
-                               (struct srcimp **)&atc->srcimps[2*1+i]);
-               if (err)
-                       return err;
-
-               atc->n_srcimp++;
-       }
 
        sum_mgr = atc->rsc_mgrs[SUM];
        sum_dsc.msr = atc->msr;
@@ -1488,6 +1528,18 @@ static void atc_connect_resources(struct ct_atc *atc)
        src = atc->srcs[3];
        mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
 
+       if (atc->model == CTSB1270) {
+               /* Titanium HD has a dedicated ADC for the Mic. */
+               dai = container_of(atc->daios[MIC], struct dai, daio);
+               atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+                       (struct src **)&atc->srcs[4],
+                       (struct srcimp **)&atc->srcimps[4]);
+               src = atc->srcs[4];
+               mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+               src = atc->srcs[5];
+               mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+       }
+
        dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
        atc_connect_dai(atc->rsc_mgrs[SRC], dai,
                        (struct src **)&atc->srcs[0],
@@ -1606,12 +1658,20 @@ static struct ct_atc atc_preset __devinitdata = {
        .line_clfe_unmute = atc_line_clfe_unmute,
        .line_rear_unmute = atc_line_rear_unmute,
        .line_in_unmute = atc_line_in_unmute,
+       .mic_unmute = atc_mic_unmute,
        .spdif_out_unmute = atc_spdif_out_unmute,
        .spdif_in_unmute = atc_spdif_in_unmute,
        .spdif_out_get_status = atc_spdif_out_get_status,
        .spdif_out_set_status = atc_spdif_out_set_status,
        .spdif_out_passthru = atc_spdif_out_passthru,
        .have_digit_io_switch = atc_have_digit_io_switch,
+       .have_dedicated_mic = atc_have_dedicated_mic,
+       .have_output_switch = atc_have_output_switch,
+       .output_switch_get = atc_output_switch_get,
+       .output_switch_put = atc_output_switch_put,
+       .have_mic_source_switch = atc_have_mic_source_switch,
+       .mic_source_switch_get = atc_mic_source_switch_get,
+       .mic_source_switch_put = atc_mic_source_switch_put,
 #ifdef CONFIG_PM
        .suspend = atc_suspend,
        .resume = atc_resume,
index 7167c0185d5281cb2c2a77bc978ce4c3dbdbdb5f..6bad27e06f4d42ec4745aea0fc322664699f1301 100644 (file)
@@ -115,12 +115,20 @@ struct ct_atc {
        int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
        int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
        int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*mic_unmute)(struct ct_atc *atc, unsigned char state);
        int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
        int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
        int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
        int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
        int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
        int (*have_digit_io_switch)(struct ct_atc *atc);
+       int (*have_dedicated_mic)(struct ct_atc *atc);
+       int (*have_output_switch)(struct ct_atc *atc);
+       int (*output_switch_get)(struct ct_atc *atc);
+       int (*output_switch_put)(struct ct_atc *atc, int position);
+       int (*have_mic_source_switch)(struct ct_atc *atc);
+       int (*mic_source_switch_get)(struct ct_atc *atc);
+       int (*mic_source_switch_put)(struct ct_atc *atc, int position);
 
        /* Don't touch! Used for internal object. */
        void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
index 47d9ea97de02e28cc0d7f9064a39a8c9222e29d0..0c00eb4088efca6639bf35c554aefa73f9e926e5 100644 (file)
 #include <linux/slab.h>
 #include <linux/kernel.h>
 
-#define DAIO_RESOURCE_NUM      NUM_DAIOTYP
 #define DAIO_OUT_MAX           SPDIFOO
 
-union daio_usage {
-       struct {
-               unsigned short lineo1:1;
-               unsigned short lineo2:1;
-               unsigned short lineo3:1;
-               unsigned short lineo4:1;
-               unsigned short spdifoo:1;
-               unsigned short lineim:1;
-               unsigned short spdifio:1;
-               unsigned short spdifi1:1;
-       } bf;
+struct daio_usage {
        unsigned short data;
 };
 
@@ -61,6 +50,7 @@ struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
        [LINEO3] = {.left = 0x50, .right = 0x51},
        [LINEO4] = {.left = 0x70, .right = 0x71},
        [LINEIM] = {.left = 0x45, .right = 0xc5},
+       [MIC]    = {.left = 0x55, .right = 0xd5},
        [SPDIFOO] = {.left = 0x00, .right = 0x01},
        [SPDIFIO] = {.left = 0x05, .right = 0x85},
 };
@@ -138,6 +128,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
                case LINEO3:    return 5;
                case LINEO4:    return 6;
                case LINEIM:    return 4;
+               case MIC:       return 5;
                default:        return -EINVAL;
                }
        default:
@@ -519,17 +510,17 @@ static int dai_rsc_uninit(struct dai *dai)
 
 static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
 {
-       if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+       if (((struct daio_usage *)mgr->rscs)->data & (0x1 << type))
                return -ENOENT;
 
-       ((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+       ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type);
 
        return 0;
 }
 
 static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
 {
-       ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+       ((struct daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
 
        return 0;
 }
@@ -712,7 +703,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
        if (!daio_mgr)
                return -ENOMEM;
 
-       err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+       err = rsc_mgr_init(&daio_mgr->mgr, DAIO, NUM_DAIOTYP, hw);
        if (err)
                goto error1;
 
index 0f52ce571ee80eb8efa42512db7a38a180cb2fdb..85ccb6ee1ab4e2c3f7df0ca5378a7e2bc54c71de 100644 (file)
@@ -33,6 +33,7 @@ enum DAIOTYP {
        SPDIFOO,        /* S/PDIF Out (Flexijack/Optical) */
        LINEIM,
        SPDIFIO,        /* S/PDIF In (Flexijack/Optical) on the card */
+       MIC,            /* Dedicated mic on Titanium HD */
        SPDIFI1,        /* S/PDIF In on internal Drive Bay */
        NUM_DAIOTYP
 };
index af55405f5dec81b0e5d4f7b7829c9ea981b5ee77..de59bbf2702f53c88b7a226d8ba15634cbd93ae5 100644 (file)
@@ -39,6 +39,7 @@ enum CTCARDS {
        CT20K2_MODEL_FIRST = CTSB0760,
        CTHENDRIX,
        CTSB0880,
+       CTSB1270,
        CT20K2_UNKNOWN,
        NUM_CTCARDS             /* This should always be the last */
 };
@@ -71,6 +72,13 @@ struct hw {
        int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
        int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
        int (*have_digit_io_switch)(struct hw *hw);
+       int (*have_dedicated_mic)(struct hw *hw);
+       int (*have_output_switch)(struct hw *hw);
+       int (*output_switch_get)(struct hw *hw);
+       int (*output_switch_put)(struct hw *hw, int position);
+       int (*have_mic_source_switch)(struct hw *hw);
+       int (*mic_source_switch_get)(struct hw *hw);
+       int (*mic_source_switch_put)(struct hw *hw, int position);
 
        /* SRC operations */
        int (*src_rsc_get_ctrl_blk)(void **rblk);
index a5c957db5cea2fc297c283f3ee339dc960204bf9..9a85a84b23ab8ed190025b0266f10eaf3a094f97 100644 (file)
@@ -1783,6 +1783,21 @@ static int hw_have_digit_io_switch(struct hw *hw)
        return !(hw->model == CTSB073X || hw->model == CTUAA);
 }
 
+static int hw_have_dedicated_mic(struct hw *hw)
+{
+       return 0;
+}
+
+static int hw_have_output_switch(struct hw *hw)
+{
+       return 0;
+}
+
+static int hw_have_mic_source_switch(struct hw *hw)
+{
+       return 0;
+}
+
 #define CTLBITS(a, b, c, d)    (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
 
 #define UAA_CFG_PWRSTATUS      0x44
@@ -2173,6 +2188,9 @@ static struct hw ct20k1_preset __devinitdata = {
        .is_adc_source_selected = hw_is_adc_input_selected,
        .select_adc_source = hw_adc_input_select,
        .have_digit_io_switch = hw_have_digit_io_switch,
+       .have_dedicated_mic = hw_have_dedicated_mic,
+       .have_output_switch = hw_have_output_switch,
+       .have_mic_source_switch = hw_have_mic_source_switch,
 #ifdef CONFIG_PM
        .suspend = hw_suspend,
        .resume = hw_resume,
index 5364164674e4cce9e39f56542182e150c582659d..8bc6e41ce64bfd20ed5d929e0d36d6732958aa7d 100644 (file)
@@ -8,7 +8,7 @@
  * @File       cthw20k2.c
  *
  * @Brief
- * This file contains the implementation of hardware access methord for 20k2.
+ * This file contains the implementation of hardware access method for 20k2.
  *
  * @Author     Liu Chun
  * @Date       May 14 2008
@@ -38,6 +38,8 @@ struct hw20k2 {
        unsigned char dev_id;
        unsigned char addr_size;
        unsigned char data_size;
+
+       int mic_source;
 };
 
 static u32 hw_read_20kx(struct hw *hw, u32 reg);
@@ -1163,7 +1165,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
                hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
                hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
        } else if (2 == info->msr) {
-               hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+               if (hw->model != CTSB1270) {
+                       hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+               } else {
+                       /* PCM4220 on Titanium HD is different. */
+                       hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11011111);
+               }
                /* Specify all playing 96khz
                 * EA [0]       - Enabled
                 * RTA [4:5]    - 96kHz
@@ -1175,6 +1182,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
                 * RTD [28:29]  - 96kHz */
                hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
                hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+       } else if ((4 == info->msr) && (hw->model == CTSB1270)) {
+               hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111);
+               hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
+               hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
        } else {
                printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
                return -EINVAL;
@@ -1182,6 +1193,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
 
        for (i = 0; i < 8; i++) {
                if (i <= 3) {
+                       /* This comment looks wrong since loop is over 4  */
+                       /* channels and emu20k2 supports 4 spdif IOs.     */
                        /* 1st 3 channels are SPDIFs (SB0960) */
                        if (i == 3)
                                data = 0x1001001;
@@ -1206,12 +1219,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
 
                        hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
                } else {
+                       /* Again, loop is over 4 channels not 5. */
                        /* Next 5 channels are I2S (SB0960) */
                        data = 0x11;
                        hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
                        if (2 == info->msr) {
                                /* Four channels per sample period */
                                data |= 0x1000;
+                       } else if (4 == info->msr) {
+                               /* FIXME: check this against the chip spec */
+                               data |= 0x2000;
                        }
                        hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data);
                }
@@ -1557,7 +1574,7 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
 
        hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
        hw20k2_i2c_wait_data_ready(hw);
-       /* Dummy write to trigger the write oprtation */
+       /* Dummy write to trigger the write operation */
        hw_write_20kx(hw, I2C_IF_WDATA, 0);
        hw20k2_i2c_wait_data_ready(hw);
 
@@ -1568,6 +1585,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
        return 0;
 }
 
+static void hw_dac_stop(struct hw *hw)
+{
+       u32 data;
+       data = hw_read_20kx(hw, GPIO_DATA);
+       data &= 0xFFFFFFFD;
+       hw_write_20kx(hw, GPIO_DATA, data);
+       mdelay(10);
+}
+
+static void hw_dac_start(struct hw *hw)
+{
+       u32 data;
+       data = hw_read_20kx(hw, GPIO_DATA);
+       data |= 0x2;
+       hw_write_20kx(hw, GPIO_DATA, data);
+       mdelay(50);
+}
+
+static void hw_dac_reset(struct hw *hw)
+{
+       hw_dac_stop(hw);
+       hw_dac_start(hw);
+}
+
 static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
 {
        int err;
@@ -1594,6 +1635,21 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
                                   0x00000000   /* Vol Control B4 */
                                 };
 
+       if (hw->model == CTSB1270) {
+               hw_dac_stop(hw);
+               data = hw_read_20kx(hw, GPIO_DATA);
+               data &= ~0x0600;
+               if (1 == info->msr)
+                       data |= 0x0000; /* Single Speed Mode 0-50kHz */
+               else if (2 == info->msr)
+                       data |= 0x0200; /* Double Speed Mode 50-100kHz */
+               else
+                       data |= 0x0600; /* Quad Speed Mode 100-200kHz */
+               hw_write_20kx(hw, GPIO_DATA, data);
+               hw_dac_start(hw);
+               return 0;
+       }
+
        /* Set DAC reset bit as output */
        data = hw_read_20kx(hw, GPIO_CTRL);
        data |= 0x02;
@@ -1606,22 +1662,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
        for (i = 0; i < 2; i++) {
                /* Reset DAC twice just in-case the chip
                 * didn't initialized properly */
-               data = hw_read_20kx(hw, GPIO_DATA);
-               /* GPIO data bit 1 */
-               data &= 0xFFFFFFFD;
-               hw_write_20kx(hw, GPIO_DATA, data);
-               mdelay(10);
-               data |= 0x2;
-               hw_write_20kx(hw, GPIO_DATA, data);
-               mdelay(50);
-
-               /* Reset the 2nd time */
-               data &= 0xFFFFFFFD;
-               hw_write_20kx(hw, GPIO_DATA, data);
-               mdelay(10);
-               data |= 0x2;
-               hw_write_20kx(hw, GPIO_DATA, data);
-               mdelay(50);
+               hw_dac_reset(hw);
+               hw_dac_reset(hw);
 
                if (hw20k2_i2c_read(hw, CS4382_MC1,  &cs_read.mode_control_1))
                        continue;
@@ -1725,7 +1767,11 @@ End:
 static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
 {
        u32 data;
-
+       if (hw->model == CTSB1270) {
+               /* Titanium HD has two ADC chips, one for line in and one */
+               /* for MIC. We don't need to switch the ADC input. */
+               return 1;
+       }
        data = hw_read_20kx(hw, GPIO_DATA);
        switch (type) {
        case ADC_MICIN:
@@ -1742,35 +1788,47 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
 
 #define MIC_BOOST_0DB 0xCF
 #define MIC_BOOST_STEPS_PER_DB 2
-#define MIC_BOOST_20DB (MIC_BOOST_0DB + 20 * MIC_BOOST_STEPS_PER_DB)
+
+static void hw_wm8775_input_select(struct hw *hw, u8 input, s8 gain_in_db)
+{
+       u32 adcmc, gain;
+
+       if (input > 3)
+               input = 3;
+
+       adcmc = ((u32)1 << input) | 0x100; /* Link L+R gain... */
+
+       hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, adcmc),
+                               MAKE_WM8775_DATA(adcmc));
+
+       if (gain_in_db < -103)
+               gain_in_db = -103;
+       if (gain_in_db > 24)
+               gain_in_db = 24;
+
+       gain = gain_in_db * MIC_BOOST_STEPS_PER_DB + MIC_BOOST_0DB;
+
+       hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, gain),
+                               MAKE_WM8775_DATA(gain));
+       /* ...so there should be no need for the following. */
+       hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, gain),
+                               MAKE_WM8775_DATA(gain));
+}
 
 static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
 {
        u32 data;
-
        data = hw_read_20kx(hw, GPIO_DATA);
        switch (type) {
        case ADC_MICIN:
                data |= (0x1 << 14);
                hw_write_20kx(hw, GPIO_DATA, data);
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
-                               MAKE_WM8775_DATA(0x101)); /* Mic-in */
-               hw20k2_i2c_write(hw,
-                               MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB),
-                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
-               hw20k2_i2c_write(hw,
-                               MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB),
-                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
+               hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */
                break;
        case ADC_LINEIN:
                data &= ~(0x1 << 14);
                hw_write_20kx(hw, GPIO_DATA, data);
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
-                               MAKE_WM8775_DATA(0x102)); /* Line-in */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
-                               MAKE_WM8775_DATA(0xCF)); /* No boost */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
-                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */
                break;
        default:
                break;
@@ -1782,7 +1840,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
 static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
 {
        int err;
-       u32 mux = 2, data, ctl;
+       u32 data, ctl;
 
        /*  Set ADC reset bit as output */
        data = hw_read_20kx(hw, GPIO_CTRL);
@@ -1796,19 +1854,42 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
                goto error;
        }
 
-       /* Make ADC in normal operation */
+       /* Reset the ADC (reset is active low). */
        data = hw_read_20kx(hw, GPIO_DATA);
        data &= ~(0x1 << 15);
+       hw_write_20kx(hw, GPIO_DATA, data);
+
+       if (hw->model == CTSB1270) {
+               /* Set up the PCM4220 ADC on Titanium HD */
+               data &= ~0x0C;
+               if (1 == info->msr)
+                       data |= 0x00; /* Single Speed Mode 32-50kHz */
+               else if (2 == info->msr)
+                       data |= 0x08; /* Double Speed Mode 50-108kHz */
+               else
+                       data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */
+               hw_write_20kx(hw, GPIO_DATA, data);
+       }
+
        mdelay(10);
+       /* Return the ADC to normal operation. */
        data |= (0x1 << 15);
        hw_write_20kx(hw, GPIO_DATA, data);
        mdelay(50);
 
+       /* I2C write to register offset 0x0B to set ADC LRCLK polarity */
+       /* invert bit, interface format to I2S, word length to 24-bit, */
+       /* enable ADC high pass filter. Fixes bug 5323?         */
+       hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_IC, 0x26),
+                        MAKE_WM8775_DATA(0x26));
+
        /* Set the master mode (256fs) */
        if (1 == info->msr) {
+               /* slave mode, 128x oversampling 256fs */
                hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
                                                MAKE_WM8775_DATA(0x02));
-       } else if (2 == info->msr) {
+       } else if ((2 == info->msr) || (4 == info->msr)) {
+               /* slave mode, 64x oversampling, 256fs */
                hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
                                                MAKE_WM8775_DATA(0x0A));
        } else {
@@ -1818,47 +1899,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
                goto error;
        }
 
-       /* Configure GPIO bit 14 change to line-in/mic-in */
-       ctl = hw_read_20kx(hw, GPIO_CTRL);
-       ctl |= 0x1 << 14;
-       hw_write_20kx(hw, GPIO_CTRL, ctl);
-
-       /* Check using Mic-in or Line-in */
-       data = hw_read_20kx(hw, GPIO_DATA);
-
-       if (mux == 1) {
-               /* Configures GPIO data to select Mic-in */
-               data |= 0x1 << 14;
-               hw_write_20kx(hw, GPIO_DATA, data);
-
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
-                               MAKE_WM8775_DATA(0x101)); /* Mic-in */
-               hw20k2_i2c_write(hw,
-                               MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB),
-                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
-               hw20k2_i2c_write(hw,
-                               MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB),
-                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
-       } else if (mux == 2) {
-               /* Configures GPIO data to select Line-in */
-               data &= ~(0x1 << 14);
-               hw_write_20kx(hw, GPIO_DATA, data);
-
-               /* Setup ADC */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
-                               MAKE_WM8775_DATA(0x102)); /* Line-in */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
-                               MAKE_WM8775_DATA(0xCF)); /* No boost */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
-                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+       if (hw->model != CTSB1270) {
+               /* Configure GPIO bit 14 change to line-in/mic-in */
+               ctl = hw_read_20kx(hw, GPIO_CTRL);
+               ctl |= 0x1 << 14;
+               hw_write_20kx(hw, GPIO_CTRL, ctl);
+               hw_adc_input_select(hw, ADC_LINEIN);
        } else {
-               printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n");
-               err = -EINVAL;
-               goto error;
+               hw_wm8775_input_select(hw, 0, 0);
        }
 
        return 0;
-
 error:
        hw20k2_i2c_uninit(hw);
        return err;
@@ -1869,6 +1920,102 @@ static int hw_have_digit_io_switch(struct hw *hw)
        return 0;
 }
 
+static int hw_have_dedicated_mic(struct hw *hw)
+{
+       return hw->model == CTSB1270;
+}
+
+static int hw_have_output_switch(struct hw *hw)
+{
+       return hw->model == CTSB1270;
+}
+
+static int hw_output_switch_get(struct hw *hw)
+{
+       u32 data = hw_read_20kx(hw, GPIO_EXT_DATA);
+
+       switch (data & 0x30) {
+       case 0x00:
+            return 0;
+       case 0x10:
+            return 1;
+       case 0x20:
+            return 2;
+       default:
+            return 3;
+       }
+}
+
+static int hw_output_switch_put(struct hw *hw, int position)
+{
+       u32 data;
+
+       if (position == hw_output_switch_get(hw))
+               return 0;
+
+       /* Mute line and headphones (intended for anti-pop). */
+       data = hw_read_20kx(hw, GPIO_DATA);
+       data |= (0x03 << 11);
+       hw_write_20kx(hw, GPIO_DATA, data);
+
+       data = hw_read_20kx(hw, GPIO_EXT_DATA) & ~0x30;
+       switch (position) {
+       case 0:
+               break;
+       case 1:
+               data |= 0x10;
+               break;
+       default:
+               data |= 0x20;
+       }
+       hw_write_20kx(hw, GPIO_EXT_DATA, data);
+
+       /* Unmute line and headphones. */
+       data = hw_read_20kx(hw, GPIO_DATA);
+       data &= ~(0x03 << 11);
+       hw_write_20kx(hw, GPIO_DATA, data);
+
+       return 1;
+}
+
+static int hw_have_mic_source_switch(struct hw *hw)
+{
+       return hw->model == CTSB1270;
+}
+
+static int hw_mic_source_switch_get(struct hw *hw)
+{
+       struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+       return hw20k2->mic_source;
+}
+
+static int hw_mic_source_switch_put(struct hw *hw, int position)
+{
+       struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+       if (position == hw20k2->mic_source)
+               return 0;
+
+       switch (position) {
+       case 0:
+               hw_wm8775_input_select(hw, 0, 0); /* Mic, 0dB */
+               break;
+       case 1:
+               hw_wm8775_input_select(hw, 1, 0); /* FP Mic, 0dB */
+               break;
+       case 2:
+               hw_wm8775_input_select(hw, 3, 0); /* Aux Ext, 0dB */
+               break;
+       default:
+               return 0;
+       }
+
+       hw20k2->mic_source = position;
+
+       return 1;
+}
+
 static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
 {
        struct hw *hw = dev_id;
@@ -2023,13 +2170,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
        /* Reset all SRC pending interrupts */
        hw_write_20kx(hw, SRC_IP, 0);
 
-       /* TODO: detect the card ID and configure GPIO accordingly. */
-       /* Configures GPIO (0xD802 0x98028) */
-       /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
-       /* Configures GPIO (SB0880) */
-       /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
-       hw_write_20kx(hw, GPIO_CTRL, 0xD802);
-
+       if (hw->model != CTSB1270) {
+               /* TODO: detect the card ID and configure GPIO accordingly. */
+               /* Configures GPIO (0xD802 0x98028) */
+               /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+               /* Configures GPIO (SB0880) */
+               /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+               hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+       } else {
+               hw_write_20kx(hw, GPIO_CTRL, 0x9E5F);
+       }
        /* Enable audio ring */
        hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
 
@@ -2107,6 +2257,13 @@ static struct hw ct20k2_preset __devinitdata = {
        .is_adc_source_selected = hw_is_adc_input_selected,
        .select_adc_source = hw_adc_input_select,
        .have_digit_io_switch = hw_have_digit_io_switch,
+       .have_dedicated_mic = hw_have_dedicated_mic,
+       .have_output_switch = hw_have_output_switch,
+       .output_switch_get = hw_output_switch_get,
+       .output_switch_put = hw_output_switch_put,
+       .have_mic_source_switch = hw_have_mic_source_switch,
+       .mic_source_switch_get = hw_mic_source_switch_get,
+       .mic_source_switch_put = hw_mic_source_switch_put,
 #ifdef CONFIG_PM
        .suspend = hw_suspend,
        .resume = hw_resume,
index c3519ff42fbbfd0b4684722b1389a81aa25e91c1..388235c43789ab006ee4d0c651902b86fb8d1a43 100644 (file)
@@ -86,9 +86,7 @@ enum CTALSA_MIXER_CTL {
        MIXER_LINEIN_C_S,
        MIXER_MIC_C_S,
        MIXER_SPDIFI_C_S,
-       MIXER_LINEIN_P_S,
        MIXER_SPDIFO_P_S,
-       MIXER_SPDIFI_P_S,
        MIXER_WAVEF_P_S,
        MIXER_WAVER_P_S,
        MIXER_WAVEC_P_S,
@@ -137,11 +135,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
        },
        [MIXER_LINEIN_P] = {
                .ctl = 1,
-               .name = "Line-in Playback Volume",
+               .name = "Line Playback Volume",
        },
        [MIXER_LINEIN_C] = {
                .ctl = 1,
-               .name = "Line-in Capture Volume",
+               .name = "Line Capture Volume",
        },
        [MIXER_MIC_P] = {
                .ctl = 1,
@@ -153,15 +151,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
        },
        [MIXER_SPDIFI_P] = {
                .ctl = 1,
-               .name = "S/PDIF-in Playback Volume",
+               .name = "IEC958 Playback Volume",
        },
        [MIXER_SPDIFI_C] = {
                .ctl = 1,
-               .name = "S/PDIF-in Capture Volume",
+               .name = "IEC958 Capture Volume",
        },
        [MIXER_SPDIFO_P] = {
                .ctl = 1,
-               .name = "S/PDIF-out Playback Volume",
+               .name = "Digital Playback Volume",
        },
        [MIXER_WAVEF_P] = {
                .ctl = 1,
@@ -179,14 +177,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
                .ctl = 1,
                .name = "Surround Playback Volume",
        },
-
        [MIXER_PCM_C_S] = {
                .ctl = 1,
                .name = "PCM Capture Switch",
        },
        [MIXER_LINEIN_C_S] = {
                .ctl = 1,
-               .name = "Line-in Capture Switch",
+               .name = "Line Capture Switch",
        },
        [MIXER_MIC_C_S] = {
                .ctl = 1,
@@ -194,19 +191,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
        },
        [MIXER_SPDIFI_C_S] = {
                .ctl = 1,
-               .name = "S/PDIF-in Capture Switch",
-       },
-       [MIXER_LINEIN_P_S] = {
-               .ctl = 1,
-               .name = "Line-in Playback Switch",
+               .name = "IEC958 Capture Switch",
        },
        [MIXER_SPDIFO_P_S] = {
                .ctl = 1,
-               .name = "S/PDIF-out Playback Switch",
-       },
-       [MIXER_SPDIFI_P_S] = {
-               .ctl = 1,
-               .name = "S/PDIF-in Playback Switch",
+               .name = "Digital Playback Switch",
        },
        [MIXER_WAVEF_P_S] = {
                .ctl = 1,
@@ -236,6 +225,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
 static void
 ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
 
+/* FIXME: this static looks like it would fail if more than one card was */
+/* installed. */
 static struct snd_kcontrol *kctls[2] = {NULL};
 
 static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
@@ -420,6 +411,77 @@ static struct snd_kcontrol_new vol_ctl = {
        .tlv            = { .p =  ct_vol_db_scale },
 };
 
+static int output_switch_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *info)
+{
+       static const char *const names[3] = {
+         "FP Headphones", "Headphones", "Speakers"
+       };
+
+       return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int output_switch_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc);
+       return 0;
+}
+
+static int output_switch_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       if (ucontrol->value.enumerated.item[0] > 2)
+               return -EINVAL;
+       return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new output_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Analog Output Playback Enum",
+       .info = output_switch_info,
+       .get = output_switch_get,
+       .put = output_switch_put,
+};
+
+static int mic_source_switch_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *info)
+{
+       static const char *const names[3] = {
+         "Mic", "FP Mic", "Aux"
+       };
+
+       return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int mic_source_switch_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc);
+       return 0;
+}
+
+static int mic_source_switch_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       if (ucontrol->value.enumerated.item[0] > 2)
+               return -EINVAL;
+       return atc->mic_source_switch_put(atc,
+                                       ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new mic_source_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Mic Source Capture Enum",
+       .info = mic_source_switch_info,
+       .get = mic_source_switch_get,
+       .put = mic_source_switch_put,
+};
+
 static void
 do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
 {
@@ -465,6 +527,7 @@ do_digit_io_switch(struct ct_atc *atc, int state)
 static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
 {
        struct ct_mixer *mixer = atc->mixer;
+       int have_dedicated_mic = atc->have_dedicated_mic(atc);
 
        /* Do changes in mixer. */
        if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
@@ -477,8 +540,17 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
                }
        }
        /* Do changes out of mixer. */
-       if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
-               do_line_mic_switch(atc, type);
+       if (!have_dedicated_mic &&
+           (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) {
+               if (state)
+                       do_line_mic_switch(atc, type);
+               atc->line_in_unmute(atc, state);
+       } else if (have_dedicated_mic && (MIXER_LINEIN_C_S == type))
+               atc->line_in_unmute(atc, state);
+       else if (have_dedicated_mic && (MIXER_MIC_C_S == type))
+               atc->mic_unmute(atc, state);
+       else if (MIXER_SPDIFI_C_S == type)
+               atc->spdif_in_unmute(atc, state);
        else if (MIXER_WAVEF_P_S == type)
                atc->line_front_unmute(atc, state);
        else if (MIXER_WAVES_P_S == type)
@@ -487,12 +559,8 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
                atc->line_clfe_unmute(atc, state);
        else if (MIXER_WAVER_P_S == type)
                atc->line_rear_unmute(atc, state);
-       else if (MIXER_LINEIN_P_S == type)
-               atc->line_in_unmute(atc, state);
        else if (MIXER_SPDIFO_P_S == type)
                atc->spdif_out_unmute(atc, state);
-       else if (MIXER_SPDIFI_P_S == type)
-               atc->spdif_in_unmute(atc, state);
        else if (MIXER_DIGITAL_IO_S == type)
                do_digit_io_switch(atc, state);
 
@@ -686,6 +754,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
 
        ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
                                        atc->have_digit_io_switch(atc);
+
        for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
                if (ct_kcontrol_init_table[type].ctl) {
                        swh_ctl.name = ct_kcontrol_init_table[type].name;
@@ -708,6 +777,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
        if (err)
                return err;
 
+       if (atc->have_output_switch(atc)) {
+               err = ct_mixer_kcontrol_new(mixer, &output_ctl);
+               if (err)
+                       return err;
+       }
+
+       if (atc->have_mic_source_switch(atc)) {
+               err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl);
+               if (err)
+                       return err;
+       }
        atc->line_front_unmute(atc, 1);
        set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
        atc->line_surround_unmute(atc, 0);
@@ -719,13 +799,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
        atc->spdif_out_unmute(atc, 0);
        set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
        atc->line_in_unmute(atc, 0);
-       set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+       if (atc->have_dedicated_mic(atc))
+               atc->mic_unmute(atc, 0);
        atc->spdif_in_unmute(atc, 0);
-       set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
-
-       set_switch_state(mixer, MIXER_PCM_C_S, 1);
-       set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
-       set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+       set_switch_state(mixer, MIXER_PCM_C_S, 0);
+       set_switch_state(mixer, MIXER_LINEIN_C_S, 0);
+       set_switch_state(mixer, MIXER_SPDIFI_C_S, 0);
 
        return 0;
 }
index f42e7e1a107457bf99091d5813b438a88fe31f2f..7afa765afa3c4585f13778cc1cb018f385264fa6 100644 (file)
@@ -80,11 +80,11 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
                       "are 48000 and 44100, Value 48000 is assumed.\n");
                reference_rate = 48000;
        }
-       if ((multiple != 1) && (multiple != 2)) {
+       if ((multiple != 1) && (multiple != 2) && (multiple != 4)) {
                printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
                       multiple);
                printk(KERN_ERR "ctxfi: The valid values for multiple are "
-                      "1 and 2, Value 2 is assumed.\n");
+                      "1, 2 and 4, Value 2 is assumed.\n");
                multiple = 2;
        }
        err = ct_atc_create(card, pci, reference_rate, multiple,