diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ef16d58b29499d..b33e5e984ef186 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1768,6 +1768,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) {"CSC3557", }, {"INT33FE", }, {"INT3515", }, + {"MAX98390", }, {"TXNW2781", }, /* Non-conforming _HID for Cirrus Logic already released */ {"CLSA0100", }, diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index db030b0f176a24..b41f0cca10e94e 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -395,6 +395,17 @@ static const struct smi_node tas2781_hda = { .bus_type = SMI_AUTO_DETECT, }; +static const struct smi_node max98390_hda = { + .instances = { + { "max98390-hda", IRQ_RESOURCE_NONE, 0 }, + { "max98390-hda", IRQ_RESOURCE_NONE, 0 }, + { "max98390-hda", IRQ_RESOURCE_NONE, 0 }, + { "max98390-hda", IRQ_RESOURCE_NONE, 0 }, + {} + }, + .bus_type = SMI_I2C, +}; + /* * Note new device-ids must also be added to ignore_serial_bus_ids in * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). @@ -407,6 +418,7 @@ static const struct acpi_device_id smi_acpi_ids[] = { { "CSC3556", (unsigned long)&cs35l56_hda }, { "CSC3557", (unsigned long)&cs35l57_hda }, { "INT3515", (unsigned long)&int3515_data }, + { "MAX98390", (unsigned long)&max98390_hda }, { "TXNW2781", (unsigned long)&tas2781_hda }, /* Non-conforming _HID for Cirrus Logic already released */ { "CLSA0100", (unsigned long)&cs35l41_hda }, diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c0a6acd7d42dc0..c44a774672a737 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -2989,6 +2989,11 @@ static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fix comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } +static void max98390_fixup_i2c_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(codec, action, "i2c", "MAX98390", "-%s:00-max98390-hda.%d", 4); +} + static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { @@ -3678,6 +3683,7 @@ enum { ALC298_FIXUP_SAMSUNG_AMP, ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, + ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS, ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -5354,6 +5360,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_samsung_amp_v2_4_amps }, + [ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = max98390_fixup_i2c_four + }, [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -6975,6 +6985,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), + SND_PCI_QUIRK(0x144d, 0xca07, "Samsung Galaxy Book4 Pro 14-inch (NP940XGK)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc890, "Samsung Galaxy Book4 Pro 16 inch (NP960XGK)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc892, "Samsung Galaxy Book4 Pro 360 (NP960QGK)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc1d8, "Samsung Galaxy Book4 Ultra (NP960XGL)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc1da, "Samsung Galaxy Book5 Pro 360 (NP960QHA)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS), SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), @@ -7478,6 +7493,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"}, {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"}, {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"}, + {.id = ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS, .name = "alc298-samsung-max98390-4-amps"}, {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"}, {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index f674e9a9c7d7af..9e9d82e016472b 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -141,3 +141,22 @@ config SND_HDA_SCODEC_TAS2781_SPI comment "Set to Y if you want auto-loading the side codec driver" depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m + +config SND_HDA_SCODEC_MAX98390 + tristate + select SND_HDA_GENERIC + select SND_HDA_SCODEC_COMPONENT + +config SND_HDA_SCODEC_MAX98390_I2C + tristate "Build MAX98390 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select SND_SOC_MAX98390 + select SND_HDA_SCODEC_MAX98390 + help + Say Y or M here to include MAX98390 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC298. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_MAX98390_I2C=m diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile index 245e84f6a121d6..e80857adcaf38e 100644 --- a/sound/hda/codecs/side-codecs/Makefile +++ b/sound/hda/codecs/side-codecs/Makefile @@ -13,6 +13,8 @@ snd-hda-scodec-component-y := hda_component.o snd-hda-scodec-tas2781-y := tas2781_hda.o snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o +snd-hda-scodec-max98390-y := max98390_hda.o max98390_hda_filters.o +snd-hda-scodec-max98390-i2c-y := max98390_hda_i2c.o obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o @@ -26,3 +28,5 @@ obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o +obj-$(CONFIG_SND_HDA_SCODEC_MAX98390) += snd-hda-scodec-max98390.o +obj-$(CONFIG_SND_HDA_SCODEC_MAX98390_I2C) += snd-hda-scodec-max98390-i2c.o diff --git a/sound/hda/codecs/side-codecs/max98390_hda.c b/sound/hda/codecs/side-codecs/max98390_hda.c new file mode 100644 index 00000000000000..869169b3915ba3 --- /dev/null +++ b/sound/hda/codecs/side-codecs/max98390_hda.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MAX98390 HDA driver +// + +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_component.h" +#include "../generic.h" +#include "max98390_hda.h" +#include "max98390_hda_filters.h" +#include "../../../soc/codecs/max98390.h" + +static void max98390_hda_playback_hook(struct device *dev, int action) +{ + struct max98390_hda_priv *priv = dev_get_drvdata(dev); + int ret; + + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + + /* Enable global and speaker amp */ + ret = regmap_write(priv->regmap, MAX98390_R23FF_GLOBAL_EN, 0x01); + if (ret < 0) + dev_err(dev, "Failed to write GLOBAL_EN: %d\n", ret); + + ret = regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x81); + if (ret < 0) + dev_err(dev, "Failed to write AMP_EN: %d\n", ret); + + break; + + case HDA_GEN_PCM_ACT_CLOSE: + /* Disable speaker amp and global */ + regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80); + regmap_write(priv->regmap, MAX98390_R23FF_GLOBAL_EN, 0x00); + break; + + default: + break; + } +} + +static int max98390_hda_bind(struct device *dev, struct device *master, void *master_data) +{ + struct max98390_hda_priv *priv = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, priv->index); + if (!comp) + return -EINVAL; + + comp->dev = dev; + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); + comp->playback_hook = max98390_hda_playback_hook; + + dev_info(dev, "MAX98390 HDA component bound (index %d)\n", priv->index); + + return 0; +} + +static void max98390_hda_unbind(struct device *dev, struct device *master, void *master_data) +{ + struct max98390_hda_priv *priv = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, priv->index); + if (comp && comp->dev == dev) { + comp->dev = NULL; + memset(comp->name, 0, sizeof(comp->name)); + comp->playback_hook = NULL; + } + + dev_info(dev, "MAX98390 HDA component unbound\n"); +} + +static const struct component_ops max98390_hda_comp_ops = { + .bind = max98390_hda_bind, + .unbind = max98390_hda_unbind, +}; + +static int max98390_hda_init(struct max98390_hda_priv *priv) +{ + int ret; + unsigned int reg, global_en, amp_en, pcm_rx; + + /* Check device ID */ + ret = regmap_read(priv->regmap, MAX98390_R24FF_REV_ID, ®); + if (ret < 0) { + return ret; + } + + /* Software reset */ + ret = regmap_write(priv->regmap, MAX98390_SOFTWARE_RESET, 0x01); + if (ret < 0) { + return ret; + } + msleep(20); + + /* Basic register initialization (minimal setup for HDA) */ + regmap_write(priv->regmap, MAX98390_CLK_MON, 0x6f); + regmap_write(priv->regmap, MAX98390_DAT_MON, 0x00); + regmap_write(priv->regmap, MAX98390_PWR_GATE_CTL, 0x00); + regmap_write(priv->regmap, MAX98390_PCM_RX_EN_A, 0x03); + regmap_write(priv->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e); + regmap_write(priv->regmap, MAX98390_BOOST_BYPASS1, 0x46); + regmap_write(priv->regmap, MAX98390_FET_SCALING3, 0x03); + + /* PCM/I2S configuration - CRITICAL for correct audio format */ + /* 0xC0 = I2S mode, 32-bit samples (standard for HDA) */ + regmap_write(priv->regmap, MAX98390_PCM_MODE_CFG, 0xc0); + regmap_write(priv->regmap, MAX98390_PCM_MASTER_MODE, 0x1c); + regmap_write(priv->regmap, MAX98390_PCM_CLK_SETUP, 0x44); + regmap_write(priv->regmap, MAX98390_PCM_SR_SETUP, 0x08); + + /* RESET EN - Write 0x00 to 0x23FF */ + regmap_write(priv->regmap, MAX98390_R23FF_GLOBAL_EN, 0x00); + + /* Wait 50ms */ + msleep(50); + + /* RESET SPK_EN - Write 0x80 to 0x203A */ + regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80); + + /* RESET DSP_GLOBAL_EN - Write 0x00 to 0x23E1 */ + regmap_write(priv->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x00); + + /* Step 6: Write non-DSM registers (0x2000-0x2084) - done in configure_filters */ + /* Step 7: Write DSM registers (0x2100-0x23E0) - done in configure_filters */ + /* Step 8-10: Enable DSP and amp - done in configure_filters */ + max98390_configure_filters(priv); + + return 0; +} + +int max98390_hda_probe(struct device *dev, const char *device_name, + int id, int irq, struct regmap *regmap, + enum max98390_hda_bus_type bus_type, int i2c_addr) +{ + struct max98390_hda_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->regmap = regmap; + priv->bus_type = bus_type; + priv->irq = irq; + priv->index = id; + priv->i2c_addr = i2c_addr; + dev_set_drvdata(dev, priv); + + ret = max98390_hda_init(priv); + if (ret) + return ret; + + ret = component_add(dev, &max98390_hda_comp_ops); + if (ret) { + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(max98390_hda_probe, "SND_HDA_SCODEC_MAX98390"); + +void max98390_hda_remove(struct device *dev) +{ + struct max98390_hda_priv *priv = dev_get_drvdata(dev); + + component_del(dev, &max98390_hda_comp_ops); + + if (priv && priv->regmap) { + /* Disable amp on removal */ + regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80); + } +} +EXPORT_SYMBOL_NS_GPL(max98390_hda_remove, "SND_HDA_SCODEC_MAX98390"); + +static int max98390_hda_runtime_suspend(struct device *dev) +{ + struct max98390_hda_priv *priv = dev_get_drvdata(dev); + + regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80); + regcache_cache_only(priv->regmap, true); + regcache_mark_dirty(priv->regmap); + + return 0; +} + +static int max98390_hda_runtime_resume(struct device *dev) +{ + struct max98390_hda_priv *priv = dev_get_drvdata(dev); + + regcache_cache_only(priv->regmap, false); + regcache_sync(priv->regmap); + + return 0; +} + +const struct dev_pm_ops max98390_hda_pm_ops = { + RUNTIME_PM_OPS(max98390_hda_runtime_suspend, max98390_hda_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(max98390_hda_pm_ops, "SND_HDA_SCODEC_MAX98390"); + +MODULE_DESCRIPTION("HDA MAX98390 side codec library"); +MODULE_AUTHOR("Kevin Cuperus "); +MODULE_LICENSE("GPL"); diff --git a/sound/hda/codecs/side-codecs/max98390_hda.h b/sound/hda/codecs/side-codecs/max98390_hda.h new file mode 100644 index 00000000000000..6c9f648e0a5c6d --- /dev/null +++ b/sound/hda/codecs/side-codecs/max98390_hda.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MAX98390 HDA audio driver + */ + +#ifndef __MAX98390_HDA_H__ +#define __MAX98390_HDA_H__ + +#include +#include + +enum max98390_hda_bus_type { + MAX98390_HDA_I2C, +}; + +struct max98390_hda_priv { + struct device *dev; + struct regmap *regmap; + enum max98390_hda_bus_type bus_type; + int irq; + int index; + const char *acpi_subsystem_id; + int i2c_addr; /* I2C address for speaker identification */ +}; + +int max98390_hda_probe(struct device *dev, const char *device_name, + int id, int irq, struct regmap *regmap, + enum max98390_hda_bus_type bus_type, int i2c_addr); +void max98390_hda_remove(struct device *dev); + +extern const struct dev_pm_ops max98390_hda_pm_ops; + +#endif /* __MAX98390_HDA_H__ */ diff --git a/sound/hda/codecs/side-codecs/max98390_hda_filters.c b/sound/hda/codecs/side-codecs/max98390_hda_filters.c new file mode 100644 index 00000000000000..a72401dea0cafa --- /dev/null +++ b/sound/hda/codecs/side-codecs/max98390_hda_filters.c @@ -0,0 +1,236 @@ +// High-pass filter configuration for MAX98390 HDA driver +// This file contains the filter configuration functions + +// Based on typical 4-speaker configurations +// Tweeters are typically at lower I2C addresses +// Woofers are typically at higher I2C addresses +// This pattern may vary by manufacturer + +#include +#include +#include "max98390_hda.h" +#include "max98390_hda_filters.h" +#include "../../../soc/codecs/max98390.h" + +void max98390_configure_filters(struct max98390_hda_priv *priv) +{ + unsigned int i2c_addr = priv->i2c_addr; + bool is_tweeter = false; + int pcm_channel = 0; + + // Identify speaker based on I2C address + // Configure PCM input channel for stereo separation + switch (i2c_addr) { + case 0x38: + is_tweeter = false; + pcm_channel = 0; // Left channel + break; + case 0x39: + is_tweeter = false; + pcm_channel = 1; // Right channel + break; + case 0x3C: + is_tweeter = true; + pcm_channel = 0; // Left channel + break; + case 0x3D: + is_tweeter = true; + pcm_channel = 1; // Right channel + break; + default: + dev_warn(priv->dev, "Unknown speaker at 0x%02x - defaulting to LEFT CH0\n", i2c_addr); + is_tweeter = false; + pcm_channel = 0; + } + + // Configure PCM input channel for stereo separation + // MAX98390_PCM_CH_SRC_1 register bits [3:0] = speaker source + // 0 = PCM Channel 0 (LEFT), 1 = PCM Channel 1 (RIGHT) + regmap_write(priv->regmap, 0x2021, pcm_channel); + + // Load appropriate DSM firmware for woofers AND tweeters + // Tweeters get high-pass filter, woofers get full-range with small boost for bass + max98390_configure_high_pass_filter(priv, 3000, is_tweeter); +} + +// Complete DSM firmware blobs from Google Redrix +// These firmwares configure ALL DSM registers (0x2050-0x23E0) +// Total: 913 bytes each +#define MAX98390_DSM_START_ADDR 0x2050 +#define MAX98390_DSM_PARAM_SIZE 913 + +// Woofer DSM firmware (for 0x38, 0x39) +static const u8 max98390_dsm_firmware_woofer[] = { + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, // 0x2050 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2060 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x80, 0x07, 0x07, 0x01, 0x00, 0x4A, 0x2B, 0x08, 0x00, // 0x2070 + 0x03, 0x03, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2080 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2090 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20A0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20B0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20C0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20D0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20E0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20F0 + 0x00, 0xd0, 0x95, 0x0e, 0x00, 0x61, 0xd4, 0xe2, 0x00, 0xd0, 0x95, 0x0e, 0x00, 0x75, 0xf4, 0xe2, // 0x2100 + 0x00, 0xb4, 0x4b, 0x0d, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x15, 0x20, 0x00, 0x00, 0x0a, 0x10, 0x00, // 0x2110 + 0x00, 0x75, 0xf4, 0xe2, 0x00, 0xb4, 0x4b, 0x0d, 0x00, 0xe5, 0xec, 0x0f, 0x00, 0x35, 0x26, 0xe0, // 0x2120 + 0x00, 0xe5, 0xec, 0x0f, 0x00, 0x4c, 0x26, 0xe0, 0x00, 0xe1, 0xd9, 0x0f, 0x00, 0x00, 0x00, 0x10, // 0x2130 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2140 + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2150 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2160 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // 0x2170 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x58, 0x00, // 0x2180 + 0x00, 0xc8, 0xb0, 0x00, 0x00, 0x64, 0x58, 0x00, 0x00, 0x3d, 0x5b, 0xe7, 0x00, 0x54, 0x06, 0x0a, // 0x2190 + 0x00, 0xc8, 0xa3, 0x10, 0x00, 0x29, 0x4d, 0xe5, 0x00, 0x32, 0x42, 0x0c, 0x00, 0x29, 0x4d, 0xe5, // 0x21a0 + 0x00, 0xfa, 0xe5, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21b0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xc5, 0x0f, 0x00, 0x7b, 0xd7, 0xe1, // 0x21c0 + 0x00, 0x77, 0xa7, 0x0e, 0x00, 0xe0, 0xbd, 0xe1, 0x00, 0x32, 0x53, 0x0e, 0x00, 0x00, 0x00, 0x00, // 0x21d0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21e0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21f0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x7e, 0x0f, 0x00, 0x0b, 0x02, 0xe1, 0x00, 0xfb, 0x7e, 0x0f, // 0x2200 + 0x00, 0xd5, 0x17, 0xe1, 0x00, 0xc0, 0x13, 0x0f, 0x00, 0xe5, 0x0a, 0x00, 0x00, 0xcb, 0x15, 0x00, // 0x2210 + 0x00, 0xe5, 0x0a, 0x00, 0x00, 0xd5, 0x17, 0xe1, 0x00, 0xc0, 0x13, 0x0f, 0x00, 0xf5, 0xdb, 0x0e, // 0x2220 + 0x00, 0x17, 0x48, 0xe2, 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0xef, 0x5c, 0xe2, 0x00, 0xc1, 0xcc, 0x0d, // 0x2230 + 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0x17, 0x48, 0xe2, 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0xef, 0x5c, 0xe2, // 0x2240 + 0x00, 0xc1, 0xcc, 0x0d, 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0x17, 0x48, 0xe2, 0x00, 0xf5, 0xdb, 0x0e, // 0x2250 + 0x00, 0xef, 0x5c, 0xe2, 0x00, 0xc1, 0xcc, 0x0d, 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0x17, 0x48, 0xe2, // 0x2260 + 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0xef, 0x5c, 0xe2, 0x00, 0xc1, 0xcc, 0x0d, 0x00, 0xf5, 0xdb, 0x0e, // 0x2270 + 0x00, 0x17, 0x48, 0xe2, 0x00, 0xf5, 0xdb, 0x0e, 0x00, 0xef, 0x5c, 0xe2, 0x00, 0xc1, 0xcc, 0x0d, // 0x2280 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2290 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22a0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22b0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x8d, 0x00, // 0x22c0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x72, 0xff, 0x00, 0xce, 0x25, 0xe1, 0x00, 0x2f, 0xe4, 0x0e, // 0x22d0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22e0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22f0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2300 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2310 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2320 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2330 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2340 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2350 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2360 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2370 + 0x0B, 0x5E, 0x0B, 0x5E, 0x00, 0x01, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x66, 0x0E, // 0x2380 + 0x94, 0x03, 0x4E, 0x43, 0x08, 0x64, 0xB6, 0x28, 0x0B, 0x03, 0x99, 0x0D, 0x00, 0x00, 0x00, 0x00, // 0x2390 + 0x00, 0x00, 0x00, 0x00, 0x2D, 0x0C, 0x1E, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23A0 + 0x00, 0x00, 0x01, 0x00, 0x00, 0x1E, 0x08, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23B0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23C0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23D0 + 0x21, // 0x23E0 - DSMIG_EN +}; + +// Tweeter DSM firmware (for 0x3C, 0x3D) +static const u8 max98390_dsm_firmware_tweeter[] = { + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, // 0x2050 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2060 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x80, 0x07, 0x07, 0x01, 0x00, 0x4A, 0x2B, 0x08, 0x00, // 0x2070 + 0x03, 0x03, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2080 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2090 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20A0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20B0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20C0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20D0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20E0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20F0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2100 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2110 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x46, 0x0d, 0x00, 0xf6, 0x73, 0xe5, // 0x2120 + 0x00, 0x05, 0x46, 0x0d, 0x00, 0xc0, 0xe9, 0xe5, 0x00, 0xd5, 0x01, 0x0b, 0x00, 0x9d, 0x78, 0x09, // 0x2130 + 0x00, 0xc5, 0x0e, 0xed, 0x00, 0x9d, 0x78, 0x09, 0x00, 0xc4, 0x4e, 0xf0, 0x00, 0x3a, 0x31, 0x06, // 0x2140 + 0x00, 0xaf, 0x75, 0x0e, 0x00, 0x8c, 0x1f, 0xf1, 0x00, 0x9e, 0x36, 0x0c, 0x00, 0x8c, 0x1f, 0xf1, // 0x2150 + 0x00, 0x4d, 0xac, 0x0a, 0x00, 0x7d, 0xa0, 0x0f, 0x00, 0xe1, 0x9c, 0xe3, 0x00, 0xe8, 0x59, 0x0e, // 0x2160 + 0x00, 0xe1, 0x9c, 0xe3, 0x00, 0x66, 0xfa, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // 0x2170 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 0x2180 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2190 + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21a0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21b0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21c0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21d0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21e0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x21f0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2200 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2210 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2220 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2230 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2240 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2250 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2260 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2270 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2280 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2290 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22a0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22b0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22c0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22d0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22e0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22f0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2300 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2310 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2320 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2330 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2340 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2350 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2360 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2370 + 0x0B, 0x5E, 0x0B, 0x5E, 0x00, 0x01, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x66, 0x0E, // 0x2380 + 0x94, 0x03, 0xAA, 0x2A, 0x09, 0x64, 0xB6, 0x28, 0x0B, 0x03, 0x99, 0x0D, 0x00, 0x00, 0x00, 0x00, // 0x2390 + 0x00, 0x00, 0x00, 0x00, 0x2D, 0x0C, 0x1E, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23A0 + 0x00, 0x00, 0x01, 0x00, 0x00, 0x1E, 0x08, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23B0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23C0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x23D0 + 0x20, // 0x23E0 - DSMIG_EN +}; + +// Load complete DSM firmware blob (913 bytes from 0x2050-0x23E0) +// Selects appropriate firmware based on speaker type (woofer vs tweeter) +static void max98390_load_dsm_firmware(struct max98390_hda_priv *priv, bool is_tweeter) +{ + const u8 *firmware; + int i; + + // Select appropriate firmware blob + if (is_tweeter) { + firmware = max98390_dsm_firmware_tweeter; + } else { + firmware = max98390_dsm_firmware_woofer; + } + + // Write all 913 bytes sequentially starting from 0x2050 + // This matches coolstar's uploadDSMBin() implementation + for (i = 0; i < MAX98390_DSM_PARAM_SIZE; i++) { + regmap_write(priv->regmap, MAX98390_DSM_START_ADDR + i, firmware[i]); + } +} + +// Configure DSM with appropriate firmware based on speaker type +// cutoff_freq: cutoff frequency in Hz (unused - filter built into tweeter firmware) +// is_tweeter: true for tweeters (0x3C, 0x3D), false for woofers (0x38, 0x39) +void max98390_configure_high_pass_filter(struct max98390_hda_priv *priv, int cutoff_freq, bool is_tweeter) +{ + // Load complete DSM firmware (913 bytes, all registers) + // Tweeter firmware includes high-pass filter in STBASS section + // Woofer firmware is full-range + max98390_load_dsm_firmware(priv, is_tweeter); + + // Set DSM_VOL_CTRL - Tweeter more quiet than woofer + if (is_tweeter) { + regmap_write(priv->regmap, 0x23BA, 0x8d); // DSM_VOL_CTRL Tweeter + } if(!is_tweeter) { + regmap_write(priv->regmap, 0x23BA, 0xA0); // DSM_VOL_CTRL Woofer + } + + // SET DSP_GLOBAL_EN TO 1 (WRITE 0x01 TO 0x23E1) + regmap_write(priv->regmap, 0x23E1, 0x01); + + // SET SPK_EN TO 1 (WRITE 0x81 TO 0x203A) + regmap_write(priv->regmap, 0x203A, 0x81); + + // SET EN TO 1 (WRITE 0x01 TO 0x23FF) + regmap_write(priv->regmap, 0x23FF, 0x01); +} + +MODULE_DESCRIPTION("HDA MAX98390 driver"); +MODULE_AUTHOR("Kevin Cuperus "); +MODULE_LICENSE("GPL"); diff --git a/sound/hda/codecs/side-codecs/max98390_hda_filters.h b/sound/hda/codecs/side-codecs/max98390_hda_filters.h new file mode 100644 index 00000000000000..d41dd4ca3b510c --- /dev/null +++ b/sound/hda/codecs/side-codecs/max98390_hda_filters.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// MAX98390 HDA filter configuration header +// + +#ifndef __MAX98390_HDA_FILTERS_H +#define __MAX98390_HDA_FILTERS_H + +struct max98390_hda_priv; + +// Function prototypes +void max98390_configure_filters(struct max98390_hda_priv *priv); +void max98390_configure_high_pass_filter(struct max98390_hda_priv *priv, int cutoff_freq, bool is_tweeter); + +#endif /* __MAX98390_HDA_FILTERS_H */ \ No newline at end of file diff --git a/sound/hda/codecs/side-codecs/max98390_hda_i2c.c b/sound/hda/codecs/side-codecs/max98390_hda_i2c.c new file mode 100644 index 00000000000000..38d2ca7f8ee661 --- /dev/null +++ b/sound/hda/codecs/side-codecs/max98390_hda_i2c.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MAX98390 HDA I2C driver +// + +#include +#include +#include + +#include "max98390_hda.h" +#include "../../../soc/codecs/max98390.h" + +static int max98390_hda_i2c_probe(struct i2c_client *clt) +{ + const char *device_name; + const char *name_suffix; + int index = 0; + + /* Match device name from serial-multi-instantiate */ + if (strstr(dev_name(&clt->dev), "MAX98390") || + strstr(dev_name(&clt->dev), "max98390")) + device_name = "MAX98390"; + else + return -ENODEV; + + /* Parse index from device name (e.g., "i2c-MAX98390:00-max98390-hda.0" -> index 0) */ + name_suffix = strrchr(dev_name(&clt->dev), '.'); + if (name_suffix) { + if (kstrtoint(name_suffix + 1, 10, &index) < 0) { + dev_err(&clt->dev, "Failed to parse device index from name: %s\n", + dev_name(&clt->dev)); + return -EINVAL; + } + } + + return max98390_hda_probe(&clt->dev, device_name, index, clt->irq, + devm_regmap_init_i2c(clt, &max98390_regmap), + MAX98390_HDA_I2C, clt->addr); +} + +static void max98390_hda_i2c_remove(struct i2c_client *clt) +{ + max98390_hda_remove(&clt->dev); +} + +static const struct i2c_device_id max98390_hda_i2c_id[] = { + { "max98390-hda", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, max98390_hda_i2c_id); + +static const struct acpi_device_id max98390_acpi_hda_match[] = { + { "MAX98390", 0 }, + { "MX98390", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, max98390_acpi_hda_match); + +static struct i2c_driver max98390_hda_i2c_driver = { + .driver = { + .name = "max98390-hda", + .acpi_match_table = max98390_acpi_hda_match, + .pm = &max98390_hda_pm_ops, + }, + .id_table = max98390_hda_i2c_id, + .probe = max98390_hda_i2c_probe, + .remove = max98390_hda_i2c_remove, +}; +module_i2c_driver(max98390_hda_i2c_driver); + +MODULE_DESCRIPTION("HDA MAX98390 driver"); +MODULE_IMPORT_NS("SND_HDA_SCODEC_MAX98390"); +MODULE_AUTHOR("Kevin Cuperus "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 3dd4dd94bc371f..446465cbb4ccd7 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -978,7 +978,7 @@ static const struct snd_soc_component_driver soc_codec_dev_max98390 = { .endianness = 1, }; -static const struct regmap_config max98390_regmap = { +const struct regmap_config max98390_regmap = { .reg_bits = 16, .val_bits = 8, .max_register = MAX98390_R24FF_REV_ID, @@ -988,6 +988,7 @@ static const struct regmap_config max98390_regmap = { .volatile_reg = max98390_volatile_reg, .cache_type = REGCACHE_RBTREE, }; +EXPORT_SYMBOL_GPL(max98390_regmap); static void max98390_slot_config(struct i2c_client *i2c, struct max98390_priv *max98390) @@ -1111,6 +1112,7 @@ MODULE_DEVICE_TABLE(of, max98390_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id max98390_acpi_match[] = { + { "MAX98390", 0 }, { "MX98390", 0 }, {}, }; diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h index f4d6758ab4c630..c235df0f2ac822 100644 --- a/sound/soc/codecs/max98390.h +++ b/sound/soc/codecs/max98390.h @@ -664,4 +664,8 @@ struct max98390_priv { unsigned int ambient_temp_value; const char *dsm_param_name; }; + +/* Exported for HDA side-codec driver */ +extern const struct regmap_config max98390_regmap; + #endif