Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/acpi/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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", },
Expand Down
12 changes: 12 additions & 0 deletions drivers/platform/x86/serial-multi-instantiate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand All @@ -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 },
Expand Down
14 changes: 14 additions & 0 deletions sound/hda/codecs/realtek/alc269.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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[]) {
Expand Down Expand Up @@ -6975,6 +6985,8 @@ 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 (NP940XGK)", 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, 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),
Expand Down Expand Up @@ -7478,6 +7490,8 @@ 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_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this line was accidentally duplicated.

Copy link
Copy Markdown
Author

@BreadJS BreadJS Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! Thank you for noticing!

{.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"},
Expand Down
19 changes: 19 additions & 0 deletions sound/hda/codecs/side-codecs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions sound/hda/codecs/side-codecs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
214 changes: 214 additions & 0 deletions sound/hda/codecs/side-codecs/max98390_hda.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0
//
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Use /* */?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.kernel.org/doc/Documentation/process/license-rules.rst :

2. Style:

   The SPDX license identifier is added in form of a comment.  The comment
   style depends on the file type::

      C source:	// SPDX-License-Identifier: <SPDX License Expression>
      C header:	/* SPDX-License-Identifier: <SPDX License Expression> */
      ASM:	/* SPDX-License-Identifier: <SPDX License Expression> */
      scripts:	# SPDX-License-Identifier: <SPDX License Expression>
      .rst:	.. SPDX-License-Identifier: <SPDX License Expression>
      .dts{i}:	// SPDX-License-Identifier: <SPDX License Expression>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know this. So mine is fine right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant

//
// MAX98390 HDA driver
//

->

/*
 * MAX98390 HDA driver
 */

Multi-line comments are more common with /* */ on the Linux kernel. But as I said, it's nit picking.

// MAX98390 HDA driver
//

#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/hda_codec.h>
#include <sound/soc.h>
#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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove whitespace line.

/* 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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace pretty much all dev_info()s to dev_dbg(). These are too verbose for upstream.


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, &reg);
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) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove curly-brackets for single-lined branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

	if (ret) {
		return ret;
	}

->

	if (ret)
		return 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 <cuperus.kevin@hotmail.com>");
MODULE_LICENSE("GPL");
33 changes: 33 additions & 0 deletions sound/hda/codecs/side-codecs/max98390_hda.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* MAX98390 HDA audio driver
*/

#ifndef __MAX98390_HDA_H__
#define __MAX98390_HDA_H__

#include <linux/regmap.h>
#include <sound/hda_codec.h>

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__ */
Loading