提交 55309216 编写于 作者: H Harry Butterworth 提交者: Takashi Iwai

ALSA: ctxfi: Add support for Creative Titanium HD

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: NHarry Butterworth <heb1001@gmail.com>
Signed-off-by: NTakashi Iwai <tiwai@suse.de>
上级 37f7ec38
...@@ -1308,6 +1308,7 @@ ...@@ -1308,6 +1308,7 @@
#define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041 #define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041
#define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042 #define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042
#define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043 #define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043
#define PCI_SUBDEVICE_ID_CREATIVE_SB1270 0x0062
#define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000 #define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000
#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ #define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
/* GPIO Registers */ /* GPIO Registers */
#define GPIO_DATA 0x1B7020 #define GPIO_DATA 0x1B7020
#define GPIO_CTRL 0x1B7024 #define GPIO_CTRL 0x1B7024
#define GPIO_EXT_DATA 0x1B70A0
/* Virtual memory registers */ /* Virtual memory registers */
#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */ #define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */
......
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
#include <sound/asoundef.h> #include <sound/asoundef.h>
#define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ #define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */
#define DAIONUM 7
#define MAX_MULTI_CHN 8 #define MAX_MULTI_CHN 8
#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \ #define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
...@@ -53,6 +52,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { ...@@ -53,6 +52,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
"SB0760", CTSB0760), "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, SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
"SB0880", CTSB0880), "SB0880", CTSB0880),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
...@@ -75,6 +76,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = { ...@@ -75,6 +76,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
[CTSB0760] = "SB076x", [CTSB0760] = "SB076x",
[CTHENDRIX] = "Hendrix", [CTHENDRIX] = "Hendrix",
[CTSB0880] = "SB0880", [CTSB0880] = "SB0880",
[CTSB1270] = "SB1270",
[CT20K2_UNKNOWN] = "Unknown", [CT20K2_UNKNOWN] = "Unknown",
}; };
...@@ -459,12 +461,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, ...@@ -459,12 +461,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
apcm->substream->runtime->rate); apcm->substream->runtime->rate);
*n_srcc = 0; *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; *n_srcc = apcm->substream->runtime->channels;
conf[0].pitch = pitch; conf[0].pitch = pitch;
conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
conf[0].vo = 1; conf[0].vo = 1;
} else if (2 == atc->msr) { } else if (2 <= atc->msr) {
if (0x8000000 < pitch) { if (0x8000000 < pitch) {
/* Need two-stage SRCs, SRCIMPs and /* Need two-stage SRCs, SRCIMPs and
* AMIXERs for converting format */ * AMIXERs for converting format */
...@@ -977,6 +979,55 @@ static int atc_have_digit_io_switch(struct ct_atc *atc) ...@@ -977,6 +979,55 @@ static int atc_have_digit_io_switch(struct ct_atc *atc)
return hw->have_digit_io_switch(hw); 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) static int atc_select_digit_io(struct ct_atc *atc)
{ {
struct hw *hw = atc->hw; struct hw *hw = atc->hw;
...@@ -1045,6 +1096,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) ...@@ -1045,6 +1096,11 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
return atc_daio_unmute(atc, state, LINEIM); 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) static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
{ {
return atc_daio_unmute(atc, state, SPDIFOO); return atc_daio_unmute(atc, state, SPDIFOO);
...@@ -1331,17 +1387,20 @@ static int atc_get_resources(struct ct_atc *atc) ...@@ -1331,17 +1387,20 @@ static int atc_get_resources(struct ct_atc *atc)
struct srcimp_mgr *srcimp_mgr; struct srcimp_mgr *srcimp_mgr;
struct sum_desc sum_dsc = {0}; struct sum_desc sum_dsc = {0};
struct sum_mgr *sum_mgr; 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) if (!atc->daios)
return -ENOMEM; return -ENOMEM;
atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
if (!atc->srcs) if (!atc->srcs)
return -ENOMEM; return -ENOMEM;
atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); atc->srcimps = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL);
if (!atc->srcimps) if (!atc->srcimps)
return -ENOMEM; return -ENOMEM;
...@@ -1351,8 +1410,9 @@ static int atc_get_resources(struct ct_atc *atc) ...@@ -1351,8 +1410,9 @@ static int atc_get_resources(struct ct_atc *atc)
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
da_desc.msr = atc->msr; da_desc.msr = atc->msr;
for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { for (i = 0, atc->n_daio = 0; i < num_daios; i++) {
da_desc.type = i; da_desc.type = (atc->model != CTSB073X) ? i :
((i == SPDIFIO) ? SPDIFI1 : i);
err = daio_mgr->get_daio(daio_mgr, &da_desc, err = daio_mgr->get_daio(daio_mgr, &da_desc,
(struct daio **)&atc->daios[i]); (struct daio **)&atc->daios[i]);
if (err) { if (err) {
...@@ -1362,23 +1422,12 @@ static int atc_get_resources(struct ct_atc *atc) ...@@ -1362,23 +1422,12 @@ static int atc_get_resources(struct ct_atc *atc)
} }
atc->n_daio++; 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_mgr = atc->rsc_mgrs[SRC];
src_dsc.multi = 1; src_dsc.multi = 1;
src_dsc.msr = atc->msr; src_dsc.msr = atc->msr;
src_dsc.mode = ARCRW; 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, err = src_mgr->get_src(src_mgr, &src_dsc,
(struct src **)&atc->srcs[i]); (struct src **)&atc->srcs[i]);
if (err) if (err)
...@@ -1388,8 +1437,8 @@ static int atc_get_resources(struct ct_atc *atc) ...@@ -1388,8 +1437,8 @@ static int atc_get_resources(struct ct_atc *atc)
} }
srcimp_mgr = atc->rsc_mgrs[SRCIMP]; srcimp_mgr = atc->rsc_mgrs[SRCIMP];
srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ srcimp_dsc.msr = 8;
for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { for (i = 0, atc->n_srcimp = 0; i < num_srcs; i++) {
err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
(struct srcimp **)&atc->srcimps[i]); (struct srcimp **)&atc->srcimps[i]);
if (err) if (err)
...@@ -1397,15 +1446,6 @@ static int atc_get_resources(struct ct_atc *atc) ...@@ -1397,15 +1446,6 @@ static int atc_get_resources(struct ct_atc *atc)
atc->n_srcimp++; 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_mgr = atc->rsc_mgrs[SUM];
sum_dsc.msr = atc->msr; sum_dsc.msr = atc->msr;
...@@ -1488,6 +1528,18 @@ static void atc_connect_resources(struct ct_atc *atc) ...@@ -1488,6 +1528,18 @@ static void atc_connect_resources(struct ct_atc *atc)
src = atc->srcs[3]; src = atc->srcs[3];
mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); 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); dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai, atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[0], (struct src **)&atc->srcs[0],
...@@ -1606,12 +1658,20 @@ static struct ct_atc atc_preset __devinitdata = { ...@@ -1606,12 +1658,20 @@ static struct ct_atc atc_preset __devinitdata = {
.line_clfe_unmute = atc_line_clfe_unmute, .line_clfe_unmute = atc_line_clfe_unmute,
.line_rear_unmute = atc_line_rear_unmute, .line_rear_unmute = atc_line_rear_unmute,
.line_in_unmute = atc_line_in_unmute, .line_in_unmute = atc_line_in_unmute,
.mic_unmute = atc_mic_unmute,
.spdif_out_unmute = atc_spdif_out_unmute, .spdif_out_unmute = atc_spdif_out_unmute,
.spdif_in_unmute = atc_spdif_in_unmute, .spdif_in_unmute = atc_spdif_in_unmute,
.spdif_out_get_status = atc_spdif_out_get_status, .spdif_out_get_status = atc_spdif_out_get_status,
.spdif_out_set_status = atc_spdif_out_set_status, .spdif_out_set_status = atc_spdif_out_set_status,
.spdif_out_passthru = atc_spdif_out_passthru, .spdif_out_passthru = atc_spdif_out_passthru,
.have_digit_io_switch = atc_have_digit_io_switch, .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 #ifdef CONFIG_PM
.suspend = atc_suspend, .suspend = atc_suspend,
.resume = atc_resume, .resume = atc_resume,
......
...@@ -115,12 +115,20 @@ struct ct_atc { ...@@ -115,12 +115,20 @@ struct ct_atc {
int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state); int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_rear_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 (*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_out_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_in_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_get_status)(struct ct_atc *atc, unsigned int *status);
int (*spdif_out_set_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 (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
int (*have_digit_io_switch)(struct ct_atc *atc); 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. */ /* Don't touch! Used for internal object. */
void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
......
...@@ -22,20 +22,9 @@ ...@@ -22,20 +22,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#define DAIO_RESOURCE_NUM NUM_DAIOTYP
#define DAIO_OUT_MAX SPDIFOO #define DAIO_OUT_MAX SPDIFOO
union daio_usage { struct 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;
unsigned short data; unsigned short data;
}; };
...@@ -61,6 +50,7 @@ struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { ...@@ -61,6 +50,7 @@ struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO3] = {.left = 0x50, .right = 0x51}, [LINEO3] = {.left = 0x50, .right = 0x51},
[LINEO4] = {.left = 0x70, .right = 0x71}, [LINEO4] = {.left = 0x70, .right = 0x71},
[LINEIM] = {.left = 0x45, .right = 0xc5}, [LINEIM] = {.left = 0x45, .right = 0xc5},
[MIC] = {.left = 0x55, .right = 0xd5},
[SPDIFOO] = {.left = 0x00, .right = 0x01}, [SPDIFOO] = {.left = 0x00, .right = 0x01},
[SPDIFIO] = {.left = 0x05, .right = 0x85}, [SPDIFIO] = {.left = 0x05, .right = 0x85},
}; };
...@@ -138,6 +128,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) ...@@ -138,6 +128,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEO3: return 5; case LINEO3: return 5;
case LINEO4: return 6; case LINEO4: return 6;
case LINEIM: return 4; case LINEIM: return 4;
case MIC: return 5;
default: return -EINVAL; default: return -EINVAL;
} }
default: default:
...@@ -519,17 +510,17 @@ static int dai_rsc_uninit(struct dai *dai) ...@@ -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) 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; return -ENOENT;
((union daio_usage *)mgr->rscs)->data |= (0x1 << type); ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type);
return 0; return 0;
} }
static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) 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; return 0;
} }
...@@ -712,7 +703,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) ...@@ -712,7 +703,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
if (!daio_mgr) if (!daio_mgr)
return -ENOMEM; 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) if (err)
goto error1; goto error1;
......
...@@ -33,6 +33,7 @@ enum DAIOTYP { ...@@ -33,6 +33,7 @@ enum DAIOTYP {
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */ SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
LINEIM, LINEIM,
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
MIC, /* Dedicated mic on Titanium HD */
SPDIFI1, /* S/PDIF In on internal Drive Bay */ SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP NUM_DAIOTYP
}; };
......
...@@ -39,6 +39,7 @@ enum CTCARDS { ...@@ -39,6 +39,7 @@ enum CTCARDS {
CT20K2_MODEL_FIRST = CTSB0760, CT20K2_MODEL_FIRST = CTSB0760,
CTHENDRIX, CTHENDRIX,
CTSB0880, CTSB0880,
CTSB1270,
CT20K2_UNKNOWN, CT20K2_UNKNOWN,
NUM_CTCARDS /* This should always be the last */ NUM_CTCARDS /* This should always be the last */
}; };
...@@ -71,6 +72,13 @@ struct hw { ...@@ -71,6 +72,13 @@ struct hw {
int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
int (*select_adc_source)(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_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 */ /* SRC operations */
int (*src_rsc_get_ctrl_blk)(void **rblk); int (*src_rsc_get_ctrl_blk)(void **rblk);
......
...@@ -1783,6 +1783,21 @@ static int hw_have_digit_io_switch(struct hw *hw) ...@@ -1783,6 +1783,21 @@ static int hw_have_digit_io_switch(struct hw *hw)
return !(hw->model == CTSB073X || hw->model == CTUAA); 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 CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
#define UAA_CFG_PWRSTATUS 0x44 #define UAA_CFG_PWRSTATUS 0x44
...@@ -2173,6 +2188,9 @@ static struct hw ct20k1_preset __devinitdata = { ...@@ -2173,6 +2188,9 @@ static struct hw ct20k1_preset __devinitdata = {
.is_adc_source_selected = hw_is_adc_input_selected, .is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select, .select_adc_source = hw_adc_input_select,
.have_digit_io_switch = hw_have_digit_io_switch, .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 #ifdef CONFIG_PM
.suspend = hw_suspend, .suspend = hw_suspend,
.resume = hw_resume, .resume = hw_resume,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* @File cthw20k2.c * @File cthw20k2.c
* *
* @Brief * @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 * @Author Liu Chun
* @Date May 14 2008 * @Date May 14 2008
...@@ -38,6 +38,8 @@ struct hw20k2 { ...@@ -38,6 +38,8 @@ struct hw20k2 {
unsigned char dev_id; unsigned char dev_id;
unsigned char addr_size; unsigned char addr_size;
unsigned char data_size; unsigned char data_size;
int mic_source;
}; };
static u32 hw_read_20kx(struct hw *hw, u32 reg); 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) ...@@ -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_TX_BLRCLK, 0x01010101);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else if (2 == info->msr) { } 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 /* Specify all playing 96khz
* EA [0] - Enabled * EA [0] - Enabled
* RTA [4:5] - 96kHz * RTA [4:5] - 96kHz
...@@ -1175,6 +1182,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) ...@@ -1175,6 +1182,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
* RTD [28:29] - 96kHz */ * RTD [28:29] - 96kHz */
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); 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 { } else {
printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
return -EINVAL; return -EINVAL;
...@@ -1182,6 +1193,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) ...@@ -1182,6 +1193,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (i <= 3) { 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) */ /* 1st 3 channels are SPDIFs (SB0960) */
if (i == 3) if (i == 3)
data = 0x1001001; data = 0x1001001;
...@@ -1206,12 +1219,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) ...@@ -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); hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
} else { } else {
/* Again, loop is over 4 channels not 5. */
/* Next 5 channels are I2S (SB0960) */ /* Next 5 channels are I2S (SB0960) */
data = 0x11; data = 0x11;
hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data); hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
if (2 == info->msr) { if (2 == info->msr) {
/* Four channels per sample period */ /* Four channels per sample period */
data |= 0x1000; 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); 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) ...@@ -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); hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
hw20k2_i2c_wait_data_ready(hw); 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); hw_write_20kx(hw, I2C_IF_WDATA, 0);
hw20k2_i2c_wait_data_ready(hw); hw20k2_i2c_wait_data_ready(hw);
...@@ -1568,6 +1585,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) ...@@ -1568,6 +1585,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
return 0; 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) static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
{ {
int err; int err;
...@@ -1594,6 +1635,21 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) ...@@ -1594,6 +1635,21 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
0x00000000 /* Vol Control B4 */ 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 */ /* Set DAC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL); data = hw_read_20kx(hw, GPIO_CTRL);
data |= 0x02; data |= 0x02;
...@@ -1606,22 +1662,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) ...@@ -1606,22 +1662,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
/* Reset DAC twice just in-case the chip /* Reset DAC twice just in-case the chip
* didn't initialized properly */ * didn't initialized properly */
data = hw_read_20kx(hw, GPIO_DATA); hw_dac_reset(hw);
/* GPIO data bit 1 */ hw_dac_reset(hw);
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);
if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1)) if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1))
continue; continue;
...@@ -1725,7 +1767,11 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) ...@@ -1725,7 +1767,11 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
{ {
u32 data; 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); data = hw_read_20kx(hw, GPIO_DATA);
switch (type) { switch (type) {
case ADC_MICIN: case ADC_MICIN:
...@@ -1742,35 +1788,47 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) ...@@ -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_0DB 0xCF
#define MIC_BOOST_STEPS_PER_DB 2 #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) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
{ {
u32 data; u32 data;
data = hw_read_20kx(hw, GPIO_DATA); data = hw_read_20kx(hw, GPIO_DATA);
switch (type) { switch (type) {
case ADC_MICIN: case ADC_MICIN:
data |= (0x1 << 14); data |= (0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data); hw_write_20kx(hw, GPIO_DATA, data);
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */
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 */
break; break;
case ADC_LINEIN: case ADC_LINEIN:
data &= ~(0x1 << 14); data &= ~(0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data); hw_write_20kx(hw, GPIO_DATA, data);
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */
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 */
break; break;
default: default:
break; break;
...@@ -1782,7 +1840,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) ...@@ -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) static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
{ {
int err; int err;
u32 mux = 2, data, ctl; u32 data, ctl;
/* Set ADC reset bit as output */ /* Set ADC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL); data = hw_read_20kx(hw, GPIO_CTRL);
...@@ -1796,19 +1854,42 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) ...@@ -1796,19 +1854,42 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
goto error; goto error;
} }
/* Make ADC in normal operation */ /* Reset the ADC (reset is active low). */
data = hw_read_20kx(hw, GPIO_DATA); data = hw_read_20kx(hw, GPIO_DATA);
data &= ~(0x1 << 15); 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); mdelay(10);
/* Return the ADC to normal operation. */
data |= (0x1 << 15); data |= (0x1 << 15);
hw_write_20kx(hw, GPIO_DATA, data); hw_write_20kx(hw, GPIO_DATA, data);
mdelay(50); 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) */ /* Set the master mode (256fs) */
if (1 == info->msr) { if (1 == info->msr) {
/* slave mode, 128x oversampling 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
MAKE_WM8775_DATA(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), hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
MAKE_WM8775_DATA(0x0A)); MAKE_WM8775_DATA(0x0A));
} else { } else {
...@@ -1818,47 +1899,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) ...@@ -1818,47 +1899,17 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
goto error; goto error;
} }
/* Configure GPIO bit 14 change to line-in/mic-in */ if (hw->model != CTSB1270) {
ctl = hw_read_20kx(hw, GPIO_CTRL); /* Configure GPIO bit 14 change to line-in/mic-in */
ctl |= 0x1 << 14; ctl = hw_read_20kx(hw, GPIO_CTRL);
hw_write_20kx(hw, GPIO_CTRL, ctl); ctl |= 0x1 << 14;
hw_write_20kx(hw, GPIO_CTRL, ctl);
/* Check using Mic-in or Line-in */ hw_adc_input_select(hw, ADC_LINEIN);
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 */
} else { } else {
printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); hw_wm8775_input_select(hw, 0, 0);
err = -EINVAL;
goto error;
} }
return 0; return 0;
error: error:
hw20k2_i2c_uninit(hw); hw20k2_i2c_uninit(hw);
return err; return err;
...@@ -1869,6 +1920,102 @@ static int hw_have_digit_io_switch(struct hw *hw) ...@@ -1869,6 +1920,102 @@ static int hw_have_digit_io_switch(struct hw *hw)
return 0; 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) static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
{ {
struct hw *hw = dev_id; struct hw *hw = dev_id;
...@@ -2023,13 +2170,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) ...@@ -2023,13 +2170,16 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
/* Reset all SRC pending interrupts */ /* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRC_IP, 0); hw_write_20kx(hw, SRC_IP, 0);
/* TODO: detect the card ID and configure GPIO accordingly. */ if (hw->model != CTSB1270) {
/* Configures GPIO (0xD802 0x98028) */ /* TODO: detect the card ID and configure GPIO accordingly. */
/*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ /* Configures GPIO (0xD802 0x98028) */
/* Configures GPIO (SB0880) */ /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
/*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ /* Configures GPIO (SB0880) */
hw_write_20kx(hw, GPIO_CTRL, 0xD802); /*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 */ /* Enable audio ring */
hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
...@@ -2107,6 +2257,13 @@ static struct hw ct20k2_preset __devinitdata = { ...@@ -2107,6 +2257,13 @@ static struct hw ct20k2_preset __devinitdata = {
.is_adc_source_selected = hw_is_adc_input_selected, .is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select, .select_adc_source = hw_adc_input_select,
.have_digit_io_switch = hw_have_digit_io_switch, .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 #ifdef CONFIG_PM
.suspend = hw_suspend, .suspend = hw_suspend,
.resume = hw_resume, .resume = hw_resume,
......
...@@ -86,9 +86,7 @@ enum CTALSA_MIXER_CTL { ...@@ -86,9 +86,7 @@ enum CTALSA_MIXER_CTL {
MIXER_LINEIN_C_S, MIXER_LINEIN_C_S,
MIXER_MIC_C_S, MIXER_MIC_C_S,
MIXER_SPDIFI_C_S, MIXER_SPDIFI_C_S,
MIXER_LINEIN_P_S,
MIXER_SPDIFO_P_S, MIXER_SPDIFO_P_S,
MIXER_SPDIFI_P_S,
MIXER_WAVEF_P_S, MIXER_WAVEF_P_S,
MIXER_WAVER_P_S, MIXER_WAVER_P_S,
MIXER_WAVEC_P_S, MIXER_WAVEC_P_S,
...@@ -137,11 +135,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { ...@@ -137,11 +135,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
}, },
[MIXER_LINEIN_P] = { [MIXER_LINEIN_P] = {
.ctl = 1, .ctl = 1,
.name = "Line-in Playback Volume", .name = "Line Playback Volume",
}, },
[MIXER_LINEIN_C] = { [MIXER_LINEIN_C] = {
.ctl = 1, .ctl = 1,
.name = "Line-in Capture Volume", .name = "Line Capture Volume",
}, },
[MIXER_MIC_P] = { [MIXER_MIC_P] = {
.ctl = 1, .ctl = 1,
...@@ -153,15 +151,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { ...@@ -153,15 +151,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
}, },
[MIXER_SPDIFI_P] = { [MIXER_SPDIFI_P] = {
.ctl = 1, .ctl = 1,
.name = "S/PDIF-in Playback Volume", .name = "IEC958 Playback Volume",
}, },
[MIXER_SPDIFI_C] = { [MIXER_SPDIFI_C] = {
.ctl = 1, .ctl = 1,
.name = "S/PDIF-in Capture Volume", .name = "IEC958 Capture Volume",
}, },
[MIXER_SPDIFO_P] = { [MIXER_SPDIFO_P] = {
.ctl = 1, .ctl = 1,
.name = "S/PDIF-out Playback Volume", .name = "Digital Playback Volume",
}, },
[MIXER_WAVEF_P] = { [MIXER_WAVEF_P] = {
.ctl = 1, .ctl = 1,
...@@ -179,14 +177,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { ...@@ -179,14 +177,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
.ctl = 1, .ctl = 1,
.name = "Surround Playback Volume", .name = "Surround Playback Volume",
}, },
[MIXER_PCM_C_S] = { [MIXER_PCM_C_S] = {
.ctl = 1, .ctl = 1,
.name = "PCM Capture Switch", .name = "PCM Capture Switch",
}, },
[MIXER_LINEIN_C_S] = { [MIXER_LINEIN_C_S] = {
.ctl = 1, .ctl = 1,
.name = "Line-in Capture Switch", .name = "Line Capture Switch",
}, },
[MIXER_MIC_C_S] = { [MIXER_MIC_C_S] = {
.ctl = 1, .ctl = 1,
...@@ -194,19 +191,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { ...@@ -194,19 +191,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
}, },
[MIXER_SPDIFI_C_S] = { [MIXER_SPDIFI_C_S] = {
.ctl = 1, .ctl = 1,
.name = "S/PDIF-in Capture Switch", .name = "IEC958 Capture Switch",
},
[MIXER_LINEIN_P_S] = {
.ctl = 1,
.name = "Line-in Playback Switch",
}, },
[MIXER_SPDIFO_P_S] = { [MIXER_SPDIFO_P_S] = {
.ctl = 1, .ctl = 1,
.name = "S/PDIF-out Playback Switch", .name = "Digital Playback Switch",
},
[MIXER_SPDIFI_P_S] = {
.ctl = 1,
.name = "S/PDIF-in Playback Switch",
}, },
[MIXER_WAVEF_P_S] = { [MIXER_WAVEF_P_S] = {
.ctl = 1, .ctl = 1,
...@@ -236,6 +225,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); ...@@ -236,6 +225,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
static void static void
ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); 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 struct snd_kcontrol *kctls[2] = {NULL};
static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) 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 = { ...@@ -420,6 +411,77 @@ static struct snd_kcontrol_new vol_ctl = {
.tlv = { .p = ct_vol_db_scale }, .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 static void
do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) 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) ...@@ -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) static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
{ {
struct ct_mixer *mixer = atc->mixer; struct ct_mixer *mixer = atc->mixer;
int have_dedicated_mic = atc->have_dedicated_mic(atc);
/* Do changes in mixer. */ /* Do changes in mixer. */
if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { 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) ...@@ -477,8 +540,17 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
} }
} }
/* Do changes out of mixer. */ /* Do changes out of mixer. */
if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) if (!have_dedicated_mic &&
do_line_mic_switch(atc, type); (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) else if (MIXER_WAVEF_P_S == type)
atc->line_front_unmute(atc, state); atc->line_front_unmute(atc, state);
else if (MIXER_WAVES_P_S == type) 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) ...@@ -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); atc->line_clfe_unmute(atc, state);
else if (MIXER_WAVER_P_S == type) else if (MIXER_WAVER_P_S == type)
atc->line_rear_unmute(atc, state); 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) else if (MIXER_SPDIFO_P_S == type)
atc->spdif_out_unmute(atc, state); 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) else if (MIXER_DIGITAL_IO_S == type)
do_digit_io_switch(atc, state); do_digit_io_switch(atc, state);
...@@ -686,6 +754,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) ...@@ -686,6 +754,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
atc->have_digit_io_switch(atc); atc->have_digit_io_switch(atc);
for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
if (ct_kcontrol_init_table[type].ctl) { if (ct_kcontrol_init_table[type].ctl) {
swh_ctl.name = ct_kcontrol_init_table[type].name; swh_ctl.name = ct_kcontrol_init_table[type].name;
...@@ -708,6 +777,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) ...@@ -708,6 +777,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
if (err) if (err)
return 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); atc->line_front_unmute(atc, 1);
set_switch_state(mixer, MIXER_WAVEF_P_S, 1); set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
atc->line_surround_unmute(atc, 0); atc->line_surround_unmute(atc, 0);
...@@ -719,13 +799,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) ...@@ -719,13 +799,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
atc->spdif_out_unmute(atc, 0); atc->spdif_out_unmute(atc, 0);
set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
atc->line_in_unmute(atc, 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); atc->spdif_in_unmute(atc, 0);
set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); set_switch_state(mixer, MIXER_PCM_C_S, 0);
set_switch_state(mixer, MIXER_LINEIN_C_S, 0);
set_switch_state(mixer, MIXER_PCM_C_S, 1); set_switch_state(mixer, MIXER_SPDIFI_C_S, 0);
set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
return 0; return 0;
} }
......
...@@ -80,11 +80,11 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) ...@@ -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"); "are 48000 and 44100, Value 48000 is assumed.\n");
reference_rate = 48000; reference_rate = 48000;
} }
if ((multiple != 1) && (multiple != 2)) { if ((multiple != 1) && (multiple != 2) && (multiple != 4)) {
printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n", printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
multiple); multiple);
printk(KERN_ERR "ctxfi: The valid values for multiple are " 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; multiple = 2;
} }
err = ct_atc_create(card, pci, reference_rate, multiple, err = ct_atc_create(card, pci, reference_rate, multiple,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册