Skip to content

Commit 8bf7966

Browse files
BreadJS1Naim
authored andcommitted
Sound support for the Samsung Galaxy Book 4
1 parent 591cd65 commit 8bf7966

12 files changed

Lines changed: 725 additions & 1 deletion

File tree

drivers/acpi/scan.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
17581758
{"CSC3557", },
17591759
{"INT33FE", },
17601760
{"INT3515", },
1761+
{"MAX98390", },
17611762
{"TXNW2781", },
17621763
/* Non-conforming _HID for Cirrus Logic already released */
17631764
{"CLSA0100", },

drivers/platform/x86/serial-multi-instantiate.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,17 @@ static const struct smi_node tas2781_hda = {
400400
.bus_type = SMI_AUTO_DETECT,
401401
};
402402

403+
static const struct smi_node max98390_hda = {
404+
.instances = {
405+
{ "max98390-hda", IRQ_RESOURCE_NONE, 0 },
406+
{ "max98390-hda", IRQ_RESOURCE_NONE, 0 },
407+
{ "max98390-hda", IRQ_RESOURCE_NONE, 0 },
408+
{ "max98390-hda", IRQ_RESOURCE_NONE, 0 },
409+
{}
410+
},
411+
.bus_type = SMI_I2C,
412+
};
413+
403414
/*
404415
* Note new device-ids must also be added to ignore_serial_bus_ids in
405416
* drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
@@ -412,6 +423,7 @@ static const struct acpi_device_id smi_acpi_ids[] = {
412423
{ "CSC3556", (unsigned long)&cs35l56_hda },
413424
{ "CSC3557", (unsigned long)&cs35l57_hda },
414425
{ "INT3515", (unsigned long)&int3515_data },
426+
{ "MAX98390", (unsigned long)&max98390_hda },
415427
{ "TXNW2781", (unsigned long)&tas2781_hda },
416428
/* Non-conforming _HID for Cirrus Logic already released */
417429
{ "CLSA0100", (unsigned long)&cs35l41_hda },

sound/hda/codecs/realtek/alc269.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,6 +3279,11 @@ static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fix
32793279
comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
32803280
}
32813281

