From: Davide Gironi <davide.gironi@gmail.com>
To: linux-sound@vger.kernel.org
Cc: perex@perex.cz, tiwai@suse.com, Davide Gironi <davide.gironi@gmail.com>
Subject: [PATCH v2 2/2] ALSA: ice1712: add TerraTec PHASE 88 support
Date: Wed, 28 Oct 2026 19:24:10 +0100 [thread overview]
Message-ID: <20261028183155.787599-3-davide.gironi@gmail.com> (raw)
In-Reply-To: <20261028183155.787599-1-davide.gironi@gmail.com>
Handle PHASE 88-specific expander, EEPROM, control, and MIDI behavior
in the EWS driver. Use a distinct TS88 model name and preserve the
expander state when a write fails.
Assisted-by: Pi:gpt-6-luna
Signed-off-by: Davide Gironi <davide.gironi@gmail.com>
---
sound/pci/ice1712/ews.c | 141 +++++++++++++++++++++++++++++++++++++---
sound/pci/ice1712/ews.h | 7 ++
2 files changed, 138 insertions(+), 10 deletions(-)
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 62574c3..5a44bfa 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -32,6 +32,8 @@ enum {
/* additional i2c devices for EWS boards */
struct ews_spec {
struct snd_i2c_device *i2cdevs[3];
+ unsigned char pcf8575_data; /* PHASE 88: shadow of the chip select byte */
+ unsigned char pcf8575_input; /* PHASE 88: shadow of the input select byte */
};
/*
@@ -128,12 +130,94 @@ static struct snd_i2c_bit_ops snd_ice1712_ewx_cs8427_bit_ops = {
* AK4524 access
*/
+/* The PHASE 88 chip-select expander is write-only; keep a shadow value. */
+/* The bit-banged bus takes gpio_mutex internally; don't hold it here. */
+static int snd_ice1712_phase88_write(struct snd_ice1712 *ice,
+ unsigned char page, unsigned char value)
+{
+ struct ews_spec *spec = ice->spec;
+ unsigned char bytes[2];
+ int err;
+
+ bytes[0] = page;
+ bytes[1] = value;
+ snd_i2c_lock(ice->i2c);
+ err = snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], bytes, 2);
+ snd_i2c_unlock(ice->i2c);
+ if (err != 2) {
+ dev_err(ice->card->dev,
+ "PHASE 88 expander write failed (page 0x%02x, addr 0x%02x, err %d)\n",
+ page, spec->i2cdevs[EWS_I2C_PCF2]->addr, err);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int snd_ice1712_phase88_chip_select(struct snd_ice1712 *ice, int chip_mask)
+{
+ struct ews_spec *spec = ice->spec;
+ unsigned char data;
+ int err;
+
+ if (snd_BUG_ON(chip_mask < 0 || chip_mask > 0x0f))
+ return -EINVAL;
+ data = (spec->pcf8575_data & 0xf0) | chip_mask;
+ err = snd_ice1712_phase88_write(ice, ICE1712_PHASE88_OUTPUT_PAGE,
+ data);
+ if (err >= 0)
+ spec->pcf8575_data = data;
+ return err;
+}
+
+/* PHASE 88 Line/Mic bits are ordered 7/8 to 1/2 (0x10 to 0x80). */
+static int snd_ice1712_phase88_linemic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
+
+ ucontrol->value.integer.value[0] =
+ !!(spec->pcf8575_input & (0x80 >> kcontrol->private_value));
+ return 0;
+}
+
+static int snd_ice1712_phase88_linemic_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
+ unsigned char bit = 0x80 >> kcontrol->private_value;
+ unsigned char val = ucontrol->value.integer.value[0] ?
+ spec->pcf8575_input | bit : spec->pcf8575_input & ~bit;
+ int err;
+
+ if (val == spec->pcf8575_input)
+ return 0;
+ err = snd_ice1712_phase88_write(ice, ICE1712_PHASE88_INPUT_PAGE, val);
+ if (err < 0)
+ return err;
+ spec->pcf8575_input = val;
+ return 1;
+}
+
+static const struct snd_kcontrol_new snd_ice1712_phase88_linemic_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line/Mic 7/8 Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_ice1712_phase88_linemic_get,
+ .put = snd_ice1712_phase88_linemic_put,
+ .private_value = 3, /* bit 0x10: the pair nothing else drives */
+};
+
/* AK4524 chip select; address 0x48 bit 0-3 */
static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mask)
{
struct ews_spec *spec = ice->spec;
unsigned char data, ndata;
+ if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_PHASE88)
+ return snd_ice1712_phase88_chip_select(ice, chip_mask);
+
if (snd_BUG_ON(chip_mask < 0 || chip_mask > 0x0f))
return -EINVAL;
snd_i2c_lock(ice->i2c);
@@ -458,9 +542,31 @@ static int snd_ice1712_ews_init(struct snd_ice1712 *ice)
}
snd_ice1712_6fire_write_pca(ice, PCF9554_REG_CONFIG, 0x80);
break;
+ case ICE1712_SUBDEVICE_PHASE88:
+ err = snd_i2c_device_create(ice->i2c, "CS8404",
+ ICE1712_EWS88MT_CS8404_ADDR,
+ &spec->i2cdevs[EWS_I2C_CS8404]);
+ if (err < 0)
+ return err;
+ err = snd_i2c_device_create(ice->i2c, "Output expander",
+ ICE1712_PHASE88_OUTPUT_ADDR,
+ &spec->i2cdevs[EWS_I2C_PCF2]);
+ if (err < 0)
+ return err;
+ spec->pcf8575_data = ICE1712_PHASE88_OUTPUT_INIT;
+ /* Check if the chip select expander is present */
+ err = snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+ if (err < 0)
+ return err;
+ /* Clear the input register before codec setup. */
+ spec->pcf8575_input = ICE1712_PHASE88_INPUT_INIT;
+ err = snd_ice1712_phase88_write(ice, ICE1712_PHASE88_INPUT_PAGE,
+ spec->pcf8575_input);
+ if (err < 0)
+ return err;
+ break;
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
- case ICE1712_SUBDEVICE_PHASE88:
case ICE1712_SUBDEVICE_TS88:
err = snd_i2c_device_create(ice->i2c, "CS8404",
@@ -982,9 +1088,15 @@ static int snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
return err;
}
break;
+ case ICE1712_SUBDEVICE_PHASE88:
+ /* The PHASE 88 has no EWS88MT sensitivity controls. */
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_ice1712_phase88_linemic_ctl, ice));
+ if (err < 0)
+ return err;
+ break;
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
- case ICE1712_SUBDEVICE_PHASE88:
case ICE1712_SUBDEVICE_TS88:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice));
if (err < 0)
@@ -1014,13 +1126,21 @@ static int snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
static int snd_ice1712_ews_eeprom_init(struct snd_ice1712 *ice)
{
- if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE &&
- !ice->dxr_enable) {
- /* Set EEPROM value to limit active ADCs and DACs to 6;
- * also disable AC'97 as no hardware in standard 6fire card/box.
- * DXR extensions are not currently supported.
- */
- ice->eeprom.data[ICE_EEP1_CODEC] = 0x3a;
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_DMX6FIRE:
+ if (!ice->dxr_enable) {
+ /* Limit active ADCs and DACs to 6, and disable AC'97
+ * as the standard 6fire card/box has no hardware for it.
+ */
+ ice->eeprom.data[ICE_EEP1_CODEC] = 0x3a;
+ }
+ break;
+ case ICE1712_SUBDEVICE_PHASE88:
+ /* No consumer AC'97 codec on PHASE 88. */
+ ice->eeprom.data[ICE_EEP1_CODEC] |= ICE1712_CFG_NO_CON_AC97;
+ /* Enable the rear MIDI UART. */
+ ice->eeprom.data[ICE_EEP1_CODEC] |= ICE1712_CFG_2xMPU401;
+ break;
}
return 0;
}
@@ -1052,13 +1172,14 @@ struct snd_ice1712_card_info snd_ice1712_ews_cards[] = {
.subvendor = ICE1712_SUBDEVICE_PHASE88,
.name = "TerraTec Phase88",
.model = "phase88",
+ .eeprom_init = snd_ice1712_ews_eeprom_init,
.chip_init = snd_ice1712_ews_init,
.build_controls = snd_ice1712_ews_add_controls,
},
{
.subvendor = ICE1712_SUBDEVICE_TS88,
.name = "terrasoniq TS88",
- .model = "phase88",
+ .model = "ts88",
.chip_init = snd_ice1712_ews_init,
.build_controls = snd_ice1712_ews_add_controls,
},
diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
index aec8dc6..064675a 100644
--- a/sound/pci/ice1712/ews.h
+++ b/sound/pci/ice1712/ews.h
@@ -58,6 +58,13 @@ extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
#define ICE1712_EWS88MT_OUTPUT_SENSE 0x40 /* mask */
#define ICE1712_EWS88D_PCF_ADDR (0x40>>1)
+/* TerraTec PHASE 88 configuration definitions */
+#define ICE1712_PHASE88_OUTPUT_ADDR (0x42>>1)
+#define ICE1712_PHASE88_OUTPUT_PAGE 0x01 /* output register */
+#define ICE1712_PHASE88_OUTPUT_INIT 0x20 /* initial output state */
+#define ICE1712_PHASE88_INPUT_PAGE 0x03 /* input register */
+#define ICE1712_PHASE88_INPUT_INIT 0x00 /* line inputs */
+
/* TerraTec DMX 6Fire configuration definitions */
#define ICE1712_6FIRE_AK4524_CS_MASK 0x07 /* AK4524 chip select #1-#3 */
#define ICE1712_6FIRE_RW 0x08 /* read/write switch for i2c; high = write */
prev parent reply other threads:[~2026-10-28 18:33 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-24 11:27 [PATCH] ASoC: ice1712: add TerraTec PHASE 88 support root
2026-10-26 7:59 ` Takashi Iwai
2026-10-28 18:24 ` [PATCH v2 0/2] ALSA: ice1712: support the TerraTec PHASE 88 Davide Gironi
2026-10-28 18:24 ` [PATCH v2 1/2] ALSA: ice1712: add a board-specific EEPROM initialization hook Davide Gironi
2026-10-28 18:24 ` Davide Gironi [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261028183155.787599-3-davide.gironi@gmail.com \
--to=davide.gironi@gmail.com \
--cc=linux-sound@vger.kernel.org \
--cc=perex@perex.cz \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.