forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathmax98390_hda.c
More file actions
214 lines (169 loc) · 5.76 KB
/
max98390_hda.c
File metadata and controls
214 lines (169 loc) · 5.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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:
/* 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 <cuperus.kevin@hotmail.com>");
MODULE_LICENSE("GPL");