summaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Kconfig2
-rw-r--r--sound/pci/hda/cs35l41_hda.c438
-rw-r--r--sound/pci/hda/cs35l41_hda.h31
-rw-r--r--sound/pci/hda/cs35l41_hda_spi.c2
-rw-r--r--sound/pci/hda/hda_codec.c11
-rw-r--r--sound/pci/hda/hda_component.h2
-rw-r--r--sound/pci/hda/hda_tegra.c2
-rw-r--r--sound/pci/hda/patch_conexant.c1
-rw-r--r--sound/pci/hda/patch_cs8409-tables.c346
-rw-r--r--sound/pci/hda/patch_cs8409.c361
-rw-r--r--sound/pci/hda/patch_cs8409.h14
-rw-r--r--sound/pci/hda/patch_realtek.c150
12 files changed, 792 insertions, 568 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 9f6c99c1d87b..79ade4787d95 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -102,6 +102,7 @@ config SND_HDA_SCODEC_CS35L41_I2C
select SND_HDA_GENERIC
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
+ select REGMAP_IRQ
help
Say Y or M here to include CS35L41 I2C HD-audio side codec support
in snd-hda-intel driver, such as ALC287.
@@ -117,6 +118,7 @@ config SND_HDA_SCODEC_CS35L41_SPI
select SND_HDA_GENERIC
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
+ select REGMAP_IRQ
help
Say Y or M here to include CS35L41 SPI HD-audio side codec support
in snd-hda-intel driver, such as ALC287.
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index 718595380868..cce27a86267f 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -17,163 +17,88 @@
#include "cs35l41_hda.h"
static const struct reg_sequence cs35l41_hda_config[] = {
- { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3200000Hz, BCLK Input, PLL_REFCLK_EN = 1
+ { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
{ CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz
{ CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1
{ CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
- { CS35L41_SP_FORMAT, 0x20200200 }, // 24 bits, I2S, BCLK Slave, FSYNC Slave
+ { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
{ CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1
{ CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB
{ CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
- { CS35L41_PWR_CTRL2, 0x00000001 }, // AMP_EN = 1
};
-static const struct reg_sequence cs35l41_hda_start_bst[] = {
- { CS35L41_PWR_CTRL2, 0x00000021 }, // BST_EN = 10, AMP_EN = 1
- { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1
-};
-
-static const struct reg_sequence cs35l41_hda_stop_bst[] = {
- { CS35L41_PWR_CTRL1, 0x00000000, 3000}, // set GLOBAL_EN = 0
-};
-
-// only on amps where GPIO1 is used to control ext. VSPK switch
-static const struct reg_sequence cs35l41_start_ext_vspk[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
- { 0x00007414, 0x08C82222 },
- { 0x0000742C, 0x00000009 },
- { 0x00011008, 0x00008001 },
- { 0x0000742C, 0x0000000F },
- { 0x0000742C, 0x00000079 },
- { 0x00007438, 0x00585941 },
- { CS35L41_PWR_CTRL1, 0x00000001, 3000}, // set GLOBAL_EN = 1
- { 0x0000742C, 0x000000F9 },
- { 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-//only on amps where GPIO1 is used to control ext. VSPK switch
-static const struct reg_sequence cs35l41_stop_ext_vspk[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
- { 0x00002014, 0x00000000, 3000}, // set GLOBAL_EN = 0
- { 0x0000742C, 0x00000009 },
- { 0x00007438, 0x00580941 },
- { 0x00011008, 0x00000001 },
- { 0x0000393C, 0x000000C0, 6000},
- { 0x0000393C, 0x00000000 },
- { 0x00007414, 0x00C82222 },
- { 0x0000742C, 0x00000000 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct reg_sequence cs35l41_safe_to_active[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x0000742C, 0x0000000F },
- { 0x0000742C, 0x00000079 },
- { 0x00007438, 0x00585941 },
- { CS35L41_PWR_CTRL1, 0x00000001, 2000 }, // GLOBAL_EN = 1
- { 0x0000742C, 0x000000F9 },
- { 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct reg_sequence cs35l41_active_to_safe[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
+static const struct reg_sequence cs35l41_hda_mute[] = {
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB
{ CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_VOL_PCM Mute
- { CS35L41_PWR_CTRL2, 0x00000000 }, // AMP_EN = 0
- { CS35L41_PWR_CTRL1, 0x00000000 },
- { 0x0000742C, 0x00000009, 2000 },
- { 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct reg_sequence cs35l41_reset_to_safe[] = {
- { 0x00000040, 0x00000055 },
- { 0x00000040, 0x000000AA },
- { 0x00007438, 0x00585941 },
- { 0x00007414, 0x08C82222 },
- { 0x0000742C, 0x00000009 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
-};
-
-static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_no_bst = {
- .probe = cs35l41_reset_to_safe,
- .num_probe = ARRAY_SIZE(cs35l41_reset_to_safe),
- .open = cs35l41_hda_config,
- .num_open = ARRAY_SIZE(cs35l41_hda_config),
- .prepare = cs35l41_safe_to_active,
- .num_prepare = ARRAY_SIZE(cs35l41_safe_to_active),
- .cleanup = cs35l41_active_to_safe,
- .num_cleanup = ARRAY_SIZE(cs35l41_active_to_safe),
};
-static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_ext_bst = {
- .open = cs35l41_hda_config,
- .num_open = ARRAY_SIZE(cs35l41_hda_config),
- .prepare = cs35l41_start_ext_vspk,
- .num_prepare = ARRAY_SIZE(cs35l41_start_ext_vspk),
- .cleanup = cs35l41_stop_ext_vspk,
- .num_cleanup = ARRAY_SIZE(cs35l41_stop_ext_vspk),
-};
+/* Protection release cycle to get the speaker out of Safe-Mode */
+static void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask)
+{
+ regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+ regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+}
-static const struct cs35l41_hda_reg_sequence cs35l41_hda_reg_seq_int_bst = {
- .open = cs35l41_hda_config,
- .num_open = ARRAY_SIZE(cs35l41_hda_config),
- .prepare = cs35l41_hda_start_bst,
- .num_prepare = ARRAY_SIZE(cs35l41_hda_start_bst),
- .cleanup = cs35l41_hda_stop_bst,
- .num_cleanup = ARRAY_SIZE(cs35l41_hda_stop_bst),
-};
+/* Clear all errors to release safe mode. Global Enable must be cleared first. */
+static void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
+{
+ cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors);
+ cs35l41->irq_errors = 0;
+}
static void cs35l41_hda_playback_hook(struct device *dev, int action)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
- const struct cs35l41_hda_reg_sequence *reg_seq = cs35l41->reg_seq;
struct regmap *reg = cs35l41->regmap;
int ret = 0;
switch (action) {
case HDA_GEN_PCM_ACT_OPEN:
- if (reg_seq->open)
- ret = regmap_multi_reg_write(reg, reg_seq->open, reg_seq->num_open);
+ regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config));
+ ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
break;
case HDA_GEN_PCM_ACT_PREPARE:
- if (reg_seq->prepare)
- ret = regmap_multi_reg_write(reg, reg_seq->prepare, reg_seq->num_prepare);
+ ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1);
break;
case HDA_GEN_PCM_ACT_CLEANUP:
- if (reg_seq->cleanup)
- ret = regmap_multi_reg_write(reg, reg_seq->cleanup, reg_seq->num_cleanup);
+ regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
+ ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0);
break;
case HDA_GEN_PCM_ACT_CLOSE:
- if (reg_seq->close)
- ret = regmap_multi_reg_write(reg, reg_seq->close, reg_seq->num_close);
+ ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
+ cs35l41_irq_release(cs35l41);
break;
default:
- ret = -EINVAL;
+ dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
break;
}
if (ret)
- dev_warn(cs35l41->dev, "Failed to apply multi reg write: %d\n", ret);
+ dev_err(cs35l41->dev, "Regmap access fail: %d\n", ret);
}
static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ static const char * const channel_name[] = { "L", "R" };
+
+ if (!cs35l41->amp_name) {
+ if (*rx_slot >= ARRAY_SIZE(channel_name))
+ return -EINVAL;
+
+ cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d",
+ channel_name[*rx_slot], cs35l41->channel_index);
+ if (!cs35l41->amp_name)
+ return -ENOMEM;
+ }
return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
rx_slot);
@@ -194,7 +119,6 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
comps->dev = dev;
strscpy(comps->name, dev_name(dev), sizeof(comps->name));
comps->playback_hook = cs35l41_hda_playback_hook;
- comps->set_channel_map = cs35l41_hda_channel_map;
return 0;
}
@@ -213,67 +137,169 @@ static const struct component_ops cs35l41_hda_comp_ops = {
.unbind = cs35l41_hda_unbind,
};
-static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41,
- const struct cs35l41_hda_hw_config *hw_cfg)
+static irqreturn_t cs35l41_bst_short_err(int irq, void *data)
{
- bool internal_boost = false;
- int ret;
+ struct cs35l41_hda *cs35l41 = data;
- if (!hw_cfg) {
- cs35l41->reg_seq = &cs35l41_hda_reg_seq_no_bst;
- return 0;
- }
+ dev_crit_ratelimited(cs35l41->dev, "LBST Error\n");
+ set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
- if (hw_cfg->bst_ind || hw_cfg->bst_cap || hw_cfg->bst_ipk)
- internal_boost = true;
+ return IRQ_HANDLED;
+}
- switch (hw_cfg->gpio1_func) {
- case CS35L41_NOT_USED:
- break;
- case CS35l41_VSPK_SWITCH:
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO1_CTRL_MASK, 1 << CS35L41_GPIO1_CTRL_SHIFT);
- break;
- case CS35l41_SYNC:
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO1_CTRL_MASK, 2 << CS35L41_GPIO1_CTRL_SHIFT);
- break;
- default:
- dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n", hw_cfg->gpio1_func);
- return -EINVAL;
- }
+static irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
- switch (hw_cfg->gpio2_func) {
- case CS35L41_NOT_USED:
- break;
- case CS35L41_INTERRUPT:
- regmap_update_bits(cs35l41->regmap, CS35L41_GPIO_PAD_CONTROL,
- CS35L41_GPIO2_CTRL_MASK, 2 << CS35L41_GPIO2_CTRL_SHIFT);
- break;
- default:
- dev_err(cs35l41->dev, "Invalid function %d for GPIO2\n", hw_cfg->gpio2_func);
+ dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+ set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_bst_ovp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
+ set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
+ set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_warn(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
+ set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_amp_short(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
+ set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static const struct cs35l41_irq cs35l41_irqs[] = {
+ CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err),
+ CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err),
+ CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err),
+ CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn),
+ CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err),
+ CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short),
+};
+
+static const struct regmap_irq cs35l41_reg_irqs[] = {
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR),
+};
+
+static const struct regmap_irq_chip cs35l41_regmap_irq_chip = {
+ .name = "cs35l41 IRQ1 Controller",
+ .status_base = CS35L41_IRQ1_STATUS1,
+ .mask_base = CS35L41_IRQ1_MASK1,
+ .ack_base = CS35L41_IRQ1_STATUS1,
+ .num_regs = 4,
+ .irqs = cs35l41_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l41_reg_irqs),
+};
+
+static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ bool using_irq = false;
+ int irq, irq_pol;
+ int ret;
+ int i;
+
+ if (!cs35l41->hw_cfg.valid)
return -EINVAL;
+
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
+
+ if (hw_cfg->gpio1.valid) {
+ switch (hw_cfg->gpio1.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35l41_VSPK_SWITCH:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_GPIO;
+ hw_cfg->gpio1.out_en = true;
+ break;
+ case CS35l41_SYNC:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_MDSYNC;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n",
+ hw_cfg->gpio1.func);
+ return -EINVAL;
+ }
}
- if (internal_boost) {
- cs35l41->reg_seq = &cs35l41_hda_reg_seq_int_bst;
- if (!(hw_cfg->bst_ind && hw_cfg->bst_cap && hw_cfg->bst_ipk))
+ if (hw_cfg->gpio2.valid) {
+ switch (hw_cfg->gpio2.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35L41_INTERRUPT:
+ using_irq = true;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func);
return -EINVAL;
- ret = cs35l41_boost_config(cs35l41->dev, cs35l41->regmap,
- hw_cfg->bst_ind, hw_cfg->bst_cap, hw_cfg->bst_ipk);
+ }
+ }
+
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
+
+ if (cs35l41->irq && using_irq) {
+ ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
if (ret)
return ret;
- } else {
- cs35l41->reg_seq = &cs35l41_hda_reg_seq_ext_bst;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
+ cs35l41_irqs[i].handler,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ cs35l41_irqs[i].name, cs35l41);
+ if (ret)
+ return ret;
+ }
}
- return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, (unsigned int *)&hw_cfg->spk_pos);
+ return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
}
-static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41,
- const char *hid, int id)
+static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
{
- struct cs35l41_hda_hw_config *hw_cfg;
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
u32 values[HDA_MAX_COMPONENTS];
struct acpi_device *adev;
struct device *physdev;
@@ -284,7 +310,7 @@ static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *c
adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
if (!adev) {
dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
}
physdev = get_device(acpi_get_first_physical_node(adev));
@@ -324,56 +350,67 @@ static struct cs35l41_hda_hw_config *cs35l41_hda_read_acpi(struct cs35l41_hda *c
cs35l41->reset_gpio = fwnode_gpiod_get_index(&adev->fwnode, "reset", cs35l41->index,
GPIOD_OUT_LOW, "cs35l41-reset");
- hw_cfg = kzalloc(sizeof(*hw_cfg), GFP_KERNEL);
- if (!hw_cfg) {
- ret = -ENOMEM;
- goto err;
- }
-
property = "cirrus,speaker-position";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret)
- goto err_free;
+ goto err;
hw_cfg->spk_pos = values[cs35l41->index];
+ cs35l41->channel_index = 0;
+ for (i = 0; i < cs35l41->index; i++)
+ if (values[i] == hw_cfg->spk_pos)
+ cs35l41->channel_index++;
+
property = "cirrus,gpio1-func";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret)
- goto err_free;
- hw_cfg->gpio1_func = values[cs35l41->index];
+ goto err;
+ hw_cfg->gpio1.func = values[cs35l41->index];
+ hw_cfg->gpio1.valid = true;
property = "cirrus,gpio2-func";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret)
- goto err_free;
- hw_cfg->gpio2_func = values[cs35l41->index];
+ goto err;
+ hw_cfg->gpio2.func = values[cs35l41->index];
+ hw_cfg->gpio2.valid = true;
property = "cirrus,boost-peak-milliamp";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret == 0)
hw_cfg->bst_ipk = values[cs35l41->index];
+ else
+ hw_cfg->bst_ipk = -1;
property = "cirrus,boost-ind-nanohenry";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret == 0)
hw_cfg->bst_ind = values[cs35l41->index];
+ else
+ hw_cfg->bst_ind = -1;
property = "cirrus,boost-cap-microfarad";
ret = device_property_read_u32_array(physdev, property, values, nval);
if (ret == 0)
hw_cfg->bst_cap = values[cs35l41->index];
+ else
+ hw_cfg->bst_cap = -1;
+ if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0)
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ else
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+
+ hw_cfg->valid = true;
put_device(physdev);
- return hw_cfg;
+ return 0;
-err_free:
- kfree(hw_cfg);
err:
put_device(physdev);
dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
- return ERR_PTR(ret);
+ return ret;
no_acpi_dsd:
/*
@@ -384,25 +421,32 @@ no_acpi_dsd:
* fwnode.
*/
if (strncmp(hid, "CLSA0100", 8) != 0)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
/* check I2C address to assign the index */
cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->hw_cfg.spk_pos = cs35l41->index;
+ cs35l41->channel_index = 0;
cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
- cs35l41->vspk_always_on = true;
+ cs35l41->hw_cfg.bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
+ hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN;
+ hw_cfg->gpio2.valid = true;
+ cs35l41->hw_cfg.valid = true;
put_device(physdev);
- return NULL;
+ return 0;
}
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
struct regmap *regmap)
{
unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status;
- struct cs35l41_hda_hw_config *acpi_hw_cfg;
struct cs35l41_hda *cs35l41;
int ret;
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs));
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ);
+
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -415,9 +459,11 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
cs35l41->regmap = regmap;
dev_set_drvdata(dev, cs35l41);
- acpi_hw_cfg = cs35l41_hda_read_acpi(cs35l41, device_name, id);
- if (IS_ERR(acpi_hw_cfg))
- return PTR_ERR(acpi_hw_cfg);
+ ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Platform not supported %d\n", ret);
+ return ret;
+ }
if (IS_ERR(cs35l41->reset_gpio)) {
ret = PTR_ERR(cs35l41->reset_gpio);
@@ -490,20 +536,9 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
if (ret)
goto err;
- ret = cs35l41_hda_apply_properties(cs35l41, acpi_hw_cfg);
+ ret = cs35l41_hda_apply_properties(cs35l41);
if (ret)
goto err;
- kfree(acpi_hw_cfg);
- acpi_hw_cfg = NULL;
-
- if (cs35l41->reg_seq->probe) {
- ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41->reg_seq->probe,
- cs35l41->reg_seq->num_probe);
- if (ret) {
- dev_err(cs35l41->dev, "Fail to apply probe reg patch: %d\n", ret);
- goto err;
- }
- }
ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
if (ret) {
@@ -516,8 +551,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
return 0;
err:
- kfree(acpi_hw_cfg);
- if (!cs35l41->vspk_always_on)
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
gpiod_put(cs35l41->reset_gpio);
@@ -531,7 +565,7 @@ void cs35l41_hda_remove(struct device *dev)
component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
- if (!cs35l41->vspk_always_on)
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
gpiod_put(cs35l41->reset_gpio);
}
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
index 74951001501c..a52ffd1f7999 100644
--- a/sound/pci/hda/cs35l41_hda.h
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -27,39 +27,18 @@ enum cs35l41_hda_gpio_function {
CS35l41_SYNC,
};
-struct cs35l41_hda_reg_sequence {
- const struct reg_sequence *probe;
- unsigned int num_probe;
- const struct reg_sequence *open;
- unsigned int num_open;
- const struct reg_sequence *prepare;
- unsigned int num_prepare;
- const struct reg_sequence *cleanup;
- unsigned int num_cleanup;
- const struct reg_sequence *close;
- unsigned int num_close;
-};
-
-struct cs35l41_hda_hw_config {
- unsigned int spk_pos;
- unsigned int gpio1_func;
- unsigned int gpio2_func;
- int bst_ind;
- int bst_ipk;
- int bst_cap;
-};
-
struct cs35l41_hda {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
- const struct cs35l41_hda_reg_sequence *reg_seq;
+ struct cs35l41_hw_cfg hw_cfg;
int irq;
int index;
-
- /* Don't put the AMP in reset of VSPK can not be turned off */
- bool vspk_always_on;
+ int channel_index;
+ unsigned volatile long irq_errors;
+ const char *amp_name;
+ struct regmap_irq_chip_data *irq_data;
};
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c
index 50eb6c0e6658..22e088f28438 100644
--- a/sound/pci/hda/cs35l41_hda_spi.c
+++ b/sound/pci/hda/cs35l41_hda_spi.c
@@ -48,7 +48,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
static struct spi_driver cs35l41_spi_driver = {
.driver = {
- .name = "cs35l41_hda",
+ .name = "cs35l41-hda",
.acpi_match_table = ACPI_PTR(cs35l41_acpi_hda_match),
},
.id_table = cs35l41_hda_spi_id,
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 5cbac315dbe1..7579a6982f47 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -2935,7 +2935,9 @@ static int hda_codec_runtime_suspend(struct device *dev)
if (!codec->card)
return 0;
- cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (!codec->bus->jackpoll_in_suspend)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+
state = hda_call_codec_suspend(codec);
if (codec->link_down_at_suspend ||
(codec_has_clkstop(codec) && codec_has_epss(codec) &&
@@ -2984,6 +2986,9 @@ static void hda_codec_pm_complete(struct device *dev)
static int hda_codec_pm_suspend(struct device *dev)
{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
dev->power.power_state = PMSG_SUSPEND;
return pm_runtime_force_suspend(dev);
}
@@ -2996,6 +3001,9 @@ static int hda_codec_pm_resume(struct device *dev)
static int hda_codec_pm_freeze(struct device *dev)
{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
dev->power.power_state = PMSG_FREEZE;
return pm_runtime_force_suspend(dev);
}
@@ -3038,6 +3046,7 @@ void snd_hda_codec_shutdown(struct hda_codec *codec)
if (!codec->registered)
return;
+ cancel_delayed_work_sync(&codec->jackpoll_work);
list_for_each_entry(cpcm, &codec->pcm_list_head, list)
snd_pcm_suspend_all(cpcm->pcm);
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
index 2e52be6db9c2..e26c896a13f3 100644
--- a/sound/pci/hda/hda_component.h
+++ b/sound/pci/hda/hda_component.h
@@ -15,6 +15,4 @@ struct hda_component {
struct device *dev;
char name[HDA_MAX_NAME_SIZE];
void (*playback_hook)(struct device *dev, int action);
- int (*set_channel_map)(struct device *dev, unsigned int rx_num, unsigned int *rx_slot,
- unsigned int tx_num, unsigned int *tx_slot);
};
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 2347d0304f93..7debb2c76aa6 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -420,6 +420,7 @@ static int hda_tegra_create(struct snd_card *card,
chip->driver_caps = driver_caps;
chip->driver_type = driver_caps & 0xff;
chip->dev_index = 0;
+ chip->jackpoll_interval = msecs_to_jiffies(5000);
INIT_LIST_HEAD(&chip->pcm_list);
chip->codec_probe_mask = -1;
@@ -436,6 +437,7 @@ static int hda_tegra_create(struct snd_card *card,
chip->bus.core.sync_write = 0;
chip->bus.core.needs_damn_long_delay = 1;
chip->bus.core.aligned_mmio = 1;
+ chip->bus.jackpoll_in_suspend = 1;
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 0515137a75b0..aa360a0af284 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -1105,6 +1105,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
static const struct hda_device_id snd_hda_id_conexant[] = {
HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 74c50ec040d9..4f4cc8215917 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -76,67 +76,74 @@ const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
{} /* terminator */
};
+const struct hda_pintbl cs8409_cs42l42_pincfgs_no_dmic[] = {
+ { 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 */
+ {} /* terminator */
+};
+
/* Vendor specific HW configuration for CS42L42 */
static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x9F },
- { 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, 0x3F },
- { 0x2303, 0x3F },
- { 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 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_HP_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff },
};
/* Vendor specific hw configuration for CS8409 */
@@ -252,7 +259,6 @@ struct sub_codec cs8409_cs42l42_codec = {
.init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
- .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
@@ -282,115 +288,115 @@ const struct hda_pintbl dolphin_pincfgs[] = {
/* Vendor specific HW configuration for CS42L42 */
static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x9F },
- { 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 },
- { 0x2001, 0x03 },
- { 0x2301, 0x3F },
- { 0x2303, 0x3F },
- { 0x2302, 0x3f },
- { 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 }
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x03 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
};
static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
- { 0x1010, 0xB0 },
- { 0x1D01, 0x00 },
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
{ 0x1D02, 0x06 },
- { 0x1D03, 0x9F },
- { 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 },
- { 0x2001, 0x01 },
- { 0x2301, 0x3F },
- { 0x2303, 0x3F },
- { 0x2302, 0x3f },
- { 0x1B75, 0xB6 },
- { 0x1B73, 0xC2 },
- { 0x1129, 0x01 },
- { 0x1121, 0xF3 },
- { 0x1103, 0x20 },
- { 0x1105, 0x00 },
- { 0x1112, 0x00 },
- { 0x1113, 0x80 },
- { 0x1C03, 0xC0 },
- { 0x1101, 0x06 },
- { 0x1316, 0xff },
- { 0x1317, 0xff },
- { 0x1318, 0xff },
- { 0x1319, 0xff },
- { 0x131a, 0xff },
- { 0x131b, 0xff },
- { 0x131c, 0xff },
- { 0x131e, 0xff },
- { 0x131f, 0xff },
- { 0x1320, 0xff },
- { 0x1b79, 0xff },
- { 0x1b7a, 0xff }
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_PWR_CTL1, 0x0E },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x01 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x06 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
};
/* Vendor specific hw configuration for CS8409 */
@@ -444,7 +450,6 @@ struct sub_codec dolphin_cs42l42_0 = {
.init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
- .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
@@ -458,7 +463,6 @@ struct sub_codec dolphin_cs42l42_1 = {
.init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
- .force_status_change = 1,
.paged = 1,
.suspended = 1,
.no_type_dect = 1,
@@ -521,6 +525,11 @@ const struct snd_pci_quirk cs8409_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0B95, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
SND_PCI_QUIRK(0x1028, 0x0B96, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0B97, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BA5, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA6, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN),
SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK),
@@ -548,6 +557,7 @@ const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" },
{ .id = CS8409_CYBORG, .name = "cyborg" },
{ .id = CS8409_DOLPHIN, .name = "dolphin" },
+ { .id = CS8409_ODIN, .name = "odin" },
{}
};
@@ -596,4 +606,10 @@ const struct hda_fixup cs8409_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = dolphin_fixups,
},
+ [CS8409_ODIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs_no_dmic,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 343fabc4387d..754aa8ddd2e4 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -419,6 +419,39 @@ static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP));
}
+static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio);
+ return 0;
+}
+
+static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) |
+ (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs8409_spk_sw_gpio_get,
+ .put = cs8409_spk_sw_gpio_put,
+};
+
/******************************************************************************
* CS42L42 Specific Functions
******************************************************************************/
@@ -481,26 +514,26 @@ static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type,
if (mute) {
if (vol_type == CS42L42_VOL_DAC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA, 0x3f);
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f);
if (chs & BIT(1))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB, 0x3f);
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f);
} else if (vol_type == CS42L42_VOL_ADC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL, 0x9f);
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f);
}
} else {
if (vol_type == CS42L42_VOL_DAC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL,
-(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET])
- & CS42L42_REG_HS_VOL_MASK);
+ & CS42L42_MIXER_CH_VOL_MASK);
if (chs & BIT(1))
- cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL,
-(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET])
- & CS42L42_REG_HS_VOL_MASK);
+ & CS42L42_MIXER_CH_VOL_MASK);
} else if (vol_type == CS42L42_VOL_ADC) {
if (chs & BIT(0))
- cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME,
cs42l42->vol[CS42L42_ADC_VOL_OFFSET]
& CS42L42_REG_AMIC_VOL_MASK);
}
@@ -601,76 +634,167 @@ static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo,
/* Configure CS42L42 slave codec for jack autodetect */
static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
{
- cs8409_i2c_write(cs42l42, 0x1b70, cs42l42->hsbias_hiz);
+ cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz);
/* Clear WAKE# */
- cs8409_i2c_write(cs42l42, 0x1b71, 0x00C1);
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1);
/* Wait ~2.5ms */
usleep_range(2500, 3000);
/* Set mode WAKE# output follows the combination logic directly */
- cs8409_i2c_write(cs42l42, 0x1b71, 0x00C0);
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0);
/* Clear interrupts status */
- cs8409_i2c_read(cs42l42, 0x130f);
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
/* Enable interrupt */
- cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
}
/* Enable and run CS42L42 slave codec jack auto detect */
static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
{
/* Clear interrupts */
- 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);
+ cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF);
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87);
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86);
+ cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07);
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
/* Wait ~20ms*/
usleep_range(20000, 25000);
- cs8409_i2c_write(cs42l42, 0x111f, 0x77);
- cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0);
}
-static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
+static int cs42l42_manual_hs_det(struct sub_codec *cs42l42)
{
- int status_changed = cs42l42->force_status_change;
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+ unsigned int hs_type;
- cs42l42->force_status_change = 0;
+ /* Set hs detect to manual, active mode */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
- /* 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);
- }
- }
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ case CS42L42_HSDET_COMP_TYPE3:
+ hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ default:
+ hs_type = CS42L42_PLUG_INVALID;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE4;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
- case CS42L42_JACK_REMOVED:
- if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ return hs_type;
+}
+
+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_TS_PLUG:
+ if (cs42l42->no_type_dect) {
status_changed = 1;
- cs42l42->hp_jack_in = 0;
+ cs42l42->hp_jack_in = 1;
cs42l42->mic_jack_in = 0;
+ } else {
+ cs42l42_run_jack_detect(cs42l42);
}
break;
+
+ case CS42L42_TS_UNPLUG:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
default:
/* jack in transition */
break;
}
+ codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
+
return status_changed;
}
static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
{
+ int current_plug_status;
int status_changed = 0;
int reg_cdc_status;
int reg_hs_status;
@@ -678,46 +802,65 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
int type;
/* Read jack detect status registers */
- reg_cdc_status = cs8409_i2c_read(cs42l42, 0x1308);
- reg_hs_status = cs8409_i2c_read(cs42l42, 0x1124);
- reg_ts_status = cs8409_i2c_read(cs42l42, 0x130f);
+ reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+ reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
/* If status values are < 0, read error has occurred. */
if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
return -EIO;
+ current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK))
+ >> CS42L42_TS_PLUG_SHIFT;
+
/* HSDET_AUTO_DONE */
- if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
+ if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) {
/* Disable HSDET_AUTO_DONE */
- cs8409_i2c_write(cs42l42, 0x131b, 0xFF);
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF);
+
+ type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT;
- type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
+ /* Configure the HSDET mode. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
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);
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
} else {
- if (!cs42l42->hp_jack_in) {
- status_changed = 1;
- cs42l42->hp_jack_in = 1;
+ if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) {
+ codec_dbg(cs42l42->codec,
+ "Auto detect value not valid (%d), running manual det\n",
+ type);
+ type = cs42l42_manual_hs_det(cs42l42);
}
- /* type = 3 has no mic */
- if ((!cs42l42->mic_jack_in) && (type != 3)) {
+
+ switch (type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
status_changed = 1;
+ cs42l42->hp_jack_in = 1;
cs42l42->mic_jack_in = 1;
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ break;
+ default:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
}
+ codec_dbg(cs42l42->codec, "Detection done (%d)\n", type);
}
- /* Configure the HSDET mode. */
- cs8409_i2c_write(cs42l42, 0x1120, 0x80);
+
/* Enable the HPOUT ground clamp and configure the HP pull-down */
- cs8409_i2c_write(cs42l42, 0x1F06, 0x02);
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02);
/* Re-Enable Tip Sense Interrupt */
- cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
} else {
- status_changed = cs42l42_handle_tip_sense(cs42l42, reg_ts_status);
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
}
return status_changed;
@@ -726,19 +869,19 @@ 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_spec *spec = codec->spec;
struct cs8409_i2c_param irq_regs[] = {
- { 0x1308, 0x00 },
- { 0x1309, 0x00 },
- { 0x130A, 0x00 },
- { 0x130F, 0x00 },
+ { CS42L42_CODEC_STATUS, 0x00 },
+ { CS42L42_DET_INT_STATUS1, 0x00 },
+ { CS42L42_DET_INT_STATUS2, 0x00 },
+ { CS42L42_TSRS_PLUG_STATUS, 0x00 },
};
int fsv_old, fsv_new;
/* 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);
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data |= cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
usleep_range(10000, 15000);
cs42l42->suspended = 0;
@@ -750,13 +893,13 @@ 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));
- fsv_old = cs8409_i2c_read(cs42l42, 0x2001);
+ fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL);
if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB)
fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK;
else
fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK;
if (fsv_new != fsv_old)
- cs8409_i2c_write(cs42l42, 0x2001, fsv_new);
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new);
/* we have to explicitly allow unsol event handling even during the
* resume phase so that the jack event is processed properly
@@ -770,41 +913,40 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
static void cs42l42_suspend(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
- unsigned int gpio_data;
+ struct cs8409_spec *spec = codec->spec;
int reg_cdc_status = 0;
const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = {
- { 0x1F06, 0x02 },
- { 0x1129, 0x00 },
- { 0x2301, 0x3F },
- { 0x2302, 0x3F },
- { 0x2303, 0x3F },
- { 0x2001, 0x0F },
- { 0x2A01, 0x00 },
- { 0x1207, 0x00 },
- { 0x1101, 0xFE },
- { 0x1102, 0x8C },
- { 0x1101, 0xFF },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_HP_CTL, 0x0F },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_PWR_CTL1, 0xFE },
+ { CS42L42_PWR_CTL2, 0x8C },
+ { CS42L42_PWR_CTL1, 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)
+ true, cs42l42, CS42L42_CODEC_STATUS) < 0)
codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n");
/* Power down CS42L42 ASP/EQ/MIX/HP */
- cs8409_i2c_write(cs42l42, 0x1102, 0x9C);
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C);
cs42l42->suspended = 1;
cs42l42->last_page = 0;
cs42l42->hp_jack_in = 0;
cs42l42->mic_jack_in = 0;
- cs42l42->force_status_change = 1;
/* 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);
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data &= ~cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
#endif
@@ -918,6 +1060,10 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
/* DMIC1_MO=00b, DMIC1/2_SR=1 */
cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003);
break;
+ case CS8409_ODIN:
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */
+ cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00);
+ break;
default:
break;
}
@@ -994,6 +1140,8 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->gen.no_primary_hp = 1;
spec->gen.suppress_vmaster = 1;
+ spec->speaker_pdn_gpio = 0;
+
/* GPIO 5 out, 3,4 in */
spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
spec->gpio_data = 0;
@@ -1005,20 +1153,35 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
- /* Set HSBIAS_SENSE_EN and Full Scale volume for some variants. */
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+
switch (codec->fixup_id) {
+ case CS8409_CYBORG:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol =
+ CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
+ case CS8409_ODIN:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
case CS8409_WARLOCK_MLK:
case CS8409_WARLOCK_MLK_DUAL_MIC:
- spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
break;
default:
- spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
spec->scodecs[CS8409_CODEC0]->full_scale_vol =
CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
break;
}
+ if (spec->speaker_pdn_gpio > 0) {
+ spec->gpio_dir |= spec->speaker_pdn_gpio;
+ spec->gpio_data |= spec->speaker_pdn_gpio;
+ }
+
break;
case HDA_FIXUP_ACT_PROBE:
/* Fix Sample Rate to 48kHz */
@@ -1027,13 +1190,17 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
/* 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);
+ if (codec->fixup_id != CS8409_ODIN)
+ /* 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, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
&cs42l42_adc_volume_mixer);
+ if (spec->speaker_pdn_gpio > 0)
+ snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
+ &cs8409_spk_sw_ctrl);
/* Disable Unsolicited Response during boot */
cs8409_enable_ur(codec, 0);
snd_hda_codec_set_name(codec, "CS8409/CS42L42");
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index 7df46bd8d2da..260388a6256c 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <sound/tlv.h>
#include <linux/workqueue.h>
+#include <sound/cs42l42.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
@@ -222,15 +223,8 @@ enum cs8409_coefficient_index_registers {
#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)
-#define CS42L42_JACK_REMOVED (0x00)
#define CS42L42_I2C_TIMEOUT_US (20000)
#define CS42L42_I2C_SLEEP_US (2000)
#define CS42L42_PDN_TIMEOUT_US (250000)
@@ -244,6 +238,8 @@ enum cs8409_coefficient_index_registers {
#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_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */
+#define CS8409_WARLOCK_SPEAKER_PDN GENMASK(1, 1) /* CS8409_GPIO1 */
#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
@@ -273,6 +269,7 @@ enum {
CS8409_FIXUPS,
CS8409_DOLPHIN,
CS8409_DOLPHIN_FIXUPS,
+ CS8409_ODIN,
};
enum {
@@ -310,7 +307,6 @@ struct sub_codec {
unsigned int hp_jack_in:1;
unsigned int mic_jack_in:1;
- unsigned int force_status_change:1;
unsigned int suspended:1;
unsigned int paged:1;
unsigned int last_page;
@@ -332,6 +328,8 @@ struct cs8409_spec {
unsigned int gpio_dir;
unsigned int gpio_data;
+ int speaker_pdn_gpio;
+
struct mutex i2c_mux;
unsigned int i2c_clck_enabled;
unsigned int dev_addr;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 4c0c593f3c0a..80e4955e8c10 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -937,6 +937,9 @@ static int alc_init(struct hda_codec *codec)
return 0;
}
+#define alc_free snd_hda_gen_free
+
+#ifdef CONFIG_PM
static inline void alc_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -950,9 +953,6 @@ static inline void alc_shutup(struct hda_codec *codec)
alc_shutup_pins(codec);
}
-#define alc_free snd_hda_gen_free
-
-#ifdef CONFIG_PM
static void alc_power_eapd(struct hda_codec *codec)
{
alc_auto_setup_eapd(codec, false);
@@ -966,9 +966,7 @@ static int alc_suspend(struct hda_codec *codec)
spec->power_hook(codec);
return 0;
}
-#endif
-#ifdef CONFIG_PM
static int alc_resume(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -3133,6 +3131,7 @@ enum {
ALC269_TYPE_ALC257,
ALC269_TYPE_ALC215,
ALC269_TYPE_ALC225,
+ ALC269_TYPE_ALC245,
ALC269_TYPE_ALC287,
ALC269_TYPE_ALC294,
ALC269_TYPE_ALC300,
@@ -3170,6 +3169,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC257:
case ALC269_TYPE_ALC215:
case ALC269_TYPE_ALC225:
+ case ALC269_TYPE_ALC245:
case ALC269_TYPE_ALC287:
case ALC269_TYPE_ALC294:
case ALC269_TYPE_ALC300:
@@ -3697,7 +3697,8 @@ static void alc225_init(struct hda_codec *codec)
hda_nid_t hp_pin = alc_get_hp_pin(spec);
bool hp1_pin_sense, hp2_pin_sense;
- if (spec->codec_variant != ALC269_TYPE_ALC287)
+ if (spec->codec_variant != ALC269_TYPE_ALC287 &&
+ spec->codec_variant != ALC269_TYPE_ALC245)
/* required only at boot or S3 and S4 resume time */
if (!spec->done_hp_init ||
is_s3_resume(codec) ||
@@ -6582,18 +6583,6 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
}
}
-static int find_comp_by_dev_name(struct alc_spec *spec, const char *name)
-{
- int i;
-
- for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
- if (strcmp(spec->comps[i].name, name) == 0)
- return i;
- }
-
- return -ENODEV;
-}
-
static int comp_bind(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
@@ -6668,50 +6657,10 @@ static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fix
cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 4);
}
-static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
- struct snd_pcm_substream *sub, int action)
-{
- struct alc_spec *spec = cdc->spec;
- unsigned int rx_slot;
- int i;
-
- switch (action) {
- case HDA_GEN_PCM_ACT_PREPARE:
- rx_slot = 0;
- i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0");
- if (i >= 0)
- spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot);
-
- rx_slot = 1;
- i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1");
- if (i >= 0)
- spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot);
- break;
- }
-
- comp_generic_playback_hook(hinfo, cdc, sub, action);
-}
-
static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
int action)
{
- struct device *dev = hda_codec_dev(cdc);
- struct alc_spec *spec = cdc->spec;
- int ret;
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- component_match_add(dev, &spec->match, component_compare_dev_name,
- "i2c-CLSA0100:00-cs35l41-hda.0");
- component_match_add(dev, &spec->match, component_compare_dev_name,
- "i2c-CLSA0100:00-cs35l41-hda.1");
- ret = component_master_add_with_match(dev, &comp_master_ops, spec->match);
- if (ret)
- codec_err(cdc, "Fail to register component aggregator %d\n", ret);
- else
- spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook;
- break;
- }
+ cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2);
}
/* for alc295_fixup_hp_top_speakers */
@@ -6780,6 +6729,41 @@ static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec,
}
}
+static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ int i;
+
+ alc269_fixup_limit_int_mic_boost(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /**
+ * Set the vref of pin 0x19 (Headset Mic) and pin 0x1b (Headphone Mic)
+ * to Hi-Z to avoid pop noises at startup and when plugging and
+ * unplugging headphones.
+ */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+ snd_hda_codec_set_pin_target(codec, 0x1b, PIN_VREFHIZ);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /**
+ * Make the internal mic (0x12) the default input source to
+ * prevent pop noises on cold boot.
+ */
+ for (i = 0; i < imux->num_items; i++) {
+ if (spec->gen.imux_pins[i] == 0x12) {
+ spec->gen.cur_mux[0] = i;
+ break;
+ }
+ }
+ break;
+ }
+}
+
enum {
ALC269_FIXUP_GPIO2,
ALC269_FIXUP_SONY_VAIO,
@@ -6821,6 +6805,7 @@ enum {
ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET,
ALC269_FIXUP_HEADSET_MODE,
ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
ALC269_FIXUP_ASPIRE_HEADSET_MIC,
@@ -7012,6 +6997,7 @@ enum {
ALC245_FIXUP_CS35L41_SPI_4,
ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED,
ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED,
+ ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -8769,6 +8755,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC287_FIXUP_CS35L41_I2C_2] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
},
[ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = {
.type = HDA_FIXUP_FUNC,
@@ -8806,6 +8794,21 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_HP_MUTE_LED,
},
+ [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell4_mic_no_presence_quiet,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ },
+ [ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1112c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -8896,6 +8899,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a38, "Dell Latitude 7520", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET),
SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC),
SND_PCI_QUIRK(0x1028, 0x0a61, "Dell XPS 15 9510", ALC289_FIXUP_DUAL_SPK),
SND_PCI_QUIRK(0x1028, 0x0a62, "Dell Precision 5560", ALC289_FIXUP_DUAL_SPK),
@@ -9023,12 +9027,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
- SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
@@ -9038,6 +9042,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89aa, "HP EliteBook 630 G9", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
@@ -9245,6 +9250,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940", ALC298_FIXUP_LENOVO_SPK_VOLUME),
SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
@@ -9287,6 +9293,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS),
+ SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1100, "TongFang GKxNRxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1111, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1119, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1129, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP),
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
@@ -9294,6 +9308,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
+ SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
#if 0
/* Below is a quirk table taken from the old code.
@@ -10084,7 +10099,10 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0245:
case 0x10ec0285:
case 0x10ec0289:
- spec->codec_variant = ALC269_TYPE_ALC215;
+ if (alc_get_coef0(codec) & 0x0010)
+ spec->codec_variant = ALC269_TYPE_ALC245;
+ else
+ spec->codec_variant = ALC269_TYPE_ALC215;
spec->shutup = alc225_shutup;
spec->init_hook = alc225_init;
spec->gen.mixer_nid = 0;