ALSA: ctxfi: Add support for Onkyo SE-300PCIE (OK0010)

Add support for the Onkyo SE-300PCIE, a Creative X-Fi CA20K2-based
sound card with a custom hardware implementation that differs
significantly from other CA20K2-based variants.

Changes:
 - PCI quirk entry for OK0010
 - Port 0x3 is utilized for dedicated RCA output (configured as I2S)
 - Modified GPIO pin mappings and states
 - 4-channel simultaneous ADC input support for line and microphone
   capture without input switching (similar to SB1270)
 - Simplified ADC initialization (no manual setup required)

Signed-off-by: Harin Lee <me@harin.net>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20251124180501.2760421-7-me@harin.net
This commit is contained in:
Harin Lee 2025-11-25 03:05:01 +09:00 committed by Takashi Iwai
parent 9b4a22733c
commit c58f520e6a
4 changed files with 43 additions and 14 deletions

View File

@ -63,6 +63,7 @@ static const struct snd_pci_quirk subsys_20k2_list[] = {
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000,
PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX", PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX",
CTHENDRIX), CTHENDRIX),
SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010),
{ } /* terminator */ { } /* terminator */
}; };
@ -78,6 +79,7 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
[CTHENDRIX] = "Hendrix", [CTHENDRIX] = "Hendrix",
[CTSB0880] = "SB0880", [CTSB0880] = "SB0880",
[CTSB1270] = "SB1270", [CTSB1270] = "SB1270",
[CTOK0010] = "OK0010",
[CT20K2_UNKNOWN] = "Unknown", [CT20K2_UNKNOWN] = "Unknown",
}; };
@ -1535,8 +1537,10 @@ static void atc_connect_resources(struct ct_atc *atc)
dao->ops->set_right_input(dao, rscs[1]); dao->ops->set_right_input(dao, rscs[1]);
} }
if (cap.dedicated_rca) if (cap.dedicated_rca) {
/* SE-300PCIE has a dedicated DAC for the RCA. */
atc_dedicated_rca_select(atc); atc_dedicated_rca_select(atc);
}
dai = container_of(atc->daios[LINEIM], struct dai, daio); dai = container_of(atc->daios[LINEIM], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai, atc_connect_dai(atc->rsc_mgrs[SRC], dai,
@ -1549,6 +1553,7 @@ static void atc_connect_resources(struct ct_atc *atc)
if (cap.dedicated_mic) { if (cap.dedicated_mic) {
/* Titanium HD has a dedicated ADC for the Mic. */ /* Titanium HD has a dedicated ADC for the Mic. */
/* SE-300PCIE has a 4-channel ADC. */
dai = container_of(atc->daios[MIC], struct dai, daio); dai = container_of(atc->daios[MIC], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai, atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[4], (struct src **)&atc->srcs[4],

View File

@ -31,7 +31,7 @@ enum DAIOTYP {
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 */ MIC, /* Dedicated mic on Titanium HD */
RCA, RCA, /* Dedicated RCA on SE-300PCIE */
SPDIFI1, /* S/PDIF In on internal Drive Bay */ SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP NUM_DAIOTYP
}; };

View File

@ -38,6 +38,7 @@ enum CTCARDS {
CTHENDRIX, CTHENDRIX,
CTSB0880, CTSB0880,
CTSB1270, CTSB1270,
CTOK0010,
CT20K2_UNKNOWN, CT20K2_UNKNOWN,
NUM_CTCARDS /* This should always be the last */ NUM_CTCARDS /* This should always be the last */
}; };

View File

@ -910,7 +910,7 @@ static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
struct dao_ctrl_blk *ctl = blk; struct dao_ctrl_blk *ctl = blk;
if (ctl->dirty.bf.atxcsl) { if (ctl->dirty.bf.atxcsl) {
if (idx < 4) { if ((idx < 4) && ((hw->model != CTOK0010) || (idx < 3))) {
/* S/PDIF SPOSx */ /* S/PDIF SPOSx */
hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx, hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx,
ctl->atxcsl); ctl->atxcsl);
@ -985,11 +985,12 @@ static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
return 0; return 0;
} }
static int daio_mgr_dao_init(struct hw *hw __maybe_unused, void *blk, unsigned int idx, unsigned int conf) static int daio_mgr_dao_init(struct hw *hw, void *blk, unsigned int idx, unsigned int conf)
{ {
struct daio_mgr_ctrl_blk *ctl = blk; struct daio_mgr_ctrl_blk *ctl = blk;
if (idx < 4) { /* Port 3 is dedicated to RCA on SE-300PCIE */
if ((idx < 4) && ((hw->model != CTOK0010) || (idx < 3))) {
/* S/PDIF output */ /* S/PDIF output */
switch ((conf & 0xf)) { switch ((conf & 0xf)) {
case 1: case 1:
@ -1176,6 +1177,10 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111); hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111);
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121); hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
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 == CTOK0010)) {
hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21212121);
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else { } else {
dev_alert(hw->card->dev, dev_alert(hw->card->dev,
"ERROR!!! Invalid sampling rate!!!\n"); "ERROR!!! Invalid sampling rate!!!\n");
@ -1183,7 +1188,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) { /* Port 3 is configured as I2S on SE-300PCIE */
if ((i < 4) && ((hw->model != CTOK0010) || (i < 3))) {
/* This comment looks wrong since loop is over 4 */ /* This comment looks wrong since loop is over 4 */
/* channels and emu20k2 supports 4 spdif IOs. */ /* channels and emu20k2 supports 4 spdif IOs. */
/* 1st 3 channels are SPDIFs (SB0960) */ /* 1st 3 channels are SPDIFs (SB0960) */
@ -1637,6 +1643,13 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
hw_write_20kx(hw, GPIO_DATA, data); hw_write_20kx(hw, GPIO_DATA, data);
hw_dac_start(hw); hw_dac_start(hw);
return 0; return 0;
} else if (hw->model == CTOK0010) {
hw_dac_stop(hw);
data = hw_read_20kx(hw, GPIO_DATA);
data |= 0x1000;
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 */
@ -1756,9 +1769,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) { if ((hw->model == CTSB1270) || (hw->model == CTOK0010)) {
/* Titanium HD has two ADC chips, one for line in and one */ /* Titanium HD has two ADC chips, one for line in and one */
/* for MIC. We don't need to switch the ADC input. */ /* for MIC. Also, SE-300PCIE has a single ADC chip that */
/* simultaneously supports 4-channel input. We don't need */
/* to switch the ADC input. */
return 1; return 1;
} }
data = hw_read_20kx(hw, GPIO_DATA); data = hw_read_20kx(hw, GPIO_DATA);
@ -1846,7 +1861,7 @@ static void hw_adc_start(struct hw *hw)
msleep(50); msleep(50);
} }
static void __maybe_unused hw_adc_reset(struct hw *hw) static void hw_adc_reset(struct hw *hw)
{ {
hw_adc_stop(hw); hw_adc_stop(hw);
hw_adc_start(hw); hw_adc_start(hw);
@ -1862,6 +1877,12 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
data |= (0x1 << 15); data |= (0x1 << 15);
hw_write_20kx(hw, GPIO_CTRL, data); hw_write_20kx(hw, GPIO_CTRL, data);
if (hw->model == CTOK0010) {
/* Manual ADC setup for SE-300PCIE is not needed. */
hw_adc_reset(hw);
return 0;
}
/* Initialize I2C */ /* Initialize I2C */
err = hw20k2_i2c_init(hw, 0x1A, 1, 1); err = hw20k2_i2c_init(hw, 0x1A, 1, 1);
if (err < 0) { if (err < 0) {
@ -1929,8 +1950,8 @@ static struct capabilities hw_capabilities(struct hw *hw)
struct capabilities cap; struct capabilities cap;
cap.digit_io_switch = 0; cap.digit_io_switch = 0;
cap.dedicated_mic = hw->model == CTSB1270; cap.dedicated_mic = (hw->model == CTSB1270) || (hw->model == CTOK0010);
cap.dedicated_rca = 0; cap.dedicated_rca = hw->model == CTOK0010;
cap.output_switch = hw->model == CTSB1270; cap.output_switch = hw->model == CTSB1270;
cap.mic_source_switch = hw->model == CTSB1270; cap.mic_source_switch = hw->model == CTSB1270;
@ -2167,15 +2188,17 @@ 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);
if (hw->model != CTSB1270) { if (hw->model == CTSB1270) {
hw_write_20kx(hw, GPIO_CTRL, 0x9E5F);
} else if (hw->model == CTOK0010) {
hw_write_20kx(hw, GPIO_CTRL, 0x9902);
} else {
/* TODO: detect the card ID and configure GPIO accordingly. */ /* TODO: detect the card ID and configure GPIO accordingly. */
/* Configures GPIO (0xD802 0x98028) */ /* Configures GPIO (0xD802 0x98028) */
/*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
/* Configures GPIO (SB0880) */ /* Configures GPIO (SB0880) */
/*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
hw_write_20kx(hw, GPIO_CTRL, 0xD802); 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);