* [PATCH 01/27] ALSA: hda/cirrus: Move CS8409 HDA bridge to separate module
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-27 6:39 ` Takashi Iwai
2021-07-26 17:46 ` [PATCH 02/27] ALSA: hda/cs8409: Move arrays of configuration to a new file Vitaly Rodionov
` (25 subsequent siblings)
26 siblings, 1 reply; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/Kconfig | 11 +
sound/pci/hda/Makefile | 2 +
sound/pci/hda/patch_cirrus.c | 1074 ----------------------------------
sound/pci/hda/patch_cs8409.c | 1060 +++++++++++++++++++++++++++++++++
sound/pci/hda/patch_cs8409.h | 91 +++
5 files changed, 1164 insertions(+), 1074 deletions(-)
create mode 100644 sound/pci/hda/patch_cs8409.c
create mode 100644 sound/pci/hda/patch_cs8409.h
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index c4360cdbc728..a0ca4ec2634d 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -150,6 +150,7 @@ comment "Set to Y if you want auto-loading the codec driver"
config SND_HDA_CODEC_CIRRUS
tristate "Build Cirrus Logic codec support"
select SND_HDA_GENERIC
+ select SND_HDA_CODEC_CS8409
help
Say Y or M here to include Cirrus Logic codec support in
snd-hda-intel driver, such as CS4206.
@@ -157,6 +158,16 @@ config SND_HDA_CODEC_CIRRUS
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m
+config SND_HDA_CODEC_CS8409
+ tristate "Build Cirrus Logic HDA bridge support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Cirrus Logic HDA bridge support in
+ snd-hda-intel driver, such as CS8409.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m
+
config SND_HDA_CODEC_CONEXANT
tristate "Build Conexant HD-audio codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index b57432f00056..1b73e08dc563 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -20,6 +20,7 @@ snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
+snd-hda-codec-cs8409-objs := patch_cs8409.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-ca0132-objs := patch_ca0132.o
snd-hda-codec-conexant-objs := patch_conexant.o
@@ -37,6 +38,7 @@ obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o
obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o
obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o
obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o
+obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o
obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o
obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 8629e84fef23..678fbcaf2a3b 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -9,7 +9,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
-#include <linux/mutex.h>
#include <linux/pci.h>
#include <sound/tlv.h>
#include <sound/hda_codec.h>
@@ -21,9 +20,6 @@
/*
*/
-#define CS42L42_HP_CH (2U)
-#define CS42L42_HS_MIC_CH (1U)
-
struct cs_spec {
struct hda_gen_spec gen;
@@ -42,18 +38,6 @@ struct cs_spec {
/* for MBP SPDIF control */
int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
-
- unsigned int cs42l42_hp_jack_in:1;
- unsigned int cs42l42_mic_jack_in:1;
- unsigned int cs42l42_volume_init:1;
- char cs42l42_hp_volume[CS42L42_HP_CH];
- char cs42l42_hs_mic_volume[CS42L42_HS_MIC_CH];
-
- struct mutex cs8409_i2c_mux;
-
- /* verb exec op override */
- int (*exec_verb)(struct hdac_device *dev, unsigned int cmd,
- unsigned int flags, unsigned int *res);
};
/* available models with CS420x */
@@ -1239,1063 +1223,6 @@ static int patch_cs4213(struct hda_codec *codec)
return err;
}
-/* Cirrus Logic CS8409 HDA bridge with
- * companion codec CS42L42
- */
-#define CS8409_VENDOR_NID 0x47
-
-#define CS8409_CS42L42_HP_PIN_NID 0x24
-#define CS8409_CS42L42_SPK_PIN_NID 0x2c
-#define CS8409_CS42L42_AMIC_PIN_NID 0x34
-#define CS8409_CS42L42_DMIC_PIN_NID 0x44
-#define CS8409_CS42L42_DMIC_ADC_PIN_NID 0x22
-
-#define CS42L42_HSDET_AUTO_DONE 0x02
-#define CS42L42_HSTYPE_MASK 0x03
-
-#define CS42L42_JACK_INSERTED 0x0C
-#define CS42L42_JACK_REMOVED 0x00
-
-#define GPIO3_INT (1 << 3)
-#define GPIO4_INT (1 << 4)
-#define GPIO5_INT (1 << 5)
-
-#define CS42L42_I2C_ADDR (0x48 << 1)
-
-#define CIR_I2C_ADDR 0x0059
-#define CIR_I2C_DATA 0x005A
-#define CIR_I2C_CTRL 0x005B
-#define CIR_I2C_STATUS 0x005C
-#define CIR_I2C_QWRITE 0x005D
-#define CIR_I2C_QREAD 0x005E
-
-#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63)
-#define CS8409_CS42L42_HP_VOL_REAL_MAX (0)
-#define CS8409_CS42L42_AMIC_VOL_REAL_MIN (-97)
-#define CS8409_CS42L42_AMIC_VOL_REAL_MAX (12)
-#define CS8409_CS42L42_REG_HS_VOLUME_CHA (0x2301)
-#define CS8409_CS42L42_REG_HS_VOLUME_CHB (0x2303)
-#define CS8409_CS42L42_REG_AMIC_VOLUME (0x1D03)
-
-struct cs8409_i2c_param {
- unsigned int addr;
- unsigned int reg;
-};
-
-struct cs8409_cir_param {
- unsigned int nid;
- unsigned int cir;
- unsigned int coeff;
-};
-
-enum {
- CS8409_BULLSEYE,
- CS8409_WARLOCK,
- CS8409_CYBORG,
- CS8409_FIXUPS,
-};
-
-static void cs8409_cs42l42_fixups(struct hda_codec *codec,
- const struct hda_fixup *fix, int action);
-static int cs8409_cs42l42_exec_verb(struct hdac_device *dev,
- unsigned int cmd, unsigned int flags, unsigned int *res);
-
-/* Dell Inspiron models with cs8409/cs42l42 */
-static const struct hda_model_fixup cs8409_models[] = {
- { .id = CS8409_BULLSEYE, .name = "bullseye" },
- { .id = CS8409_WARLOCK, .name = "warlock" },
- { .id = CS8409_CYBORG, .name = "cyborg" },
- {}
-};
-
-/* Dell Inspiron platforms
- * with cs8409 bridge and cs42l42 codec
- */
-static const struct snd_pci_quirk cs8409_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
- {} /* terminator */
-};
-
-static const struct hda_verb cs8409_cs42l42_init_verbs[] = {
- { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
- { 0x47, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
- { 0x47, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
- { 0x47, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
- { 0x47, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
- { 0x47, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
- {} /* terminator */
-};
-
-static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
- { 0x24, 0x042120f0 }, /* ASP-1-TX */
- { 0x34, 0x04a12050 }, /* ASP-1-RX */
- { 0x2c, 0x901000f0 }, /* ASP-2-TX */
- { 0x44, 0x90a00090 }, /* DMIC-1 */
- {} /* terminator */
-};
-
-static const struct hda_fixup cs8409_fixups[] = {
- [CS8409_BULLSEYE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_WARLOCK] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_CYBORG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_FIXUPS] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = cs8409_cs42l42_fixups,
- },
-};
-
-/* Vendor specific HW configuration for CS42L42 */
-static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
- { 0x1D02, 0x06 },
- { 0x1D03, 0x00 },
- { 0x1107, 0x01 },
- { 0x1009, 0x02 },
- { 0x1007, 0x03 },
- { 0x1201, 0x00 },
- { 0x1208, 0x13 },
- { 0x1205, 0xFF },
- { 0x1206, 0x00 },
- { 0x1207, 0x20 },
- { 0x1202, 0x0D },
- { 0x2A02, 0x02 },
- { 0x2A03, 0x00 },
- { 0x2A04, 0x00 },
- { 0x2A05, 0x02 },
- { 0x2A06, 0x00 },
- { 0x2A07, 0x20 },
- { 0x2A08, 0x02 },
- { 0x2A09, 0x00 },
- { 0x2A0A, 0x80 },
- { 0x2A0B, 0x02 },
- { 0x2A0C, 0x00 },
- { 0x2A0D, 0xA0 },
- { 0x2A01, 0x0C },
- { 0x2902, 0x01 },
- { 0x2903, 0x02 },
- { 0x2904, 0x00 },
- { 0x2905, 0x00 },
- { 0x2901, 0x01 },
- { 0x1101, 0x0A },
- { 0x1102, 0x84 },
- { 0x2301, 0x00 },
- { 0x2303, 0x00 },
- { 0x2302, 0x3f },
- { 0x2001, 0x03 },
- { 0x1B75, 0xB6 },
- { 0x1B73, 0xC2 },
- { 0x1129, 0x01 },
- { 0x1121, 0xF3 },
- { 0x1103, 0x20 },
- { 0x1105, 0x00 },
- { 0x1112, 0xC0 },
- { 0x1113, 0x80 },
- { 0x1C03, 0xC0 },
- { 0x1105, 0x00 },
- { 0x1112, 0xC0 },
- { 0x1101, 0x02 },
- {} /* Terminator */
-};
-
-/* Vendor specific hw configuration for CS8409 */
-static const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
- { 0x47, 0x00, 0xb008 }, /* +PLL1/2_EN, +I2C_EN */
- { 0x47, 0x01, 0x0002 }, /* ASP1/2_EN=0, ASP1_STP=1 */
- { 0x47, 0x02, 0x0a80 }, /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
- { 0x47, 0x19, 0x0800 }, /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
- { 0x47, 0x1a, 0x0820 }, /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
- { 0x47, 0x29, 0x0800 }, /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
- { 0x47, 0x2a, 0x2800 }, /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
- { 0x47, 0x39, 0x0800 }, /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
- { 0x47, 0x3a, 0x0800 }, /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
- { 0x47, 0x03, 0x8000 }, /* ASP1: LCHI = 00h */
- { 0x47, 0x04, 0x28ff }, /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
- { 0x47, 0x05, 0x0062 }, /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
- { 0x47, 0x06, 0x801f }, /* ASP2: LCHI=1Fh */
- { 0x47, 0x07, 0x283f }, /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
- { 0x47, 0x08, 0x805c }, /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
- { 0x47, 0x09, 0x0023 }, /* DMIC1_MO=10b, DMIC1/2_SR=1 */
- { 0x47, 0x0a, 0x0000 }, /* ASP1/2_BEEP=0 */
- { 0x47, 0x01, 0x0062 }, /* ASP1/2_EN=1, ASP1_STP=1 */
- { 0x47, 0x00, 0x9008 }, /* -PLL2_EN */
- { 0x47, 0x68, 0x0000 }, /* TX2.A: pre-scale att.=0 dB */
- { 0x47, 0x82, 0xfc03 }, /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
- { 0x47, 0xc0, 0x9999 }, /* test mode on */
- { 0x47, 0xc5, 0x0000 }, /* GPIO hysteresis = 30 us */
- { 0x47, 0xc0, 0x0000 }, /* test mode off */
- {} /* Terminator */
-};
-
-static const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
- { 0x47, 0x65, 0x4000 }, /* EQ_SEL=1, EQ1/2_EN=0 */
- { 0x47, 0x64, 0x4000 }, /* +EQ_ACC */
- { 0x47, 0x65, 0x4010 }, /* +EQ2_EN */
- { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
- { 0x47, 0x64, 0xc0c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
- { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
- { 0x47, 0x64, 0xc1c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
- { 0x47, 0x63, 0xf370 }, /* EQ_DATA_HI=0xf370 */
- { 0x47, 0x64, 0xc271 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
- { 0x47, 0x63, 0x1ef8 }, /* EQ_DATA_HI=0x1ef8 */
- { 0x47, 0x64, 0xc348 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
- { 0x47, 0x63, 0xc110 }, /* EQ_DATA_HI=0xc110 */
- { 0x47, 0x64, 0xc45a }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
- { 0x47, 0x63, 0x1f29 }, /* EQ_DATA_HI=0x1f29 */
- { 0x47, 0x64, 0xc574 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
- { 0x47, 0x63, 0x1d7a }, /* EQ_DATA_HI=0x1d7a */
- { 0x47, 0x64, 0xc653 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
- { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
- { 0x47, 0x64, 0xc714 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
- { 0x47, 0x63, 0x1ca3 }, /* EQ_DATA_HI=0x1ca3 */
- { 0x47, 0x64, 0xc8c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
- { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
- { 0x47, 0x64, 0xc914 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
- { 0x47, 0x64, 0x0000 }, /* -EQ_ACC, -EQ_WRT */
- {} /* Terminator */
-};
-
-/**
- * cs8409_enable_i2c_clock - Enable I2C clocks
- * @codec: the codec instance
- * @enable: Enable or disable I2C clocks
- *
- * Enable or Disable I2C clocks.
- */
-static void cs8409_enable_i2c_clock(struct hda_codec *codec, unsigned int enable)
-{
- unsigned int retval;
- unsigned int newval;
-
- retval = cs_vendor_coef_get(codec, 0x0);
- newval = (enable) ? (retval | 0x8) : (retval & 0xfffffff7);
- cs_vendor_coef_set(codec, 0x0, newval);
-}
-
-/**
- * cs8409_i2c_wait_complete - Wait for I2C transaction
- * @codec: the codec instance
- *
- * Wait for I2C transaction to complete.
- * Return -1 if transaction wait times out.
- */
-static int cs8409_i2c_wait_complete(struct hda_codec *codec)
-{
- int repeat = 5;
- unsigned int retval;
-
- do {
- retval = cs_vendor_coef_get(codec, CIR_I2C_STATUS);
- if ((retval & 0x18) != 0x18) {
- usleep_range(2000, 4000);
- --repeat;
- } else
- return 0;
-
- } while (repeat);
-
- return -1;
-}
-
-/**
- * cs8409_i2c_read - CS8409 I2C Read.
- * @codec: the codec instance
- * @i2c_address: I2C Address
- * @i2c_reg: Register to read
- * @paged: Is a paged transaction
- *
- * CS8409 I2C Read.
- * Returns negative on error, otherwise returns read value in bits 0-7.
- */
-static int cs8409_i2c_read(struct hda_codec *codec,
- unsigned int i2c_address,
- unsigned int i2c_reg,
- unsigned int paged)
-{
- unsigned int i2c_reg_data;
- unsigned int read_data;
-
- cs8409_enable_i2c_clock(codec, 1);
- cs_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address);
-
- if (paged) {
- cs_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec,
- "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
- }
-
- i2c_reg_data = (i2c_reg << 8) & 0x0ffff;
- cs_vendor_coef_set(codec, CIR_I2C_QREAD, i2c_reg_data);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
-
- /* Register in bits 15-8 and the data in 7-0 */
- read_data = cs_vendor_coef_get(codec, CIR_I2C_QREAD);
-
- cs8409_enable_i2c_clock(codec, 0);
-
- return read_data & 0x0ff;
-}
-
-/**
- * cs8409_i2c_write - CS8409 I2C Write.
- * @codec: the codec instance
- * @i2c_address: I2C Address
- * @i2c_reg: Register to write to
- * @i2c_data: Data to write
- * @paged: Is a paged transaction
- *
- * CS8409 I2C Write.
- * Returns negative on error, otherwise returns 0.
- */
-static int cs8409_i2c_write(struct hda_codec *codec,
- unsigned int i2c_address, unsigned int i2c_reg,
- unsigned int i2c_data,
- unsigned int paged)
-{
- unsigned int i2c_reg_data;
-
- cs8409_enable_i2c_clock(codec, 1);
- cs_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address);
-
- if (paged) {
- cs_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec,
- "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
- }
-
- i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | (i2c_data & 0x0ff);
- cs_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg_data);
-
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
-
- cs8409_enable_i2c_clock(codec, 0);
-
- return 0;
-}
-
-static int cs8409_cs42l42_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- u16 nid = get_amp_nid(kcontrol);
- u8 chs = get_amp_channels(kcontrol);
-
- codec_dbg(codec, "%s() nid: %d\n", __func__, nid);
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = CS8409_CS42L42_HP_VOL_REAL_MIN;
- uinfo->value.integer.max = CS8409_CS42L42_HP_VOL_REAL_MAX;
- break;
- case CS8409_CS42L42_AMIC_PIN_NID:
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
- uinfo->value.integer.max = CS8409_CS42L42_AMIC_VOL_REAL_MAX;
- break;
- default:
- break;
- }
- return 0;
-}
-
-static void cs8409_cs42l42_update_volume(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int data;
-
- mutex_lock(&spec->cs8409_i2c_mux);
- data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHA, 1);
- if (data >= 0)
- spec->cs42l42_hp_volume[0] = -data;
- else
- spec->cs42l42_hp_volume[0] = CS8409_CS42L42_HP_VOL_REAL_MIN;
- data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHB, 1);
- if (data >= 0)
- spec->cs42l42_hp_volume[1] = -data;
- else
- spec->cs42l42_hp_volume[1] = CS8409_CS42L42_HP_VOL_REAL_MIN;
- data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_AMIC_VOLUME, 1);
- if (data >= 0)
- spec->cs42l42_hs_mic_volume[0] = -data;
- else
- spec->cs42l42_hs_mic_volume[0] = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
- mutex_unlock(&spec->cs8409_i2c_mux);
- spec->cs42l42_volume_init = 1;
-}
-
-static int cs8409_cs42l42_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int chs = get_amp_channels(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- if (!spec->cs42l42_volume_init) {
- snd_hda_power_up(codec);
- cs8409_cs42l42_update_volume(codec);
- snd_hda_power_down(codec);
- }
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
- if (chs & BIT(0))
- *valp++ = spec->cs42l42_hp_volume[0];
- if (chs & BIT(1))
- *valp++ = spec->cs42l42_hp_volume[1];
- break;
- case CS8409_CS42L42_AMIC_PIN_NID:
- if (chs & BIT(0))
- *valp++ = spec->cs42l42_hs_mic_volume[0];
- break;
- default:
- break;
- }
- return 0;
-}
-
-static int cs8409_cs42l42_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int chs = get_amp_channels(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change = 0;
- char vol;
-
- snd_hda_power_up(codec);
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
- mutex_lock(&spec->cs8409_i2c_mux);
- if (chs & BIT(0)) {
- vol = -(*valp);
- change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHA, vol, 1);
- valp++;
- }
- if (chs & BIT(1)) {
- vol = -(*valp);
- change |= cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHB, vol, 1);
- }
- mutex_unlock(&spec->cs8409_i2c_mux);
- break;
- case CS8409_CS42L42_AMIC_PIN_NID:
- mutex_lock(&spec->cs8409_i2c_mux);
- if (chs & BIT(0)) {
- change = cs8409_i2c_write(
- codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_AMIC_VOLUME, (char)*valp, 1);
- valp++;
- }
- mutex_unlock(&spec->cs8409_i2c_mux);
- break;
- default:
- break;
- }
- cs8409_cs42l42_update_volume(codec);
- snd_hda_power_down(codec);
- return change;
-}
-
-static const DECLARE_TLV_DB_SCALE(
- cs8409_cs42l42_hp_db_scale,
- CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
-
-static const DECLARE_TLV_DB_SCALE(
- cs8409_cs42l42_amic_db_scale,
- CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
-
-static const struct snd_kcontrol_new cs8409_cs42l42_hp_volume_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .index = 0,
- .name = "Headphone Playback Volume",
- .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE
- | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .info = cs8409_cs42l42_volume_info,
- .get = cs8409_cs42l42_volume_get,
- .put = cs8409_cs42l42_volume_put,
- .tlv = { .p = cs8409_cs42l42_hp_db_scale },
- .private_value = HDA_COMPOSE_AMP_VAL(
- CS8409_CS42L42_HP_PIN_NID, 3, 0, HDA_OUTPUT)
- | HDA_AMP_VAL_MIN_MUTE
-};
-
-static const struct snd_kcontrol_new cs8409_cs42l42_amic_volume_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .index = 0,
- .name = "Mic Capture Volume",
- .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE
- | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .info = cs8409_cs42l42_volume_info,
- .get = cs8409_cs42l42_volume_get,
- .put = cs8409_cs42l42_volume_put,
- .tlv = { .p = cs8409_cs42l42_amic_db_scale },
- .private_value = HDA_COMPOSE_AMP_VAL(
- CS8409_CS42L42_AMIC_PIN_NID, 1, 0, HDA_INPUT)
- | HDA_AMP_VAL_MIN_MUTE
-};
-
-/* Assert/release RTS# line to CS42L42 */
-static void cs8409_cs42l42_reset(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
-
- /* Assert RTS# line */
- snd_hda_codec_write(codec,
- codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0);
- /* wait ~10ms */
- usleep_range(10000, 15000);
- /* Release RTS# line */
- snd_hda_codec_write(codec,
- codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, GPIO5_INT);
- /* wait ~10ms */
- usleep_range(10000, 15000);
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
- /* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F, 1);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-
-}
-
-/* Configure CS42L42 slave codec for jack autodetect */
-static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
- /* Set TIP_SENSE_EN for analog front-end of tip sense. */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1);
- /* Clear WAKE# */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0001, 1);
- /* Wait ~2.5ms */
- usleep_range(2500, 3000);
- /* Set mode WAKE# output follows the combination logic directly */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0020, 1);
- /* Clear interrupts status */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
- /* Enable interrupt */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0x03, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b79, 0x00, 1);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-}
-
-/* Enable and run CS42L42 slave codec jack auto detect */
-static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
- /* Clear interrupts */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77, 1);
-
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0x01, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80, 1);
- /* Wait ~110ms*/
- usleep_range(110000, 200000);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0, 1);
- /* Wait ~10ms */
- usleep_range(10000, 25000);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-
-}
-
-static void cs8409_cs42l42_reg_setup(struct hda_codec *codec)
-{
- const struct cs8409_i2c_param *seq = cs42l42_init_reg_seq;
- struct cs_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
- for (; seq->addr; seq++)
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg, 1);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-
-}
-
-/*
- * In the case of CS8409 we do not have unsolicited events from NID's 0x24
- * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will
- * generate interrupt via gpio 4 to notify jack events. We have to overwrite
- * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
- * and then notify status via generic snd_hda_jack_unsol_event() call.
- */
-static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct cs_spec *spec = codec->spec;
- int status_changed = 0;
- int reg_cdc_status;
- int reg_hs_status;
- int reg_ts_status;
- int type;
- struct hda_jack_tbl *jk;
-
- /* jack_unsol_event() will be called every time gpio line changing state.
- * In this case gpio4 line goes up as a result of reading interrupt status
- * registers in previous cs8409_jack_unsol_event() call.
- * We don't need to handle this event, ignoring...
- */
- if ((res & (1 << 4)))
- return;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
- /* Read jack detect status registers */
- reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
- reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1);
- reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
-
- /* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-
- /* If status values are < 0, read error has occurred. */
- if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
- return;
-
- /* HSDET_AUTO_DONE */
- if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
-
- type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
- /* CS42L42 reports optical jack as type 4
- * We don't handle optical jack
- */
- if (type != 4) {
- if (!spec->cs42l42_hp_jack_in) {
- status_changed = 1;
- spec->cs42l42_hp_jack_in = 1;
- }
- /* type = 3 has no mic */
- if ((!spec->cs42l42_mic_jack_in) && (type != 3)) {
- status_changed = 1;
- spec->cs42l42_mic_jack_in = 1;
- }
- } else {
- if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
- status_changed = 1;
- spec->cs42l42_hp_jack_in = 0;
- spec->cs42l42_mic_jack_in = 0;
- }
- }
-
- } else {
- /* TIP_SENSE INSERT/REMOVE */
- switch (reg_ts_status) {
- case CS42L42_JACK_INSERTED:
- cs8409_cs42l42_run_jack_detect(codec);
- break;
-
- case CS42L42_JACK_REMOVED:
- if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
- status_changed = 1;
- spec->cs42l42_hp_jack_in = 0;
- spec->cs42l42_mic_jack_in = 0;
- }
- break;
-
- default:
- /* jack in transition */
- status_changed = 0;
- break;
- }
- }
-
- if (status_changed) {
-
- snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
- spec->cs42l42_hp_jack_in ? 0 : PIN_OUT);
-
- /* Report jack*/
- jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
- if (jk) {
- snd_hda_jack_unsol_event(codec,
- (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG);
- }
- /* Report jack*/
- jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0);
- if (jk) {
- snd_hda_jack_unsol_event(codec,
- (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG);
- }
- }
-}
-
-#ifdef CONFIG_PM
-/* Manage PDREF, when transition to D3hot */
-static int cs8409_suspend(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
- /* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1);
- mutex_unlock(&spec->cs8409_i2c_mux);
- /* Assert CS42L42 RTS# line */
- snd_hda_codec_write(codec,
- codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0);
-
- snd_hda_shutup_pins(codec);
-
- return 0;
-}
-#endif
-
-/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
-static void cs8409_enable_ur(struct hda_codec *codec, int flag)
-{
- /* GPIO4 INT# and GPIO3 WAKE# */
- snd_hda_codec_write(codec, codec->core.afg,
- 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
- flag ? (GPIO3_INT | GPIO4_INT) : 0);
-
- snd_hda_codec_write(codec, codec->core.afg,
- 0, AC_VERB_SET_UNSOLICITED_ENABLE,
- flag ? AC_UNSOL_ENABLED : 0);
-
-}
-
-/* Vendor specific HW configuration
- * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
- */
-static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
-{
- const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg;
- const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn;
- struct cs_spec *spec = codec->spec;
-
- if (spec->gpio_mask) {
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
- spec->gpio_mask);
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
- spec->gpio_dir);
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_data);
- }
-
- for (; seq->nid; seq++)
- cs_vendor_coef_set(codec, seq->cir, seq->coeff);
-
- if (codec->fixup_id == CS8409_BULLSEYE)
- for (; seq_bullseye->nid; seq_bullseye++)
- cs_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
-
- /* Disable Unsolicited Response during boot */
- cs8409_enable_ur(codec, 0);
-
- /* Reset CS42L42 */
- cs8409_cs42l42_reset(codec);
-
- /* Initialise CS42L42 companion codec */
- cs8409_cs42l42_reg_setup(codec);
-
- if (codec->fixup_id == CS8409_WARLOCK ||
- codec->fixup_id == CS8409_CYBORG) {
- /* FULL_SCALE_VOL = 0 for Warlock / Cyborg */
- mutex_lock(&spec->cs8409_i2c_mux);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01, 1);
- mutex_unlock(&spec->cs8409_i2c_mux);
- /* DMIC1_MO=00b, DMIC1/2_SR=1 */
- cs_vendor_coef_set(codec, 0x09, 0x0003);
- }
-
- /* Restore Volumes after Resume */
- if (spec->cs42l42_volume_init) {
- mutex_lock(&spec->cs8409_i2c_mux);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHA,
- -spec->cs42l42_hp_volume[0],
- 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHB,
- -spec->cs42l42_hp_volume[1],
- 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_AMIC_VOLUME,
- spec->cs42l42_hs_mic_volume[0],
- 1);
- mutex_unlock(&spec->cs8409_i2c_mux);
- }
-
- cs8409_cs42l42_update_volume(codec);
-
- cs8409_cs42l42_enable_jack_detect(codec);
-
- /* Enable Unsolicited Response */
- cs8409_enable_ur(codec, 1);
-}
-
-static int cs8409_cs42l42_init(struct hda_codec *codec)
-{
- int ret = snd_hda_gen_init(codec);
-
- if (!ret)
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
-
- return ret;
-}
-
-static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
- .build_controls = cs_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = cs8409_cs42l42_init,
- .free = cs_free,
- .unsol_event = cs8409_jack_unsol_event,
-#ifdef CONFIG_PM
- .suspend = cs8409_suspend,
-#endif
-};
-
-static void cs8409_cs42l42_fixups(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct cs_spec *spec = codec->spec;
- int caps;
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs);
- /* verb exec op override */
- spec->exec_verb = codec->core.exec_verb;
- codec->core.exec_verb = cs8409_cs42l42_exec_verb;
-
- mutex_init(&spec->cs8409_i2c_mux);
-
- codec->patch_ops = cs8409_cs42l42_patch_ops;
-
- spec->gen.suppress_auto_mute = 1;
- spec->gen.no_primary_hp = 1;
- spec->gen.suppress_vmaster = 1;
-
- /* GPIO 5 out, 3,4 in */
- spec->gpio_dir = GPIO5_INT;
- spec->gpio_data = 0;
- spec->gpio_mask = 0x03f;
-
- spec->cs42l42_hp_jack_in = 0;
- spec->cs42l42_mic_jack_in = 0;
-
- /* Basic initial sequence for specific hw configuration */
- snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
-
- /* CS8409 is simple HDA bridge and intended to be used with a remote
- * companion codec. Most of input/output PIN(s) have only basic
- * capabilities. NID(s) 0x24 and 0x34 have only OUTC and INC
- * capabilities and no presence detect capable (PDC) and call to
- * snd_hda_gen_build_controls() will mark them as non detectable
- * phantom jacks. However, in this configuration companion codec
- * CS42L42 is connected to these pins and it has jack detect
- * capabilities. We have to override pin capabilities,
- * otherwise they will not be created as input devices.
- */
- caps = snd_hdac_read_parm(&codec->core, CS8409_CS42L42_HP_PIN_NID,
- AC_PAR_PIN_CAP);
- if (caps >= 0)
- snd_hdac_override_parm(&codec->core,
- CS8409_CS42L42_HP_PIN_NID, AC_PAR_PIN_CAP,
- (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
-
- caps = snd_hdac_read_parm(&codec->core, CS8409_CS42L42_AMIC_PIN_NID,
- AC_PAR_PIN_CAP);
- if (caps >= 0)
- snd_hdac_override_parm(&codec->core,
- CS8409_CS42L42_AMIC_PIN_NID, AC_PAR_PIN_CAP,
- (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
-
- snd_hda_override_wcaps(codec, CS8409_CS42L42_HP_PIN_NID,
- (get_wcaps(codec, CS8409_CS42L42_HP_PIN_NID) | AC_WCAP_UNSOL_CAP));
-
- snd_hda_override_wcaps(codec, CS8409_CS42L42_AMIC_PIN_NID,
- (get_wcaps(codec, CS8409_CS42L42_AMIC_PIN_NID) | AC_WCAP_UNSOL_CAP));
- break;
- case HDA_FIXUP_ACT_PROBE:
-
- /* Set initial DMIC volume to -26 dB */
- snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
- HDA_INPUT, 0, 0xff, 0x19);
- snd_hda_gen_add_kctl(&spec->gen,
- NULL, &cs8409_cs42l42_hp_volume_mixer);
- snd_hda_gen_add_kctl(&spec->gen,
- NULL, &cs8409_cs42l42_amic_volume_mixer);
- cs8409_cs42l42_hw_init(codec);
- snd_hda_codec_set_name(codec, "CS8409/CS42L42");
- break;
- case HDA_FIXUP_ACT_INIT:
- cs8409_cs42l42_hw_init(codec);
- fallthrough;
- case HDA_FIXUP_ACT_BUILD:
- /* Run jack auto detect first time on boot
- * after controls have been added, to check if jack has
- * been already plugged in.
- * Run immediately after init.
- */
- cs8409_cs42l42_run_jack_detect(codec);
- usleep_range(100000, 150000);
- break;
- default:
- break;
- }
-}
-
-static int cs8409_cs42l42_exec_verb(struct hdac_device *dev,
- unsigned int cmd, unsigned int flags, unsigned int *res)
-{
- struct hda_codec *codec = container_of(dev, struct hda_codec, core);
- struct cs_spec *spec = codec->spec;
-
- unsigned int nid = ((cmd >> 20) & 0x07f);
- unsigned int verb = ((cmd >> 8) & 0x0fff);
-
- /* CS8409 pins have no AC_PINSENSE_PRESENCE
- * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34
- * and return correct pin sense values for read_pin_sense() call from
- * hda_jack based on CS42L42 jack detect status.
- */
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
- if (verb == AC_VERB_GET_PIN_SENSE) {
- *res = (spec->cs42l42_hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
- return 0;
- }
- break;
-
- case CS8409_CS42L42_AMIC_PIN_NID:
- if (verb == AC_VERB_GET_PIN_SENSE) {
- *res = (spec->cs42l42_mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
- return 0;
- }
- break;
-
- default:
- break;
- }
-
- return spec->exec_verb(dev, cmd, flags, res);
-}
-
-static int patch_cs8409(struct hda_codec *codec)
-{
- int err;
-
- if (!cs_alloc_spec(codec, CS8409_VENDOR_NID))
- return -ENOMEM;
-
- snd_hda_pick_fixup(codec,
- cs8409_models, cs8409_fixup_tbl, cs8409_fixups);
-
- codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n",
- codec->fixup_id,
- codec->bus->pci->subsystem_vendor,
- codec->bus->pci->subsystem_device);
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- err = cs_parse_auto_config(codec);
- if (err < 0) {
- cs_free(codec);
- return err;
- }
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
- return 0;
-}
-
/*
* patch entries
*/
@@ -2305,7 +1232,6 @@ static const struct hda_device_id snd_hda_id_cirrus[] = {
HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
- HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409),
{} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
new file mode 100644
index 000000000000..9b16f1b5b828
--- /dev/null
+++ b/sound/pci/hda/patch_cs8409.c
@@ -0,0 +1,1060 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+#include "patch_cs8409.h"
+
+static int cs8409_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ /* keep the ADCs powered up when it's dynamically switchable */
+ if (spec->gen.dyn_adc_switch) {
+ unsigned int done = 0;
+
+ for (i = 0; i < spec->gen.input_mux.num_items; i++) {
+ int idx = spec->gen.dyn_adc_idx[i];
+
+ if (done & (1 << idx))
+ continue;
+ snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]);
+ done |= 1 << idx;
+ }
+ }
+
+ return 0;
+}
+
+/* Dell Inspiron models with cs8409/cs42l42 */
+static const struct hda_model_fixup cs8409_models[] = {
+ { .id = CS8409_BULLSEYE, .name = "bullseye" },
+ { .id = CS8409_WARLOCK, .name = "warlock" },
+ { .id = CS8409_CYBORG, .name = "cyborg" },
+ {}
+};
+
+/* Dell Inspiron platforms
+ * with cs8409 bridge and cs42l42 codec
+ */
+static const struct snd_pci_quirk cs8409_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+ {} /* terminator */
+};
+
+static const struct hda_verb cs8409_cs42l42_init_verbs[] = {
+ { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
+ { 0x47, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { 0x47, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { 0x47, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { 0x47, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { 0x47, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
+ { 0x24, 0x042120f0 }, /* ASP-1-TX */
+ { 0x34, 0x04a12050 }, /* ASP-1-RX */
+ { 0x2c, 0x901000f0 }, /* ASP-2-TX */
+ { 0x44, 0x90a00090 }, /* DMIC-1 */
+ {} /* terminator */
+};
+
+static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ codec->power_save_node = 1;
+ snd_hda_gen_spec_init(&spec->gen);
+
+ return spec;
+}
+
+/* Vendor specific HW configuration for CS42L42 */
+static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
+ { 0x1010, 0xB0 },
+ { 0x1D01, 0x00 },
+ { 0x1D02, 0x06 },
+ { 0x1D03, 0x00 },
+ { 0x1107, 0x01 },
+ { 0x1009, 0x02 },
+ { 0x1007, 0x03 },
+ { 0x1201, 0x00 },
+ { 0x1208, 0x13 },
+ { 0x1205, 0xFF },
+ { 0x1206, 0x00 },
+ { 0x1207, 0x20 },
+ { 0x1202, 0x0D },
+ { 0x2A02, 0x02 },
+ { 0x2A03, 0x00 },
+ { 0x2A04, 0x00 },
+ { 0x2A05, 0x02 },
+ { 0x2A06, 0x00 },
+ { 0x2A07, 0x20 },
+ { 0x2A08, 0x02 },
+ { 0x2A09, 0x00 },
+ { 0x2A0A, 0x80 },
+ { 0x2A0B, 0x02 },
+ { 0x2A0C, 0x00 },
+ { 0x2A0D, 0xA0 },
+ { 0x2A01, 0x0C },
+ { 0x2902, 0x01 },
+ { 0x2903, 0x02 },
+ { 0x2904, 0x00 },
+ { 0x2905, 0x00 },
+ { 0x2901, 0x01 },
+ { 0x1101, 0x0A },
+ { 0x1102, 0x84 },
+ { 0x2301, 0x00 },
+ { 0x2303, 0x00 },
+ { 0x2302, 0x3f },
+ { 0x2001, 0x03 },
+ { 0x1B75, 0xB6 },
+ { 0x1B73, 0xC2 },
+ { 0x1129, 0x01 },
+ { 0x1121, 0xF3 },
+ { 0x1103, 0x20 },
+ { 0x1105, 0x00 },
+ { 0x1112, 0xC0 },
+ { 0x1113, 0x80 },
+ { 0x1C03, 0xC0 },
+ { 0x1105, 0x00 },
+ { 0x1112, 0xC0 },
+ { 0x1101, 0x02 },
+ {} /* Terminator */
+};
+
+/* Vendor specific hw configuration for CS8409 */
+static const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
+ { 0x47, 0x00, 0xb008 }, /* +PLL1/2_EN, +I2C_EN */
+ { 0x47, 0x01, 0x0002 }, /* ASP1/2_EN=0, ASP1_STP=1 */
+ { 0x47, 0x02, 0x0a80 }, /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { 0x47, 0x19, 0x0800 }, /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { 0x47, 0x1a, 0x0820 }, /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { 0x47, 0x29, 0x0800 }, /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { 0x47, 0x2a, 0x2800 }, /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { 0x47, 0x39, 0x0800 }, /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { 0x47, 0x3a, 0x0800 }, /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { 0x47, 0x03, 0x8000 }, /* ASP1: LCHI = 00h */
+ { 0x47, 0x04, 0x28ff }, /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { 0x47, 0x05, 0x0062 }, /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { 0x47, 0x06, 0x801f }, /* ASP2: LCHI=1Fh */
+ { 0x47, 0x07, 0x283f }, /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { 0x47, 0x08, 0x805c }, /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { 0x47, 0x09, 0x0023 }, /* DMIC1_MO=10b, DMIC1/2_SR=1 */
+ { 0x47, 0x0a, 0x0000 }, /* ASP1/2_BEEP=0 */
+ { 0x47, 0x01, 0x0062 }, /* ASP1/2_EN=1, ASP1_STP=1 */
+ { 0x47, 0x00, 0x9008 }, /* -PLL2_EN */
+ { 0x47, 0x68, 0x0000 }, /* TX2.A: pre-scale att.=0 dB */
+ { 0x47, 0x82, 0xfc03 }, /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { 0x47, 0xc0, 0x9999 }, /* test mode on */
+ { 0x47, 0xc5, 0x0000 }, /* GPIO hysteresis = 30 us */
+ { 0x47, 0xc0, 0x0000 }, /* test mode off */
+ {} /* Terminator */
+};
+
+static const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
+ { 0x47, 0x65, 0x4000 }, /* EQ_SEL=1, EQ1/2_EN=0 */
+ { 0x47, 0x64, 0x4000 }, /* +EQ_ACC */
+ { 0x47, 0x65, 0x4010 }, /* +EQ2_EN */
+ { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
+ { 0x47, 0x64, 0xc0c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
+ { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
+ { 0x47, 0x64, 0xc1c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
+ { 0x47, 0x63, 0xf370 }, /* EQ_DATA_HI=0xf370 */
+ { 0x47, 0x64, 0xc271 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
+ { 0x47, 0x63, 0x1ef8 }, /* EQ_DATA_HI=0x1ef8 */
+ { 0x47, 0x64, 0xc348 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
+ { 0x47, 0x63, 0xc110 }, /* EQ_DATA_HI=0xc110 */
+ { 0x47, 0x64, 0xc45a }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
+ { 0x47, 0x63, 0x1f29 }, /* EQ_DATA_HI=0x1f29 */
+ { 0x47, 0x64, 0xc574 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
+ { 0x47, 0x63, 0x1d7a }, /* EQ_DATA_HI=0x1d7a */
+ { 0x47, 0x64, 0xc653 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
+ { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
+ { 0x47, 0x64, 0xc714 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
+ { 0x47, 0x63, 0x1ca3 }, /* EQ_DATA_HI=0x1ca3 */
+ { 0x47, 0x64, 0xc8c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
+ { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
+ { 0x47, 0x64, 0xc914 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
+ { 0x47, 0x64, 0x0000 }, /* -EQ_ACC, -EQ_WRT */
+ {} /* Terminator */
+};
+
+static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_COEF, coef);
+}
+
+/**
+ * cs8409_enable_i2c_clock - Enable I2C clocks
+ * @codec: the codec instance
+ * @enable: Enable or disable I2C clocks
+ *
+ * Enable or Disable I2C clocks.
+ */
+static void cs8409_enable_i2c_clock(struct hda_codec *codec, unsigned int enable)
+{
+ unsigned int retval;
+ unsigned int newval;
+
+ retval = cs8409_vendor_coef_get(codec, 0x0);
+ newval = (enable) ? (retval | 0x8) : (retval & 0xfffffff7);
+ cs8409_vendor_coef_set(codec, 0x0, newval);
+}
+
+/**
+ * cs8409_i2c_wait_complete - Wait for I2C transaction
+ * @codec: the codec instance
+ *
+ * Wait for I2C transaction to complete.
+ * Return -1 if transaction wait times out.
+ */
+static int cs8409_i2c_wait_complete(struct hda_codec *codec)
+{
+ int repeat = 5;
+ unsigned int retval;
+
+ do {
+ retval = cs8409_vendor_coef_get(codec, CIR_I2C_STATUS);
+ if ((retval & 0x18) != 0x18) {
+ usleep_range(2000, 4000);
+ --repeat;
+ } else
+ return 0;
+
+ } while (repeat);
+
+ return -1;
+}
+
+/**
+ * cs8409_i2c_read - CS8409 I2C Read.
+ * @codec: the codec instance
+ * @i2c_address: I2C Address
+ * @i2c_reg: Register to read
+ * @paged: Is a paged transaction
+ *
+ * CS8409 I2C Read.
+ * Returns negative on error, otherwise returns read value in bits 0-7.
+ */
+static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
+ unsigned int paged)
+{
+ unsigned int i2c_reg_data;
+ unsigned int read_data;
+
+ cs8409_enable_i2c_clock(codec, 1);
+ cs8409_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address);
+
+ if (paged) {
+ cs8409_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8);
+ if (cs8409_i2c_wait_complete(codec) < 0) {
+ codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
+ __func__, i2c_address, i2c_reg);
+ return -EIO;
+ }
+ }
+
+ i2c_reg_data = (i2c_reg << 8) & 0x0ffff;
+ cs8409_vendor_coef_set(codec, CIR_I2C_QREAD, i2c_reg_data);
+ if (cs8409_i2c_wait_complete(codec) < 0) {
+ codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
+ __func__, i2c_address, i2c_reg);
+ return -EIO;
+ }
+
+ /* Register in bits 15-8 and the data in 7-0 */
+ read_data = cs8409_vendor_coef_get(codec, CIR_I2C_QREAD);
+
+ cs8409_enable_i2c_clock(codec, 0);
+
+ return read_data & 0x0ff;
+}
+
+/**
+ * cs8409_i2c_write - CS8409 I2C Write.
+ * @codec: the codec instance
+ * @i2c_address: I2C Address
+ * @i2c_reg: Register to write to
+ * @i2c_data: Data to write
+ * @paged: Is a paged transaction
+ *
+ * CS8409 I2C Write.
+ * Returns negative on error, otherwise returns 0.
+ */
+static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
+ unsigned int i2c_data, unsigned int paged)
+{
+ unsigned int i2c_reg_data;
+
+ cs8409_enable_i2c_clock(codec, 1);
+ cs8409_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address);
+
+ if (paged) {
+ cs8409_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8);
+ if (cs8409_i2c_wait_complete(codec) < 0) {
+ codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
+ __func__, i2c_address, i2c_reg);
+ return -EIO;
+ }
+ }
+
+ i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | (i2c_data & 0x0ff);
+ cs8409_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0) {
+ codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
+ __func__, i2c_address, i2c_reg);
+ return -EIO;
+ }
+
+ cs8409_enable_i2c_clock(codec, 0);
+
+ return 0;
+}
+
+static int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
+{
+ u16 nid = get_amp_nid(kctrl);
+ u8 chs = get_amp_channels(kctrl);
+
+ switch (nid) {
+ case CS8409_CS42L42_HP_PIN_NID:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = CS8409_CS42L42_HP_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS8409_CS42L42_HP_VOL_REAL_MAX;
+ break;
+ case CS8409_CS42L42_AMIC_PIN_NID:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS8409_CS42L42_AMIC_VOL_REAL_MAX;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void cs8409_cs42l42_update_volume(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int data;
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+ data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHA, 1);
+ if (data >= 0)
+ spec->cs42l42_hp_volume[0] = -data;
+ else
+ spec->cs42l42_hp_volume[0] = CS8409_CS42L42_HP_VOL_REAL_MIN;
+ data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHB, 1);
+ if (data >= 0)
+ spec->cs42l42_hp_volume[1] = -data;
+ else
+ spec->cs42l42_hp_volume[1] = CS8409_CS42L42_HP_VOL_REAL_MIN;
+ data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOLUME, 1);
+ if (data >= 0)
+ spec->cs42l42_hs_mic_volume[0] = -data;
+ else
+ spec->cs42l42_hs_mic_volume[0] = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
+ mutex_unlock(&spec->cs8409_i2c_mux);
+ spec->cs42l42_volume_init = 1;
+}
+
+static int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kctrl);
+ struct cs8409_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kctrl);
+ int chs = get_amp_channels(kctrl);
+ long *valp = uctrl->value.integer.value;
+
+ if (!spec->cs42l42_volume_init) {
+ snd_hda_power_up(codec);
+ cs8409_cs42l42_update_volume(codec);
+ snd_hda_power_down(codec);
+ }
+ switch (nid) {
+ case CS8409_CS42L42_HP_PIN_NID:
+ if (chs & BIT(0))
+ *valp++ = spec->cs42l42_hp_volume[0];
+ if (chs & BIT(1))
+ *valp++ = spec->cs42l42_hp_volume[1];
+ break;
+ case CS8409_CS42L42_AMIC_PIN_NID:
+ if (chs & BIT(0))
+ *valp++ = spec->cs42l42_hs_mic_volume[0];
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kctrl);
+ struct cs8409_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kctrl);
+ int chs = get_amp_channels(kctrl);
+ long *valp = uctrl->value.integer.value;
+ int change = 0;
+ char vol;
+
+ snd_hda_power_up(codec);
+ switch (nid) {
+ case CS8409_CS42L42_HP_PIN_NID:
+ mutex_lock(&spec->cs8409_i2c_mux);
+ if (chs & BIT(0)) {
+ vol = -(*valp);
+ change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
+ CS8409_CS42L42_REG_HS_VOLUME_CHA, vol, 1);
+ valp++;
+ }
+ if (chs & BIT(1)) {
+ vol = -(*valp);
+ change |= cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
+ CS8409_CS42L42_REG_HS_VOLUME_CHB, vol, 1);
+ }
+ mutex_unlock(&spec->cs8409_i2c_mux);
+ break;
+ case CS8409_CS42L42_AMIC_PIN_NID:
+ mutex_lock(&spec->cs8409_i2c_mux);
+ if (chs & BIT(0)) {
+ change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
+ CS8409_CS42L42_REG_AMIC_VOLUME, (char)*valp, 1);
+ valp++;
+ }
+ mutex_unlock(&spec->cs8409_i2c_mux);
+ break;
+ default:
+ break;
+ }
+ cs8409_cs42l42_update_volume(codec);
+ snd_hda_power_down(codec);
+ return change;
+}
+
+static const DECLARE_TLV_DB_SCALE(cs8409_cs42l42_hp_db_scale,
+ CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(cs8409_cs42l42_amic_db_scale,
+ CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
+
+static const struct snd_kcontrol_new cs8409_cs42l42_hp_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .name = "Headphone Playback Volume",
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs8409_cs42l42_volume_info,
+ .get = cs8409_cs42l42_volume_get,
+ .put = cs8409_cs42l42_volume_put,
+ .tlv = { .p = cs8409_cs42l42_hp_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL(CS8409_CS42L42_HP_PIN_NID, 3, 0, HDA_OUTPUT) |
+ HDA_AMP_VAL_MIN_MUTE
+};
+
+static const struct snd_kcontrol_new cs8409_cs42l42_amic_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .name = "Mic Capture Volume",
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs8409_cs42l42_volume_info,
+ .get = cs8409_cs42l42_volume_get,
+ .put = cs8409_cs42l42_volume_put,
+ .tlv = { .p = cs8409_cs42l42_amic_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL(CS8409_CS42L42_AMIC_PIN_NID, 1, 0, HDA_INPUT) |
+ HDA_AMP_VAL_MIN_MUTE
+};
+
+/* Assert/release RTS# line to CS42L42 */
+static void cs8409_cs42l42_reset(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ /* Assert RTS# line */
+ snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0);
+ /* wait ~10ms */
+ usleep_range(10000, 15000);
+ /* Release RTS# line */
+ snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, GPIO5_INT);
+ /* wait ~10ms */
+ usleep_range(10000, 15000);
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+
+ /* Clear interrupts, by reading interrupt status registers */
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F, 1);
+
+ mutex_unlock(&spec->cs8409_i2c_mux);
+
+}
+
+/* Configure CS42L42 slave codec for jack autodetect */
+static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+
+ /* Set TIP_SENSE_EN for analog front-end of tip sense. */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1);
+ /* Clear WAKE# */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0001, 1);
+ /* Wait ~2.5ms */
+ usleep_range(2500, 3000);
+ /* Set mode WAKE# output follows the combination logic directly */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0020, 1);
+ /* Clear interrupts status */
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
+ /* Enable interrupt */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0x03, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b79, 0x00, 1);
+
+ mutex_unlock(&spec->cs8409_i2c_mux);
+}
+
+/* Enable and run CS42L42 slave codec jack auto detect */
+static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+
+ /* Clear interrupts */
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77, 1);
+
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0x01, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80, 1);
+ /* Wait ~110ms*/
+ usleep_range(110000, 200000);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0, 1);
+ /* Wait ~10ms */
+ usleep_range(10000, 25000);
+
+ mutex_unlock(&spec->cs8409_i2c_mux);
+
+}
+
+static void cs8409_cs42l42_reg_setup(struct hda_codec *codec)
+{
+ const struct cs8409_i2c_param *seq = cs42l42_init_reg_seq;
+ struct cs8409_spec *spec = codec->spec;
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+
+ for (; seq->addr; seq++)
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg, 1);
+
+ mutex_unlock(&spec->cs8409_i2c_mux);
+
+}
+
+/*
+ * In the case of CS8409 we do not have unsolicited events from NID's 0x24
+ * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via gpio 4 to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int status_changed = 0;
+ int reg_cdc_status;
+ int reg_hs_status;
+ int reg_ts_status;
+ int type;
+ struct hda_jack_tbl *jk;
+
+ /* jack_unsol_event() will be called every time gpio line changing state.
+ * In this case gpio4 line goes up as a result of reading interrupt status
+ * registers in previous cs8409_jack_unsol_event() call.
+ * We don't need to handle this event, ignoring...
+ */
+ if ((res & (1 << 4)))
+ return;
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+
+ /* Read jack detect status registers */
+ reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
+ reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1);
+ reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
+
+ /* Clear interrupts, by reading interrupt status registers */
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
+
+ mutex_unlock(&spec->cs8409_i2c_mux);
+
+ /* If status values are < 0, read error has occurred. */
+ if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
+ return;
+
+ /* HSDET_AUTO_DONE */
+ if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
+
+ type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
+ /* CS42L42 reports optical jack as type 4
+ * We don't handle optical jack
+ */
+ if (type != 4) {
+ if (!spec->cs42l42_hp_jack_in) {
+ status_changed = 1;
+ spec->cs42l42_hp_jack_in = 1;
+ }
+ /* type = 3 has no mic */
+ if ((!spec->cs42l42_mic_jack_in) && (type != 3)) {
+ status_changed = 1;
+ spec->cs42l42_mic_jack_in = 1;
+ }
+ } else {
+ if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
+ status_changed = 1;
+ spec->cs42l42_hp_jack_in = 0;
+ spec->cs42l42_mic_jack_in = 0;
+ }
+ }
+
+ } else {
+ /* TIP_SENSE INSERT/REMOVE */
+ switch (reg_ts_status) {
+ case CS42L42_JACK_INSERTED:
+ cs8409_cs42l42_run_jack_detect(codec);
+ break;
+
+ case CS42L42_JACK_REMOVED:
+ if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
+ status_changed = 1;
+ spec->cs42l42_hp_jack_in = 0;
+ spec->cs42l42_mic_jack_in = 0;
+ }
+ break;
+
+ default:
+ /* jack in transition */
+ status_changed = 0;
+ break;
+ }
+ }
+
+ if (status_changed) {
+
+ snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
+ spec->cs42l42_hp_jack_in ? 0 : PIN_OUT);
+
+ /* Report jack*/
+ jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
+ if (jk) {
+ snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+ /* Report jack*/
+ jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0);
+ if (jk) {
+ snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+/* Manage PDREF, when transition to D3hot */
+static int cs8409_suspend(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ mutex_lock(&spec->cs8409_i2c_mux);
+ /* Power down CS42L42 ASP/EQ/MIX/HP */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1);
+ mutex_unlock(&spec->cs8409_i2c_mux);
+ /* Assert CS42L42 RTS# line */
+ snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0);
+
+ snd_hda_shutup_pins(codec);
+
+ return 0;
+}
+#endif
+
+/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
+static void cs8409_enable_ur(struct hda_codec *codec, int flag)
+{
+ /* GPIO4 INT# and GPIO3 WAKE# */
+ snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
+ flag ? (GPIO3_INT | GPIO4_INT) : 0);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
+ flag ? AC_UNSOL_ENABLED : 0);
+
+}
+
+/* Vendor specific HW configuration
+ * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
+ */
+static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg;
+ const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn;
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ }
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+
+ if (codec->fixup_id == CS8409_BULLSEYE)
+ for (; seq_bullseye->nid; seq_bullseye++)
+ cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
+
+ /* Disable Unsolicited Response during boot */
+ cs8409_enable_ur(codec, 0);
+
+ /* Reset CS42L42 */
+ cs8409_cs42l42_reset(codec);
+
+ /* Initialise CS42L42 companion codec */
+ cs8409_cs42l42_reg_setup(codec);
+
+ if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG) {
+ /* FULL_SCALE_VOL = 0 for Warlock / Cyborg */
+ mutex_lock(&spec->cs8409_i2c_mux);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01, 1);
+ mutex_unlock(&spec->cs8409_i2c_mux);
+ /* DMIC1_MO=00b, DMIC1/2_SR=1 */
+ cs8409_vendor_coef_set(codec, 0x09, 0x0003);
+ }
+
+ /* Restore Volumes after Resume */
+ if (spec->cs42l42_volume_init) {
+ mutex_lock(&spec->cs8409_i2c_mux);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHA,
+ -spec->cs42l42_hp_volume[0], 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHB,
+ -spec->cs42l42_hp_volume[1], 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOLUME,
+ spec->cs42l42_hs_mic_volume[0], 1);
+ mutex_unlock(&spec->cs8409_i2c_mux);
+ }
+
+ cs8409_cs42l42_update_volume(codec);
+
+ cs8409_cs42l42_enable_jack_detect(codec);
+
+ /* Enable Unsolicited Response */
+ cs8409_enable_ur(codec, 1);
+}
+
+static int cs8409_cs42l42_init(struct hda_codec *codec)
+{
+ int ret = snd_hda_gen_init(codec);
+
+ if (!ret)
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return ret;
+}
+
+static int cs8409_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+
+ return 0;
+}
+
+static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
+ .build_controls = cs8409_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs8409_cs42l42_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = cs8409_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cs8409_suspend,
+#endif
+};
+
+static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct cs8409_spec *spec = codec->spec;
+
+ unsigned int nid = ((cmd >> 20) & 0x07f);
+ unsigned int verb = ((cmd >> 8) & 0x0fff);
+
+ /* CS8409 pins have no AC_PINSENSE_PRESENCE
+ * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34
+ * and return correct pin sense values for read_pin_sense() call from
+ * hda_jack based on CS42L42 jack detect status.
+ */
+ switch (nid) {
+ case CS8409_CS42L42_HP_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (spec->cs42l42_hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+
+ case CS8409_CS42L42_AMIC_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (spec->cs42l42_mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return spec->exec_verb(dev, cmd, flags, res);
+}
+
+static void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int caps;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs);
+ /* verb exec op override */
+ spec->exec_verb = codec->core.exec_verb;
+ codec->core.exec_verb = cs8409_cs42l42_exec_verb;
+
+ mutex_init(&spec->cs8409_i2c_mux);
+
+ codec->patch_ops = cs8409_cs42l42_patch_ops;
+
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.no_primary_hp = 1;
+ spec->gen.suppress_vmaster = 1;
+
+ /* GPIO 5 out, 3,4 in */
+ spec->gpio_dir = GPIO5_INT;
+ spec->gpio_data = 0;
+ spec->gpio_mask = 0x03f;
+
+ spec->cs42l42_hp_jack_in = 0;
+ spec->cs42l42_mic_jack_in = 0;
+
+ /* Basic initial sequence for specific hw configuration */
+ snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
+
+ /* CS8409 is simple HDA bridge and intended to be used with a remote
+ * companion codec. Most of input/output PIN(s) have only basic
+ * capabilities. NID(s) 0x24 and 0x34 have only OUTC and INC
+ * capabilities and no presence detect capable (PDC) and call to
+ * snd_hda_gen_build_controls() will mark them as non detectable
+ * phantom jacks. However, in this configuration companion codec
+ * CS42L42 is connected to these pins and it has jack detect
+ * capabilities. We have to override pin capabilities,
+ * otherwise they will not be created as input devices.
+ */
+ caps = snd_hdac_read_parm(&codec->core, CS8409_CS42L42_HP_PIN_NID,
+ AC_PAR_PIN_CAP);
+ if (caps >= 0)
+ snd_hdac_override_parm(&codec->core,
+ CS8409_CS42L42_HP_PIN_NID, AC_PAR_PIN_CAP,
+ (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
+
+ caps = snd_hdac_read_parm(&codec->core, CS8409_CS42L42_AMIC_PIN_NID,
+ AC_PAR_PIN_CAP);
+ if (caps >= 0)
+ snd_hdac_override_parm(&codec->core,
+ CS8409_CS42L42_AMIC_PIN_NID, AC_PAR_PIN_CAP,
+ (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
+
+ snd_hda_override_wcaps(codec, CS8409_CS42L42_HP_PIN_NID,
+ (get_wcaps(codec, CS8409_CS42L42_HP_PIN_NID) | AC_WCAP_UNSOL_CAP));
+
+ snd_hda_override_wcaps(codec, CS8409_CS42L42_AMIC_PIN_NID,
+ (get_wcaps(codec, CS8409_CS42L42_AMIC_PIN_NID) | AC_WCAP_UNSOL_CAP));
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Set initial DMIC volume to -26 dB */
+ snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
+ HDA_INPUT, 0, 0xff, 0x19);
+ snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_hp_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_amic_volume_mixer);
+ cs8409_cs42l42_hw_init(codec);
+ snd_hda_codec_set_name(codec, "CS8409/CS42L42");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cs8409_cs42l42_hw_init(codec);
+ fallthrough;
+ case HDA_FIXUP_ACT_BUILD:
+ /* Run jack auto detect first time on boot
+ * after controls have been added, to check if jack has
+ * been already plugged in.
+ * Run immediately after init.
+ */
+ cs8409_cs42l42_run_jack_detect(codec);
+ usleep_range(100000, 150000);
+ break;
+ default:
+ break;
+ }
+}
+
+static const struct hda_fixup cs8409_fixups[] = {
+ [CS8409_BULLSEYE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_CYBORG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cs42l42_fixups,
+ },
+};
+
+static int patch_cs8409(struct hda_codec *codec)
+{
+ int err;
+
+ if (!cs8409_alloc_spec(codec))
+ return -ENOMEM;
+
+ snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups);
+
+ codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id,
+ codec->bus->pci->subsystem_vendor,
+ codec->bus->pci->subsystem_device);
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = cs8409_parse_auto_config(codec);
+ if (err < 0) {
+ snd_hda_gen_free(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ return 0;
+}
+
+static const struct hda_device_id snd_hda_id_cs8409[] = {
+ HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409);
+
+static struct hda_codec_driver cs8409_driver = {
+ .id = snd_hda_id_cs8409,
+};
+module_hda_codec_driver(cs8409_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
new file mode 100644
index 000000000000..2ab02a520f5a
--- /dev/null
+++ b/sound/pci/hda/patch_cs8409.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS8409_PATCH_H
+#define __CS8409_PATCH_H
+
+/* Cirrus Logic CS8409 HDA bridge with
+ * companion codec CS42L42
+ */
+#define CS42L42_HP_CH (2U)
+#define CS42L42_HS_MIC_CH (1U)
+
+#define CS8409_VENDOR_NID 0x47
+
+#define CS8409_CS42L42_HP_PIN_NID 0x24
+#define CS8409_CS42L42_SPK_PIN_NID 0x2c
+#define CS8409_CS42L42_AMIC_PIN_NID 0x34
+#define CS8409_CS42L42_DMIC_PIN_NID 0x44
+#define CS8409_CS42L42_DMIC_ADC_PIN_NID 0x22
+
+#define CS42L42_HSDET_AUTO_DONE 0x02
+#define CS42L42_HSTYPE_MASK 0x03
+
+#define CS42L42_JACK_INSERTED 0x0C
+#define CS42L42_JACK_REMOVED 0x00
+
+#define GPIO3_INT (1 << 3)
+#define GPIO4_INT (1 << 4)
+#define GPIO5_INT (1 << 5)
+
+#define CS42L42_I2C_ADDR (0x48 << 1)
+
+#define CIR_I2C_ADDR 0x0059
+#define CIR_I2C_DATA 0x005A
+#define CIR_I2C_CTRL 0x005B
+#define CIR_I2C_STATUS 0x005C
+#define CIR_I2C_QWRITE 0x005D
+#define CIR_I2C_QREAD 0x005E
+
+#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63)
+#define CS8409_CS42L42_HP_VOL_REAL_MAX (0)
+#define CS8409_CS42L42_AMIC_VOL_REAL_MIN (-97)
+#define CS8409_CS42L42_AMIC_VOL_REAL_MAX (12)
+#define CS8409_CS42L42_REG_HS_VOLUME_CHA (0x2301)
+#define CS8409_CS42L42_REG_HS_VOLUME_CHB (0x2303)
+#define CS8409_CS42L42_REG_AMIC_VOLUME (0x1D03)
+
+enum {
+ CS8409_BULLSEYE,
+ CS8409_WARLOCK,
+ CS8409_CYBORG,
+ CS8409_FIXUPS,
+};
+
+struct cs8409_i2c_param {
+ unsigned int addr;
+ unsigned int reg;
+};
+
+struct cs8409_cir_param {
+ unsigned int nid;
+ unsigned int cir;
+ unsigned int coeff;
+};
+
+struct cs8409_spec {
+ struct hda_gen_spec gen;
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+
+ unsigned int cs42l42_hp_jack_in:1;
+ unsigned int cs42l42_mic_jack_in:1;
+ unsigned int cs42l42_volume_init:1;
+ char cs42l42_hp_volume[CS42L42_HP_CH];
+ char cs42l42_hs_mic_volume[CS42L42_HS_MIC_CH];
+
+ struct mutex cs8409_i2c_mux;
+
+ /* verb exec op override */
+ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res);
+};
+
+#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 01/27] ALSA: hda/cirrus: Move CS8409 HDA bridge to separate module
2021-07-26 17:46 ` [PATCH 01/27] ALSA: hda/cirrus: Move CS8409 HDA bridge to separate module Vitaly Rodionov
@ 2021-07-27 6:39 ` Takashi Iwai
0 siblings, 0 replies; 31+ messages in thread
From: Takashi Iwai @ 2021-07-27 6:39 UTC (permalink / raw)
To: Vitaly Rodionov
Cc: Jaroslav Kysela, Takashi Iwai, alsa-devel, patches, linux-kernel,
Lucas Tanure
On Mon, 26 Jul 2021 19:46:14 +0200,
Vitaly Rodionov wrote:
> --- a/sound/pci/hda/Kconfig
> +++ b/sound/pci/hda/Kconfig
> @@ -150,6 +150,7 @@ comment "Set to Y if you want auto-loading the codec driver"
> config SND_HDA_CODEC_CIRRUS
> tristate "Build Cirrus Logic codec support"
> select SND_HDA_GENERIC
> + select SND_HDA_CODEC_CS8409
> help
> Say Y or M here to include Cirrus Logic codec support in
> snd-hda-intel driver, such as CS4206.
This reverse-selection doesn't make sense, as both modules should be
completely individual.
thanks,
Takashi
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 02/27] ALSA: hda/cs8409: Move arrays of configuration to a new file
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 01/27] ALSA: hda/cirrus: Move CS8409 HDA bridge to separate module Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 03/27] ALSA: hda/cs8409: Use enums for register names and coefficients Vitaly Rodionov
` (24 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/Makefile | 2 +-
sound/pci/hda/patch_cs8409-tables.c | 220 ++++++++++++++++++++++++++++
sound/pci/hda/patch_cs8409.c | 218 +--------------------------
sound/pci/hda/patch_cs8409.h | 19 +++
4 files changed, 241 insertions(+), 218 deletions(-)
create mode 100644 sound/pci/hda/patch_cs8409-tables.c
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 1b73e08dc563..b8fa682ce66a 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -20,7 +20,7 @@ snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
-snd-hda-codec-cs8409-objs := patch_cs8409.o
+snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-ca0132-objs := patch_ca0132.o
snd-hda-codec-conexant-objs := patch_conexant.o
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
new file mode 100644
index 000000000000..4adc7a4c4a25
--- /dev/null
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * patch_cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#include "patch_cs8409.h"
+
+/* Dell Inspiron platforms
+ * with cs8409 bridge and cs42l42 codec
+ */
+const struct snd_pci_quirk cs8409_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+ {} /* terminator */
+};
+
+/* Dell Inspiron models with cs8409/cs42l42 */
+const struct hda_model_fixup cs8409_models[] = {
+ { .id = CS8409_BULLSEYE, .name = "bullseye" },
+ { .id = CS8409_WARLOCK, .name = "warlock" },
+ { .id = CS8409_CYBORG, .name = "cyborg" },
+ {}
+};
+
+const struct hda_fixup cs8409_fixups[] = {
+ [CS8409_BULLSEYE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_CYBORG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cs42l42_fixups,
+ },
+};
+
+const struct hda_verb cs8409_cs42l42_init_verbs[] = {
+ { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
+ { 0x47, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { 0x47, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { 0x47, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { 0x47, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { 0x47, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
+ { 0x24, 0x042120f0 }, /* ASP-1-TX */
+ { 0x34, 0x04a12050 }, /* ASP-1-RX */
+ { 0x2c, 0x901000f0 }, /* ASP-2-TX */
+ { 0x44, 0x90a00090 }, /* DMIC-1 */
+ {} /* terminator */
+};
+
+/* Vendor specific HW configuration for CS42L42 */
+const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
+ { 0x1010, 0xB0 },
+ { 0x1D01, 0x00 },
+ { 0x1D02, 0x06 },
+ { 0x1D03, 0x00 },
+ { 0x1107, 0x01 },
+ { 0x1009, 0x02 },
+ { 0x1007, 0x03 },
+ { 0x1201, 0x00 },
+ { 0x1208, 0x13 },
+ { 0x1205, 0xFF },
+ { 0x1206, 0x00 },
+ { 0x1207, 0x20 },
+ { 0x1202, 0x0D },
+ { 0x2A02, 0x02 },
+ { 0x2A03, 0x00 },
+ { 0x2A04, 0x00 },
+ { 0x2A05, 0x02 },
+ { 0x2A06, 0x00 },
+ { 0x2A07, 0x20 },
+ { 0x2A08, 0x02 },
+ { 0x2A09, 0x00 },
+ { 0x2A0A, 0x80 },
+ { 0x2A0B, 0x02 },
+ { 0x2A0C, 0x00 },
+ { 0x2A0D, 0xA0 },
+ { 0x2A01, 0x0C },
+ { 0x2902, 0x01 },
+ { 0x2903, 0x02 },
+ { 0x2904, 0x00 },
+ { 0x2905, 0x00 },
+ { 0x2901, 0x01 },
+ { 0x1101, 0x0A },
+ { 0x1102, 0x84 },
+ { 0x2301, 0x00 },
+ { 0x2303, 0x00 },
+ { 0x2302, 0x3f },
+ { 0x2001, 0x03 },
+ { 0x1B75, 0xB6 },
+ { 0x1B73, 0xC2 },
+ { 0x1129, 0x01 },
+ { 0x1121, 0xF3 },
+ { 0x1103, 0x20 },
+ { 0x1105, 0x00 },
+ { 0x1112, 0xC0 },
+ { 0x1113, 0x80 },
+ { 0x1C03, 0xC0 },
+ { 0x1105, 0x00 },
+ { 0x1112, 0xC0 },
+ { 0x1101, 0x02 },
+ {} /* Terminator */
+};
+
+/* Vendor specific hw configuration for CS8409 */
+const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
+ { 0x47, 0x00, 0xb008 }, /* +PLL1/2_EN, +I2C_EN */
+ { 0x47, 0x01, 0x0002 }, /* ASP1/2_EN=0, ASP1_STP=1 */
+ { 0x47, 0x02, 0x0a80 }, /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { 0x47, 0x19, 0x0800 }, /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { 0x47, 0x1a, 0x0820 }, /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { 0x47, 0x29, 0x0800 }, /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { 0x47, 0x2a, 0x2800 }, /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { 0x47, 0x39, 0x0800 }, /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { 0x47, 0x3a, 0x0800 }, /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { 0x47, 0x03, 0x8000 }, /* ASP1: LCHI = 00h */
+ { 0x47, 0x04, 0x28ff }, /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { 0x47, 0x05, 0x0062 }, /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { 0x47, 0x06, 0x801f }, /* ASP2: LCHI=1Fh */
+ { 0x47, 0x07, 0x283f }, /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { 0x47, 0x08, 0x805c }, /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { 0x47, 0x09, 0x0023 }, /* DMIC1_MO=10b, DMIC1/2_SR=1 */
+ { 0x47, 0x0a, 0x0000 }, /* ASP1/2_BEEP=0 */
+ { 0x47, 0x01, 0x0062 }, /* ASP1/2_EN=1, ASP1_STP=1 */
+ { 0x47, 0x00, 0x9008 }, /* -PLL2_EN */
+ { 0x47, 0x68, 0x0000 }, /* TX2.A: pre-scale att.=0 dB */
+ { 0x47, 0x82, 0xfc03 }, /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { 0x47, 0xc0, 0x9999 }, /* test mode on */
+ { 0x47, 0xc5, 0x0000 }, /* GPIO hysteresis = 30 us */
+ { 0x47, 0xc0, 0x0000 }, /* test mode off */
+ {} /* Terminator */
+};
+
+const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
+ { 0x47, 0x65, 0x4000 }, /* EQ_SEL=1, EQ1/2_EN=0 */
+ { 0x47, 0x64, 0x4000 }, /* +EQ_ACC */
+ { 0x47, 0x65, 0x4010 }, /* +EQ2_EN */
+ { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
+ { 0x47, 0x64, 0xc0c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
+ { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
+ { 0x47, 0x64, 0xc1c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
+ { 0x47, 0x63, 0xf370 }, /* EQ_DATA_HI=0xf370 */
+ { 0x47, 0x64, 0xc271 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
+ { 0x47, 0x63, 0x1ef8 }, /* EQ_DATA_HI=0x1ef8 */
+ { 0x47, 0x64, 0xc348 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
+ { 0x47, 0x63, 0xc110 }, /* EQ_DATA_HI=0xc110 */
+ { 0x47, 0x64, 0xc45a }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
+ { 0x47, 0x63, 0x1f29 }, /* EQ_DATA_HI=0x1f29 */
+ { 0x47, 0x64, 0xc574 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
+ { 0x47, 0x63, 0x1d7a }, /* EQ_DATA_HI=0x1d7a */
+ { 0x47, 0x64, 0xc653 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
+ { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
+ { 0x47, 0x64, 0xc714 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
+ { 0x47, 0x63, 0x1ca3 }, /* EQ_DATA_HI=0x1ca3 */
+ { 0x47, 0x64, 0xc8c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
+ { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
+ { 0x47, 0x64, 0xc914 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
+ { 0x47, 0x64, 0x0000 }, /* -EQ_ACC, -EQ_WRT */
+ {} /* Terminator */
+};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 9b16f1b5b828..b56fc89ad2cd 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -11,13 +11,6 @@
#include <linux/module.h>
#include <sound/core.h>
#include <linux/mutex.h>
-#include <linux/pci.h>
-#include <sound/tlv.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-#include "hda_auto_parser.h"
-#include "hda_jack.h"
-#include "hda_generic.h"
#include "patch_cs8409.h"
@@ -52,79 +45,6 @@ static int cs8409_parse_auto_config(struct hda_codec *codec)
return 0;
}
-/* Dell Inspiron models with cs8409/cs42l42 */
-static const struct hda_model_fixup cs8409_models[] = {
- { .id = CS8409_BULLSEYE, .name = "bullseye" },
- { .id = CS8409_WARLOCK, .name = "warlock" },
- { .id = CS8409_CYBORG, .name = "cyborg" },
- {}
-};
-
-/* Dell Inspiron platforms
- * with cs8409 bridge and cs42l42 codec
- */
-static const struct snd_pci_quirk cs8409_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
- {} /* terminator */
-};
-
-static const struct hda_verb cs8409_cs42l42_init_verbs[] = {
- { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
- { 0x47, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
- { 0x47, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
- { 0x47, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
- { 0x47, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
- { 0x47, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
- {} /* terminator */
-};
-
-static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
- { 0x24, 0x042120f0 }, /* ASP-1-TX */
- { 0x34, 0x04a12050 }, /* ASP-1-RX */
- { 0x2c, 0x901000f0 }, /* ASP-2-TX */
- { 0x44, 0x90a00090 }, /* DMIC-1 */
- {} /* terminator */
-};
-
static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
{
struct cs8409_spec *spec;
@@ -139,117 +59,6 @@ static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
return spec;
}
-/* Vendor specific HW configuration for CS42L42 */
-static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
- { 0x1D02, 0x06 },
- { 0x1D03, 0x00 },
- { 0x1107, 0x01 },
- { 0x1009, 0x02 },
- { 0x1007, 0x03 },
- { 0x1201, 0x00 },
- { 0x1208, 0x13 },
- { 0x1205, 0xFF },
- { 0x1206, 0x00 },
- { 0x1207, 0x20 },
- { 0x1202, 0x0D },
- { 0x2A02, 0x02 },
- { 0x2A03, 0x00 },
- { 0x2A04, 0x00 },
- { 0x2A05, 0x02 },
- { 0x2A06, 0x00 },
- { 0x2A07, 0x20 },
- { 0x2A08, 0x02 },
- { 0x2A09, 0x00 },
- { 0x2A0A, 0x80 },
- { 0x2A0B, 0x02 },
- { 0x2A0C, 0x00 },
- { 0x2A0D, 0xA0 },
- { 0x2A01, 0x0C },
- { 0x2902, 0x01 },
- { 0x2903, 0x02 },
- { 0x2904, 0x00 },
- { 0x2905, 0x00 },
- { 0x2901, 0x01 },
- { 0x1101, 0x0A },
- { 0x1102, 0x84 },
- { 0x2301, 0x00 },
- { 0x2303, 0x00 },
- { 0x2302, 0x3f },
- { 0x2001, 0x03 },
- { 0x1B75, 0xB6 },
- { 0x1B73, 0xC2 },
- { 0x1129, 0x01 },
- { 0x1121, 0xF3 },
- { 0x1103, 0x20 },
- { 0x1105, 0x00 },
- { 0x1112, 0xC0 },
- { 0x1113, 0x80 },
- { 0x1C03, 0xC0 },
- { 0x1105, 0x00 },
- { 0x1112, 0xC0 },
- { 0x1101, 0x02 },
- {} /* Terminator */
-};
-
-/* Vendor specific hw configuration for CS8409 */
-static const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
- { 0x47, 0x00, 0xb008 }, /* +PLL1/2_EN, +I2C_EN */
- { 0x47, 0x01, 0x0002 }, /* ASP1/2_EN=0, ASP1_STP=1 */
- { 0x47, 0x02, 0x0a80 }, /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
- { 0x47, 0x19, 0x0800 }, /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
- { 0x47, 0x1a, 0x0820 }, /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
- { 0x47, 0x29, 0x0800 }, /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
- { 0x47, 0x2a, 0x2800 }, /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
- { 0x47, 0x39, 0x0800 }, /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
- { 0x47, 0x3a, 0x0800 }, /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
- { 0x47, 0x03, 0x8000 }, /* ASP1: LCHI = 00h */
- { 0x47, 0x04, 0x28ff }, /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
- { 0x47, 0x05, 0x0062 }, /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
- { 0x47, 0x06, 0x801f }, /* ASP2: LCHI=1Fh */
- { 0x47, 0x07, 0x283f }, /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
- { 0x47, 0x08, 0x805c }, /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
- { 0x47, 0x09, 0x0023 }, /* DMIC1_MO=10b, DMIC1/2_SR=1 */
- { 0x47, 0x0a, 0x0000 }, /* ASP1/2_BEEP=0 */
- { 0x47, 0x01, 0x0062 }, /* ASP1/2_EN=1, ASP1_STP=1 */
- { 0x47, 0x00, 0x9008 }, /* -PLL2_EN */
- { 0x47, 0x68, 0x0000 }, /* TX2.A: pre-scale att.=0 dB */
- { 0x47, 0x82, 0xfc03 }, /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
- { 0x47, 0xc0, 0x9999 }, /* test mode on */
- { 0x47, 0xc5, 0x0000 }, /* GPIO hysteresis = 30 us */
- { 0x47, 0xc0, 0x0000 }, /* test mode off */
- {} /* Terminator */
-};
-
-static const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
- { 0x47, 0x65, 0x4000 }, /* EQ_SEL=1, EQ1/2_EN=0 */
- { 0x47, 0x64, 0x4000 }, /* +EQ_ACC */
- { 0x47, 0x65, 0x4010 }, /* +EQ2_EN */
- { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
- { 0x47, 0x64, 0xc0c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
- { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
- { 0x47, 0x64, 0xc1c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
- { 0x47, 0x63, 0xf370 }, /* EQ_DATA_HI=0xf370 */
- { 0x47, 0x64, 0xc271 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
- { 0x47, 0x63, 0x1ef8 }, /* EQ_DATA_HI=0x1ef8 */
- { 0x47, 0x64, 0xc348 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
- { 0x47, 0x63, 0xc110 }, /* EQ_DATA_HI=0xc110 */
- { 0x47, 0x64, 0xc45a }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
- { 0x47, 0x63, 0x1f29 }, /* EQ_DATA_HI=0x1f29 */
- { 0x47, 0x64, 0xc574 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
- { 0x47, 0x63, 0x1d7a }, /* EQ_DATA_HI=0x1d7a */
- { 0x47, 0x64, 0xc653 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
- { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
- { 0x47, 0x64, 0xc714 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
- { 0x47, 0x63, 0x1ca3 }, /* EQ_DATA_HI=0x1ca3 */
- { 0x47, 0x64, 0xc8c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
- { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
- { 0x47, 0x64, 0xc914 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
- { 0x47, 0x64, 0x0000 }, /* -EQ_ACC, -EQ_WRT */
- {} /* Terminator */
-};
-
static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
{
snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_COEF_INDEX, idx);
@@ -908,7 +717,7 @@ static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, u
return spec->exec_verb(dev, cmd, flags, res);
}
-static void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
{
struct cs8409_spec *spec = codec->spec;
int caps;
@@ -995,31 +804,6 @@ static void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixu
}
}
-static const struct hda_fixup cs8409_fixups[] = {
- [CS8409_BULLSEYE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_WARLOCK] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_CYBORG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_FIXUPS] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = cs8409_cs42l42_fixups,
- },
-};
-
static int patch_cs8409(struct hda_codec *codec)
{
int err;
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 2ab02a520f5a..516123a411db 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -9,6 +9,14 @@
#ifndef __CS8409_PATCH_H
#define __CS8409_PATCH_H
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
/* Cirrus Logic CS8409 HDA bridge with
* companion codec CS42L42
*/
@@ -88,4 +96,15 @@ struct cs8409_spec {
unsigned int *res);
};
+extern const struct snd_pci_quirk cs8409_fixup_tbl[];
+extern const struct hda_model_fixup cs8409_models[];
+extern const struct hda_fixup cs8409_fixups[];
+extern const struct hda_verb cs8409_cs42l42_init_verbs[];
+extern const struct hda_pintbl cs8409_cs42l42_pincfgs[];
+extern const struct cs8409_i2c_param cs42l42_init_reg_seq[];
+extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
+extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
+
+void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 03/27] ALSA: hda/cs8409: Use enums for register names and coefficients
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 01/27] ALSA: hda/cirrus: Move CS8409 HDA bridge to separate module Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 02/27] ALSA: hda/cs8409: Move arrays of configuration to a new file Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 04/27] ALSA: hda/cs8409: Mask all CS42L42 interrupts on initialization Vitaly Rodionov
` (23 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 164 ++++++++++++-------
sound/pci/hda/patch_cs8409.c | 49 +++---
sound/pci/hda/patch_cs8409.h | 239 ++++++++++++++++++++++++----
3 files changed, 343 insertions(+), 109 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 4adc7a4c4a25..5766433325a9 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -91,20 +91,20 @@ const struct hda_fixup cs8409_fixups[] = {
};
const struct hda_verb cs8409_cs42l42_init_verbs[] = {
- { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
- { 0x47, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
- { 0x47, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
- { 0x47, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
- { 0x47, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
- { 0x47, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ { CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
{} /* terminator */
};
const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
- { 0x24, 0x042120f0 }, /* ASP-1-TX */
- { 0x34, 0x04a12050 }, /* ASP-1-RX */
- { 0x2c, 0x901000f0 }, /* ASP-2-TX */
- { 0x44, 0x90a00090 }, /* DMIC-1 */
+ { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */
+ { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ { CS8409_PIN_DMIC1_IN, 0x90a00090 }, /* DMIC-1 */
{} /* terminator */
};
@@ -164,57 +164,105 @@ const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
/* Vendor specific hw configuration for CS8409 */
const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
- { 0x47, 0x00, 0xb008 }, /* +PLL1/2_EN, +I2C_EN */
- { 0x47, 0x01, 0x0002 }, /* ASP1/2_EN=0, ASP1_STP=1 */
- { 0x47, 0x02, 0x0a80 }, /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
- { 0x47, 0x19, 0x0800 }, /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
- { 0x47, 0x1a, 0x0820 }, /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
- { 0x47, 0x29, 0x0800 }, /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
- { 0x47, 0x2a, 0x2800 }, /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
- { 0x47, 0x39, 0x0800 }, /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
- { 0x47, 0x3a, 0x0800 }, /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
- { 0x47, 0x03, 0x8000 }, /* ASP1: LCHI = 00h */
- { 0x47, 0x04, 0x28ff }, /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
- { 0x47, 0x05, 0x0062 }, /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
- { 0x47, 0x06, 0x801f }, /* ASP2: LCHI=1Fh */
- { 0x47, 0x07, 0x283f }, /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
- { 0x47, 0x08, 0x805c }, /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
- { 0x47, 0x09, 0x0023 }, /* DMIC1_MO=10b, DMIC1/2_SR=1 */
- { 0x47, 0x0a, 0x0000 }, /* ASP1/2_BEEP=0 */
- { 0x47, 0x01, 0x0062 }, /* ASP1/2_EN=1, ASP1_STP=1 */
- { 0x47, 0x00, 0x9008 }, /* -PLL2_EN */
- { 0x47, 0x68, 0x0000 }, /* TX2.A: pre-scale att.=0 dB */
- { 0x47, 0x82, 0xfc03 }, /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
- { 0x47, 0xc0, 0x9999 }, /* test mode on */
- { 0x47, 0xc5, 0x0000 }, /* GPIO hysteresis = 30 us */
- { 0x47, 0xc0, 0x0000 }, /* test mode off */
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1/2_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 },
+ /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 },
+ /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 },
+ /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 },
+ /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 },
+ /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP2: LCHI=1Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f },
+ /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f },
+ /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c },
+ /* DMIC1_MO=10b, DMIC1/2_SR=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DMIC_CFG, 0x0023 },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1/2_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 },
+ /* TX2.A: pre-scale att.=0 dB */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 },
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 },
+ /* test mode on */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 },
+ /* GPIO hysteresis = 30 us */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 },
+ /* test mode off */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 },
{} /* Terminator */
};
const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
- { 0x47, 0x65, 0x4000 }, /* EQ_SEL=1, EQ1/2_EN=0 */
- { 0x47, 0x64, 0x4000 }, /* +EQ_ACC */
- { 0x47, 0x65, 0x4010 }, /* +EQ2_EN */
- { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
- { 0x47, 0x64, 0xc0c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
- { 0x47, 0x63, 0x0647 }, /* EQ_DATA_HI=0x0647 */
- { 0x47, 0x64, 0xc1c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
- { 0x47, 0x63, 0xf370 }, /* EQ_DATA_HI=0xf370 */
- { 0x47, 0x64, 0xc271 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
- { 0x47, 0x63, 0x1ef8 }, /* EQ_DATA_HI=0x1ef8 */
- { 0x47, 0x64, 0xc348 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
- { 0x47, 0x63, 0xc110 }, /* EQ_DATA_HI=0xc110 */
- { 0x47, 0x64, 0xc45a }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
- { 0x47, 0x63, 0x1f29 }, /* EQ_DATA_HI=0x1f29 */
- { 0x47, 0x64, 0xc574 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
- { 0x47, 0x63, 0x1d7a }, /* EQ_DATA_HI=0x1d7a */
- { 0x47, 0x64, 0xc653 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
- { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
- { 0x47, 0x64, 0xc714 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
- { 0x47, 0x63, 0x1ca3 }, /* EQ_DATA_HI=0x1ca3 */
- { 0x47, 0x64, 0xc8c7 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
- { 0x47, 0x63, 0xc38c }, /* EQ_DATA_HI=0xc38c */
- { 0x47, 0x64, 0xc914 }, /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
- { 0x47, 0x64, 0x0000 }, /* -EQ_ACC, -EQ_WRT */
+ /* EQ_SEL=1, EQ1/2_EN=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4000 },
+ /* +EQ_ACC */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x4000 },
+ /* +EQ2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4010 },
+ /* EQ_DATA_HI=0x0647 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc0c7 },
+ /* EQ_DATA_HI=0x0647 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc1c7 },
+ /* EQ_DATA_HI=0xf370 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xf370 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc271 },
+ /* EQ_DATA_HI=0x1ef8 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ef8 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc348 },
+ /* EQ_DATA_HI=0xc110 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc110 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc45a },
+ /* EQ_DATA_HI=0x1f29 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1f29 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc574 },
+ /* EQ_DATA_HI=0x1d7a */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1d7a },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc653 },
+ /* EQ_DATA_HI=0xc38c */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc714 },
+ /* EQ_DATA_HI=0x1ca3 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ca3 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc8c7 },
+ /* EQ_DATA_HI=0xc38c */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc914 },
+ /* -EQ_ACC, -EQ_WRT */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 },
{} /* Terminator */
};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index b56fc89ad2cd..e4319a0b9cf6 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -61,15 +61,15 @@ static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
{
- snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_COEF_INDEX, idx);
- return snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0);
}
static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
unsigned int coef)
{
- snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_COEF_INDEX, idx);
- snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_COEF, coef);
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef);
}
/**
@@ -102,7 +102,7 @@ static int cs8409_i2c_wait_complete(struct hda_codec *codec)
unsigned int retval;
do {
- retval = cs8409_vendor_coef_get(codec, CIR_I2C_STATUS);
+ retval = cs8409_vendor_coef_get(codec, CS8409_I2C_STS);
if ((retval & 0x18) != 0x18) {
usleep_range(2000, 4000);
--repeat;
@@ -131,10 +131,10 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
unsigned int read_data;
cs8409_enable_i2c_clock(codec, 1);
- cs8409_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
if (paged) {
- cs8409_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
if (cs8409_i2c_wait_complete(codec) < 0) {
codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
__func__, i2c_address, i2c_reg);
@@ -143,7 +143,7 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
}
i2c_reg_data = (i2c_reg << 8) & 0x0ffff;
- cs8409_vendor_coef_set(codec, CIR_I2C_QREAD, i2c_reg_data);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
if (cs8409_i2c_wait_complete(codec) < 0) {
codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
__func__, i2c_address, i2c_reg);
@@ -151,7 +151,7 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
}
/* Register in bits 15-8 and the data in 7-0 */
- read_data = cs8409_vendor_coef_get(codec, CIR_I2C_QREAD);
+ read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
cs8409_enable_i2c_clock(codec, 0);
@@ -175,10 +175,10 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
unsigned int i2c_reg_data;
cs8409_enable_i2c_clock(codec, 1);
- cs8409_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
if (paged) {
- cs8409_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
if (cs8409_i2c_wait_complete(codec) < 0) {
codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
__func__, i2c_address, i2c_reg);
@@ -187,7 +187,7 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
}
i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | (i2c_data & 0x0ff);
- cs8409_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg_data);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
if (cs8409_i2c_wait_complete(codec) < 0) {
codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
@@ -363,11 +363,11 @@ static void cs8409_cs42l42_reset(struct hda_codec *codec)
struct cs8409_spec *spec = codec->spec;
/* Assert RTS# line */
- snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
/* wait ~10ms */
usleep_range(10000, 15000);
/* Release RTS# line */
- snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, GPIO5_INT);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
/* wait ~10ms */
usleep_range(10000, 15000);
@@ -471,7 +471,7 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
* registers in previous cs8409_jack_unsol_event() call.
* We don't need to handle this event, ignoring...
*/
- if ((res & (1 << 4)))
+ if (res & CS8409_CS42L42_INT)
return;
mutex_lock(&spec->cs8409_i2c_mux);
@@ -568,7 +568,7 @@ static int cs8409_suspend(struct hda_codec *codec)
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1);
mutex_unlock(&spec->cs8409_i2c_mux);
/* Assert CS42L42 RTS# line */
- snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
snd_hda_shutup_pins(codec);
@@ -580,10 +580,10 @@ static int cs8409_suspend(struct hda_codec *codec)
static void cs8409_enable_ur(struct hda_codec *codec, int flag)
{
/* GPIO4 INT# and GPIO3 WAKE# */
- snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
- flag ? (GPIO3_INT | GPIO4_INT) : 0);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
+ flag ? CS8409_CS42L42_INT : 0);
- snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
flag ? AC_UNSOL_ENABLED : 0);
}
@@ -598,9 +598,12 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
struct cs8409_spec *spec = codec->spec;
if (spec->gpio_mask) {
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir);
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
}
for (; seq->nid; seq++)
@@ -738,7 +741,7 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->gen.suppress_vmaster = 1;
/* GPIO 5 out, 3,4 in */
- spec->gpio_dir = GPIO5_INT;
+ spec->gpio_dir = CS8409_CS42L42_RESET;
spec->gpio_data = 0;
spec->gpio_mask = 0x03f;
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 516123a411db..1d3ce28415fa 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -17,38 +17,206 @@
#include "hda_jack.h"
#include "hda_generic.h"
-/* Cirrus Logic CS8409 HDA bridge with
- * companion codec CS42L42
- */
-#define CS42L42_HP_CH (2U)
-#define CS42L42_HS_MIC_CH (1U)
-
-#define CS8409_VENDOR_NID 0x47
+/* CS8409 Specific Definitions */
-#define CS8409_CS42L42_HP_PIN_NID 0x24
-#define CS8409_CS42L42_SPK_PIN_NID 0x2c
-#define CS8409_CS42L42_AMIC_PIN_NID 0x34
-#define CS8409_CS42L42_DMIC_PIN_NID 0x44
-#define CS8409_CS42L42_DMIC_ADC_PIN_NID 0x22
-
-#define CS42L42_HSDET_AUTO_DONE 0x02
-#define CS42L42_HSTYPE_MASK 0x03
-
-#define CS42L42_JACK_INSERTED 0x0C
-#define CS42L42_JACK_REMOVED 0x00
+enum cs8409_pins {
+ CS8409_PIN_ROOT,
+ CS8409_PIN_AFG,
+ CS8409_PIN_ASP1_OUT_A,
+ CS8409_PIN_ASP1_OUT_B,
+ CS8409_PIN_ASP1_OUT_C,
+ CS8409_PIN_ASP1_OUT_D,
+ CS8409_PIN_ASP1_OUT_E,
+ CS8409_PIN_ASP1_OUT_F,
+ CS8409_PIN_ASP1_OUT_G,
+ CS8409_PIN_ASP1_OUT_H,
+ CS8409_PIN_ASP2_OUT_A,
+ CS8409_PIN_ASP2_OUT_B,
+ CS8409_PIN_ASP2_OUT_C,
+ CS8409_PIN_ASP2_OUT_D,
+ CS8409_PIN_ASP2_OUT_E,
+ CS8409_PIN_ASP2_OUT_F,
+ CS8409_PIN_ASP2_OUT_G,
+ CS8409_PIN_ASP2_OUT_H,
+ CS8409_PIN_ASP1_IN_A,
+ CS8409_PIN_ASP1_IN_B,
+ CS8409_PIN_ASP1_IN_C,
+ CS8409_PIN_ASP1_IN_D,
+ CS8409_PIN_ASP1_IN_E,
+ CS8409_PIN_ASP1_IN_F,
+ CS8409_PIN_ASP1_IN_G,
+ CS8409_PIN_ASP1_IN_H,
+ CS8409_PIN_ASP2_IN_A,
+ CS8409_PIN_ASP2_IN_B,
+ CS8409_PIN_ASP2_IN_C,
+ CS8409_PIN_ASP2_IN_D,
+ CS8409_PIN_ASP2_IN_E,
+ CS8409_PIN_ASP2_IN_F,
+ CS8409_PIN_ASP2_IN_G,
+ CS8409_PIN_ASP2_IN_H,
+ CS8409_PIN_DMIC1,
+ CS8409_PIN_DMIC2,
+ CS8409_PIN_ASP1_TRANSMITTER_A,
+ CS8409_PIN_ASP1_TRANSMITTER_B,
+ CS8409_PIN_ASP1_TRANSMITTER_C,
+ CS8409_PIN_ASP1_TRANSMITTER_D,
+ CS8409_PIN_ASP1_TRANSMITTER_E,
+ CS8409_PIN_ASP1_TRANSMITTER_F,
+ CS8409_PIN_ASP1_TRANSMITTER_G,
+ CS8409_PIN_ASP1_TRANSMITTER_H,
+ CS8409_PIN_ASP2_TRANSMITTER_A,
+ CS8409_PIN_ASP2_TRANSMITTER_B,
+ CS8409_PIN_ASP2_TRANSMITTER_C,
+ CS8409_PIN_ASP2_TRANSMITTER_D,
+ CS8409_PIN_ASP2_TRANSMITTER_E,
+ CS8409_PIN_ASP2_TRANSMITTER_F,
+ CS8409_PIN_ASP2_TRANSMITTER_G,
+ CS8409_PIN_ASP2_TRANSMITTER_H,
+ CS8409_PIN_ASP1_RECEIVER_A,
+ CS8409_PIN_ASP1_RECEIVER_B,
+ CS8409_PIN_ASP1_RECEIVER_C,
+ CS8409_PIN_ASP1_RECEIVER_D,
+ CS8409_PIN_ASP1_RECEIVER_E,
+ CS8409_PIN_ASP1_RECEIVER_F,
+ CS8409_PIN_ASP1_RECEIVER_G,
+ CS8409_PIN_ASP1_RECEIVER_H,
+ CS8409_PIN_ASP2_RECEIVER_A,
+ CS8409_PIN_ASP2_RECEIVER_B,
+ CS8409_PIN_ASP2_RECEIVER_C,
+ CS8409_PIN_ASP2_RECEIVER_D,
+ CS8409_PIN_ASP2_RECEIVER_E,
+ CS8409_PIN_ASP2_RECEIVER_F,
+ CS8409_PIN_ASP2_RECEIVER_G,
+ CS8409_PIN_ASP2_RECEIVER_H,
+ CS8409_PIN_DMIC1_IN,
+ CS8409_PIN_DMIC2_IN,
+ CS8409_PIN_BEEP_GEN,
+ CS8409_PIN_VENDOR_WIDGET
+};
-#define GPIO3_INT (1 << 3)
-#define GPIO4_INT (1 << 4)
-#define GPIO5_INT (1 << 5)
+enum cs8409_coefficient_index_registers {
+ CS8409_DEV_CFG1,
+ CS8409_DEV_CFG2,
+ CS8409_DEV_CFG3,
+ CS8409_ASP1_CLK_CTRL1,
+ CS8409_ASP1_CLK_CTRL2,
+ CS8409_ASP1_CLK_CTRL3,
+ CS8409_ASP2_CLK_CTRL1,
+ CS8409_ASP2_CLK_CTRL2,
+ CS8409_ASP2_CLK_CTRL3,
+ CS8409_DMIC_CFG,
+ CS8409_BEEP_CFG,
+ ASP1_RX_NULL_INS_RMV,
+ ASP1_Rx_RATE1,
+ ASP1_Rx_RATE2,
+ ASP1_Tx_NULL_INS_RMV,
+ ASP1_Tx_RATE1,
+ ASP1_Tx_RATE2,
+ ASP2_Rx_NULL_INS_RMV,
+ ASP2_Rx_RATE1,
+ ASP2_Rx_RATE2,
+ ASP2_Tx_NULL_INS_RMV,
+ ASP2_Tx_RATE1,
+ ASP2_Tx_RATE2,
+ ASP1_SYNC_CTRL,
+ ASP2_SYNC_CTRL,
+ ASP1_A_TX_CTRL1,
+ ASP1_A_TX_CTRL2,
+ ASP1_B_TX_CTRL1,
+ ASP1_B_TX_CTRL2,
+ ASP1_C_TX_CTRL1,
+ ASP1_C_TX_CTRL2,
+ ASP1_D_TX_CTRL1,
+ ASP1_D_TX_CTRL2,
+ ASP1_E_TX_CTRL1,
+ ASP1_E_TX_CTRL2,
+ ASP1_F_TX_CTRL1,
+ ASP1_F_TX_CTRL2,
+ ASP1_G_TX_CTRL1,
+ ASP1_G_TX_CTRL2,
+ ASP1_H_TX_CTRL1,
+ ASP1_H_TX_CTRL2,
+ ASP2_A_TX_CTRL1,
+ ASP2_A_TX_CTRL2,
+ ASP2_B_TX_CTRL1,
+ ASP2_B_TX_CTRL2,
+ ASP2_C_TX_CTRL1,
+ ASP2_C_TX_CTRL2,
+ ASP2_D_TX_CTRL1,
+ ASP2_D_TX_CTRL2,
+ ASP2_E_TX_CTRL1,
+ ASP2_E_TX_CTRL2,
+ ASP2_F_TX_CTRL1,
+ ASP2_F_TX_CTRL2,
+ ASP2_G_TX_CTRL1,
+ ASP2_G_TX_CTRL2,
+ ASP2_H_TX_CTRL1,
+ ASP2_H_TX_CTRL2,
+ ASP1_A_RX_CTRL1,
+ ASP1_A_RX_CTRL2,
+ ASP1_B_RX_CTRL1,
+ ASP1_B_RX_CTRL2,
+ ASP1_C_RX_CTRL1,
+ ASP1_C_RX_CTRL2,
+ ASP1_D_RX_CTRL1,
+ ASP1_D_RX_CTRL2,
+ ASP1_E_RX_CTRL1,
+ ASP1_E_RX_CTRL2,
+ ASP1_F_RX_CTRL1,
+ ASP1_F_RX_CTRL2,
+ ASP1_G_RX_CTRL1,
+ ASP1_G_RX_CTRL2,
+ ASP1_H_RX_CTRL1,
+ ASP1_H_RX_CTRL2,
+ ASP2_A_RX_CTRL1,
+ ASP2_A_RX_CTRL2,
+ ASP2_B_RX_CTRL1,
+ ASP2_B_RX_CTRL2,
+ ASP2_C_RX_CTRL1,
+ ASP2_C_RX_CTRL2,
+ ASP2_D_RX_CTRL1,
+ ASP2_D_RX_CTRL2,
+ ASP2_E_RX_CTRL1,
+ ASP2_E_RX_CTRL2,
+ ASP2_F_RX_CTRL1,
+ ASP2_F_RX_CTRL2,
+ ASP2_G_RX_CTRL1,
+ ASP2_G_RX_CTRL2,
+ ASP2_H_RX_CTRL1,
+ ASP2_H_RX_CTRL2,
+ CS8409_I2C_ADDR,
+ CS8409_I2C_DATA,
+ CS8409_I2C_CTRL,
+ CS8409_I2C_STS,
+ CS8409_I2C_QWRITE,
+ CS8409_I2C_QREAD,
+ CS8409_SPI_CTRL,
+ CS8409_SPI_TX_DATA,
+ CS8409_SPI_RX_DATA,
+ CS8409_SPI_STS,
+ CS8409_PFE_COEF_W1, /* Parametric filter engine coefficient write 1*/
+ CS8409_PFE_COEF_W2,
+ CS8409_PFE_CTRL1,
+ CS8409_PFE_CTRL2,
+ CS8409_PRE_SCALE_ATTN1,
+ CS8409_PRE_SCALE_ATTN2,
+ CS8409_PFE_COEF_MON1, /* Parametric filter engine coefficient monitor 1*/
+ CS8409_PFE_COEF_MON2,
+ CS8409_ASP1_INTRN_STS,
+ CS8409_ASP2_INTRN_STS,
+ CS8409_ASP1_RX_SCLK_COUNT,
+ CS8409_ASP1_TX_SCLK_COUNT,
+ CS8409_ASP2_RX_SCLK_COUNT,
+ CS8409_ASP2_TX_SCLK_COUNT,
+ CS8409_ASP_UNS_RESP_MASK,
+ CS8409_LOOPBACK_CTRL = 0x80,
+ CS8409_PAD_CFG_SLW_RATE_CTRL = 0x82, /* Pad Config and Slew Rate Control (CIR = 0x0082) */
+};
-#define CS42L42_I2C_ADDR (0x48 << 1)
+/* CS42L42 Specific Definitions */
-#define CIR_I2C_ADDR 0x0059
-#define CIR_I2C_DATA 0x005A
-#define CIR_I2C_CTRL 0x005B
-#define CIR_I2C_STATUS 0x005C
-#define CIR_I2C_QWRITE 0x005D
-#define CIR_I2C_QREAD 0x005E
+#define CS42L42_HP_CH (2U)
+#define CS42L42_HS_MIC_CH (1U)
#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63)
#define CS8409_CS42L42_HP_VOL_REAL_MAX (0)
@@ -57,6 +225,21 @@
#define CS8409_CS42L42_REG_HS_VOLUME_CHA (0x2301)
#define CS8409_CS42L42_REG_HS_VOLUME_CHB (0x2303)
#define CS8409_CS42L42_REG_AMIC_VOLUME (0x1D03)
+#define CS42L42_HSDET_AUTO_DONE (0x02)
+#define CS42L42_HSTYPE_MASK (0x03)
+#define CS42L42_JACK_INSERTED (0x0C)
+#define CS42L42_JACK_REMOVED (0x00)
+
+/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */
+
+#define CS42L42_I2C_ADDR (0x48 << 1)
+#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */
+#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */
+#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
+#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A
+#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
+#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
+#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
enum {
CS8409_BULLSEYE,
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 04/27] ALSA: hda/cs8409: Mask all CS42L42 interrupts on initialization
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (2 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 03/27] ALSA: hda/cs8409: Use enums for register names and coefficients Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 05/27] ALSA: hda/cs8409: Reduce HS pops/clicks for Cyborg Vitaly Rodionov
` (22 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 5766433325a9..91b6a5b2824a 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -159,6 +159,18 @@ const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
{ 0x1105, 0x00 },
{ 0x1112, 0xC0 },
{ 0x1101, 0x02 },
+ { 0x1316, 0xff },
+ { 0x1317, 0xff },
+ { 0x1318, 0xff },
+ { 0x1319, 0xff },
+ { 0x131a, 0xff },
+ { 0x131b, 0xff },
+ { 0x131c, 0xff },
+ { 0x131e, 0xff },
+ { 0x131f, 0xff },
+ { 0x1320, 0xff },
+ { 0x1b79, 0xff },
+ { 0x1b7a, 0xff },
{} /* Terminator */
};
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 05/27] ALSA: hda/cs8409: Reduce HS pops/clicks for Cyborg
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (3 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 04/27] ALSA: hda/cs8409: Mask all CS42L42 interrupts on initialization Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 06/27] ALSA: hda/cs8409: Disable unnecessary Ring Sense for Cyborg/Warlock/Bullseye Vitaly Rodionov
` (21 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Enable HSBIAS_SENSE_EN for Cyborg during jack detect to reduce
pops and clicks.
Do not enable this for Warlock/Bullseye, as it causes ESD issues.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index e4319a0b9cf6..1745f8b188c6 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -390,8 +390,14 @@ static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
mutex_lock(&spec->cs8409_i2c_mux);
- /* Set TIP_SENSE_EN for analog front-end of tip sense. */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1);
+ /* Set TIP_SENSE_EN for analog front-end of tip sense.
+ * Additionally set HSBIAS_SENSE_EN for some variants.
+ */
+ if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_BULLSEYE)
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1);
+ else
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x00a0, 1);
+
/* Clear WAKE# */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0001, 1);
/* Wait ~2.5ms */
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 06/27] ALSA: hda/cs8409: Disable unnecessary Ring Sense for Cyborg/Warlock/Bullseye
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (4 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 05/27] ALSA: hda/cs8409: Reduce HS pops/clicks for Cyborg Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 07/27] ALSA: hda/cs8409: Disable unsolicited responses during suspend Vitaly Rodionov
` (20 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Also remove unnecessary repeated register writes.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 91b6a5b2824a..07d3ae28c105 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -153,11 +153,9 @@ const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
{ 0x1121, 0xF3 },
{ 0x1103, 0x20 },
{ 0x1105, 0x00 },
- { 0x1112, 0xC0 },
+ { 0x1112, 0x00 },
{ 0x1113, 0x80 },
{ 0x1C03, 0xC0 },
- { 0x1105, 0x00 },
- { 0x1112, 0xC0 },
{ 0x1101, 0x02 },
{ 0x1316, 0xff },
{ 0x1317, 0xff },
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 07/27] ALSA: hda/cs8409: Disable unsolicited responses during suspend
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (5 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 06/27] ALSA: hda/cs8409: Disable unnecessary Ring Sense for Cyborg/Warlock/Bullseye Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 08/27] ALSA: hda/cs8409: Disable unsolicited response for the first boot Vitaly Rodionov
` (19 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Ensure unsolicited responses cannot occur whilst the sub codecs are
being disabled.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 1745f8b188c6..4906d2913603 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -563,12 +563,26 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
+/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
+static void cs8409_enable_ur(struct hda_codec *codec, int flag)
+{
+ /* GPIO4 INT# and GPIO3 WAKE# */
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
+ flag ? CS8409_CS42L42_INT : 0);
+
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
+ flag ? AC_UNSOL_ENABLED : 0);
+
+}
+
#ifdef CONFIG_PM
/* Manage PDREF, when transition to D3hot */
static int cs8409_suspend(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
+ cs8409_enable_ur(codec, 0);
+
mutex_lock(&spec->cs8409_i2c_mux);
/* Power down CS42L42 ASP/EQ/MIX/HP */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1);
@@ -582,18 +596,6 @@ static int cs8409_suspend(struct hda_codec *codec)
}
#endif
-/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
-static void cs8409_enable_ur(struct hda_codec *codec, int flag)
-{
- /* GPIO4 INT# and GPIO3 WAKE# */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
- flag ? CS8409_CS42L42_INT : 0);
-
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
- flag ? AC_UNSOL_ENABLED : 0);
-
-}
-
/* Vendor specific HW configuration
* PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
*/
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 08/27] ALSA: hda/cs8409: Disable unsolicited response for the first boot
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (6 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 07/27] ALSA: hda/cs8409: Disable unsolicited responses during suspend Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 09/27] ALSA: hda/cs8409: Mask CS42L42 wake events Vitaly Rodionov
` (18 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
The subsequence suspend and remuse have their enable/disable
unsolicited responses at the correct place already
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 4906d2913603..2ed07ab3f47e 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -621,9 +621,6 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
for (; seq_bullseye->nid; seq_bullseye++)
cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
- /* Disable Unsolicited Response during boot */
- cs8409_enable_ur(codec, 0);
-
/* Reset CS42L42 */
cs8409_cs42l42_reset(codec);
@@ -795,6 +792,8 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
HDA_INPUT, 0, 0xff, 0x19);
snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_hp_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_amic_volume_mixer);
+ /* Disable Unsolicited Response during boot */
+ cs8409_enable_ur(codec, 0);
cs8409_cs42l42_hw_init(codec);
snd_hda_codec_set_name(codec, "CS8409/CS42L42");
break;
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 09/27] ALSA: hda/cs8409: Mask CS42L42 wake events
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (7 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 08/27] ALSA: hda/cs8409: Disable unsolicited response for the first boot Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 10/27] ALSA: hda/cs8409: Simplify CS42L42 jack detect Vitaly Rodionov
` (17 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Wake events are not needed for jack detect.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 2ed07ab3f47e..f4401c1e8572 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -399,11 +399,11 @@ static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x00a0, 1);
/* Clear WAKE# */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0001, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C1, 1);
/* Wait ~2.5ms */
usleep_range(2500, 3000);
/* Set mode WAKE# output follows the combination logic directly */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0020, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C0, 1);
/* Clear interrupts status */
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 10/27] ALSA: hda/cs8409: Simplify CS42L42 jack detect.
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (8 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 09/27] ALSA: hda/cs8409: Mask CS42L42 wake events Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 11/27] ALSA: hda/cs8409: Prevent I2C access during suspend time Vitaly Rodionov
` (16 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Cleanup interrupt masks.
Remove unnecessary read/writes.
Ensure Tip Sense/Type Detection interrupts are enabled/disabled
when needed.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index f4401c1e8572..4ad832f5c4ba 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -406,10 +406,8 @@ static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C0, 1);
/* Clear interrupts status */
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
/* Enable interrupt */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0x03, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b79, 0x00, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3, 1);
mutex_unlock(&spec->cs8409_i2c_mux);
}
@@ -424,11 +422,13 @@ static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
/* Clear interrupts */
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xFF, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87, 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86, 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0x01, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFD, 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80, 1);
/* Wait ~110ms*/
usleep_range(110000, 200000);
@@ -487,9 +487,6 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1);
reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
- /* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1);
-
mutex_unlock(&spec->cs8409_i2c_mux);
/* If status values are < 0, read error has occurred. */
@@ -499,6 +496,11 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
/* HSDET_AUTO_DONE */
if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
+ mutex_lock(&spec->cs8409_i2c_mux);
+ /* Disable HSDET_AUTO_DONE */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFF, 1);
+ mutex_unlock(&spec->cs8409_i2c_mux);
+
type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
/* CS42L42 reports optical jack as type 4
* We don't handle optical jack
@@ -521,6 +523,11 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
+ mutex_lock(&spec->cs8409_i2c_mux);
+ /* Re-Enable Tip Sense Interrupt */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3, 1);
+ mutex_unlock(&spec->cs8409_i2c_mux);
+
} else {
/* TIP_SENSE INSERT/REMOVE */
switch (reg_ts_status) {
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 11/27] ALSA: hda/cs8409: Prevent I2C access during suspend time
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (9 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 10/27] ALSA: hda/cs8409: Simplify CS42L42 jack detect Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 12/27] ALSA: hda/cs8409: Generalize volume controls Vitaly Rodionov
` (15 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 14 ++++++++++++++
sound/pci/hda/patch_cs8409.h | 1 +
2 files changed, 15 insertions(+)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 4ad832f5c4ba..0b13bcecd778 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -127,9 +127,13 @@ static int cs8409_i2c_wait_complete(struct hda_codec *codec)
static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
unsigned int paged)
{
+ struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
unsigned int read_data;
+ if (spec->cs42l42_suspended)
+ return -EPERM;
+
cs8409_enable_i2c_clock(codec, 1);
cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
@@ -172,8 +176,12 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
unsigned int i2c_data, unsigned int paged)
{
+ struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
+ if (spec->cs42l42_suspended)
+ return -EPERM;
+
cs8409_enable_i2c_clock(codec, 1);
cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
@@ -371,6 +379,8 @@ static void cs8409_cs42l42_reset(struct hda_codec *codec)
/* wait ~10ms */
usleep_range(10000, 15000);
+ spec->cs42l42_suspended = 0;
+
mutex_lock(&spec->cs8409_i2c_mux);
/* Clear interrupts, by reading interrupt status registers */
@@ -594,6 +604,9 @@ static int cs8409_suspend(struct hda_codec *codec)
/* Power down CS42L42 ASP/EQ/MIX/HP */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1);
mutex_unlock(&spec->cs8409_i2c_mux);
+
+ spec->cs42l42_suspended = 1;
+
/* Assert CS42L42 RTS# line */
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
@@ -759,6 +772,7 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->cs42l42_hp_jack_in = 0;
spec->cs42l42_mic_jack_in = 0;
+ spec->cs42l42_suspended = 1;
/* Basic initial sequence for specific hw configuration */
snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 1d3ce28415fa..0f2084b6ec8e 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -269,6 +269,7 @@ struct cs8409_spec {
unsigned int cs42l42_hp_jack_in:1;
unsigned int cs42l42_mic_jack_in:1;
unsigned int cs42l42_volume_init:1;
+ unsigned int cs42l42_suspended:1;
char cs42l42_hp_volume[CS42L42_HP_CH];
char cs42l42_hs_mic_volume[CS42L42_HS_MIC_CH];
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 12/27] ALSA: hda/cs8409: Generalize volume controls
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (10 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 11/27] ALSA: hda/cs8409: Prevent I2C access during suspend time Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 13/27] ALSA: hda/cs8409: Dont disable I2C clock between consecutive accesses Vitaly Rodionov
` (14 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Use amp offsets as indexes for saved volumes.
Remove dependencies on NID inside volume control functions.
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 37 +++++++
sound/pci/hda/patch_cs8409.c | 165 +++++++++-------------------
sound/pci/hda/patch_cs8409.h | 27 +++--
3 files changed, 105 insertions(+), 124 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 07d3ae28c105..b03ddef2a25f 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -10,6 +10,43 @@
#include "patch_cs8409.h"
+/******************************************************************************
+ * CS42L42 Specific Data
+ *
+ ******************************************************************************/
+
+static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale,
+ CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale,
+ CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
+
+const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs8409_cs42l42_volume_info,
+ .get = cs8409_cs42l42_volume_get,
+ .put = cs8409_cs42l42_volume_put,
+ .tlv = { .p = cs42l42_dac_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, 0,
+ HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
+};
+
+const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs8409_cs42l42_volume_info,
+ .get = cs8409_cs42l42_volume_get,
+ .put = cs8409_cs42l42_volume_put,
+ .tlv = { .p = cs42l42_adc_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, 0,
+ HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
+};
+
/* Dell Inspiron platforms
* with cs8409 bridge and cs42l42 codec
*/
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 0b13bcecd778..08205c19698c 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -208,162 +208,97 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
return 0;
}
-static int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
+int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
{
- u16 nid = get_amp_nid(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
u8 chs = get_amp_channels(kctrl);
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.step = 1;
+ uinfo->count = chs == 3 ? 2 : 1;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
uinfo->value.integer.min = CS8409_CS42L42_HP_VOL_REAL_MIN;
uinfo->value.integer.max = CS8409_CS42L42_HP_VOL_REAL_MAX;
break;
- case CS8409_CS42L42_AMIC_PIN_NID:
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
+ case CS42L42_VOL_ADC:
uinfo->value.integer.min = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
uinfo->value.integer.max = CS8409_CS42L42_AMIC_VOL_REAL_MAX;
break;
default:
break;
}
- return 0;
-}
-static void cs8409_cs42l42_update_volume(struct hda_codec *codec)
-{
- struct cs8409_spec *spec = codec->spec;
- int data;
-
- mutex_lock(&spec->cs8409_i2c_mux);
- data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHA, 1);
- if (data >= 0)
- spec->cs42l42_hp_volume[0] = -data;
- else
- spec->cs42l42_hp_volume[0] = CS8409_CS42L42_HP_VOL_REAL_MIN;
- data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHB, 1);
- if (data >= 0)
- spec->cs42l42_hp_volume[1] = -data;
- else
- spec->cs42l42_hp_volume[1] = CS8409_CS42L42_HP_VOL_REAL_MIN;
- data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOLUME, 1);
- if (data >= 0)
- spec->cs42l42_hs_mic_volume[0] = -data;
- else
- spec->cs42l42_hs_mic_volume[0] = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
- mutex_unlock(&spec->cs8409_i2c_mux);
- spec->cs42l42_volume_init = 1;
+ return 0;
}
-static int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kctrl);
int chs = get_amp_channels(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
long *valp = uctrl->value.integer.value;
- if (!spec->cs42l42_volume_init) {
- snd_hda_power_up(codec);
- cs8409_cs42l42_update_volume(codec);
- snd_hda_power_down(codec);
- }
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
if (chs & BIT(0))
- *valp++ = spec->cs42l42_hp_volume[0];
+ *valp++ = spec->vol[ofs];
if (chs & BIT(1))
- *valp++ = spec->cs42l42_hp_volume[1];
+ *valp = spec->vol[ofs+1];
break;
- case CS8409_CS42L42_AMIC_PIN_NID:
+ case CS42L42_VOL_ADC:
if (chs & BIT(0))
- *valp++ = spec->cs42l42_hs_mic_volume[0];
+ *valp = spec->vol[ofs];
break;
default:
break;
}
+
return 0;
}
-static int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kctrl);
int chs = get_amp_channels(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
long *valp = uctrl->value.integer.value;
- int change = 0;
- char vol;
- snd_hda_power_up(codec);
- switch (nid) {
- case CS8409_CS42L42_HP_PIN_NID:
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
mutex_lock(&spec->cs8409_i2c_mux);
if (chs & BIT(0)) {
- vol = -(*valp);
- change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHA, vol, 1);
- valp++;
+ spec->vol[ofs] = *valp;
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
+ -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
}
if (chs & BIT(1)) {
- vol = -(*valp);
- change |= cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_HS_VOLUME_CHB, vol, 1);
+ ofs++;
+ valp++;
+ spec->vol[ofs] = *valp;
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
+ -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
}
mutex_unlock(&spec->cs8409_i2c_mux);
break;
- case CS8409_CS42L42_AMIC_PIN_NID:
+ case CS42L42_VOL_ADC:
mutex_lock(&spec->cs8409_i2c_mux);
if (chs & BIT(0)) {
- change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
- CS8409_CS42L42_REG_AMIC_VOLUME, (char)*valp, 1);
- valp++;
+ spec->vol[ofs] = *valp;
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
+ spec->vol[ofs] & CS8409_CS42L42_REG_AMIC_VOL_MASK, 1);
}
mutex_unlock(&spec->cs8409_i2c_mux);
break;
default:
break;
}
- cs8409_cs42l42_update_volume(codec);
- snd_hda_power_down(codec);
- return change;
-}
-
-static const DECLARE_TLV_DB_SCALE(cs8409_cs42l42_hp_db_scale,
- CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
-
-static const DECLARE_TLV_DB_SCALE(cs8409_cs42l42_amic_db_scale,
- CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
-
-static const struct snd_kcontrol_new cs8409_cs42l42_hp_volume_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .index = 0,
- .name = "Headphone Playback Volume",
- .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .info = cs8409_cs42l42_volume_info,
- .get = cs8409_cs42l42_volume_get,
- .put = cs8409_cs42l42_volume_put,
- .tlv = { .p = cs8409_cs42l42_hp_db_scale },
- .private_value = HDA_COMPOSE_AMP_VAL(CS8409_CS42L42_HP_PIN_NID, 3, 0, HDA_OUTPUT) |
- HDA_AMP_VAL_MIN_MUTE
-};
-static const struct snd_kcontrol_new cs8409_cs42l42_amic_volume_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .index = 0,
- .name = "Mic Capture Volume",
- .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .info = cs8409_cs42l42_volume_info,
- .get = cs8409_cs42l42_volume_get,
- .put = cs8409_cs42l42_volume_put,
- .tlv = { .p = cs8409_cs42l42_amic_db_scale },
- .private_value = HDA_COMPOSE_AMP_VAL(CS8409_CS42L42_AMIC_PIN_NID, 1, 0, HDA_INPUT) |
- HDA_AMP_VAL_MIN_MUTE
-};
+ return 0;
+}
/* Assert/release RTS# line to CS42L42 */
static void cs8409_cs42l42_reset(struct hda_codec *codec)
@@ -657,18 +592,14 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
}
/* Restore Volumes after Resume */
- if (spec->cs42l42_volume_init) {
- mutex_lock(&spec->cs8409_i2c_mux);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHA,
- -spec->cs42l42_hp_volume[0], 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHB,
- -spec->cs42l42_hp_volume[1], 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOLUME,
- spec->cs42l42_hs_mic_volume[0], 1);
- mutex_unlock(&spec->cs8409_i2c_mux);
- }
-
- cs8409_cs42l42_update_volume(codec);
+ mutex_lock(&spec->cs8409_i2c_mux);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
+ -(spec->vol[1]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
+ -(spec->vol[2]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
+ spec->vol[0] & CS8409_CS42L42_REG_AMIC_VOL_MASK, 1);
+ mutex_unlock(&spec->cs8409_i2c_mux);
cs8409_cs42l42_enable_jack_detect(codec);
@@ -811,8 +742,10 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
/* Set initial DMIC volume to -26 dB */
snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
HDA_INPUT, 0, 0xff, 0x19);
- snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_hp_volume_mixer);
- snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_amic_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
+ &cs42l42_adc_volume_mixer);
/* Disable Unsolicited Response during boot */
cs8409_enable_ur(codec, 0);
cs8409_cs42l42_hw_init(codec);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 0f2084b6ec8e..bf0e8a4cc4cc 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -215,16 +215,17 @@ enum cs8409_coefficient_index_registers {
/* CS42L42 Specific Definitions */
-#define CS42L42_HP_CH (2U)
-#define CS42L42_HS_MIC_CH (1U)
+#define CS42L42_VOLUMES (4U)
#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63)
#define CS8409_CS42L42_HP_VOL_REAL_MAX (0)
#define CS8409_CS42L42_AMIC_VOL_REAL_MIN (-97)
#define CS8409_CS42L42_AMIC_VOL_REAL_MAX (12)
-#define CS8409_CS42L42_REG_HS_VOLUME_CHA (0x2301)
-#define CS8409_CS42L42_REG_HS_VOLUME_CHB (0x2303)
-#define CS8409_CS42L42_REG_AMIC_VOLUME (0x1D03)
+#define CS8409_CS42L42_REG_HS_VOL_CHA (0x2301)
+#define CS8409_CS42L42_REG_HS_VOL_CHB (0x2303)
+#define CS8409_CS42L42_REG_HS_VOL_MASK (0x003F)
+#define CS8409_CS42L42_REG_AMIC_VOL (0x1D03)
+#define CS8409_CS42L42_REG_AMIC_VOL_MASK (0x00FF)
#define CS42L42_HSDET_AUTO_DONE (0x02)
#define CS42L42_HSTYPE_MASK (0x03)
#define CS42L42_JACK_INSERTED (0x0C)
@@ -248,6 +249,11 @@ enum {
CS8409_FIXUPS,
};
+enum {
+ CS42L42_VOL_ADC,
+ CS42L42_VOL_DAC,
+};
+
struct cs8409_i2c_param {
unsigned int addr;
unsigned int reg;
@@ -268,10 +274,8 @@ struct cs8409_spec {
unsigned int cs42l42_hp_jack_in:1;
unsigned int cs42l42_mic_jack_in:1;
- unsigned int cs42l42_volume_init:1;
unsigned int cs42l42_suspended:1;
- char cs42l42_hp_volume[CS42L42_HP_CH];
- char cs42l42_hs_mic_volume[CS42L42_HS_MIC_CH];
+ s8 vol[CS42L42_VOLUMES];
struct mutex cs8409_i2c_mux;
@@ -280,6 +284,13 @@ struct cs8409_spec {
unsigned int *res);
};
+extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
+extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer;
+
+int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo);
+int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+
extern const struct snd_pci_quirk cs8409_fixup_tbl[];
extern const struct hda_model_fixup cs8409_models[];
extern const struct hda_fixup cs8409_fixups[];
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 13/27] ALSA: hda/cs8409: Dont disable I2C clock between consecutive accesses
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (11 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 12/27] ALSA: hda/cs8409: Generalize volume controls Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-27 6:47 ` Takashi Iwai
2021-07-26 17:46 ` [PATCH 14/27] ALSA: hda/cs8409: Avoid setting the same I2C address for every access Vitaly Rodionov
` (13 subsequent siblings)
26 siblings, 1 reply; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Only disable I2C clock 25 ms after not being used
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 42 +++++++++++++++++++++++-------------
sound/pci/hda/patch_cs8409.h | 4 ++++
2 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 08205c19698c..335bcdc69106 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -53,6 +53,7 @@ static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
if (!spec)
return NULL;
codec->spec = spec;
+ spec->codec = codec;
codec->power_save_node = 1;
snd_hda_gen_spec_init(&spec->gen);
@@ -72,21 +73,34 @@ static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int
snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef);
}
-/**
+/*
+ * cs8409_disable_i2c_clock - Worker that disable the I2C Clock after 25ms without use
+ */
+static void cs8409_disable_i2c_clock(struct work_struct *work)
+{
+ struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work);
+
+ cs8409_vendor_coef_set(spec->codec, 0x0,
+ cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7);
+ spec->i2c_clck_enabled = 0;
+}
+
+/*
* cs8409_enable_i2c_clock - Enable I2C clocks
* @codec: the codec instance
- * @enable: Enable or disable I2C clocks
- *
* Enable or Disable I2C clocks.
*/
-static void cs8409_enable_i2c_clock(struct hda_codec *codec, unsigned int enable)
+static void cs8409_enable_i2c_clock(struct hda_codec *codec)
{
- unsigned int retval;
- unsigned int newval;
+ struct cs8409_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->i2c_clk_work);
- retval = cs8409_vendor_coef_get(codec, 0x0);
- newval = (enable) ? (retval | 0x8) : (retval & 0xfffffff7);
- cs8409_vendor_coef_set(codec, 0x0, newval);
+ if (!spec->i2c_clck_enabled) {
+ cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8);
+ spec->i2c_clck_enabled = 1;
+ }
+ queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25));
}
/**
@@ -134,7 +148,7 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
if (spec->cs42l42_suspended)
return -EPERM;
- cs8409_enable_i2c_clock(codec, 1);
+ cs8409_enable_i2c_clock(codec);
cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
if (paged) {
@@ -157,8 +171,6 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
/* Register in bits 15-8 and the data in 7-0 */
read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
- cs8409_enable_i2c_clock(codec, 0);
-
return read_data & 0x0ff;
}
@@ -182,7 +194,7 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
if (spec->cs42l42_suspended)
return -EPERM;
- cs8409_enable_i2c_clock(codec, 1);
+ cs8409_enable_i2c_clock(codec);
cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
if (paged) {
@@ -203,8 +215,6 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
return -EIO;
}
- cs8409_enable_i2c_clock(codec, 0);
-
return 0;
}
@@ -705,6 +715,8 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->cs42l42_mic_jack_in = 0;
spec->cs42l42_suspended = 1;
+ INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock);
+
/* Basic initial sequence for specific hw configuration */
snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index bf0e8a4cc4cc..542582c213d2 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -11,6 +11,7 @@
#include <linux/pci.h>
#include <sound/tlv.h>
+#include <linux/workqueue.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
@@ -267,6 +268,7 @@ struct cs8409_cir_param {
struct cs8409_spec {
struct hda_gen_spec gen;
+ struct hda_codec *codec;
unsigned int gpio_mask;
unsigned int gpio_dir;
@@ -278,6 +280,8 @@ struct cs8409_spec {
s8 vol[CS42L42_VOLUMES];
struct mutex cs8409_i2c_mux;
+ unsigned int i2c_clck_enabled;
+ struct delayed_work i2c_clk_work;
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 13/27] ALSA: hda/cs8409: Dont disable I2C clock between consecutive accesses
2021-07-26 17:46 ` [PATCH 13/27] ALSA: hda/cs8409: Dont disable I2C clock between consecutive accesses Vitaly Rodionov
@ 2021-07-27 6:47 ` Takashi Iwai
0 siblings, 0 replies; 31+ messages in thread
From: Takashi Iwai @ 2021-07-27 6:47 UTC (permalink / raw)
To: Vitaly Rodionov
Cc: Jaroslav Kysela, Takashi Iwai, alsa-devel, patches, linux-kernel,
Lucas Tanure
On Mon, 26 Jul 2021 19:46:26 +0200,
Vitaly Rodionov wrote:
>
> From: Lucas Tanure <tanureal@opensource.cirrus.com>
>
> Only disable I2C clock 25 ms after not being used
This is error-prone and might be racy, I'm afraid.
e.g. the offloaded work isn't canceled when unbinding the driver, so
it'll lead to a use-after-free. And the work doesn't take a mutex so
it may conflict with re-enabling side.
Above all, there is no description "why" this has to be done so.
Is it about the performance, or any other reason?
thanks,
Takashi
>
> Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
> Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
> ---
> sound/pci/hda/patch_cs8409.c | 42 +++++++++++++++++++++++-------------
> sound/pci/hda/patch_cs8409.h | 4 ++++
> 2 files changed, 31 insertions(+), 15 deletions(-)
>
> diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
> index 08205c19698c..335bcdc69106 100644
> --- a/sound/pci/hda/patch_cs8409.c
> +++ b/sound/pci/hda/patch_cs8409.c
> @@ -53,6 +53,7 @@ static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
> if (!spec)
> return NULL;
> codec->spec = spec;
> + spec->codec = codec;
> codec->power_save_node = 1;
> snd_hda_gen_spec_init(&spec->gen);
>
> @@ -72,21 +73,34 @@ static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int
> snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef);
> }
>
> -/**
> +/*
> + * cs8409_disable_i2c_clock - Worker that disable the I2C Clock after 25ms without use
> + */
> +static void cs8409_disable_i2c_clock(struct work_struct *work)
> +{
> + struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work);
> +
> + cs8409_vendor_coef_set(spec->codec, 0x0,
> + cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7);
> + spec->i2c_clck_enabled = 0;
> +}
> +
> +/*
> * cs8409_enable_i2c_clock - Enable I2C clocks
> * @codec: the codec instance
> - * @enable: Enable or disable I2C clocks
> - *
> * Enable or Disable I2C clocks.
> */
> -static void cs8409_enable_i2c_clock(struct hda_codec *codec, unsigned int enable)
> +static void cs8409_enable_i2c_clock(struct hda_codec *codec)
> {
> - unsigned int retval;
> - unsigned int newval;
> + struct cs8409_spec *spec = codec->spec;
> +
> + cancel_delayed_work_sync(&spec->i2c_clk_work);
>
> - retval = cs8409_vendor_coef_get(codec, 0x0);
> - newval = (enable) ? (retval | 0x8) : (retval & 0xfffffff7);
> - cs8409_vendor_coef_set(codec, 0x0, newval);
> + if (!spec->i2c_clck_enabled) {
> + cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8);
> + spec->i2c_clck_enabled = 1;
> + }
> + queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25));
> }
>
> /**
> @@ -134,7 +148,7 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
> if (spec->cs42l42_suspended)
> return -EPERM;
>
> - cs8409_enable_i2c_clock(codec, 1);
> + cs8409_enable_i2c_clock(codec);
> cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
>
> if (paged) {
> @@ -157,8 +171,6 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
> /* Register in bits 15-8 and the data in 7-0 */
> read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
>
> - cs8409_enable_i2c_clock(codec, 0);
> -
> return read_data & 0x0ff;
> }
>
> @@ -182,7 +194,7 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
> if (spec->cs42l42_suspended)
> return -EPERM;
>
> - cs8409_enable_i2c_clock(codec, 1);
> + cs8409_enable_i2c_clock(codec);
> cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
>
> if (paged) {
> @@ -203,8 +215,6 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
> return -EIO;
> }
>
> - cs8409_enable_i2c_clock(codec, 0);
> -
> return 0;
> }
>
> @@ -705,6 +715,8 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
> spec->cs42l42_mic_jack_in = 0;
> spec->cs42l42_suspended = 1;
>
> + INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock);
> +
> /* Basic initial sequence for specific hw configuration */
> snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
>
> diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
> index bf0e8a4cc4cc..542582c213d2 100644
> --- a/sound/pci/hda/patch_cs8409.h
> +++ b/sound/pci/hda/patch_cs8409.h
> @@ -11,6 +11,7 @@
>
> #include <linux/pci.h>
> #include <sound/tlv.h>
> +#include <linux/workqueue.h>
> #include <sound/hda_codec.h>
> #include "hda_local.h"
> #include "hda_auto_parser.h"
> @@ -267,6 +268,7 @@ struct cs8409_cir_param {
>
> struct cs8409_spec {
> struct hda_gen_spec gen;
> + struct hda_codec *codec;
>
> unsigned int gpio_mask;
> unsigned int gpio_dir;
> @@ -278,6 +280,8 @@ struct cs8409_spec {
> s8 vol[CS42L42_VOLUMES];
>
> struct mutex cs8409_i2c_mux;
> + unsigned int i2c_clck_enabled;
> + struct delayed_work i2c_clk_work;
>
> /* verb exec op override */
> int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 14/27] ALSA: hda/cs8409: Avoid setting the same I2C address for every access
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (12 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 13/27] ALSA: hda/cs8409: Dont disable I2C clock between consecutive accesses Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 15/27] ALSA: hda/cs8409: Avoid re-setting the same page as the last access Vitaly Rodionov
` (12 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 19 +++++++++++++++++--
sound/pci/hda/patch_cs8409.h | 1 +
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 335bcdc69106..474b6750e9b4 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -128,6 +128,21 @@ static int cs8409_i2c_wait_complete(struct hda_codec *codec)
return -1;
}
+/**
+ * cs8409_set_i2c_dev_addr - Set i2c address for transaction
+ * @codec: the codec instance
+ * @addr: I2C Address
+ */
+static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->dev_addr != addr) {
+ cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr);
+ spec->dev_addr = addr;
+ }
+}
+
/**
* cs8409_i2c_read - CS8409 I2C Read.
* @codec: the codec instance
@@ -149,7 +164,7 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
return -EPERM;
cs8409_enable_i2c_clock(codec);
- cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
+ cs8409_set_i2c_dev_addr(codec, i2c_address);
if (paged) {
cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
@@ -195,7 +210,7 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
return -EPERM;
cs8409_enable_i2c_clock(codec);
- cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, i2c_address);
+ cs8409_set_i2c_dev_addr(codec, i2c_address);
if (paged) {
cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 542582c213d2..c2c208218e34 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -281,6 +281,7 @@ struct cs8409_spec {
struct mutex cs8409_i2c_mux;
unsigned int i2c_clck_enabled;
+ unsigned int dev_addr;
struct delayed_work i2c_clk_work;
/* verb exec op override */
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 15/27] ALSA: hda/cs8409: Avoid re-setting the same page as the last access
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (13 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 14/27] ALSA: hda/cs8409: Avoid setting the same I2C address for every access Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 16/27] ALSA: hda/cs8409: Support i2c bulk read/write functions Vitaly Rodionov
` (11 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 124 +++++++++++++++++++----------------
sound/pci/hda/patch_cs8409.h | 2 +
2 files changed, 71 insertions(+), 55 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 474b6750e9b4..2f9ffc26bbf5 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -143,18 +143,37 @@ static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr)
}
}
+/**
+ * cs8409_i2c_set_page - CS8409 I2C set page register.
+ * @codec: the codec instance
+ * @i2c_reg: Page register
+ *
+ * Returns negative on error.
+ */
+static int cs8409_i2c_set_page(struct hda_codec *codec, unsigned int i2c_reg)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->paged && (spec->last_page != (i2c_reg >> 8))) {
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ return -EIO;
+ spec->last_page = i2c_reg >> 8;
+ }
+
+ return 0;
+}
+
/**
* cs8409_i2c_read - CS8409 I2C Read.
* @codec: the codec instance
* @i2c_address: I2C Address
* @i2c_reg: Register to read
- * @paged: Is a paged transaction
*
* CS8409 I2C Read.
* Returns negative on error, otherwise returns read value in bits 0-7.
*/
-static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
- unsigned int paged)
+static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg)
{
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
@@ -166,13 +185,10 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
cs8409_enable_i2c_clock(codec);
cs8409_set_i2c_dev_addr(codec, i2c_address);
- if (paged) {
- cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
+ if (cs8409_i2c_set_page(codec, i2c_reg)) {
+ codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
+ __func__, i2c_address, i2c_reg);
+ return -EIO;
}
i2c_reg_data = (i2c_reg << 8) & 0x0ffff;
@@ -195,13 +211,12 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
* @i2c_address: I2C Address
* @i2c_reg: Register to write to
* @i2c_data: Data to write
- * @paged: Is a paged transaction
*
* CS8409 I2C Write.
* Returns negative on error, otherwise returns 0.
*/
static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
- unsigned int i2c_data, unsigned int paged)
+ unsigned int i2c_data)
{
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
@@ -212,13 +227,10 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
cs8409_enable_i2c_clock(codec);
cs8409_set_i2c_dev_addr(codec, i2c_address);
- if (paged) {
- cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
+ if (cs8409_i2c_set_page(codec, i2c_reg)) {
+ codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
+ __func__, i2c_address, i2c_reg);
+ return -EIO;
}
i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | (i2c_data & 0x0ff);
@@ -298,14 +310,14 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
if (chs & BIT(0)) {
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
- -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
+ -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK);
}
if (chs & BIT(1)) {
ofs++;
valp++;
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
- -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
+ -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK);
}
mutex_unlock(&spec->cs8409_i2c_mux);
break;
@@ -314,7 +326,7 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
if (chs & BIT(0)) {
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
- spec->vol[ofs] & CS8409_CS42L42_REG_AMIC_VOL_MASK, 1);
+ spec->vol[ofs] & CS8409_CS42L42_REG_AMIC_VOL_MASK);
}
mutex_unlock(&spec->cs8409_i2c_mux);
break;
@@ -340,14 +352,15 @@ static void cs8409_cs42l42_reset(struct hda_codec *codec)
usleep_range(10000, 15000);
spec->cs42l42_suspended = 0;
+ spec->last_page = 0;
mutex_lock(&spec->cs8409_i2c_mux);
/* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F);
mutex_unlock(&spec->cs8409_i2c_mux);
@@ -364,20 +377,20 @@ static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
* Additionally set HSBIAS_SENSE_EN for some variants.
*/
if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_BULLSEYE)
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020);
else
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x00a0, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x00a0);
/* Clear WAKE# */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C1, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C1);
/* Wait ~2.5ms */
usleep_range(2500, 3000);
/* Set mode WAKE# output follows the combination logic directly */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C0, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C0);
/* Clear interrupts status */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
/* Enable interrupt */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
mutex_unlock(&spec->cs8409_i2c_mux);
}
@@ -390,20 +403,20 @@ static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
mutex_lock(&spec->cs8409_i2c_mux);
/* Clear interrupts */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xFF, 1);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
-
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFD, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80, 1);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xFF);
+ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
+
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFD);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80);
/* Wait ~110ms*/
usleep_range(110000, 200000);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77, 1);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0);
/* Wait ~10ms */
usleep_range(10000, 25000);
@@ -419,7 +432,7 @@ static void cs8409_cs42l42_reg_setup(struct hda_codec *codec)
mutex_lock(&spec->cs8409_i2c_mux);
for (; seq->addr; seq++)
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg);
mutex_unlock(&spec->cs8409_i2c_mux);
@@ -453,9 +466,9 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
mutex_lock(&spec->cs8409_i2c_mux);
/* Read jack detect status registers */
- reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1);
- reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1);
- reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1);
+ reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
+ reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124);
+ reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
mutex_unlock(&spec->cs8409_i2c_mux);
@@ -468,7 +481,7 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
mutex_lock(&spec->cs8409_i2c_mux);
/* Disable HSDET_AUTO_DONE */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFF, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFF);
mutex_unlock(&spec->cs8409_i2c_mux);
type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
@@ -495,7 +508,7 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
mutex_lock(&spec->cs8409_i2c_mux);
/* Re-Enable Tip Sense Interrupt */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
mutex_unlock(&spec->cs8409_i2c_mux);
} else {
@@ -562,7 +575,7 @@ static int cs8409_suspend(struct hda_codec *codec)
mutex_lock(&spec->cs8409_i2c_mux);
/* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe);
mutex_unlock(&spec->cs8409_i2c_mux);
spec->cs42l42_suspended = 1;
@@ -610,7 +623,7 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG) {
/* FULL_SCALE_VOL = 0 for Warlock / Cyborg */
mutex_lock(&spec->cs8409_i2c_mux);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01, 1);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01);
mutex_unlock(&spec->cs8409_i2c_mux);
/* DMIC1_MO=00b, DMIC1/2_SR=1 */
cs8409_vendor_coef_set(codec, 0x09, 0x0003);
@@ -619,11 +632,11 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
/* Restore Volumes after Resume */
mutex_lock(&spec->cs8409_i2c_mux);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
- -(spec->vol[1]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
+ -(spec->vol[1]) & CS8409_CS42L42_REG_HS_VOL_MASK);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
- -(spec->vol[2]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
+ -(spec->vol[2]) & CS8409_CS42L42_REG_HS_VOL_MASK);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
- spec->vol[0] & CS8409_CS42L42_REG_AMIC_VOL_MASK, 1);
+ spec->vol[0] & CS8409_CS42L42_REG_AMIC_VOL_MASK);
mutex_unlock(&spec->cs8409_i2c_mux);
cs8409_cs42l42_enable_jack_detect(codec);
@@ -730,6 +743,7 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->cs42l42_mic_jack_in = 0;
spec->cs42l42_suspended = 1;
+ spec->paged = 1;
INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock);
/* Basic initial sequence for specific hw configuration */
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index c2c208218e34..ee66fd0c01dc 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -283,6 +283,8 @@ struct cs8409_spec {
unsigned int i2c_clck_enabled;
unsigned int dev_addr;
struct delayed_work i2c_clk_work;
+ unsigned int paged;
+ unsigned int last_page;
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 16/27] ALSA: hda/cs8409: Support i2c bulk read/write functions
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (14 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 15/27] ALSA: hda/cs8409: Avoid re-setting the same page as the last access Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 17/27] ALSA: hda/cs8409: Separate CS8409, CS42L42 and project functions Vitaly Rodionov
` (10 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
This allows mutex locks to be moved into i2c functions, as
the bulk read/write functions can lock/unlock themselves to
prevent interruption of sequence reads/writes.
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 3 +-
sound/pci/hda/patch_cs8409.c | 204 +++++++++++++++++-----------
sound/pci/hda/patch_cs8409.h | 8 +-
3 files changed, 134 insertions(+), 81 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index b03ddef2a25f..0f2fd8bb92bf 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -146,7 +146,7 @@ const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
};
/* Vendor specific HW configuration for CS42L42 */
-const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
+const struct cs8409_i2c_param cs42l42_init_reg_seq[CS42L42_INIT_REG_SEQ_SIZE] = {
{ 0x1010, 0xB0 },
{ 0x1D01, 0x00 },
{ 0x1D02, 0x06 },
@@ -206,7 +206,6 @@ const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
{ 0x1320, 0xff },
{ 0x1b79, 0xff },
{ 0x1b7a, 0xff },
- {} /* Terminator */
};
/* Vendor specific hw configuration for CS8409 */
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 2f9ffc26bbf5..2f40030998b1 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -168,12 +168,12 @@ static int cs8409_i2c_set_page(struct hda_codec *codec, unsigned int i2c_reg)
* cs8409_i2c_read - CS8409 I2C Read.
* @codec: the codec instance
* @i2c_address: I2C Address
- * @i2c_reg: Register to read
+ * @addr: Register to read
*
* CS8409 I2C Read.
* Returns negative on error, otherwise returns read value in bits 0-7.
*/
-static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg)
+static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int addr)
{
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
@@ -182,41 +182,90 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
if (spec->cs42l42_suspended)
return -EPERM;
+ mutex_lock(&spec->i2c_mux);
cs8409_enable_i2c_clock(codec);
cs8409_set_i2c_dev_addr(codec, i2c_address);
- if (cs8409_i2c_set_page(codec, i2c_reg)) {
+ if (cs8409_i2c_set_page(codec, addr)) {
codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
+ __func__, i2c_address, addr);
return -EIO;
}
- i2c_reg_data = (i2c_reg << 8) & 0x0ffff;
+ i2c_reg_data = (addr << 8) & 0x0ffff;
cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
- }
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
/* Register in bits 15-8 and the data in 7-0 */
read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
+ mutex_unlock(&spec->i2c_mux);
return read_data & 0x0ff;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, i2c_address, addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence.
+ * @codec: the codec instance
+ * @seq: Register Sequence to read
+ * @count: Number of registeres to read
+ *
+ * Returns negative on error, values are read into value element of cs8409_i2c_param sequence.
+ */
+static int cs8409_i2c_bulk_read(struct hda_codec *codec, unsigned int i2c_address,
+ struct cs8409_i2c_param *seq, int count)
+{
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ int i;
+
+ if (spec->cs42l42_suspended)
+ return -EPERM;
+
+ mutex_lock(&spec->i2c_mux);
+ cs8409_set_i2c_dev_addr(codec, i2c_address);
+
+ for (i = 0; i < count; i++) {
+ cs8409_enable_i2c_clock(codec);
+ if (cs8409_i2c_set_page(codec, seq[i].addr))
+ goto error;
+
+ i2c_reg_data = (seq[i].addr << 8) & 0x0ffff;
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff;
+ }
+
+ mutex_unlock(&spec->i2c_mux);
+
+ return 0;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", i2c_address);
+ return -EIO;
}
/**
* cs8409_i2c_write - CS8409 I2C Write.
* @codec: the codec instance
* @i2c_address: I2C Address
- * @i2c_reg: Register to write to
- * @i2c_data: Data to write
+ * @addr: Register to write to
+ * @value: Data to write
*
* CS8409 I2C Write.
* Returns negative on error, otherwise returns 0.
*/
-static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int i2c_reg,
- unsigned int i2c_data)
+static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int addr,
+ unsigned int value)
{
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
@@ -224,25 +273,73 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
if (spec->cs42l42_suspended)
return -EPERM;
+ mutex_lock(&spec->i2c_mux);
+
cs8409_enable_i2c_clock(codec);
cs8409_set_i2c_dev_addr(codec, i2c_address);
- if (cs8409_i2c_set_page(codec, i2c_reg)) {
+ if (cs8409_i2c_set_page(codec, addr)) {
codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
+ __func__, i2c_address, addr);
return -EIO;
}
- i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | (i2c_data & 0x0ff);
+ i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff);
cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
- if (cs8409_i2c_wait_complete(codec) < 0) {
- codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, i2c_reg);
- return -EIO;
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ mutex_unlock(&spec->i2c_mux);
+ return 0;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, i2c_address, addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence.
+ * @codec: the codec instance
+ * @seq: Register Sequence to write
+ * @count: Number of registeres to write
+ *
+ * Returns negative on error.
+ */
+static int cs8409_i2c_bulk_write(struct hda_codec *codec, unsigned int i2c_address,
+ const struct cs8409_i2c_param *seq, int count)
+{
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ int i;
+
+ if (spec->cs42l42_suspended)
+ return -EPERM;
+
+ mutex_lock(&spec->i2c_mux);
+ cs8409_set_i2c_dev_addr(codec, i2c_address);
+
+ for (i = 0; i < count; i++) {
+ cs8409_enable_i2c_clock(codec);
+ if (cs8409_i2c_set_page(codec, seq[i].addr))
+ goto error;
+
+ i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
}
+ mutex_unlock(&spec->i2c_mux);
+
return 0;
+
+error:
+ mutex_unlock(&spec->i2c_mux);
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", i2c_address);
+ return -EIO;
}
int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
@@ -306,7 +403,6 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
switch (ofs) {
case CS42L42_VOL_DAC:
- mutex_lock(&spec->cs8409_i2c_mux);
if (chs & BIT(0)) {
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
@@ -319,16 +415,13 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
-(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK);
}
- mutex_unlock(&spec->cs8409_i2c_mux);
break;
case CS42L42_VOL_ADC:
- mutex_lock(&spec->cs8409_i2c_mux);
if (chs & BIT(0)) {
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
spec->vol[ofs] & CS8409_CS42L42_REG_AMIC_VOL_MASK);
}
- mutex_unlock(&spec->cs8409_i2c_mux);
break;
default:
break;
@@ -341,6 +434,12 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
static void cs8409_cs42l42_reset(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
+ struct cs8409_i2c_param irq_regs[] = {
+ { 0x1308, 0x00 },
+ { 0x1309, 0x00 },
+ { 0x130A, 0x00 },
+ { 0x130F, 0x00 },
+ };
/* Assert RTS# line */
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
@@ -354,25 +453,13 @@ static void cs8409_cs42l42_reset(struct hda_codec *codec)
spec->cs42l42_suspended = 0;
spec->last_page = 0;
- mutex_lock(&spec->cs8409_i2c_mux);
-
/* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-
+ cs8409_i2c_bulk_read(codec, CS42L42_I2C_ADDR, irq_regs, ARRAY_SIZE(irq_regs));
}
/* Configure CS42L42 slave codec for jack autodetect */
static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
{
- struct cs8409_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
/* Set TIP_SENSE_EN for analog front-end of tip sense.
* Additionally set HSBIAS_SENSE_EN for some variants.
*/
@@ -391,17 +478,11 @@ static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
/* Enable interrupt */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
}
/* Enable and run CS42L42 slave codec jack auto detect */
static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
{
- struct cs8409_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
/* Clear interrupts */
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77);
@@ -420,22 +501,6 @@ static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
/* Wait ~10ms */
usleep_range(10000, 25000);
- mutex_unlock(&spec->cs8409_i2c_mux);
-
-}
-
-static void cs8409_cs42l42_reg_setup(struct hda_codec *codec)
-{
- const struct cs8409_i2c_param *seq = cs42l42_init_reg_seq;
- struct cs8409_spec *spec = codec->spec;
-
- mutex_lock(&spec->cs8409_i2c_mux);
-
- for (; seq->addr; seq++)
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg);
-
- mutex_unlock(&spec->cs8409_i2c_mux);
-
}
/*
@@ -463,15 +528,11 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
if (res & CS8409_CS42L42_INT)
return;
- mutex_lock(&spec->cs8409_i2c_mux);
-
/* Read jack detect status registers */
reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124);
reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
- mutex_unlock(&spec->cs8409_i2c_mux);
-
/* If status values are < 0, read error has occurred. */
if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
return;
@@ -479,10 +540,8 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
/* HSDET_AUTO_DONE */
if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
- mutex_lock(&spec->cs8409_i2c_mux);
/* Disable HSDET_AUTO_DONE */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFF);
- mutex_unlock(&spec->cs8409_i2c_mux);
type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
/* CS42L42 reports optical jack as type 4
@@ -506,10 +565,8 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
- mutex_lock(&spec->cs8409_i2c_mux);
/* Re-Enable Tip Sense Interrupt */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
- mutex_unlock(&spec->cs8409_i2c_mux);
} else {
/* TIP_SENSE INSERT/REMOVE */
@@ -573,10 +630,8 @@ static int cs8409_suspend(struct hda_codec *codec)
cs8409_enable_ur(codec, 0);
- mutex_lock(&spec->cs8409_i2c_mux);
/* Power down CS42L42 ASP/EQ/MIX/HP */
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe);
- mutex_unlock(&spec->cs8409_i2c_mux);
spec->cs42l42_suspended = 1;
@@ -618,26 +673,23 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
cs8409_cs42l42_reset(codec);
/* Initialise CS42L42 companion codec */
- cs8409_cs42l42_reg_setup(codec);
+ cs8409_i2c_bulk_write(codec, CS42L42_I2C_ADDR, cs42l42_init_reg_seq,
+ CS42L42_INIT_REG_SEQ_SIZE);
if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG) {
/* FULL_SCALE_VOL = 0 for Warlock / Cyborg */
- mutex_lock(&spec->cs8409_i2c_mux);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01);
- mutex_unlock(&spec->cs8409_i2c_mux);
/* DMIC1_MO=00b, DMIC1/2_SR=1 */
cs8409_vendor_coef_set(codec, 0x09, 0x0003);
}
/* Restore Volumes after Resume */
- mutex_lock(&spec->cs8409_i2c_mux);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
-(spec->vol[1]) & CS8409_CS42L42_REG_HS_VOL_MASK);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
-(spec->vol[2]) & CS8409_CS42L42_REG_HS_VOL_MASK);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
spec->vol[0] & CS8409_CS42L42_REG_AMIC_VOL_MASK);
- mutex_unlock(&spec->cs8409_i2c_mux);
cs8409_cs42l42_enable_jack_detect(codec);
@@ -726,7 +778,7 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->exec_verb = codec->core.exec_verb;
codec->core.exec_verb = cs8409_cs42l42_exec_verb;
- mutex_init(&spec->cs8409_i2c_mux);
+ mutex_init(&spec->i2c_mux);
codec->patch_ops = cs8409_cs42l42_patch_ops;
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index ee66fd0c01dc..d84cda94dfb9 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -243,6 +243,8 @@ enum cs8409_coefficient_index_registers {
#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
+#define CS42L42_INIT_REG_SEQ_SIZE 59
+
enum {
CS8409_BULLSEYE,
CS8409_WARLOCK,
@@ -257,7 +259,7 @@ enum {
struct cs8409_i2c_param {
unsigned int addr;
- unsigned int reg;
+ unsigned int value;
};
struct cs8409_cir_param {
@@ -279,7 +281,7 @@ struct cs8409_spec {
unsigned int cs42l42_suspended:1;
s8 vol[CS42L42_VOLUMES];
- struct mutex cs8409_i2c_mux;
+ struct mutex i2c_mux;
unsigned int i2c_clck_enabled;
unsigned int dev_addr;
struct delayed_work i2c_clk_work;
@@ -303,7 +305,7 @@ extern const struct hda_model_fixup cs8409_models[];
extern const struct hda_fixup cs8409_fixups[];
extern const struct hda_verb cs8409_cs42l42_init_verbs[];
extern const struct hda_pintbl cs8409_cs42l42_pincfgs[];
-extern const struct cs8409_i2c_param cs42l42_init_reg_seq[];
+extern const struct cs8409_i2c_param cs42l42_init_reg_seq[CS42L42_INIT_REG_SEQ_SIZE];
extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 17/27] ALSA: hda/cs8409: Separate CS8409, CS42L42 and project functions
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (15 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 16/27] ALSA: hda/cs8409: Support i2c bulk read/write functions Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 18/27] ALSA: hda/cs8409: Move codec properties to its own struct Vitaly Rodionov
` (9 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 18 +-
sound/pci/hda/patch_cs8409.c | 315 +++++++++++++++-------------
sound/pci/hda/patch_cs8409.h | 24 +--
3 files changed, 187 insertions(+), 170 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 0f2fd8bb92bf..77a7b2f42128 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -15,20 +15,18 @@
*
******************************************************************************/
-static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale,
- CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale, CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
-static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale,
- CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale, CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .info = cs8409_cs42l42_volume_info,
- .get = cs8409_cs42l42_volume_get,
- .put = cs8409_cs42l42_volume_put,
+ .info = cs42l42_volume_info,
+ .get = cs42l42_volume_get,
+ .put = cs42l42_volume_put,
.tlv = { .p = cs42l42_dac_db_scale },
.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, 0,
HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
@@ -39,9 +37,9 @@ const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
.index = 0,
.subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .info = cs8409_cs42l42_volume_info,
- .get = cs8409_cs42l42_volume_get,
- .put = cs8409_cs42l42_volume_put,
+ .info = cs42l42_volume_info,
+ .get = cs42l42_volume_get,
+ .put = cs42l42_volume_put,
.tlv = { .p = cs42l42_adc_db_scale },
.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, 0,
HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 2f40030998b1..c17f72b332d5 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -14,6 +14,10 @@
#include "patch_cs8409.h"
+/******************************************************************************
+ * CS8409 Specific Functions
+ ******************************************************************************/
+
static int cs8409_parse_auto_config(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
@@ -45,6 +49,8 @@ static int cs8409_parse_auto_config(struct hda_codec *codec)
return 0;
}
+static void cs8409_disable_i2c_clock(struct work_struct *work);
+
static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
{
struct cs8409_spec *spec;
@@ -55,6 +61,8 @@ static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
codec->spec = spec;
spec->codec = codec;
codec->power_save_node = 1;
+ mutex_init(&spec->i2c_mux);
+ INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock);
snd_hda_gen_spec_init(&spec->gen);
return spec;
@@ -342,7 +350,67 @@ static int cs8409_i2c_bulk_write(struct hda_codec *codec, unsigned int i2c_addre
return -EIO;
}
-int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
+static int cs8409_init(struct hda_codec *codec)
+{
+ int ret = snd_hda_gen_init(codec);
+
+ if (!ret)
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return ret;
+}
+
+static int cs8409_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+
+ return 0;
+}
+
+/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
+static void cs8409_enable_ur(struct hda_codec *codec, int flag)
+{
+ /* GPIO4 INT# and GPIO3 WAKE# */
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
+ flag ? CS8409_CS42L42_INT : 0);
+
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
+ flag ? AC_UNSOL_ENABLED : 0);
+
+}
+
+static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
+{
+ int caps;
+
+ /* CS8409 is simple HDA bridge and intended to be used with a remote
+ * companion codec. Most of input/output PIN(s) have only basic
+ * capabilities. Receive and Transmit NID(s) have only OUTC and INC
+ * capabilities and no presence detect capable (PDC) and call to
+ * snd_hda_gen_build_controls() will mark them as non detectable
+ * phantom jacks. However, a companion codec may be
+ * connected to these pins which supports jack detect
+ * capabilities. We have to override pin capabilities,
+ * otherwise they will not be created as input devices.
+ */
+ caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP);
+ if (caps >= 0)
+ snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP,
+ (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
+
+ snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP));
+}
+
+/******************************************************************************
+ * CS42L42 Specific Functions
+ ******************************************************************************/
+
+int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
{
unsigned int ofs = get_amp_offset(kctrl);
u8 chs = get_amp_channels(kctrl);
@@ -353,12 +421,12 @@ int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_i
switch (ofs) {
case CS42L42_VOL_DAC:
- uinfo->value.integer.min = CS8409_CS42L42_HP_VOL_REAL_MIN;
- uinfo->value.integer.max = CS8409_CS42L42_HP_VOL_REAL_MAX;
+ uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX;
break;
case CS42L42_VOL_ADC:
- uinfo->value.integer.min = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
- uinfo->value.integer.max = CS8409_CS42L42_AMIC_VOL_REAL_MAX;
+ uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX;
break;
default:
break;
@@ -367,7 +435,7 @@ int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_i
return 0;
}
-int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
@@ -393,7 +461,7 @@ int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
return 0;
}
-int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
@@ -405,22 +473,22 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
case CS42L42_VOL_DAC:
if (chs & BIT(0)) {
spec->vol[ofs] = *valp;
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
- -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHA,
+ -(spec->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
}
if (chs & BIT(1)) {
ofs++;
valp++;
spec->vol[ofs] = *valp;
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
- -(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHB,
+ -(spec->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
}
break;
case CS42L42_VOL_ADC:
if (chs & BIT(0)) {
spec->vol[ofs] = *valp;
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
- spec->vol[ofs] & CS8409_CS42L42_REG_AMIC_VOL_MASK);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_AMIC_VOL,
+ spec->vol[ofs] & CS42L42_REG_AMIC_VOL_MASK);
}
break;
default:
@@ -430,35 +498,8 @@ int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_va
return 0;
}
-/* Assert/release RTS# line to CS42L42 */
-static void cs8409_cs42l42_reset(struct hda_codec *codec)
-{
- struct cs8409_spec *spec = codec->spec;
- struct cs8409_i2c_param irq_regs[] = {
- { 0x1308, 0x00 },
- { 0x1309, 0x00 },
- { 0x130A, 0x00 },
- { 0x130F, 0x00 },
- };
-
- /* Assert RTS# line */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
- /* wait ~10ms */
- usleep_range(10000, 15000);
- /* Release RTS# line */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
- /* wait ~10ms */
- usleep_range(10000, 15000);
-
- spec->cs42l42_suspended = 0;
- spec->last_page = 0;
-
- /* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_bulk_read(codec, CS42L42_I2C_ADDR, irq_regs, ARRAY_SIZE(irq_regs));
-}
-
/* Configure CS42L42 slave codec for jack autodetect */
-static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
+static void cs42l42_enable_jack_detect(struct hda_codec *codec)
{
/* Set TIP_SENSE_EN for analog front-end of tip sense.
* Additionally set HSBIAS_SENSE_EN for some variants.
@@ -481,7 +522,7 @@ static void cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec)
}
/* Enable and run CS42L42 slave codec jack auto detect */
-static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
+static void cs42l42_run_jack_detect(struct hda_codec *codec)
{
/* Clear interrupts */
cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
@@ -503,14 +544,7 @@ static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec)
}
-/*
- * In the case of CS8409 we do not have unsolicited events from NID's 0x24
- * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will
- * generate interrupt via gpio 4 to notify jack events. We have to overwrite
- * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
- * and then notify status via generic snd_hda_jack_unsol_event() call.
- */
-static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+static int cs42l42_jack_unsol_event(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
int status_changed = 0;
@@ -518,15 +552,6 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
int reg_hs_status;
int reg_ts_status;
int type;
- struct hda_jack_tbl *jk;
-
- /* jack_unsol_event() will be called every time gpio line changing state.
- * In this case gpio4 line goes up as a result of reading interrupt status
- * registers in previous cs8409_jack_unsol_event() call.
- * We don't need to handle this event, ignoring...
- */
- if (res & CS8409_CS42L42_INT)
- return;
/* Read jack detect status registers */
reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
@@ -535,7 +560,7 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
/* If status values are < 0, read error has occurred. */
if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
- return;
+ return -EIO;
/* HSDET_AUTO_DONE */
if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
@@ -572,7 +597,7 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
/* TIP_SENSE INSERT/REMOVE */
switch (reg_ts_status) {
case CS42L42_JACK_INSERTED:
- cs8409_cs42l42_run_jack_detect(codec);
+ cs42l42_run_jack_detect(codec);
break;
case CS42L42_JACK_REMOVED:
@@ -590,48 +615,94 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
- if (status_changed) {
+ return status_changed;
+}
+/* Assert/release RTS# line to CS42L42 */
+static void cs42l42_reset(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct cs8409_i2c_param irq_regs[] = {
+ { 0x1308, 0x00 },
+ { 0x1309, 0x00 },
+ { 0x130A, 0x00 },
+ { 0x130F, 0x00 },
+ };
+
+ /* Assert RTS# line */
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
+ /* wait ~10ms */
+ usleep_range(10000, 15000);
+ /* Release RTS# line */
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
+ /* wait ~10ms */
+ usleep_range(10000, 15000);
+
+ spec->cs42l42_suspended = 0;
+ spec->last_page = 0;
+
+ /* Clear interrupts, by reading interrupt status registers */
+ cs8409_i2c_bulk_read(codec, CS42L42_I2C_ADDR, irq_regs, ARRAY_SIZE(irq_regs));
+}
+
+#ifdef CONFIG_PM
+static void cs42l42_suspend(struct hda_codec *codec)
+{
+ /* Power down CS42L42 ASP/EQ/MIX/HP */
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe);
+}
+#endif
+
+/******************************************************************************
+ * BULLSEYE / WARLOCK / CYBORG Specific Functions
+ * CS8409/CS42L42
+ ******************************************************************************/
+
+/*
+ * In the case of CS8409 we do not have unsolicited events from NID's 0x24
+ * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via gpio 4 to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct hda_jack_tbl *jk;
+
+ /* jack_unsol_event() will be called every time gpio line changing state.
+ * In this case gpio4 line goes up as a result of reading interrupt status
+ * registers in previous cs8409_jack_unsol_event() call.
+ * We don't need to handle this event, ignoring...
+ */
+ if (res & CS8409_CS42L42_INT)
+ return;
+
+ if (cs42l42_jack_unsol_event(codec)) {
snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
spec->cs42l42_hp_jack_in ? 0 : PIN_OUT);
-
/* Report jack*/
jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
- if (jk) {
+ if (jk)
snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG);
- }
/* Report jack*/
jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0);
- if (jk) {
+ if (jk)
snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG);
- }
}
}
-/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
-static void cs8409_enable_ur(struct hda_codec *codec, int flag)
-{
- /* GPIO4 INT# and GPIO3 WAKE# */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
- flag ? CS8409_CS42L42_INT : 0);
-
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
- flag ? AC_UNSOL_ENABLED : 0);
-
-}
-
#ifdef CONFIG_PM
/* Manage PDREF, when transition to D3hot */
-static int cs8409_suspend(struct hda_codec *codec)
+static int cs8409_cs42l42_suspend(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
cs8409_enable_ur(codec, 0);
- /* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe);
+ cs42l42_suspend(codec);
spec->cs42l42_suspended = 1;
@@ -670,7 +741,7 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
/* Reset CS42L42 */
- cs8409_cs42l42_reset(codec);
+ cs42l42_reset(codec);
/* Initialise CS42L42 companion codec */
cs8409_i2c_bulk_write(codec, CS42L42_I2C_ADDR, cs42l42_init_reg_seq,
@@ -684,49 +755,27 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
}
/* Restore Volumes after Resume */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
- -(spec->vol[1]) & CS8409_CS42L42_REG_HS_VOL_MASK);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
- -(spec->vol[2]) & CS8409_CS42L42_REG_HS_VOL_MASK);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
- spec->vol[0] & CS8409_CS42L42_REG_AMIC_VOL_MASK);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHA,
+ -(spec->vol[1]) & CS42L42_REG_HS_VOL_MASK);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHB,
+ -(spec->vol[2]) & CS42L42_REG_HS_VOL_MASK);
+ cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_AMIC_VOL,
+ spec->vol[0] & CS42L42_REG_AMIC_VOL_MASK);
- cs8409_cs42l42_enable_jack_detect(codec);
+ cs42l42_enable_jack_detect(codec);
/* Enable Unsolicited Response */
cs8409_enable_ur(codec, 1);
}
-static int cs8409_cs42l42_init(struct hda_codec *codec)
-{
- int ret = snd_hda_gen_init(codec);
-
- if (!ret)
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
-
- return ret;
-}
-
-static int cs8409_build_controls(struct hda_codec *codec)
-{
- int err;
-
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
- return err;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
-
- return 0;
-}
-
static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
.build_controls = cs8409_build_controls,
.build_pcms = snd_hda_gen_build_pcms,
- .init = cs8409_cs42l42_init,
+ .init = cs8409_init,
.free = snd_hda_gen_free,
.unsol_event = cs8409_jack_unsol_event,
#ifdef CONFIG_PM
- .suspend = cs8409_suspend,
+ .suspend = cs8409_cs42l42_suspend,
#endif
};
@@ -769,7 +818,6 @@ static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, u
void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
{
struct cs8409_spec *spec = codec->spec;
- int caps;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
@@ -778,8 +826,6 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->exec_verb = codec->core.exec_verb;
codec->core.exec_verb = cs8409_cs42l42_exec_verb;
- mutex_init(&spec->i2c_mux);
-
codec->patch_ops = cs8409_cs42l42_patch_ops;
spec->gen.suppress_auto_mute = 1;
@@ -796,40 +842,13 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->cs42l42_suspended = 1;
spec->paged = 1;
- INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock);
/* Basic initial sequence for specific hw configuration */
snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
- /* CS8409 is simple HDA bridge and intended to be used with a remote
- * companion codec. Most of input/output PIN(s) have only basic
- * capabilities. NID(s) 0x24 and 0x34 have only OUTC and INC
- * capabilities and no presence detect capable (PDC) and call to
- * snd_hda_gen_build_controls() will mark them as non detectable
- * phantom jacks. However, in this configuration companion codec
- * CS42L42 is connected to these pins and it has jack detect
- * capabilities. We have to override pin capabilities,
- * otherwise they will not be created as input devices.
- */
- caps = snd_hdac_read_parm(&codec->core, CS8409_CS42L42_HP_PIN_NID,
- AC_PAR_PIN_CAP);
- if (caps >= 0)
- snd_hdac_override_parm(&codec->core,
- CS8409_CS42L42_HP_PIN_NID, AC_PAR_PIN_CAP,
- (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
-
- caps = snd_hdac_read_parm(&codec->core, CS8409_CS42L42_AMIC_PIN_NID,
- AC_PAR_PIN_CAP);
- if (caps >= 0)
- snd_hdac_override_parm(&codec->core,
- CS8409_CS42L42_AMIC_PIN_NID, AC_PAR_PIN_CAP,
- (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
-
- snd_hda_override_wcaps(codec, CS8409_CS42L42_HP_PIN_NID,
- (get_wcaps(codec, CS8409_CS42L42_HP_PIN_NID) | AC_WCAP_UNSOL_CAP));
-
- snd_hda_override_wcaps(codec, CS8409_CS42L42_AMIC_PIN_NID,
- (get_wcaps(codec, CS8409_CS42L42_AMIC_PIN_NID) | AC_WCAP_UNSOL_CAP));
+ cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
+ cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
+
break;
case HDA_FIXUP_ACT_PROBE:
/* Set initial DMIC volume to -26 dB */
@@ -853,7 +872,7 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
* been already plugged in.
* Run immediately after init.
*/
- cs8409_cs42l42_run_jack_detect(codec);
+ cs42l42_run_jack_detect(codec);
usleep_range(100000, 150000);
break;
default:
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index d84cda94dfb9..ac68cca2bc11 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -218,15 +218,15 @@ enum cs8409_coefficient_index_registers {
#define CS42L42_VOLUMES (4U)
-#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63)
-#define CS8409_CS42L42_HP_VOL_REAL_MAX (0)
-#define CS8409_CS42L42_AMIC_VOL_REAL_MIN (-97)
-#define CS8409_CS42L42_AMIC_VOL_REAL_MAX (12)
-#define CS8409_CS42L42_REG_HS_VOL_CHA (0x2301)
-#define CS8409_CS42L42_REG_HS_VOL_CHB (0x2303)
-#define CS8409_CS42L42_REG_HS_VOL_MASK (0x003F)
-#define CS8409_CS42L42_REG_AMIC_VOL (0x1D03)
-#define CS8409_CS42L42_REG_AMIC_VOL_MASK (0x00FF)
+#define CS42L42_HP_VOL_REAL_MIN (-63)
+#define CS42L42_HP_VOL_REAL_MAX (0)
+#define CS42L42_AMIC_VOL_REAL_MIN (-97)
+#define CS42L42_AMIC_VOL_REAL_MAX (12)
+#define CS42L42_REG_HS_VOL_CHA (0x2301)
+#define CS42L42_REG_HS_VOL_CHB (0x2303)
+#define CS42L42_REG_HS_VOL_MASK (0x003F)
+#define CS42L42_REG_AMIC_VOL (0x1D03)
+#define CS42L42_REG_AMIC_VOL_MASK (0x00FF)
#define CS42L42_HSDET_AUTO_DONE (0x02)
#define CS42L42_HSTYPE_MASK (0x03)
#define CS42L42_JACK_INSERTED (0x0C)
@@ -296,9 +296,9 @@ struct cs8409_spec {
extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer;
-int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo);
-int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
-int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo);
+int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
extern const struct snd_pci_quirk cs8409_fixup_tbl[];
extern const struct hda_model_fixup cs8409_models[];
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 18/27] ALSA: hda/cs8409: Move codec properties to its own struct
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (16 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 17/27] ALSA: hda/cs8409: Separate CS8409, CS42L42 and project functions Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 19/27] ALSA: hda/cs8409: Support multiple sub_codecs for Suspend/Resume/Unsol events Vitaly Rodionov
` (8 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure
From: Lucas Tanure <tanureal@opensource.cirrus.com>
To accommodate move, cs42l42_resume has been added to mirror
the existing function cs42l42_suspend.
Function cs42l42_reset is no longer required, since cs42l42_resume
and cs42l42_suspend perform the same operations.
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 183 ++++++++--------
sound/pci/hda/patch_cs8409.c | 313 ++++++++++++++--------------
sound/pci/hda/patch_cs8409.h | 39 +++-
3 files changed, 284 insertions(+), 251 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 77a7b2f42128..117c70536ff0 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -28,7 +28,7 @@ const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
.get = cs42l42_volume_get,
.put = cs42l42_volume_put,
.tlv = { .p = cs42l42_dac_db_scale },
- .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, 0,
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, CS8409_CODEC0,
HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
};
@@ -41,89 +41,14 @@ const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
.get = cs42l42_volume_get,
.put = cs42l42_volume_put,
.tlv = { .p = cs42l42_adc_db_scale },
- .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, 0,
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, CS8409_CODEC0,
HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
};
-/* Dell Inspiron platforms
- * with cs8409 bridge and cs42l42 codec
- */
-const struct snd_pci_quirk cs8409_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
- SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
- SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
- SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
- {} /* terminator */
-};
-
-/* Dell Inspiron models with cs8409/cs42l42 */
-const struct hda_model_fixup cs8409_models[] = {
- { .id = CS8409_BULLSEYE, .name = "bullseye" },
- { .id = CS8409_WARLOCK, .name = "warlock" },
- { .id = CS8409_CYBORG, .name = "cyborg" },
- {}
-};
-
-const struct hda_fixup cs8409_fixups[] = {
- [CS8409_BULLSEYE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_WARLOCK] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_CYBORG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = cs8409_cs42l42_pincfgs,
- .chained = true,
- .chain_id = CS8409_FIXUPS,
- },
- [CS8409_FIXUPS] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = cs8409_cs42l42_fixups,
- },
-};
+/******************************************************************************
+ * BULLSEYE / WARLOCK / CYBORG Specific Arrays
+ * CS8409/CS42L42
+ ******************************************************************************/
const struct hda_verb cs8409_cs42l42_init_verbs[] = {
{ CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
@@ -144,7 +69,7 @@ const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
};
/* Vendor specific HW configuration for CS42L42 */
-const struct cs8409_i2c_param cs42l42_init_reg_seq[CS42L42_INIT_REG_SEQ_SIZE] = {
+static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
{ 0x1010, 0xB0 },
{ 0x1D01, 0x00 },
{ 0x1D02, 0x06 },
@@ -310,3 +235,97 @@ const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
{ CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 },
{} /* Terminator */
};
+
+struct sub_codec cs8409_cs42l42_codec = {
+ .addr = CS42L42_I2C_ADDR,
+ .reset_gpio = CS8409_CS42L42_RESET,
+ .irq_mask = CS8409_CS42L42_INT,
+ .init_seq = cs42l42_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+};
+
+/******************************************************************************
+ * CS8409 Patch Driver Structs
+ * Arrays Used for all projects using CS8409
+ ******************************************************************************/
+
+const struct snd_pci_quirk cs8409_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+ {} /* terminator */
+};
+
+/* Dell Inspiron models with cs8409/cs42l42 */
+const struct hda_model_fixup cs8409_models[] = {
+ { .id = CS8409_BULLSEYE, .name = "bullseye" },
+ { .id = CS8409_WARLOCK, .name = "warlock" },
+ { .id = CS8409_CYBORG, .name = "cyborg" },
+ {}
+};
+
+const struct hda_fixup cs8409_fixups[] = {
+ [CS8409_BULLSEYE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_CYBORG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cs42l42_fixups,
+ },
+};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index c17f72b332d5..df65a5908201 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -153,20 +153,20 @@ static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr)
/**
* cs8409_i2c_set_page - CS8409 I2C set page register.
- * @codec: the codec instance
+ * @scodec: the codec instance
* @i2c_reg: Page register
*
* Returns negative on error.
*/
-static int cs8409_i2c_set_page(struct hda_codec *codec, unsigned int i2c_reg)
+static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg)
{
- struct cs8409_spec *spec = codec->spec;
+ struct hda_codec *codec = scodec->codec;
- if (spec->paged && (spec->last_page != (i2c_reg >> 8))) {
+ if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) {
cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
if (cs8409_i2c_wait_complete(codec) < 0)
return -EIO;
- spec->last_page = i2c_reg >> 8;
+ scodec->last_page = i2c_reg >> 8;
}
return 0;
@@ -174,31 +174,27 @@ static int cs8409_i2c_set_page(struct hda_codec *codec, unsigned int i2c_reg)
/**
* cs8409_i2c_read - CS8409 I2C Read.
- * @codec: the codec instance
- * @i2c_address: I2C Address
+ * @scodec: the codec instance
* @addr: Register to read
*
- * CS8409 I2C Read.
* Returns negative on error, otherwise returns read value in bits 0-7.
*/
-static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int addr)
+static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr)
{
+ struct hda_codec *codec = scodec->codec;
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
unsigned int read_data;
- if (spec->cs42l42_suspended)
+ if (scodec->suspended)
return -EPERM;
mutex_lock(&spec->i2c_mux);
cs8409_enable_i2c_clock(codec);
- cs8409_set_i2c_dev_addr(codec, i2c_address);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
- if (cs8409_i2c_set_page(codec, addr)) {
- codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, addr);
- return -EIO;
- }
+ if (cs8409_i2c_set_page(scodec, addr))
+ goto error;
i2c_reg_data = (addr << 8) & 0x0ffff;
cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
@@ -213,34 +209,34 @@ static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
error:
mutex_unlock(&spec->i2c_mux);
- codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, i2c_address, addr);
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
return -EIO;
}
/**
* cs8409_i2c_bulk_read - CS8409 I2C Read Sequence.
- * @codec: the codec instance
+ * @scodec: the codec instance
* @seq: Register Sequence to read
* @count: Number of registeres to read
*
* Returns negative on error, values are read into value element of cs8409_i2c_param sequence.
*/
-static int cs8409_i2c_bulk_read(struct hda_codec *codec, unsigned int i2c_address,
- struct cs8409_i2c_param *seq, int count)
+static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count)
{
+ struct hda_codec *codec = scodec->codec;
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
int i;
- if (spec->cs42l42_suspended)
+ if (scodec->suspended)
return -EPERM;
mutex_lock(&spec->i2c_mux);
- cs8409_set_i2c_dev_addr(codec, i2c_address);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
for (i = 0; i < count; i++) {
cs8409_enable_i2c_clock(codec);
- if (cs8409_i2c_set_page(codec, seq[i].addr))
+ if (cs8409_i2c_set_page(scodec, seq[i].addr))
goto error;
i2c_reg_data = (seq[i].addr << 8) & 0x0ffff;
@@ -258,39 +254,34 @@ static int cs8409_i2c_bulk_read(struct hda_codec *codec, unsigned int i2c_addres
error:
mutex_unlock(&spec->i2c_mux);
- codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", i2c_address);
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
return -EIO;
}
/**
* cs8409_i2c_write - CS8409 I2C Write.
- * @codec: the codec instance
- * @i2c_address: I2C Address
+ * @scodec: the codec instance
* @addr: Register to write to
* @value: Data to write
*
- * CS8409 I2C Write.
* Returns negative on error, otherwise returns 0.
*/
-static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int addr,
- unsigned int value)
+static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value)
{
+ struct hda_codec *codec = scodec->codec;
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
- if (spec->cs42l42_suspended)
+ if (scodec->suspended)
return -EPERM;
mutex_lock(&spec->i2c_mux);
cs8409_enable_i2c_clock(codec);
- cs8409_set_i2c_dev_addr(codec, i2c_address);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
- if (cs8409_i2c_set_page(codec, addr)) {
- codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
- __func__, i2c_address, addr);
- return -EIO;
- }
+ if (cs8409_i2c_set_page(scodec, addr))
+ goto error;
i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff);
cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
@@ -303,34 +294,35 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
error:
mutex_unlock(&spec->i2c_mux);
- codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, i2c_address, addr);
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
return -EIO;
}
/**
* cs8409_i2c_bulk_write - CS8409 I2C Write Sequence.
- * @codec: the codec instance
+ * @scodec: the codec instance
* @seq: Register Sequence to write
* @count: Number of registeres to write
*
* Returns negative on error.
*/
-static int cs8409_i2c_bulk_write(struct hda_codec *codec, unsigned int i2c_address,
- const struct cs8409_i2c_param *seq, int count)
+static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq,
+ int count)
{
+ struct hda_codec *codec = scodec->codec;
struct cs8409_spec *spec = codec->spec;
unsigned int i2c_reg_data;
int i;
- if (spec->cs42l42_suspended)
+ if (scodec->suspended)
return -EPERM;
mutex_lock(&spec->i2c_mux);
- cs8409_set_i2c_dev_addr(codec, i2c_address);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
for (i = 0; i < count; i++) {
cs8409_enable_i2c_clock(codec);
- if (cs8409_i2c_set_page(codec, seq[i].addr))
+ if (cs8409_i2c_set_page(scodec, seq[i].addr))
goto error;
i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff);
@@ -346,7 +338,7 @@ static int cs8409_i2c_bulk_write(struct hda_codec *codec, unsigned int i2c_addre
error:
mutex_unlock(&spec->i2c_mux);
- codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", i2c_address);
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
return -EIO;
}
@@ -439,6 +431,7 @@ int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
int chs = get_amp_channels(kctrl);
unsigned int ofs = get_amp_offset(kctrl);
long *valp = uctrl->value.integer.value;
@@ -446,13 +439,13 @@ int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
switch (ofs) {
case CS42L42_VOL_DAC:
if (chs & BIT(0))
- *valp++ = spec->vol[ofs];
+ *valp++ = cs42l42->vol[ofs];
if (chs & BIT(1))
- *valp = spec->vol[ofs+1];
+ *valp = cs42l42->vol[ofs+1];
break;
case CS42L42_VOL_ADC:
if (chs & BIT(0))
- *valp = spec->vol[ofs];
+ *valp = cs42l42->vol[ofs];
break;
default:
break;
@@ -465,6 +458,7 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
int chs = get_amp_channels(kctrl);
unsigned int ofs = get_amp_offset(kctrl);
long *valp = uctrl->value.integer.value;
@@ -472,23 +466,23 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
switch (ofs) {
case CS42L42_VOL_DAC:
if (chs & BIT(0)) {
- spec->vol[ofs] = *valp;
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHA,
- -(spec->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
+ cs42l42->vol[ofs] = *valp;
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+ -(cs42l42->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
}
if (chs & BIT(1)) {
ofs++;
valp++;
- spec->vol[ofs] = *valp;
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHB,
- -(spec->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
+ cs42l42->vol[ofs] = *valp;
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+ -(cs42l42->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
}
break;
case CS42L42_VOL_ADC:
if (chs & BIT(0)) {
- spec->vol[ofs] = *valp;
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_AMIC_VOL,
- spec->vol[ofs] & CS42L42_REG_AMIC_VOL_MASK);
+ cs42l42->vol[ofs] = *valp;
+ cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+ cs42l42->vol[ofs] & CS42L42_REG_AMIC_VOL_MASK);
}
break;
default:
@@ -499,54 +493,45 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
}
/* Configure CS42L42 slave codec for jack autodetect */
-static void cs42l42_enable_jack_detect(struct hda_codec *codec)
+static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
{
- /* Set TIP_SENSE_EN for analog front-end of tip sense.
- * Additionally set HSBIAS_SENSE_EN for some variants.
- */
- if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_BULLSEYE)
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020);
- else
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x00a0);
-
+ cs8409_i2c_write(cs42l42, 0x1b70, cs42l42->hsbias_hiz);
/* Clear WAKE# */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C1);
+ cs8409_i2c_write(cs42l42, 0x1b71, 0x00C1);
/* Wait ~2.5ms */
usleep_range(2500, 3000);
/* Set mode WAKE# output follows the combination logic directly */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C0);
+ cs8409_i2c_write(cs42l42, 0x1b71, 0x00C0);
/* Clear interrupts status */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
+ cs8409_i2c_read(cs42l42, 0x130f);
/* Enable interrupt */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
+ cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
}
/* Enable and run CS42L42 slave codec jack auto detect */
-static void cs42l42_run_jack_detect(struct hda_codec *codec)
+static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
{
/* Clear interrupts */
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xFF);
- cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
-
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFD);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80);
+ cs8409_i2c_read(cs42l42, 0x1308);
+ cs8409_i2c_read(cs42l42, 0x1b77);
+ cs8409_i2c_write(cs42l42, 0x1320, 0xFF);
+ cs8409_i2c_read(cs42l42, 0x130f);
+
+ cs8409_i2c_write(cs42l42, 0x1102, 0x87);
+ cs8409_i2c_write(cs42l42, 0x1f06, 0x86);
+ cs8409_i2c_write(cs42l42, 0x1b74, 0x07);
+ cs8409_i2c_write(cs42l42, 0x131b, 0xFD);
+ cs8409_i2c_write(cs42l42, 0x1120, 0x80);
/* Wait ~110ms*/
usleep_range(110000, 200000);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0);
+ cs8409_i2c_write(cs42l42, 0x111f, 0x77);
+ cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
/* Wait ~10ms */
usleep_range(10000, 25000);
-
}
-static int cs42l42_jack_unsol_event(struct hda_codec *codec)
+static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
{
- struct cs8409_spec *spec = codec->spec;
int status_changed = 0;
int reg_cdc_status;
int reg_hs_status;
@@ -554,9 +539,9 @@ static int cs42l42_jack_unsol_event(struct hda_codec *codec)
int type;
/* Read jack detect status registers */
- reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
- reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124);
- reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
+ reg_cdc_status = cs8409_i2c_read(cs42l42, 0x1308);
+ reg_hs_status = cs8409_i2c_read(cs42l42, 0x1124);
+ reg_ts_status = cs8409_i2c_read(cs42l42, 0x130f);
/* If status values are < 0, read error has occurred. */
if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
@@ -566,45 +551,45 @@ static int cs42l42_jack_unsol_event(struct hda_codec *codec)
if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
/* Disable HSDET_AUTO_DONE */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFF);
+ cs8409_i2c_write(cs42l42, 0x131b, 0xFF);
type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
/* CS42L42 reports optical jack as type 4
* We don't handle optical jack
*/
if (type != 4) {
- if (!spec->cs42l42_hp_jack_in) {
+ if (!cs42l42->hp_jack_in) {
status_changed = 1;
- spec->cs42l42_hp_jack_in = 1;
+ cs42l42->hp_jack_in = 1;
}
/* type = 3 has no mic */
- if ((!spec->cs42l42_mic_jack_in) && (type != 3)) {
+ if ((!cs42l42->mic_jack_in) && (type != 3)) {
status_changed = 1;
- spec->cs42l42_mic_jack_in = 1;
+ cs42l42->mic_jack_in = 1;
}
} else {
- if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
+ if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
status_changed = 1;
- spec->cs42l42_hp_jack_in = 0;
- spec->cs42l42_mic_jack_in = 0;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
}
}
/* Re-Enable Tip Sense Interrupt */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
+ cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
} else {
/* TIP_SENSE INSERT/REMOVE */
switch (reg_ts_status) {
case CS42L42_JACK_INSERTED:
- cs42l42_run_jack_detect(codec);
+ cs42l42_run_jack_detect(cs42l42);
break;
case CS42L42_JACK_REMOVED:
- if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
+ if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
status_changed = 1;
- spec->cs42l42_hp_jack_in = 0;
- spec->cs42l42_mic_jack_in = 0;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
}
break;
@@ -618,10 +603,8 @@ static int cs42l42_jack_unsol_event(struct hda_codec *codec)
return status_changed;
}
-/* Assert/release RTS# line to CS42L42 */
-static void cs42l42_reset(struct hda_codec *codec)
+static void cs42l42_resume(struct sub_codec *cs42l42)
{
- struct cs8409_spec *spec = codec->spec;
struct cs8409_i2c_param irq_regs[] = {
{ 0x1308, 0x00 },
{ 0x1309, 0x00 },
@@ -629,27 +612,35 @@ static void cs42l42_reset(struct hda_codec *codec)
{ 0x130F, 0x00 },
};
- /* Assert RTS# line */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
- /* wait ~10ms */
- usleep_range(10000, 15000);
- /* Release RTS# line */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
- /* wait ~10ms */
- usleep_range(10000, 15000);
+ cs42l42->suspended = 0;
- spec->cs42l42_suspended = 0;
- spec->last_page = 0;
+ /* Initialize CS42L42 companion codec */
+ cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num);
/* Clear interrupts, by reading interrupt status registers */
- cs8409_i2c_bulk_read(codec, CS42L42_I2C_ADDR, irq_regs, ARRAY_SIZE(irq_regs));
+ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
+
+ /* Restore Volumes after Resume */
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+ -(cs42l42->vol[1]) & CS42L42_REG_HS_VOL_MASK);
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+ -(cs42l42->vol[2]) & CS42L42_REG_HS_VOL_MASK);
+ cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+ cs42l42->vol[0] & CS42L42_REG_AMIC_VOL_MASK);
+
+ if (cs42l42->full_scale_vol)
+ cs8409_i2c_write(cs42l42, 0x2001, 0x01);
+
+ cs42l42_enable_jack_detect(cs42l42);
}
#ifdef CONFIG_PM
-static void cs42l42_suspend(struct hda_codec *codec)
+static void cs42l42_suspend(struct sub_codec *cs42l42)
{
/* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe);
+ cs8409_i2c_write(cs42l42, 0x1101, 0xfe);
+ cs42l42->suspended = 1;
+ cs42l42->last_page = 0;
}
#endif
@@ -665,9 +656,10 @@ static void cs42l42_suspend(struct hda_codec *codec)
* generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
* and then notify status via generic snd_hda_jack_unsol_event() call.
*/
-static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res)
{
struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
struct hda_jack_tbl *jk;
/* jack_unsol_event() will be called every time gpio line changing state.
@@ -675,12 +667,12 @@ static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
* registers in previous cs8409_jack_unsol_event() call.
* We don't need to handle this event, ignoring...
*/
- if (res & CS8409_CS42L42_INT)
+ if (res & cs42l42->irq_mask)
return;
- if (cs42l42_jack_unsol_event(codec)) {
+ if (cs42l42_jack_unsol_event(cs42l42)) {
snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
- spec->cs42l42_hp_jack_in ? 0 : PIN_OUT);
+ cs42l42->hp_jack_in ? 0 : PIN_OUT);
/* Report jack*/
jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
if (jk)
@@ -702,9 +694,7 @@ static int cs8409_cs42l42_suspend(struct hda_codec *codec)
cs8409_enable_ur(codec, 0);
- cs42l42_suspend(codec);
-
- spec->cs42l42_suspended = 1;
+ cs42l42_suspend(spec->scodecs[CS8409_CODEC0]);
/* Assert CS42L42 RTS# line */
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
@@ -723,6 +713,7 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg;
const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn;
struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
if (spec->gpio_mask) {
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
@@ -736,33 +727,21 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
for (; seq->nid; seq++)
cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
- if (codec->fixup_id == CS8409_BULLSEYE)
+ if (codec->fixup_id == CS8409_BULLSEYE) {
for (; seq_bullseye->nid; seq_bullseye++)
cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
+ }
- /* Reset CS42L42 */
- cs42l42_reset(codec);
-
- /* Initialise CS42L42 companion codec */
- cs8409_i2c_bulk_write(codec, CS42L42_I2C_ADDR, cs42l42_init_reg_seq,
- CS42L42_INIT_REG_SEQ_SIZE);
-
- if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG) {
- /* FULL_SCALE_VOL = 0 for Warlock / Cyborg */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01);
- /* DMIC1_MO=00b, DMIC1/2_SR=1 */
+ /* DMIC1_MO=00b, DMIC1/2_SR=1 */
+ if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG)
cs8409_vendor_coef_set(codec, 0x09, 0x0003);
- }
- /* Restore Volumes after Resume */
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHA,
- -(spec->vol[1]) & CS42L42_REG_HS_VOL_MASK);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHB,
- -(spec->vol[2]) & CS42L42_REG_HS_VOL_MASK);
- cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_AMIC_VOL,
- spec->vol[0] & CS42L42_REG_AMIC_VOL_MASK);
+ /* Release RTS# line */
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
+ /* wait ~10ms */
+ usleep_range(10000, 15000);
- cs42l42_enable_jack_detect(codec);
+ cs42l42_resume(cs42l42);
/* Enable Unsolicited Response */
cs8409_enable_ur(codec, 1);
@@ -773,7 +752,7 @@ static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
.build_pcms = snd_hda_gen_build_pcms,
.init = cs8409_init,
.free = snd_hda_gen_free,
- .unsol_event = cs8409_jack_unsol_event,
+ .unsol_event = cs8409_cs42l42_jack_unsol_event,
#ifdef CONFIG_PM
.suspend = cs8409_cs42l42_suspend,
#endif
@@ -784,6 +763,7 @@ static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, u
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
unsigned int nid = ((cmd >> 20) & 0x07f);
unsigned int verb = ((cmd >> 8) & 0x0fff);
@@ -796,18 +776,16 @@ static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, u
switch (nid) {
case CS8409_CS42L42_HP_PIN_NID:
if (verb == AC_VERB_GET_PIN_SENSE) {
- *res = (spec->cs42l42_hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
return 0;
}
break;
-
case CS8409_CS42L42_AMIC_PIN_NID:
if (verb == AC_VERB_GET_PIN_SENSE) {
- *res = (spec->cs42l42_mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
return 0;
}
break;
-
default:
break;
}
@@ -826,6 +804,9 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->exec_verb = codec->core.exec_verb;
codec->core.exec_verb = cs8409_cs42l42_exec_verb;
+ spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec;
+ spec->num_scodecs = 1;
+ spec->scodecs[CS8409_CODEC0]->codec = codec;
codec->patch_ops = cs8409_cs42l42_patch_ops;
spec->gen.suppress_auto_mute = 1;
@@ -833,22 +814,38 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->gen.suppress_vmaster = 1;
/* GPIO 5 out, 3,4 in */
- spec->gpio_dir = CS8409_CS42L42_RESET;
+ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
spec->gpio_data = 0;
spec->gpio_mask = 0x03f;
- spec->cs42l42_hp_jack_in = 0;
- spec->cs42l42_mic_jack_in = 0;
- spec->cs42l42_suspended = 1;
-
- spec->paged = 1;
-
/* Basic initial sequence for specific hw configuration */
snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
+ /* Set TIP_SENSE_EN for analog front-end of tip sense.
+ * Additionally set HSBIAS_SENSE_EN and Full Scale volume for some variants.
+ */
+ switch (codec->fixup_id) {
+ case CS8409_WARLOCK:
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = 1;
+ break;
+ case CS8409_BULLSEYE:
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = 0;
+ break;
+ case CS8409_CYBORG:
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x00a0;
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = 1;
+ break;
+ default:
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0003;
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = 1;
+ break;
+ }
+
break;
case HDA_FIXUP_ACT_PROBE:
/* Set initial DMIC volume to -26 dB */
@@ -872,7 +869,7 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
* been already plugged in.
* Run immediately after init.
*/
- cs42l42_run_jack_detect(codec);
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
usleep_range(100000, 150000);
break;
default:
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index ac68cca2bc11..817df295d594 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -216,8 +216,8 @@ enum cs8409_coefficient_index_registers {
/* CS42L42 Specific Definitions */
+#define CS8409_MAX_CODECS 8
#define CS42L42_VOLUMES (4U)
-
#define CS42L42_HP_VOL_REAL_MIN (-63)
#define CS42L42_HP_VOL_REAL_MAX (0)
#define CS42L42_AMIC_VOL_REAL_MIN (-97)
@@ -243,8 +243,6 @@ enum cs8409_coefficient_index_registers {
#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
-#define CS42L42_INIT_REG_SEQ_SIZE 59
-
enum {
CS8409_BULLSEYE,
CS8409_WARLOCK,
@@ -252,6 +250,10 @@ enum {
CS8409_FIXUPS,
};
+enum {
+ CS8409_CODEC0,
+};
+
enum {
CS42L42_VOL_ADC,
CS42L42_VOL_DAC,
@@ -268,25 +270,40 @@ struct cs8409_cir_param {
unsigned int coeff;
};
+struct sub_codec {
+ struct hda_codec *codec;
+ unsigned int addr;
+ unsigned int reset_gpio;
+ unsigned int irq_mask;
+ const struct cs8409_i2c_param *init_seq;
+ unsigned int init_seq_num;
+
+ unsigned int hp_jack_in:1;
+ unsigned int mic_jack_in:1;
+ unsigned int suspended:1;
+ unsigned int paged:1;
+ unsigned int last_page;
+ unsigned int hsbias_hiz;
+ unsigned int full_scale_vol:1;
+
+ s8 vol[CS42L42_VOLUMES];
+};
+
struct cs8409_spec {
struct hda_gen_spec gen;
struct hda_codec *codec;
+ struct sub_codec *scodecs[CS8409_MAX_CODECS];
+ unsigned int num_scodecs;
+
unsigned int gpio_mask;
unsigned int gpio_dir;
unsigned int gpio_data;
- unsigned int cs42l42_hp_jack_in:1;
- unsigned int cs42l42_mic_jack_in:1;
- unsigned int cs42l42_suspended:1;
- s8 vol[CS42L42_VOLUMES];
-
struct mutex i2c_mux;
unsigned int i2c_clck_enabled;
unsigned int dev_addr;
struct delayed_work i2c_clk_work;
- unsigned int paged;
- unsigned int last_page;
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
@@ -305,9 +322,9 @@ extern const struct hda_model_fixup cs8409_models[];
extern const struct hda_fixup cs8409_fixups[];
extern const struct hda_verb cs8409_cs42l42_init_verbs[];
extern const struct hda_pintbl cs8409_cs42l42_pincfgs[];
-extern const struct cs8409_i2c_param cs42l42_init_reg_seq[CS42L42_INIT_REG_SEQ_SIZE];
extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
+extern struct sub_codec cs8409_cs42l42_codec;
void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 19/27] ALSA: hda/cs8409: Support multiple sub_codecs for Suspend/Resume/Unsol events
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (17 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 18/27] ALSA: hda/cs8409: Move codec properties to its own struct Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 20/27] ALSA: hda/cs8409: Add Support to disable jack type detection for CS42L42 Vitaly Rodionov
` (7 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 41 ++++++++++++++++++++++++------------
1 file changed, 28 insertions(+), 13 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index df65a5908201..8f02724621bd 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -364,16 +364,21 @@ static int cs8409_build_controls(struct hda_codec *codec)
return 0;
}
-/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */
+/* Enable/Disable Unsolicited Response */
static void cs8409_enable_ur(struct hda_codec *codec, int flag)
{
- /* GPIO4 INT# and GPIO3 WAKE# */
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int ur_gpios = 0;
+ int i;
+
+ for (i = 0; i < spec->num_scodecs; i++)
+ ur_gpios |= spec->scodecs[i]->irq_mask;
+
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
- flag ? CS8409_CS42L42_INT : 0);
+ flag ? ur_gpios : 0);
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
flag ? AC_UNSOL_ENABLED : 0);
-
}
static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
@@ -605,6 +610,8 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
static void cs42l42_resume(struct sub_codec *cs42l42)
{
+ struct hda_codec *codec = cs42l42->codec;
+ unsigned int gpio_data;
struct cs8409_i2c_param irq_regs[] = {
{ 0x1308, 0x00 },
{ 0x1309, 0x00 },
@@ -612,6 +619,12 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
{ 0x130F, 0x00 },
};
+ /* Bring CS42L42 out of Reset */
+ gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ gpio_data |= cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
+ usleep_range(10000, 15000);
+
cs42l42->suspended = 0;
/* Initialize CS42L42 companion codec */
@@ -637,10 +650,18 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
#ifdef CONFIG_PM
static void cs42l42_suspend(struct sub_codec *cs42l42)
{
+ struct hda_codec *codec = cs42l42->codec;
+ unsigned int gpio_data;
+
/* Power down CS42L42 ASP/EQ/MIX/HP */
cs8409_i2c_write(cs42l42, 0x1101, 0xfe);
cs42l42->suspended = 1;
cs42l42->last_page = 0;
+
+ /* Put CS42L42 into Reset */
+ gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ gpio_data &= ~cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
}
#endif
@@ -691,13 +712,12 @@ static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned in
static int cs8409_cs42l42_suspend(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
+ int i;
cs8409_enable_ur(codec, 0);
- cs42l42_suspend(spec->scodecs[CS8409_CODEC0]);
-
- /* Assert CS42L42 RTS# line */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
+ for (i = 0; i < spec->num_scodecs; i++)
+ cs42l42_suspend(spec->scodecs[i]);
snd_hda_shutup_pins(codec);
@@ -736,11 +756,6 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG)
cs8409_vendor_coef_set(codec, 0x09, 0x0003);
- /* Release RTS# line */
- snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
- /* wait ~10ms */
- usleep_range(10000, 15000);
-
cs42l42_resume(cs42l42);
/* Enable Unsolicited Response */
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 20/27] ALSA: hda/cs8409: Add Support to disable jack type detection for CS42L42
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (18 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 19/27] ALSA: hda/cs8409: Support multiple sub_codecs for Suspend/Resume/Unsol events Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 21/27] ALSA: hda/cs8409: Add support for dolphin Vitaly Rodionov
` (6 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Some hardware configurations do not support jack type detection.
Instead, for those configurations, only tip detection is supported.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 1 +
sound/pci/hda/patch_cs8409.c | 72 ++++++++++++++++-------------
sound/pci/hda/patch_cs8409.h | 1 +
3 files changed, 43 insertions(+), 31 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 117c70536ff0..be9feb84aaa2 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -246,6 +246,7 @@ struct sub_codec cs8409_cs42l42_codec = {
.mic_jack_in = 0,
.paged = 1,
.suspended = 1,
+ .no_type_dect = 0,
};
/******************************************************************************
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 8f02724621bd..081e348b16ef 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -535,6 +535,39 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
usleep_range(10000, 25000);
}
+static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
+{
+ int status_changed = 0;
+
+ /* TIP_SENSE INSERT/REMOVE */
+ switch (reg_ts_status) {
+ case CS42L42_JACK_INSERTED:
+ if (!cs42l42->hp_jack_in) {
+ if (cs42l42->no_type_dect) {
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ } else {
+ cs42l42_run_jack_detect(cs42l42);
+ }
+ }
+ break;
+
+ case CS42L42_JACK_REMOVED:
+ if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ }
+ break;
+ default:
+ /* jack in transition */
+ break;
+ }
+
+ return status_changed;
+}
+
static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
{
int status_changed = 0;
@@ -559,10 +592,13 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
cs8409_i2c_write(cs42l42, 0x131b, 0xFF);
type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
- /* CS42L42 reports optical jack as type 4
- * We don't handle optical jack
- */
- if (type != 4) {
+
+ if (cs42l42->no_type_dect) {
+ status_changed = cs42l42_handle_tip_sense(cs42l42, reg_ts_status);
+ } else if (type == 4) {
+ /* Type 4 not supported */
+ status_changed = cs42l42_handle_tip_sense(cs42l42, CS42L42_JACK_REMOVED);
+ } else {
if (!cs42l42->hp_jack_in) {
status_changed = 1;
cs42l42->hp_jack_in = 1;
@@ -572,37 +608,11 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
status_changed = 1;
cs42l42->mic_jack_in = 1;
}
- } else {
- if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
- status_changed = 1;
- cs42l42->hp_jack_in = 0;
- cs42l42->mic_jack_in = 0;
- }
}
-
/* Re-Enable Tip Sense Interrupt */
cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
-
} else {
- /* TIP_SENSE INSERT/REMOVE */
- switch (reg_ts_status) {
- case CS42L42_JACK_INSERTED:
- cs42l42_run_jack_detect(cs42l42);
- break;
-
- case CS42L42_JACK_REMOVED:
- if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
- status_changed = 1;
- cs42l42->hp_jack_in = 0;
- cs42l42->mic_jack_in = 0;
- }
- break;
-
- default:
- /* jack in transition */
- status_changed = 0;
- break;
- }
+ status_changed = cs42l42_handle_tip_sense(cs42l42, reg_ts_status);
}
return status_changed;
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 817df295d594..a105c3c9023d 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -285,6 +285,7 @@ struct sub_codec {
unsigned int last_page;
unsigned int hsbias_hiz;
unsigned int full_scale_vol:1;
+ unsigned int no_type_dect:1;
s8 vol[CS42L42_VOLUMES];
};
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 21/27] ALSA: hda/cs8409: Add support for dolphin
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (19 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 20/27] ALSA: hda/cs8409: Add Support to disable jack type detection for CS42L42 Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-27 0:25 ` kernel test robot
2021-07-26 17:46 ` [PATCH 22/27] ALSA: hda/cs8409: Enable Full Scale Volume for Line Out Codec on Dolphin Vitaly Rodionov
` (5 subsequent siblings)
26 siblings, 1 reply; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Lucas Tanure, Stefan Binding
From: Lucas Tanure <tanureal@opensource.cirrus.com>
Dolphin devices have CS8409 HDA Bridge connected to two CS42L42 codecs.
Codec 1 supports Headphone and Headset Mic.
Codec 2 supports Line Out.
Features:
- Front and Read Jacks appear as separate jacks; Removal or connection
of on jack should not affect the connection of the other.
- Front Jack only shows up on jack detection.
- Rear Jack is Phantom Jack.
- Separate Volume Controls for each Jack
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 220 ++++++++++++++++++++++++++++
sound/pci/hda/patch_cs8409.c | 196 +++++++++++++++++++++++++
sound/pci/hda/patch_cs8409.h | 24 +++
3 files changed, 440 insertions(+)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index be9feb84aaa2..6453a7ec3856 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -249,6 +249,210 @@ struct sub_codec cs8409_cs42l42_codec = {
.no_type_dect = 0,
};
+/******************************************************************************
+ * Dolphin Specific Arrays
+ * CS8409/ 2 X CS42L42
+ ******************************************************************************/
+
+const struct hda_verb dolphin_init_verbs[] = {
+ { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, DOLPHIN_WAKE }, /* WAKE from GPIO 0,4 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+const struct hda_pintbl dolphin_pincfgs[] = {
+ { 0x24, 0x022210f0 }, /* ASP-1-TX-A */
+ { 0x25, 0x010240f0 }, /* ASP-1-TX-B */
+ { 0x34, 0x02a21050 }, /* ASP-1-RX */
+ {} /* terminator */
+};
+
+/* Vendor specific HW configuration for CS42L42 */
+static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
+ { 0x1010, 0xB0 },
+ { 0x1D01, 0x00 },
+ { 0x1D02, 0x06 },
+ { 0x1D03, 0x00 },
+ { 0x1107, 0x01 },
+ { 0x1009, 0x02 },
+ { 0x1007, 0x03 },
+ { 0x1201, 0x00 },
+ { 0x1208, 0x13 },
+ { 0x1205, 0xFF },
+ { 0x1206, 0x00 },
+ { 0x1207, 0x20 },
+ { 0x1202, 0x0D },
+ { 0x2A02, 0x02 },
+ { 0x2A03, 0x00 },
+ { 0x2A04, 0x00 },
+ { 0x2A05, 0x02 },
+ { 0x2A06, 0x00 },
+ { 0x2A07, 0x20 },
+ { 0x2A01, 0x0C },
+ { 0x2902, 0x01 },
+ { 0x2903, 0x02 },
+ { 0x2904, 0x00 },
+ { 0x2905, 0x00 },
+ { 0x2901, 0x01 },
+ { 0x1101, 0x0A },
+ { 0x1102, 0x84 },
+ { 0x2301, 0x00 },
+ { 0x2303, 0x00 },
+ { 0x2302, 0x3f },
+ { 0x2001, 0x03 },
+ { 0x1B75, 0xB6 },
+ { 0x1B73, 0xC2 },
+ { 0x1129, 0x01 },
+ { 0x1121, 0xF3 },
+ { 0x1103, 0x20 },
+ { 0x1105, 0x00 },
+ { 0x1112, 0x00 },
+ { 0x1113, 0x80 },
+ { 0x1C03, 0xC0 },
+ { 0x1101, 0x02 },
+ { 0x1316, 0xff },
+ { 0x1317, 0xff },
+ { 0x1318, 0xff },
+ { 0x1319, 0xff },
+ { 0x131a, 0xff },
+ { 0x131b, 0xff },
+ { 0x131c, 0xff },
+ { 0x131e, 0xff },
+ { 0x131f, 0xff },
+ { 0x1320, 0xff },
+ { 0x1b79, 0xff },
+ { 0x1b7a, 0xff }
+};
+
+static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
+ { 0x1010, 0xB0 },
+ { 0x1D01, 0x00 },
+ { 0x1D02, 0x06 },
+ { 0x1D03, 0x00 },
+ { 0x1107, 0x01 },
+ { 0x1009, 0x02 },
+ { 0x1007, 0x03 },
+ { 0x1201, 0x00 },
+ { 0x1208, 0x13 },
+ { 0x1205, 0xFF },
+ { 0x1206, 0x00 },
+ { 0x1207, 0x20 },
+ { 0x1202, 0x0D },
+ { 0x2A02, 0x02 },
+ { 0x2A03, 0x00 },
+ { 0x2A04, 0x80 },
+ { 0x2A05, 0x02 },
+ { 0x2A06, 0x00 },
+ { 0x2A07, 0xA0 },
+ { 0x2A01, 0x0C },
+ { 0x2902, 0x00 },
+ { 0x2903, 0x02 },
+ { 0x2904, 0x00 },
+ { 0x2905, 0x00 },
+ { 0x2901, 0x00 },
+ { 0x1101, 0x0E },
+ { 0x1102, 0x84 },
+ { 0x2301, 0x00 },
+ { 0x2303, 0x00 },
+ { 0x2302, 0x3f },
+ { 0x2001, 0x03 },
+ { 0x1B75, 0xB6 },
+ { 0x1B73, 0xC2 },
+ { 0x1129, 0x01 },
+ { 0x1121, 0xF3 },
+ { 0x1103, 0x20 },
+ { 0x1105, 0x00 },
+ { 0x1112, 0x00 },
+ { 0x1113, 0x80 },
+ { 0x1C03, 0xC0 },
+ { 0x1101, 0x02 },
+ { 0x1316, 0xff },
+ { 0x1317, 0xff },
+ { 0x1318, 0xff },
+ { 0x1319, 0xff },
+ { 0x131a, 0xff },
+ { 0x131b, 0xff },
+ { 0x131c, 0xff },
+ { 0x131e, 0xff },
+ { 0x131f, 0xff },
+ { 0x1320, 0xff },
+ { 0x1b79, 0xff },
+ { 0x1b7a, 0xff }
+};
+
+/* Vendor specific hw configuration for CS8409 */
+const struct cs8409_cir_param dolphin_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 },
+ /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 },
+ /* ASP1.B: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=128 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL1, 0x0880 },
+ /* ASP1.B: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=160 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL2, 0x08a0 },
+ /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 },
+ /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0022 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 },
+ /* ASP1_xxx_EN=1, ASP1_MCLK_EN=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0x5400 },
+ /* test mode on */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 },
+ /* GPIO hysteresis = 30 us */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 },
+ /* test mode off */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 },
+ {} /* Terminator */
+};
+
+struct sub_codec dolphin_cs42l42_0 = {
+ .addr = DOLPHIN_C0_I2C_ADDR,
+ .reset_gpio = DOLPHIN_C0_RESET,
+ .irq_mask = DOLPHIN_C0_INT,
+ .init_seq = dolphin_c0_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 0,
+};
+
+struct sub_codec dolphin_cs42l42_1 = {
+ .addr = DOLPHIN_C1_I2C_ADDR,
+ .reset_gpio = DOLPHIN_C1_RESET,
+ .irq_mask = DOLPHIN_C1_INT,
+ .init_seq = dolphin_c1_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 1,
+};
+
/******************************************************************************
* CS8409 Patch Driver Structs
* Arrays Used for all projects using CS8409
@@ -295,6 +499,11 @@ const struct snd_pci_quirk cs8409_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AD0, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD1, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD2, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD3, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0ACF, "Dolphin", CS8409_DOLPHIN),
{} /* terminator */
};
@@ -303,6 +512,7 @@ const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_BULLSEYE, .name = "bullseye" },
{ .id = CS8409_WARLOCK, .name = "warlock" },
{ .id = CS8409_CYBORG, .name = "cyborg" },
+ { .id = CS8409_DOLPHIN, .name = "dolphin" },
{}
};
@@ -329,4 +539,14 @@ const struct hda_fixup cs8409_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs8409_cs42l42_fixups,
},
+ [CS8409_DOLPHIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dolphin_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_DOLPHIN_FIXUPS,
+ },
+ [CS8409_DOLPHIN_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = dolphin_fixups,
+ },
};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 081e348b16ef..566aa84dfe5b 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -902,6 +902,202 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
}
}
+/******************************************************************************
+ * Dolphin Specific Functions
+ * CS8409/ 2 X CS42L42
+ ******************************************************************************/
+
+/*
+ * In the case of CS8409 we do not have unsolicited events when
+ * hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via irq_mask to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ struct hda_jack_tbl *jk;
+
+ cs42l42 = spec->scodecs[CS8409_CODEC0];
+ if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
+ cs42l42_jack_unsol_event(cs42l42)) {
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+
+ cs42l42 = spec->scodecs[CS8409_CODEC1];
+ if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
+ cs42l42_jack_unsol_event(cs42l42)) {
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+}
+
+/* Vendor specific HW configuration
+ * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
+ */
+static void dolphin_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = dolphin_hw_cfg;
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_resume(cs42l42);
+ }
+
+ /* Enable Unsolicited Response */
+ cs8409_enable_ur(codec, 1);
+}
+
+static const struct hda_codec_ops cs8409_dolphin_patch_ops = {
+ .build_controls = cs8409_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs8409_init,
+ .free = snd_hda_gen_free,
+ .unsol_event = dolphin_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = cs8409_cs42l42_suspend,
+#endif
+};
+
+static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ unsigned int nid = ((cmd >> 20) & 0x07f);
+ unsigned int verb = ((cmd >> 8) & 0x0fff);
+
+ /* CS8409 pins have no AC_PINSENSE_PRESENCE
+ * capabilities. We have to intercept calls for CS42L42 pins
+ * and return correct pin sense values for read_pin_sense() call from
+ * hda_jack based on CS42L42 jack detect status.
+ */
+ switch (nid) {
+ case DOLPHIN_HP_PIN_NID:
+ case DOLPHIN_LO_PIN_NID:
+ if (nid == DOLPHIN_LO_PIN_NID)
+ cs42l42 = spec->scodecs[CS8409_CODEC1];
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ case DOLPHIN_AMIC_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return spec->exec_verb(dev, cmd, flags, res);
+}
+
+void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct snd_kcontrol_new *kctrl;
+ int i;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, dolphin_init_verbs);
+ /* verb exec op override */
+ spec->exec_verb = codec->core.exec_verb;
+ codec->core.exec_verb = dolphin_exec_verb;
+
+ spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0;
+ spec->scodecs[CS8409_CODEC0]->codec = codec;
+ spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1;
+ spec->scodecs[CS8409_CODEC1]->codec = codec;
+ spec->num_scodecs = 2;
+
+ codec->patch_ops = cs8409_dolphin_patch_ops;
+
+ /* GPIO 1,5 out, 0,4 in */
+ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio |
+ spec->scodecs[CS8409_CODEC1]->reset_gpio;
+ spec->gpio_data = 0;
+ spec->gpio_mask = 0x03f;
+
+ /* Basic initial sequence for specific hw configuration */
+ snd_hda_sequence_write(codec, dolphin_init_verbs);
+
+ snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true,
+ SND_JACK_HEADPHONE, 0);
+
+ cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID);
+ cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID);
+ cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID);
+
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
+ kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ /* Update Line Out kcontrol template */
+ kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1,
+ HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE;
+ cs8409_enable_ur(codec, 0);
+ dolphin_hw_init(codec);
+ snd_hda_codec_set_name(codec, "CS8409/CS42L42");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ dolphin_hw_init(codec);
+ fallthrough;
+ case HDA_FIXUP_ACT_BUILD:
+ /* Run jack auto detect first time on boot
+ * after controls have been added, to check if jack has
+ * been already plugged in.
+ * Run immediately after init.
+ */
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42_run_jack_detect(spec->scodecs[i]);
+ usleep_range(100000, 150000);
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
static int patch_cs8409(struct hda_codec *codec)
{
int err;
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index a105c3c9023d..1b5a8d04ba0f 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -243,15 +243,32 @@ enum cs8409_coefficient_index_registers {
#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
+/* Dolphin */
+
+#define DOLPHIN_C0_I2C_ADDR (0x48 << 1)
+#define DOLPHIN_C1_I2C_ADDR (0x49 << 1)
+#define DOLPHIN_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
+#define DOLPHIN_LO_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_B
+#define DOLPHIN_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
+
+#define DOLPHIN_C0_INT GENMASK(4, 4)
+#define DOLPHIN_C1_INT GENMASK(0, 0)
+#define DOLPHIN_C0_RESET GENMASK(5, 5)
+#define DOLPHIN_C1_RESET GENMASK(1, 1)
+#define DOLPHIN_WAKE (DOLPHIN_C0_INT | DOLPHIN_C1_INT)
+
enum {
CS8409_BULLSEYE,
CS8409_WARLOCK,
CS8409_CYBORG,
CS8409_FIXUPS,
+ CS8409_DOLPHIN,
+ CS8409_DOLPHIN_FIXUPS,
};
enum {
CS8409_CODEC0,
+ CS8409_CODEC1
};
enum {
@@ -327,6 +344,13 @@ extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
extern struct sub_codec cs8409_cs42l42_codec;
+extern const struct hda_verb dolphin_init_verbs[];
+extern const struct hda_pintbl dolphin_pincfgs[];
+extern const struct cs8409_cir_param dolphin_hw_cfg[];
+extern struct sub_codec dolphin_cs42l42_0;
+extern struct sub_codec dolphin_cs42l42_1;
+
void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 21/27] ALSA: hda/cs8409: Add support for dolphin
2021-07-26 17:46 ` [PATCH 21/27] ALSA: hda/cs8409: Add support for dolphin Vitaly Rodionov
@ 2021-07-27 0:25 ` kernel test robot
0 siblings, 0 replies; 31+ messages in thread
From: kernel test robot @ 2021-07-27 0:25 UTC (permalink / raw)
To: Vitaly Rodionov, Jaroslav Kysela, Takashi Iwai
Cc: kbuild-all, alsa-devel, patches, linux-kernel, Lucas Tanure,
Stefan Binding
[-- Attachment #1: Type: text/plain, Size: 4736 bytes --]
Hi Vitaly,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on sound/for-next]
[also build test WARNING on v5.14-rc3 next-20210726]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Vitaly-Rodionov/ALSA-hda-cirrus-Split-generic-cirrus-HDA-codecs-and-CS8490-bridge-into-separate-modules/20210727-022225
base: https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: ia64-randconfig-s031-20210726 (attached as .config)
compiler: ia64-linux-gcc (GCC) 10.3.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.3-341-g8af24329-dirty
# https://github.com/0day-ci/linux/commit/25eb89e84220ba0be273351c13cd6d92fc4910b0
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Vitaly-Rodionov/ALSA-hda-cirrus-Split-generic-cirrus-HDA-codecs-and-CS8490-bridge-into-separate-modules/20210727-022225
git checkout 25eb89e84220ba0be273351c13cd6d92fc4910b0
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-10.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=ia64 SHELL=/bin/bash sound/pci/hda/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
sparse warnings: (new ones prefixed by >>)
>> sound/pci/hda/patch_cs8409.c:1061:59: sparse: sparse: Using plain integer as NULL pointer
vim +1061 sound/pci/hda/patch_cs8409.c
1029
1030 void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
1031 {
1032 struct cs8409_spec *spec = codec->spec;
1033 struct snd_kcontrol_new *kctrl;
1034 int i;
1035
1036 switch (action) {
1037 case HDA_FIXUP_ACT_PRE_PROBE:
1038 snd_hda_add_verbs(codec, dolphin_init_verbs);
1039 /* verb exec op override */
1040 spec->exec_verb = codec->core.exec_verb;
1041 codec->core.exec_verb = dolphin_exec_verb;
1042
1043 spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0;
1044 spec->scodecs[CS8409_CODEC0]->codec = codec;
1045 spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1;
1046 spec->scodecs[CS8409_CODEC1]->codec = codec;
1047 spec->num_scodecs = 2;
1048
1049 codec->patch_ops = cs8409_dolphin_patch_ops;
1050
1051 /* GPIO 1,5 out, 0,4 in */
1052 spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio |
1053 spec->scodecs[CS8409_CODEC1]->reset_gpio;
1054 spec->gpio_data = 0;
1055 spec->gpio_mask = 0x03f;
1056
1057 /* Basic initial sequence for specific hw configuration */
1058 snd_hda_sequence_write(codec, dolphin_init_verbs);
1059
1060 snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true,
> 1061 SND_JACK_HEADPHONE, 0);
1062
1063 cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID);
1064 cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID);
1065 cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID);
1066
1067 break;
1068 case HDA_FIXUP_ACT_PROBE:
1069 snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
1070 &cs42l42_dac_volume_mixer);
1071 snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
1072 kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume",
1073 &cs42l42_dac_volume_mixer);
1074 /* Update Line Out kcontrol template */
1075 kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1,
1076 HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE;
1077 cs8409_enable_ur(codec, 0);
1078 dolphin_hw_init(codec);
1079 snd_hda_codec_set_name(codec, "CS8409/CS42L42");
1080 break;
1081 case HDA_FIXUP_ACT_INIT:
1082 dolphin_hw_init(codec);
1083 fallthrough;
1084 case HDA_FIXUP_ACT_BUILD:
1085 /* Run jack auto detect first time on boot
1086 * after controls have been added, to check if jack has
1087 * been already plugged in.
1088 * Run immediately after init.
1089 */
1090 for (i = 0; i < spec->num_scodecs; i++) {
1091 cs42l42_run_jack_detect(spec->scodecs[i]);
1092 usleep_range(100000, 150000);
1093 }
1094
1095 break;
1096 default:
1097 break;
1098 }
1099 }
1100
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 28634 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 22/27] ALSA: hda/cs8409: Enable Full Scale Volume for Line Out Codec on Dolphin
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (20 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 21/27] ALSA: hda/cs8409: Add support for dolphin Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 23/27] ALSA: hda/cs8409: Set fixed sample rate of 48kHz for CS42L42 Vitaly Rodionov
` (4 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Headphones codec will keep reduced maximum volume.
Line Out codec will have increased maximum volume.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 6453a7ec3856..a39b2c20f61c 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -300,10 +300,10 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
{ 0x2901, 0x01 },
{ 0x1101, 0x0A },
{ 0x1102, 0x84 },
+ { 0x2001, 0x03 },
{ 0x2301, 0x00 },
{ 0x2303, 0x00 },
{ 0x2302, 0x3f },
- { 0x2001, 0x03 },
{ 0x1B75, 0xB6 },
{ 0x1B73, 0xC2 },
{ 0x1129, 0x01 },
@@ -356,10 +356,10 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
{ 0x2901, 0x00 },
{ 0x1101, 0x0E },
{ 0x1102, 0x84 },
+ { 0x2001, 0x01 },
{ 0x2301, 0x00 },
{ 0x2303, 0x00 },
{ 0x2302, 0x3f },
- { 0x2001, 0x03 },
{ 0x1B75, 0xB6 },
{ 0x1B73, 0xC2 },
{ 0x1129, 0x01 },
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 23/27] ALSA: hda/cs8409: Set fixed sample rate of 48kHz for CS42L42
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (21 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 22/27] ALSA: hda/cs8409: Enable Full Scale Volume for Line Out Codec on Dolphin Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 24/27] ALSA: hda/cs8409: Use timeout rather than retries for I2C transaction waits Vitaly Rodionov
` (3 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
CS42L42 is configured to use a fixed sample rate of 48kHz.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 8 ++++++++
sound/pci/hda/patch_cs8409.c | 6 ++++++
sound/pci/hda/patch_cs8409.h | 2 ++
3 files changed, 16 insertions(+)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index a39b2c20f61c..a9a0b8e3b2a9 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -45,6 +45,14 @@ const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
};
+const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback = {
+ .rates = SNDRV_PCM_RATE_48000, /* fixed rate */
+};
+
+const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture = {
+ .rates = SNDRV_PCM_RATE_48000, /* fixed rate */
+};
+
/******************************************************************************
* BULLSEYE / WARLOCK / CYBORG Specific Arrays
* CS8409/CS42L42
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 566aa84dfe5b..1dd55bf66a06 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -873,6 +873,9 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
break;
case HDA_FIXUP_ACT_PROBE:
+ /* Fix Sample Rate to 48kHz */
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
/* Set initial DMIC volume to -26 dB */
snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
HDA_INPUT, 0, 0xff, 0x19);
@@ -1066,6 +1069,9 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac
break;
case HDA_FIXUP_ACT_PROBE:
+ /* Fix Sample Rate to 48kHz */
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 1b5a8d04ba0f..2208be2ffad1 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -335,6 +335,8 @@ int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *ui
int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback;
+extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture;
extern const struct snd_pci_quirk cs8409_fixup_tbl[];
extern const struct hda_model_fixup cs8409_models[];
extern const struct hda_fixup cs8409_fixups[];
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 24/27] ALSA: hda/cs8409: Use timeout rather than retries for I2C transaction waits
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (22 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 23/27] ALSA: hda/cs8409: Set fixed sample rate of 48kHz for CS42L42 Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 25/27] ALSA: hda/cs8409: Remove unnecessary delays Vitaly Rodionov
` (2 subsequent siblings)
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 16 +++-------------
sound/pci/hda/patch_cs8409.h | 2 ++
2 files changed, 5 insertions(+), 13 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 1dd55bf66a06..e0ea27124985 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -116,24 +116,14 @@ static void cs8409_enable_i2c_clock(struct hda_codec *codec)
* @codec: the codec instance
*
* Wait for I2C transaction to complete.
- * Return -1 if transaction wait times out.
+ * Return -ETIMEDOUT if transaction wait times out.
*/
static int cs8409_i2c_wait_complete(struct hda_codec *codec)
{
- int repeat = 5;
unsigned int retval;
- do {
- retval = cs8409_vendor_coef_get(codec, CS8409_I2C_STS);
- if ((retval & 0x18) != 0x18) {
- usleep_range(2000, 4000);
- --repeat;
- } else
- return 0;
-
- } while (repeat);
-
- return -1;
+ return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18,
+ CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS);
}
/**
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 2208be2ffad1..71dbbd8e2f3b 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -231,6 +231,8 @@ enum cs8409_coefficient_index_registers {
#define CS42L42_HSTYPE_MASK (0x03)
#define CS42L42_JACK_INSERTED (0x0C)
#define CS42L42_JACK_REMOVED (0x00)
+#define CS42L42_I2C_TIMEOUT_US (20000)
+#define CS42L42_I2C_SLEEP_US (2000)
/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 25/27] ALSA: hda/cs8409: Remove unnecessary delays
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (23 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 24/27] ALSA: hda/cs8409: Use timeout rather than retries for I2C transaction waits Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 26/27] ALSA: hda/cs8409: Follow correct CS42L42 power down sequence for suspend Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 27/27] ALSA: hda/cs8409: Unmute/Mute codec when stream starts/stops Vitaly Rodionov
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Since delays when starting jack detection after initialization
have been reduced/removed, it is necessary to add back in an extra
20ms delay after the init sequence to allow the CS42L42 to power up
correctly.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index e0ea27124985..54780b008547 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -517,12 +517,10 @@ static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
cs8409_i2c_write(cs42l42, 0x1b74, 0x07);
cs8409_i2c_write(cs42l42, 0x131b, 0xFD);
cs8409_i2c_write(cs42l42, 0x1120, 0x80);
- /* Wait ~110ms*/
- usleep_range(110000, 200000);
+ /* Wait ~100us*/
+ usleep_range(100, 200);
cs8409_i2c_write(cs42l42, 0x111f, 0x77);
cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
- /* Wait ~10ms */
- usleep_range(10000, 25000);
}
static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
@@ -629,6 +627,7 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
/* Initialize CS42L42 companion codec */
cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num);
+ usleep_range(20000, 25000);
/* Clear interrupts, by reading interrupt status registers */
cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
@@ -888,7 +887,6 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
* Run immediately after init.
*/
cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
- usleep_range(100000, 150000);
break;
default:
break;
@@ -1083,10 +1081,8 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac
* been already plugged in.
* Run immediately after init.
*/
- for (i = 0; i < spec->num_scodecs; i++) {
+ for (i = 0; i < spec->num_scodecs; i++)
cs42l42_run_jack_detect(spec->scodecs[i]);
- usleep_range(100000, 150000);
- }
break;
default:
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 26/27] ALSA: hda/cs8409: Follow correct CS42L42 power down sequence for suspend
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (24 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 25/27] ALSA: hda/cs8409: Remove unnecessary delays Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
2021-07-26 17:46 ` [PATCH 27/27] ALSA: hda/cs8409: Unmute/Mute codec when stream starts/stops Vitaly Rodionov
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409.c | 23 ++++++++++++++++++++++-
sound/pci/hda/patch_cs8409.h | 2 ++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 54780b008547..64e3e0884e28 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <sound/core.h>
#include <linux/mutex.h>
+#include <linux/iopoll.h>
#include "patch_cs8409.h"
@@ -195,6 +196,7 @@ static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr)
read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
mutex_unlock(&spec->i2c_mux);
+
return read_data & 0x0ff;
error:
@@ -651,9 +653,28 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
unsigned int gpio_data;
+ int reg_cdc_status = 0;
+ const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = {
+ { 0x2301, 0x3F },
+ { 0x2302, 0x3F },
+ { 0x2303, 0x3F },
+ { 0x2001, 0x0F },
+ { 0x2A01, 0x00 },
+ { 0x1207, 0x00 },
+ { 0x1101, 0xFE },
+ { 0x1102, 0x8C },
+ { 0x1101, 0xFF },
+ };
+
+ cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq));
+
+ if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status,
+ (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US,
+ true, cs42l42, 0x1308) < 0)
+ codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n");
/* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(cs42l42, 0x1101, 0xfe);
+ cs8409_i2c_write(cs42l42, 0x1102, 0x9C);
cs42l42->suspended = 1;
cs42l42->last_page = 0;
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 71dbbd8e2f3b..09987daa9cbf 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -233,6 +233,8 @@ enum cs8409_coefficient_index_registers {
#define CS42L42_JACK_REMOVED (0x00)
#define CS42L42_I2C_TIMEOUT_US (20000)
#define CS42L42_I2C_SLEEP_US (2000)
+#define CS42L42_PDN_TIMEOUT_US (250000)
+#define CS42L42_PDN_SLEEP_US (2000)
/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 27/27] ALSA: hda/cs8409: Unmute/Mute codec when stream starts/stops
2021-07-26 17:46 [PATCH 00/27] ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules Vitaly Rodionov
` (25 preceding siblings ...)
2021-07-26 17:46 ` [PATCH 26/27] ALSA: hda/cs8409: Follow correct CS42L42 power down sequence for suspend Vitaly Rodionov
@ 2021-07-26 17:46 ` Vitaly Rodionov
26 siblings, 0 replies; 31+ messages in thread
From: Vitaly Rodionov @ 2021-07-26 17:46 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: alsa-devel, patches, linux-kernel, Stefan Binding
From: Stefan Binding <sbinding@opensource.cirrus.com>
Codec is muted on init, and then unmuted when the stream starts.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
sound/pci/hda/patch_cs8409-tables.c | 20 ++---
sound/pci/hda/patch_cs8409.c | 123 +++++++++++++++++++++++-----
sound/pci/hda/patch_cs8409.h | 7 ++
3 files changed, 120 insertions(+), 30 deletions(-)
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index a9a0b8e3b2a9..0fb0a428428b 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -81,7 +81,7 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
{ 0x1010, 0xB0 },
{ 0x1D01, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x00 },
+ { 0x1D03, 0x9F },
{ 0x1107, 0x01 },
{ 0x1009, 0x02 },
{ 0x1007, 0x03 },
@@ -111,8 +111,8 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
{ 0x2901, 0x01 },
{ 0x1101, 0x0A },
{ 0x1102, 0x84 },
- { 0x2301, 0x00 },
- { 0x2303, 0x00 },
+ { 0x2301, 0x3F },
+ { 0x2303, 0x3F },
{ 0x2302, 0x3f },
{ 0x2001, 0x03 },
{ 0x1B75, 0xB6 },
@@ -284,7 +284,7 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
{ 0x1010, 0xB0 },
{ 0x1D01, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x00 },
+ { 0x1D03, 0x9F },
{ 0x1107, 0x01 },
{ 0x1009, 0x02 },
{ 0x1007, 0x03 },
@@ -309,8 +309,8 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
{ 0x1101, 0x0A },
{ 0x1102, 0x84 },
{ 0x2001, 0x03 },
- { 0x2301, 0x00 },
- { 0x2303, 0x00 },
+ { 0x2301, 0x3F },
+ { 0x2303, 0x3F },
{ 0x2302, 0x3f },
{ 0x1B75, 0xB6 },
{ 0x1B73, 0xC2 },
@@ -340,7 +340,7 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
{ 0x1010, 0xB0 },
{ 0x1D01, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x00 },
+ { 0x1D03, 0x9F },
{ 0x1107, 0x01 },
{ 0x1009, 0x02 },
{ 0x1007, 0x03 },
@@ -365,8 +365,8 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
{ 0x1101, 0x0E },
{ 0x1102, 0x84 },
{ 0x2001, 0x01 },
- { 0x2301, 0x00 },
- { 0x2303, 0x00 },
+ { 0x2301, 0x3F },
+ { 0x2303, 0x3F },
{ 0x2302, 0x3f },
{ 0x1B75, 0xB6 },
{ 0x1B73, 0xC2 },
@@ -377,7 +377,7 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
{ 0x1112, 0x00 },
{ 0x1113, 0x80 },
{ 0x1C03, 0xC0 },
- { 0x1101, 0x02 },
+ { 0x1101, 0x06 },
{ 0x1316, 0xff },
{ 0x1317, 0xff },
{ 0x1318, 0xff },
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 64e3e0884e28..1dbdcd41baf5 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -451,6 +451,38 @@ int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
return 0;
}
+static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type,
+ unsigned int chs, bool mute)
+{
+ if (mute) {
+ if (vol_type == CS42L42_VOL_DAC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA, 0x3f);
+ if (chs & BIT(1))
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB, 0x3f);
+ } else if (vol_type == CS42L42_VOL_ADC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL, 0x9f);
+ }
+ } else {
+ if (vol_type == CS42L42_VOL_DAC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+ -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET])
+ & CS42L42_REG_HS_VOL_MASK);
+ if (chs & BIT(1))
+ cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+ -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET])
+ & CS42L42_REG_HS_VOL_MASK);
+ } else if (vol_type == CS42L42_VOL_ADC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+ cs42l42->vol[CS42L42_ADC_VOL_OFFSET]
+ & CS42L42_REG_AMIC_VOL_MASK);
+ }
+ }
+}
+
int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
@@ -462,25 +494,20 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
switch (ofs) {
case CS42L42_VOL_DAC:
- if (chs & BIT(0)) {
+ if (chs & BIT(0))
cs42l42->vol[ofs] = *valp;
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
- -(cs42l42->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
- }
if (chs & BIT(1)) {
- ofs++;
valp++;
- cs42l42->vol[ofs] = *valp;
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
- -(cs42l42->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
+ cs42l42->vol[ofs + 1] = *valp;
}
+ if (spec->playback_started)
+ cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false);
break;
case CS42L42_VOL_ADC:
- if (chs & BIT(0)) {
+ if (chs & BIT(0))
cs42l42->vol[ofs] = *valp;
- cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
- cs42l42->vol[ofs] & CS42L42_REG_AMIC_VOL_MASK);
- }
+ if (spec->capture_started)
+ cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false);
break;
default:
break;
@@ -489,6 +516,64 @@ int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
return 0;
}
+static void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+ bool mute;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mute = false;
+ spec->playback_started = 1;
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mute = true;
+ spec->playback_started = 0;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute);
+ }
+}
+
+static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+ bool mute;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mute = false;
+ spec->capture_started = 1;
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mute = true;
+ spec->capture_started = 0;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute);
+ }
+}
+
/* Configure CS42L42 slave codec for jack autodetect */
static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
{
@@ -634,14 +719,6 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
/* Clear interrupts, by reading interrupt status registers */
cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
- /* Restore Volumes after Resume */
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
- -(cs42l42->vol[1]) & CS42L42_REG_HS_VOL_MASK);
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
- -(cs42l42->vol[2]) & CS42L42_REG_HS_VOL_MASK);
- cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
- cs42l42->vol[0] & CS42L42_REG_AMIC_VOL_MASK);
-
if (cs42l42->full_scale_vol)
cs8409_i2c_write(cs42l42, 0x2001, 0x01);
@@ -886,6 +963,9 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
/* Fix Sample Rate to 48kHz */
spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
+ /* add hooks */
+ spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
/* Set initial DMIC volume to -26 dB */
snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
HDA_INPUT, 0, 0xff, 0x19);
@@ -1081,6 +1161,9 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac
/* Fix Sample Rate to 48kHz */
spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
+ /* add hooks */
+ spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 09987daa9cbf..207315ad5bf6 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -280,6 +280,10 @@ enum {
CS42L42_VOL_DAC,
};
+#define CS42L42_ADC_VOL_OFFSET (CS42L42_VOL_ADC)
+#define CS42L42_DAC_CH0_VOL_OFFSET (CS42L42_VOL_DAC)
+#define CS42L42_DAC_CH1_VOL_OFFSET (CS42L42_VOL_DAC + 1)
+
struct cs8409_i2c_param {
unsigned int addr;
unsigned int value;
@@ -327,6 +331,9 @@ struct cs8409_spec {
unsigned int dev_addr;
struct delayed_work i2c_clk_work;
+ unsigned int playback_started:1;
+ unsigned int capture_started:1;
+
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
unsigned int *res);
--
2.25.1
^ permalink raw reply related [flat|nested] 31+ messages in thread