1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver |
4 | * |
5 | * Copyright (C) Nokia Corporation |
6 | * |
7 | * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/device.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/gpio.h> |
15 | #include <linux/regulator/consumer.h> |
16 | #include <linux/slab.h> |
17 | #include <sound/tpa6130a2-plat.h> |
18 | #include <sound/soc.h> |
19 | #include <sound/tlv.h> |
20 | #include <linux/of.h> |
21 | #include <linux/of_gpio.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | #include "tpa6130a2.h" |
25 | |
26 | enum tpa_model { |
27 | TPA6130A2, |
28 | TPA6140A2, |
29 | }; |
30 | |
31 | /* This struct is used to save the context */ |
32 | struct tpa6130a2_data { |
33 | struct device *dev; |
34 | struct regmap *regmap; |
35 | struct regulator *supply; |
36 | int power_gpio; |
37 | enum tpa_model id; |
38 | }; |
39 | |
40 | static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) |
41 | { |
42 | int ret = 0, ret2; |
43 | |
44 | if (enable) { |
45 | ret = regulator_enable(regulator: data->supply); |
46 | if (ret != 0) { |
47 | dev_err(data->dev, |
48 | "Failed to enable supply: %d\n" , ret); |
49 | return ret; |
50 | } |
51 | /* Power on */ |
52 | if (data->power_gpio >= 0) |
53 | gpio_set_value(gpio: data->power_gpio, value: 1); |
54 | |
55 | /* Sync registers */ |
56 | regcache_cache_only(map: data->regmap, enable: false); |
57 | ret = regcache_sync(map: data->regmap); |
58 | if (ret != 0) { |
59 | dev_err(data->dev, |
60 | "Failed to sync registers: %d\n" , ret); |
61 | regcache_cache_only(map: data->regmap, enable: true); |
62 | if (data->power_gpio >= 0) |
63 | gpio_set_value(gpio: data->power_gpio, value: 0); |
64 | ret2 = regulator_disable(regulator: data->supply); |
65 | if (ret2 != 0) |
66 | dev_err(data->dev, |
67 | "Failed to disable supply: %d\n" , ret2); |
68 | return ret; |
69 | } |
70 | } else { |
71 | /* Powered off device does not retain registers. While device |
72 | * is off, any register updates (i.e. volume changes) should |
73 | * happen in cache only. |
74 | */ |
75 | regcache_mark_dirty(map: data->regmap); |
76 | regcache_cache_only(map: data->regmap, enable: true); |
77 | |
78 | /* Power off */ |
79 | if (data->power_gpio >= 0) |
80 | gpio_set_value(gpio: data->power_gpio, value: 0); |
81 | |
82 | ret = regulator_disable(regulator: data->supply); |
83 | if (ret != 0) { |
84 | dev_err(data->dev, |
85 | "Failed to disable supply: %d\n" , ret); |
86 | return ret; |
87 | } |
88 | } |
89 | |
90 | return ret; |
91 | } |
92 | |
93 | static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w, |
94 | struct snd_kcontrol *kctrl, int event) |
95 | { |
96 | struct snd_soc_component *c = snd_soc_dapm_to_component(dapm: w->dapm); |
97 | struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c); |
98 | |
99 | if (SND_SOC_DAPM_EVENT_ON(event)) { |
100 | /* Before widget power up: turn chip on, sync registers */ |
101 | return tpa6130a2_power(data, enable: true); |
102 | } else { |
103 | /* After widget power down: turn chip off */ |
104 | return tpa6130a2_power(data, enable: false); |
105 | } |
106 | } |
107 | |
108 | /* |
109 | * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going |
110 | * down in gain. |
111 | */ |
112 | static const DECLARE_TLV_DB_RANGE(tpa6130_tlv, |
113 | 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0), |
114 | 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0), |
115 | 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0), |
116 | 6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0), |
117 | 8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0), |
118 | 10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0), |
119 | 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0), |
120 | 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0), |
121 | 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0), |
122 | 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0) |
123 | ); |
124 | |
125 | static const struct snd_kcontrol_new tpa6130a2_controls[] = { |
126 | SOC_SINGLE_TLV("Headphone Playback Volume" , |
127 | TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, |
128 | tpa6130_tlv), |
129 | }; |
130 | |
131 | static const DECLARE_TLV_DB_RANGE(tpa6140_tlv, |
132 | 0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0), |
133 | 9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0), |
134 | 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0) |
135 | ); |
136 | |
137 | static const struct snd_kcontrol_new tpa6140a2_controls[] = { |
138 | SOC_SINGLE_TLV("Headphone Playback Volume" , |
139 | TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, |
140 | tpa6140_tlv), |
141 | }; |
142 | |
143 | static int tpa6130a2_component_probe(struct snd_soc_component *component) |
144 | { |
145 | struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c: component); |
146 | |
147 | if (data->id == TPA6140A2) |
148 | return snd_soc_add_component_controls(component, |
149 | controls: tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls)); |
150 | else |
151 | return snd_soc_add_component_controls(component, |
152 | controls: tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls)); |
153 | } |
154 | |
155 | static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { |
156 | SND_SOC_DAPM_INPUT("LEFTIN" ), |
157 | SND_SOC_DAPM_INPUT("RIGHTIN" ), |
158 | SND_SOC_DAPM_OUTPUT("HPLEFT" ), |
159 | SND_SOC_DAPM_OUTPUT("HPRIGHT" ), |
160 | |
161 | SND_SOC_DAPM_PGA("Left Mute" , TPA6130A2_REG_VOL_MUTE, |
162 | TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0), |
163 | SND_SOC_DAPM_PGA("Right Mute" , TPA6130A2_REG_VOL_MUTE, |
164 | TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0), |
165 | SND_SOC_DAPM_PGA("Left PGA" , TPA6130A2_REG_CONTROL, |
166 | TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0), |
167 | SND_SOC_DAPM_PGA("Right PGA" , TPA6130A2_REG_CONTROL, |
168 | TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0), |
169 | |
170 | SND_SOC_DAPM_SUPPLY("Power" , TPA6130A2_REG_CONTROL, |
171 | TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event, |
172 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
173 | }; |
174 | |
175 | static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = { |
176 | { "Left PGA" , NULL, "LEFTIN" }, |
177 | { "Right PGA" , NULL, "RIGHTIN" }, |
178 | |
179 | { "Left Mute" , NULL, "Left PGA" }, |
180 | { "Right Mute" , NULL, "Right PGA" }, |
181 | |
182 | { "HPLEFT" , NULL, "Left Mute" }, |
183 | { "HPRIGHT" , NULL, "Right Mute" }, |
184 | |
185 | { "Left PGA" , NULL, "Power" }, |
186 | { "Right PGA" , NULL, "Power" }, |
187 | }; |
188 | |
189 | static const struct snd_soc_component_driver tpa6130a2_component_driver = { |
190 | .name = "tpa6130a2" , |
191 | .probe = tpa6130a2_component_probe, |
192 | .dapm_widgets = tpa6130a2_dapm_widgets, |
193 | .num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets), |
194 | .dapm_routes = tpa6130a2_dapm_routes, |
195 | .num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes), |
196 | }; |
197 | |
198 | static const struct reg_default tpa6130a2_reg_defaults[] = { |
199 | { TPA6130A2_REG_CONTROL, TPA6130A2_SWS }, |
200 | { TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L }, |
201 | }; |
202 | |
203 | static const struct regmap_config tpa6130a2_regmap_config = { |
204 | .reg_bits = 8, |
205 | .val_bits = 8, |
206 | .max_register = TPA6130A2_REG_VERSION, |
207 | .reg_defaults = tpa6130a2_reg_defaults, |
208 | .num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults), |
209 | .cache_type = REGCACHE_RBTREE, |
210 | }; |
211 | |
212 | static const struct i2c_device_id tpa6130a2_id[] = { |
213 | { "tpa6130a2" , TPA6130A2 }, |
214 | { "tpa6140a2" , TPA6140A2 }, |
215 | { } |
216 | }; |
217 | MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); |
218 | |
219 | static int tpa6130a2_probe(struct i2c_client *client) |
220 | { |
221 | struct device *dev; |
222 | struct tpa6130a2_data *data; |
223 | struct tpa6130a2_platform_data *pdata = client->dev.platform_data; |
224 | struct device_node *np = client->dev.of_node; |
225 | const struct i2c_device_id *id; |
226 | const char *regulator; |
227 | unsigned int version; |
228 | int ret; |
229 | |
230 | dev = &client->dev; |
231 | |
232 | data = devm_kzalloc(dev: &client->dev, size: sizeof(*data), GFP_KERNEL); |
233 | if (!data) |
234 | return -ENOMEM; |
235 | |
236 | data->dev = dev; |
237 | |
238 | data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config); |
239 | if (IS_ERR(ptr: data->regmap)) |
240 | return PTR_ERR(ptr: data->regmap); |
241 | |
242 | if (pdata) { |
243 | data->power_gpio = pdata->power_gpio; |
244 | } else if (np) { |
245 | data->power_gpio = of_get_named_gpio(np, list_name: "power-gpio" , index: 0); |
246 | } else { |
247 | dev_err(dev, "Platform data not set\n" ); |
248 | dump_stack(); |
249 | return -ENODEV; |
250 | } |
251 | |
252 | i2c_set_clientdata(client, data); |
253 | |
254 | id = i2c_match_id(id: tpa6130a2_id, client); |
255 | data->id = id->driver_data; |
256 | |
257 | if (data->power_gpio >= 0) { |
258 | ret = devm_gpio_request(dev, gpio: data->power_gpio, |
259 | label: "tpa6130a2 enable" ); |
260 | if (ret < 0) { |
261 | dev_err(dev, "Failed to request power GPIO (%d)\n" , |
262 | data->power_gpio); |
263 | return ret; |
264 | } |
265 | gpio_direction_output(gpio: data->power_gpio, value: 0); |
266 | } |
267 | |
268 | switch (data->id) { |
269 | default: |
270 | dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n" , |
271 | data->id); |
272 | fallthrough; |
273 | case TPA6130A2: |
274 | regulator = "Vdd" ; |
275 | break; |
276 | case TPA6140A2: |
277 | regulator = "AVdd" ; |
278 | break; |
279 | } |
280 | |
281 | data->supply = devm_regulator_get(dev, id: regulator); |
282 | if (IS_ERR(ptr: data->supply)) { |
283 | ret = PTR_ERR(ptr: data->supply); |
284 | dev_err(dev, "Failed to request supply: %d\n" , ret); |
285 | return ret; |
286 | } |
287 | |
288 | ret = tpa6130a2_power(data, enable: true); |
289 | if (ret != 0) |
290 | return ret; |
291 | |
292 | |
293 | /* Read version */ |
294 | regmap_read(map: data->regmap, TPA6130A2_REG_VERSION, val: &version); |
295 | version &= TPA6130A2_VERSION_MASK; |
296 | if ((version != 1) && (version != 2)) |
297 | dev_warn(dev, "UNTESTED version detected (%d)\n" , version); |
298 | |
299 | /* Disable the chip */ |
300 | ret = tpa6130a2_power(data, enable: false); |
301 | if (ret != 0) |
302 | return ret; |
303 | |
304 | return devm_snd_soc_register_component(dev: &client->dev, |
305 | component_driver: &tpa6130a2_component_driver, NULL, num_dai: 0); |
306 | } |
307 | |
308 | #if IS_ENABLED(CONFIG_OF) |
309 | static const struct of_device_id tpa6130a2_of_match[] = { |
310 | { .compatible = "ti,tpa6130a2" , }, |
311 | { .compatible = "ti,tpa6140a2" }, |
312 | {}, |
313 | }; |
314 | MODULE_DEVICE_TABLE(of, tpa6130a2_of_match); |
315 | #endif |
316 | |
317 | static struct i2c_driver tpa6130a2_i2c_driver = { |
318 | .driver = { |
319 | .name = "tpa6130a2" , |
320 | .of_match_table = of_match_ptr(tpa6130a2_of_match), |
321 | }, |
322 | .probe = tpa6130a2_probe, |
323 | .id_table = tpa6130a2_id, |
324 | }; |
325 | |
326 | module_i2c_driver(tpa6130a2_i2c_driver); |
327 | |
328 | MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>" ); |
329 | MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver" ); |
330 | MODULE_LICENSE("GPL" ); |
331 | |