-
Notifications
You must be signed in to change notification settings - Fork 140
Sound support for the Samsung Galaxy Book 4 #5616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: topic/sof-dev
Are you sure you want to change the base?
Changes from all commits
57088c5
4898a18
4a4b843
0a4ffae
774c875
c4a897b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| // SPDX-License-Identifier: GPL-2.0 | ||
| // | ||
| // 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: | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace pretty much all |
||
|
|
||
| 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) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove curly-brackets for single-lined branch.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not understand There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -> |
||
| 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"); | ||
| 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__ */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Use
/* */?