Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
16 changes: 16 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,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),
Expand Down Expand Up @@ -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"},
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

Choose a reason for hiding this comment

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

Nit: Use /* */?

// 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

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

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

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
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

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 <[email protected]>");
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