3282+
static void max98390_fixup_i2c_four(struct hda_codec *codec, const struct hda_fixup *fix, int action)
3283+
{
3284+
comp_generic_fixup(codec, action, "i2c", "MAX98390", "-%s:00-max98390-hda.%d", 4);
3285+
}
3286+
32823287
static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
32833288
int action)
32843289
{
@@ -4001,6 +4006,7 @@ enum {
40014006
ALC298_FIXUP_SAMSUNG_AMP,
40024007
ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS,
40034008
ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS,
4009+
ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS,
40044010
ALC298_FIXUP_LG_GRAM_STYLE_14,
40054011
ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
40064012
ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
@@ -5719,6 +5725,10 @@ static const struct hda_fixup alc269_fixups[] = {
57195725
.type = HDA_FIXUP_FUNC,
57205726
.v.func = alc298_fixup_lg_gram_style_14
57215727
},
5728+
[ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS] = {
5729+
.type = HDA_FIXUP_FUNC,
5730+
.v.func = max98390_fixup_i2c_four
5731+
},
57225732
[ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
57235733
.type = HDA_FIXUP_VERBS,
57245734
.v.verbs = (const struct hda_verb[]) {
@@ -7430,6 +7440,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
74307440
SND_PCI_QUIRK(0x144d, 0xc876, "Samsung 730QED (NP730QED-KA2US)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
74317441
SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP),
74327442
SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
7443+
SND_PCI_QUIRK(0x144d, 0xca07, "Samsung Galaxy Book4 Pro (MAX98390)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS),
7444+
SND_PCI_QUIRK(0x144d, 0xc892, "Samsung Galaxy Book4 360 (MAX98390)", ALC298_FIXUP_SAMSUNG_MAX98390_4_AMPS),
74337445
SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP),
74347446
SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS),
74357447
SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS),

sound/hda/codecs/side-codecs/Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,22 @@ config SND_HDA_SCODEC_TAS2781_SPI
141141

142142
comment "Set to Y if you want auto-loading the side codec driver"
143143
depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m
144+
145+
config SND_HDA_SCODEC_MAX98390
146+
tristate
147+
select SND_HDA_GENERIC
148+
select SND_HDA_SCODEC_COMPONENT
149+
150+
config SND_HDA_SCODEC_MAX98390_I2C
151+
tristate "Build MAX98390 HD-audio side codec support for I2C Bus"
152+
depends on I2C
153+
depends on ACPI
154+
depends on SND_SOC
155+
select SND_SOC_MAX98390
156+
select SND_HDA_SCODEC_MAX98390
157+
help
158+
Say Y or M here to include MAX98390 I2C HD-audio side codec support
159+
in snd-hda-intel driver, such as ALC298.
160+
161+
comment "Set to Y if you want auto-loading the side codec driver"
162+
depends on SND_HDA=y && SND_HDA_SCODEC_MAX98390_I2C=m

sound/hda/codecs/side-codecs/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ snd-hda-scodec-component-y := hda_component.o
1313
snd-hda-scodec-tas2781-y := tas2781_hda.o
1414
snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
1515
snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o
16+
snd-hda-scodec-max98390-y := max98390_hda.o max98390_hda_filters.o
17+
snd-hda-scodec-max98390-i2c-y := max98390_hda_i2c.o
1618

1719
obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o
1820
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
2628
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o
2729
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
2830
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
31+
obj-$(CONFIG_SND_HDA_SCODEC_MAX98390) += snd-hda-scodec-max98390.o
32+
obj-$(CONFIG_SND_HDA_SCODEC_MAX98390_I2C) += snd-hda-scodec-max98390-i2c.o
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
//
3+
// MAX98390 HDA driver
4+
//
5+
6+
#include <linux/module.h>
7+
#include <linux/regmap.h>
8+
#include <sound/hda_codec.h>
9+
#include <sound/soc.h>
10+
#include "hda_local.h"
11+
#include "hda_component.h"
12+
#include "../generic.h"
13+
#include "max98390_hda.h"
14+
#include "max98390_hda_filters.h"
15+
#include "../../../soc/codecs/max98390.h"
16+
17+
static void max98390_hda_playback_hook(struct device *dev, int action)
18+
{
19+
struct max98390_hda_priv *priv = dev_get_drvdata(dev);
20+
int ret;
21+
22+
switch (action) {
23+
case HDA_GEN_PCM_ACT_OPEN:
24+
25+
/* Enable global and speaker amp */
26+
ret = regmap_write(priv->regmap, MAX98390_R23FF_GLOBAL_EN, 0x01);
27+
if (ret < 0)
28+
dev_err(dev, "Failed to write GLOBAL_EN: %d\n", ret);
29+
30+
ret = regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x81);
31+
if (ret < 0)
32+
dev_err(dev, "Failed to write AMP_EN: %d\n", ret);
33+
34+
break;
35+
36+
case HDA_GEN_PCM_ACT_CLOSE:
37+
/* Disable speaker amp and global */
38+
regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80);
39+
regmap_write(priv->regmap, MAX98390_R23FF_GLOBAL_EN, 0x00);
40+
break;
41+
42+
default:
43+
break;
44+
}
45+
}
46+
47+
static int max98390_hda_bind(struct device *dev, struct device *master, void *master_data)
48+
{
49+
struct max98390_hda_priv *priv = dev_get_drvdata(dev);
50+
struct hda_component_parent *parent = master_data;
51+
struct hda_component *comp;
52+
53+
comp = hda_component_from_index(parent, priv->index);
54+
if (!comp)
55+
return -EINVAL;
56+
57+
comp->dev = dev;
58+
strscpy(comp->name, dev_name(dev), sizeof(comp->name));
59+
comp->playback_hook = max98390_hda_playback_hook;
60+
61+
dev_info(dev, "MAX98390 HDA component bound (index %d)\n", priv->index);
62+
63+
return 0;
64+
}
65+
66+
static void max98390_hda_unbind(struct device *dev, struct device *master, void *master_data)
67+
{
68+
struct max98390_hda_priv *priv = dev_get_drvdata(dev);
69+
struct hda_component_parent *parent = master_data;
70+
struct hda_component *comp;
71+
72+
comp = hda_component_from_index(parent, priv->index);
73+
if (comp && comp->dev == dev) {
74+
comp->dev = NULL;
75+
memset(comp->name, 0, sizeof(comp->name));
76+
comp->playback_hook = NULL;
77+
}
78+
79+
dev_info(dev, "MAX98390 HDA component unbound\n");
80+
}
81+
82+
static const struct component_ops max98390_hda_comp_ops = {
83+
.bind = max98390_hda_bind,
84+
.unbind = max98390_hda_unbind,
85+
};
86+
87+
static int max98390_hda_init(struct max98390_hda_priv *priv)
88+
{
89+
int ret;
90+
unsigned int reg, global_en, amp_en, pcm_rx;
91+
92+
/* Check device ID */
93+
ret = regmap_read(priv->regmap, MAX98390_R24FF_REV_ID, &reg);
94+
if (ret < 0) {
95+
return ret;
96+
}
97+
98+
/* Software reset */
99+
ret = regmap_write(priv->regmap, MAX98390_SOFTWARE_RESET, 0x01);
100+
if (ret < 0) {
101+
return ret;
102+
}
103+
msleep(20);
104+
105+
/* Basic register initialization (minimal setup for HDA) */
106+
regmap_write(priv->regmap, MAX98390_CLK_MON, 0x6f);
107+
regmap_write(priv->regmap, MAX98390_DAT_MON, 0x00);
108+
regmap_write(priv->regmap, MAX98390_PWR_GATE_CTL, 0x00);
109+
regmap_write(priv->regmap, MAX98390_PCM_RX_EN_A, 0x03);
110+
regmap_write(priv->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e);
111+
regmap_write(priv->regmap, MAX98390_BOOST_BYPASS1, 0x46);
112+
regmap_write(priv->regmap, MAX98390_FET_SCALING3, 0x03);
113+
114+
/* PCM/I2S configuration - CRITICAL for correct audio format */
115+
/* 0xC0 = I2S mode, 32-bit samples (standard for HDA) */
116+
regmap_write(priv->regmap, MAX98390_PCM_MODE_CFG, 0xc0);
117+
regmap_write(priv->regmap, MAX98390_PCM_MASTER_MODE, 0x1c);
118+
regmap_write(priv->regmap, MAX98390_PCM_CLK_SETUP, 0x44);
119+
regmap_write(priv->regmap, MAX98390_PCM_SR_SETUP, 0x08);
120+
121+
/* RESET EN - Write 0x00 to 0x23FF */
122+
regmap_write(priv->regmap, MAX98390_R23FF_GLOBAL_EN, 0x00);
123+
124+
/* Wait 50ms */
125+
msleep(50);
126+
127+
/* RESET SPK_EN - Write 0x80 to 0x203A */
128+
regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80);
129+
130+
/* RESET DSP_GLOBAL_EN - Write 0x00 to 0x23E1 */
131+
regmap_write(priv->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x00);
132+
133+
/* Step 6: Write non-DSM registers (0x2000-0x2084) - done in configure_filters */
134+
/* Step 7: Write DSM registers (0x2100-0x23E0) - done in configure_filters */
135+
/* Step 8-10: Enable DSP and amp - done in configure_filters */
136+
max98390_configure_filters(priv);
137+
138+
return 0;
139+
}
140+
141+
int max98390_hda_probe(struct device *dev, const char *device_name,
142+
int id, int irq, struct regmap *regmap,
143+
enum max98390_hda_bus_type bus_type, int i2c_addr)
144+
{
145+
struct max98390_hda_priv *priv;
146+
int ret;
147+
148+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
149+
if (!priv)
150+
return -ENOMEM;
151+
152+
priv->dev = dev;
153+
priv->regmap = regmap;
154+
priv->bus_type = bus_type;
155+
priv->irq = irq;
156+
priv->index = id;
157+
priv->i2c_addr = i2c_addr;
158+
dev_set_drvdata(dev, priv);
159+
160+
ret = max98390_hda_init(priv);
161+
if (ret)
162+
return ret;
163+
164+
ret = component_add(dev, &max98390_hda_comp_ops);
165+
if (ret) {
166+
return ret;
167+
}
168+
169+
return 0;
170+
}
171+
EXPORT_SYMBOL_NS_GPL(max98390_hda_probe, "SND_HDA_SCODEC_MAX98390");
172+
173+
void max98390_hda_remove(struct device *dev)
174+
{
175+
struct max98390_hda_priv *priv = dev_get_drvdata(dev);
176+
177+
component_del(dev, &max98390_hda_comp_ops);
178+
179+
if (priv && priv->regmap) {
180+
/* Disable amp on removal */
181+
regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80);
182+
}
183+
}
184+
EXPORT_SYMBOL_NS_GPL(max98390_hda_remove, "SND_HDA_SCODEC_MAX98390");
185+
186+
static int max98390_hda_runtime_suspend(struct device *dev)
187+
{
188+
struct max98390_hda_priv *priv = dev_get_drvdata(dev);
189+
190+
regmap_write(priv->regmap, MAX98390_R203A_AMP_EN, 0x80);
191+
regcache_cache_only(priv->regmap, true);
192+
regcache_mark_dirty(priv->regmap);
193+
194+
return 0;
195+
}
196+
197+
static int max98390_hda_runtime_resume(struct device *dev)
198+
{
199+
struct max98390_hda_priv *priv = dev_get_drvdata(dev);
200+
201+
regcache_cache_only(priv->regmap, false);
202+
regcache_sync(priv->regmap);
203+
204+
return 0;
205+
}
206+
207+
const struct dev_pm_ops max98390_hda_pm_ops = {
208+
RUNTIME_PM_OPS(max98390_hda_runtime_suspend, max98390_hda_runtime_resume, NULL)
209+
};
210+
EXPORT_SYMBOL_NS_GPL(max98390_hda_pm_ops, "SND_HDA_SCODEC_MAX98390");
211+
212+
MODULE_DESCRIPTION("HDA MAX98390 side codec library");
213+
MODULE_AUTHOR("Kevin Cuperus <cuperus.kevin@hotmail.com>");
214+
MODULE_LICENSE("GPL");
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* MAX98390 HDA audio driver
4+
*/
5+
6+
#ifndef __MAX98390_HDA_H__
7+
#define __MAX98390_HDA_H__
8+
9+
#include <linux/regmap.h>
10+
#include <sound/hda_codec.h>
11+
12+
enum max98390_hda_bus_type {
13+
MAX98390_HDA_I2C,
14+
};
15+
16+
struct max98390_hda_priv {
17+
struct device *dev;
18+
struct regmap *regmap;
19+
enum max98390_hda_bus_type bus_type;
20+
int irq;
21+
int index;
22+
const char *acpi_subsystem_id;
23+
int i2c_addr; /* I2C address for speaker identification */
24+
};
25+
26+
int max98390_hda_probe(struct device *dev, const char *device_name,
27+
int id, int irq, struct regmap *regmap,
28+
enum max98390_hda_bus_type bus_type, int i2c_addr);
29+
void max98390_hda_remove(struct device *dev);
30+
31+
extern const struct dev_pm_ops max98390_hda_pm_ops;
32+
33+
#endif /* __MAX98390_HDA_H__ */

0 commit comments

Comments
 (0)