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: Harry Butterworth <heb1001@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Harry Butterworth 2011-06-11 16:02:06 +08:00 коммит произвёл Takashi Iwai
Родитель 37f7ec38ea
Коммит 55309216ba
11 изменённых файлов: 488 добавлений и 164 удалений

Просмотреть файл

@ -1308,6 +1308,7 @@
#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 */

Просмотреть файл

@ -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) */

Просмотреть файл

@ -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;
atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
num_daios = ((atc->model == CTSB1270) ? 8 : 7);
num_srcs = ((atc->model == CTSB1270) ? 6 : 4);
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,

Просмотреть файл

@ -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 */

Просмотреть файл

@ -22,20 +22,9 @@
#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;

Просмотреть файл

@ -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
};

Просмотреть файл

@ -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);

Просмотреть файл

@ -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,

Просмотреть файл

@ -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,

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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,