[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251124180501.2760421-6-me@harin.net>
Date: Tue, 25 Nov 2025 03:05:00 +0900
From: Harin Lee <me@...in.net>
To: Jaroslav Kysela <perex@...ex.cz>,
Takashi Iwai <tiwai@...e.com>
Cc: linux-sound@...r.kernel.org,
linux-kernel@...r.kernel.org,
Harin Lee <me@...in.net>
Subject: [PATCH v3 5/6] ALSA: ctxfi: Add support for dedicated RCA switching
Add feature to support switching between the dedicated RCA output and
the 7.1ch Front output. This is required for hardware that utilizes
separate DAC circuits for RCA and 7.1ch channels.
Changes:
- Add dedicated_rca capability flag
- Add "Analog Playback Route" mixer control
- Implement logic to swap DAO inputs between RCA and Front ports
Signed-off-by: Harin Lee <me@...in.net>
---
sound/pci/ctxfi/ctatc.c | 33 +++++++++++++++-
sound/pci/ctxfi/ctatc.h | 4 ++
sound/pci/ctxfi/ctdaio.c | 2 +
sound/pci/ctxfi/ctdaio.h | 1 +
sound/pci/ctxfi/cthardware.h | 1 +
sound/pci/ctxfi/cthw20k1.c | 1 +
sound/pci/ctxfi/cthw20k2.c | 1 +
sound/pci/ctxfi/ctmixer.c | 73 ++++++++++++++++++++++++++++++++++--
8 files changed, 111 insertions(+), 5 deletions(-)
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index ce2b16722118..ff3694b3021e 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -987,6 +987,24 @@ static struct capabilities atc_capabilities(struct ct_atc *atc)
return hw->capabilities(hw);
}
+static void atc_dedicated_rca_select(struct ct_atc *atc)
+{
+ struct dao *dao;
+ struct ct_mixer *mixer = atc->mixer;
+ struct rsc *rscs[2] = {NULL};
+
+ dao = container_of(atc->daios[atc->rca_state ? RCA : LINEO1],
+ struct dao, daio);
+ dao->ops->clear_left_input(dao);
+ dao->ops->clear_right_input(dao);
+
+ mixer->get_output_ports(mixer, MIX_WAVE_FRONT, &rscs[0], &rscs[1]);
+ dao = container_of(atc->daios[atc->rca_state ? LINEO1 : RCA],
+ struct dao, daio);
+ dao->ops->set_left_input(dao, rscs[0]);
+ dao->ops->set_right_input(dao, rscs[1]);
+}
+
static int atc_output_switch_get(struct ct_atc *atc)
{
struct hw *hw = atc->hw;
@@ -1088,6 +1106,11 @@ static int atc_mic_unmute(struct ct_atc *atc, unsigned char state)
return atc_daio_unmute(atc, state, MIC);
}
+static int atc_rca_unmute(struct ct_atc *atc, unsigned char state)
+{
+ return atc_daio_unmute(atc, state, RCA);
+}
+
static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
{
return atc_daio_unmute(atc, state, SPDIFOO);
@@ -1303,6 +1326,7 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid)
dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n",
atc->chip_name, atc->model_name,
vendor_id, device_id);
+ atc->rca_state = 0;
return 0;
}
@@ -1400,11 +1424,11 @@ 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; i < NUM_DAIOTYP; i++) {
- if ((i == MIC) && !cap.dedicated_mic)
+ if (((i == MIC) && !cap.dedicated_mic) || ((i == RCA) && !cap.dedicated_rca))
continue;
da_desc.type = (atc->model != CTSB073X) ? i :
((i == SPDIFIO) ? SPDIFI1 : i);
- da_desc.output = i < LINEIM;
+ da_desc.output = (i < LINEIM) || (i == RCA);
err = daio_mgr->get_daio(daio_mgr, &da_desc,
(struct daio **)&atc->daios[i]);
if (err) {
@@ -1511,6 +1535,9 @@ static void atc_connect_resources(struct ct_atc *atc)
dao->ops->set_right_input(dao, rscs[1]);
}
+ if (cap.dedicated_rca)
+ atc_dedicated_rca_select(atc);
+
dai = container_of(atc->daios[LINEIM], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[2],
@@ -1643,12 +1670,14 @@ static const struct ct_atc atc_preset = {
.line_rear_unmute = atc_line_rear_unmute,
.line_in_unmute = atc_line_in_unmute,
.mic_unmute = atc_mic_unmute,
+ .rca_unmute = atc_rca_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,
.capabilities = atc_capabilities,
+ .dedicated_rca_select = atc_dedicated_rca_select,
.output_switch_get = atc_output_switch_get,
.output_switch_put = atc_output_switch_put,
.mic_source_switch_get = atc_mic_source_switch_get,
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index 671edc46bd50..ca0a9d5b86d8 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -82,6 +82,8 @@ struct ct_atc {
const char *chip_name;
const char *model_name;
+ unsigned char rca_state; /* 0 = dedicated RCA, 1 = 7.1ch Front */
+
struct ct_vm *vm; /* device virtual memory manager for this card */
int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
@@ -113,12 +115,14 @@ struct ct_atc {
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 (*rca_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);
struct capabilities (*capabilities)(struct ct_atc *atc);
+ void (*dedicated_rca_select)(struct ct_atc *atc);
int (*output_switch_get)(struct ct_atc *atc);
int (*output_switch_put)(struct ct_atc *atc, int position);
int (*mic_source_switch_get)(struct ct_atc *atc);
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index f012d47689d1..1c8f8efd836c 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -45,6 +45,7 @@ static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO4] = {.left = 0x70, .right = 0x71},
[LINEIM] = {.left = 0x45, .right = 0xc5},
[MIC] = {.left = 0x55, .right = 0xd5},
+ [RCA] = {.left = 0x30, .right = 0x31},
[SPDIFOO] = {.left = 0x00, .right = 0x01},
[SPDIFIO] = {.left = 0x05, .right = 0x85},
};
@@ -123,6 +124,7 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEO4: return 6;
case LINEIM: return 4;
case MIC: return 5;
+ case RCA: return 3;
default: return -EINVAL;
}
default:
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
index b337da2de8b5..99d55f19e3ca 100644
--- a/sound/pci/ctxfi/ctdaio.h
+++ b/sound/pci/ctxfi/ctdaio.h
@@ -31,6 +31,7 @@ enum DAIOTYP {
LINEIM,
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
MIC, /* Dedicated mic on Titanium HD */
+ RCA,
SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP
};
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index d29b4e5b3fcc..84ea690763e7 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -62,6 +62,7 @@ struct card_conf {
struct capabilities {
unsigned int digit_io_switch:1;
unsigned int dedicated_mic:1;
+ unsigned int dedicated_rca:1;
unsigned int output_switch:1;
unsigned int mic_source_switch:1;
};
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 60cc1d14453a..ea0a928937b6 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1775,6 +1775,7 @@ static struct capabilities hw_capabilities(struct hw *hw)
/* SB073x and Vista compatible cards have no digit IO switch */
cap.digit_io_switch = !(hw->model == CTSB073X || hw->model == CTUAA);
cap.dedicated_mic = 0;
+ cap.dedicated_rca = 0;
cap.output_switch = 0;
cap.mic_source_switch = 0;
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 5d39bc943648..214a83977a70 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1930,6 +1930,7 @@ static struct capabilities hw_capabilities(struct hw *hw)
cap.digit_io_switch = 0;
cap.dedicated_mic = hw->model == CTSB1270;
+ cap.dedicated_rca = 0;
cap.output_switch = hw->model == CTSB1270;
cap.mic_source_switch = hw->model == CTSB1270;
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index 496682613db5..fc9fde284fb3 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -547,8 +547,14 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
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_WAVEF_P_S == type) {
+ if (cap.dedicated_rca) {
+ atc->rca_unmute(atc, atc->rca_state ? 0 : state);
+ atc->line_front_unmute(atc, atc->rca_state ? state : 0);
+ } else {
+ atc->line_front_unmute(atc, state);
+ }
+ }
else if (MIXER_WAVES_P_S == type)
atc->line_surround_unmute(atc, state);
else if (MIXER_WAVEC_P_S == type)
@@ -612,6 +618,57 @@ static struct snd_kcontrol_new swh_ctl = {
.put = ct_alsa_mix_switch_put
};
+static int dedicated_rca_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = {
+ "RCA", "Front"
+ };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int dedicated_rca_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->rca_state;
+ return 0;
+}
+
+static int dedicated_rca_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ unsigned int rca_state = ucontrol->value.enumerated.item[0];
+ unsigned char state;
+
+ if (rca_state > 1)
+ return -EINVAL;
+
+ if (rca_state == atc->rca_state)
+ return 0;
+
+ state = get_switch_state(atc->mixer, MIXER_WAVEF_P_S);
+ do_switch(atc, MIXER_WAVEF_P_S, 0);
+
+ atc->rca_state = rca_state;
+ atc->dedicated_rca_select(atc);
+
+ do_switch(atc, MIXER_WAVEF_P_S, state);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new rca_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Route",
+ .info = dedicated_rca_info,
+ .get = dedicated_rca_get,
+ .put = dedicated_rca_put,
+};
+
static int ct_spdif_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -784,7 +841,17 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
if (err)
return err;
}
- atc->line_front_unmute(atc, 1);
+
+ if (cap.dedicated_rca) {
+ err = ct_mixer_kcontrol_new(mixer, &rca_ctl);
+ if (err)
+ return err;
+
+ atc->line_front_unmute(atc, 0);
+ atc->rca_unmute(atc, 1);
+ } else {
+ atc->line_front_unmute(atc, 1);
+ }
set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
atc->line_surround_unmute(atc, 0);
set_switch_state(mixer, MIXER_WAVES_P_S, 0);
--
2.52.0
Powered by blists - more mailing lists