1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * Copyright (C) 2021 Stephan Gerhold |
4 | * |
5 | * Register definitions/sequences taken from various tfa98xx kernel drivers: |
6 | * Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved. |
7 | * Copyright (C) 2013 Sony Mobile Communications Inc. |
8 | */ |
9 | |
10 | #include <linux/gpio/consumer.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/regulator/consumer.h> |
15 | #include <sound/soc.h> |
16 | |
17 | #define TFA989X_STATUSREG 0x00 |
18 | #define TFA989X_BATTERYVOLTAGE 0x01 |
19 | #define TFA989X_TEMPERATURE 0x02 |
20 | #define TFA989X_REVISIONNUMBER 0x03 |
21 | #define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */ |
22 | #define TFA989X_I2SREG 0x04 |
23 | #define TFA989X_I2SREG_RCV 2 /* receiver mode */ |
24 | #define TFA989X_I2SREG_CHSA 6 /* amplifier input select */ |
25 | #define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6) |
26 | #define TFA989X_I2SREG_I2SSR 12 /* sample rate */ |
27 | #define TFA989X_I2SREG_I2SSR_MSK GENMASK(15, 12) |
28 | #define TFA989X_BAT_PROT 0x05 |
29 | #define TFA989X_AUDIO_CTR 0x06 |
30 | #define TFA989X_DCDCBOOST 0x07 |
31 | #define TFA989X_SPKR_CALIBRATION 0x08 |
32 | #define TFA989X_SYS_CTRL 0x09 |
33 | #define TFA989X_SYS_CTRL_PWDN 0 /* power down */ |
34 | #define TFA989X_SYS_CTRL_I2CR 1 /* I2C reset */ |
35 | #define TFA989X_SYS_CTRL_CFE 2 /* enable CoolFlux DSP */ |
36 | #define TFA989X_SYS_CTRL_AMPE 3 /* enable amplifier */ |
37 | #define TFA989X_SYS_CTRL_DCA 4 /* enable boost */ |
38 | #define TFA989X_SYS_CTRL_SBSL 5 /* DSP configured */ |
39 | #define TFA989X_SYS_CTRL_AMPC 6 /* amplifier enabled by DSP */ |
40 | #define TFA989X_I2S_SEL_REG 0x0a |
41 | #define TFA989X_I2S_SEL_REG_SPKR_MSK GENMASK(10, 9) /* speaker impedance */ |
42 | #define TFA989X_I2S_SEL_REG_DCFG_MSK GENMASK(14, 11) /* DCDC compensation */ |
43 | #define TFA989X_HIDE_UNHIDE_KEY 0x40 |
44 | #define TFA989X_PWM_CONTROL 0x41 |
45 | #define TFA989X_CURRENTSENSE1 0x46 |
46 | #define TFA989X_CURRENTSENSE2 0x47 |
47 | #define TFA989X_CURRENTSENSE3 0x48 |
48 | #define TFA989X_CURRENTSENSE4 0x49 |
49 | |
50 | #define TFA9890_REVISION 0x80 |
51 | #define TFA9895_REVISION 0x12 |
52 | #define TFA9897_REVISION 0x97 |
53 | |
54 | struct tfa989x_rev { |
55 | unsigned int rev; |
56 | int (*init)(struct regmap *regmap); |
57 | }; |
58 | |
59 | struct tfa989x { |
60 | const struct tfa989x_rev *rev; |
61 | struct regulator *vddd_supply; |
62 | struct gpio_desc *rcv_gpiod; |
63 | }; |
64 | |
65 | static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) |
66 | { |
67 | return reg > TFA989X_REVISIONNUMBER; |
68 | } |
69 | |
70 | static bool tfa989x_volatile_reg(struct device *dev, unsigned int reg) |
71 | { |
72 | return reg < TFA989X_REVISIONNUMBER; |
73 | } |
74 | |
75 | static const struct regmap_config tfa989x_regmap = { |
76 | .reg_bits = 8, |
77 | .val_bits = 16, |
78 | |
79 | .writeable_reg = tfa989x_writeable_reg, |
80 | .volatile_reg = tfa989x_volatile_reg, |
81 | .cache_type = REGCACHE_RBTREE, |
82 | }; |
83 | |
84 | static const char * const chsa_text[] = { "Left", "Right", /* "DSP" */ }; |
85 | static SOC_ENUM_SINGLE_DECL(chsa_enum, TFA989X_I2SREG, TFA989X_I2SREG_CHSA, chsa_text); |
86 | static const struct snd_kcontrol_new chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum); |
87 | |
88 | static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = { |
89 | SND_SOC_DAPM_OUTPUT("OUT"), |
90 | SND_SOC_DAPM_SUPPLY("POWER", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_PWDN, 1, NULL, 0), |
91 | SND_SOC_DAPM_OUT_DRV("AMPE", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_AMPE, 0, NULL, 0), |
92 | |
93 | SND_SOC_DAPM_MUX("Amp Input", SND_SOC_NOPM, 0, 0, &chsa_mux), |
94 | SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), |
95 | SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), |
96 | }; |
97 | |
98 | static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { |
99 | {"OUT", NULL, "AMPE"}, |
100 | {"AMPE", NULL, "POWER"}, |
101 | {"AMPE", NULL, "Amp Input"}, |
102 | {"Amp Input", "Left", "AIFINL"}, |
103 | {"Amp Input", "Right", "AIFINR"}, |
104 | }; |
105 | |
106 | static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
107 | { |
108 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
109 | struct tfa989x *tfa989x = snd_soc_component_get_drvdata(c: component); |
110 | |
111 | gpiod_set_value_cansleep(desc: tfa989x->rcv_gpiod, value: ucontrol->value.enumerated.item[0]); |
112 | |
113 | return snd_soc_put_enum_double(kcontrol, ucontrol); |
114 | } |
115 | |
116 | static const char * const mode_text[] = { "Speaker", "Receiver"}; |
117 | static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); |
118 | static const struct snd_kcontrol_new tfa989x_mode_controls[] = { |
119 | SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), |
120 | }; |
121 | |
122 | static int tfa989x_probe(struct snd_soc_component *component) |
123 | { |
124 | struct tfa989x *tfa989x = snd_soc_component_get_drvdata(c: component); |
125 | |
126 | if (tfa989x->rev->rev == TFA9897_REVISION) |
127 | return snd_soc_add_component_controls(component, controls: tfa989x_mode_controls, |
128 | ARRAY_SIZE(tfa989x_mode_controls)); |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static const struct snd_soc_component_driver tfa989x_component = { |
134 | .probe = tfa989x_probe, |
135 | .dapm_widgets = tfa989x_dapm_widgets, |
136 | .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets), |
137 | .dapm_routes = tfa989x_dapm_routes, |
138 | .num_dapm_routes = ARRAY_SIZE(tfa989x_dapm_routes), |
139 | .use_pmdown_time = 1, |
140 | .endianness = 1, |
141 | }; |
142 | |
143 | static const unsigned int tfa989x_rates[] = { |
144 | 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 |
145 | }; |
146 | |
147 | static int tfa989x_find_sample_rate(unsigned int rate) |
148 | { |
149 | int i; |
150 | |
151 | for (i = 0; i < ARRAY_SIZE(tfa989x_rates); ++i) |
152 | if (tfa989x_rates[i] == rate) |
153 | return i; |
154 | |
155 | return -EINVAL; |
156 | } |
157 | |
158 | static int tfa989x_hw_params(struct snd_pcm_substream *substream, |
159 | struct snd_pcm_hw_params *params, |
160 | struct snd_soc_dai *dai) |
161 | { |
162 | struct snd_soc_component *component = dai->component; |
163 | int sr; |
164 | |
165 | sr = tfa989x_find_sample_rate(rate: params_rate(p: params)); |
166 | if (sr < 0) |
167 | return sr; |
168 | |
169 | return snd_soc_component_update_bits(component, TFA989X_I2SREG, |
170 | TFA989X_I2SREG_I2SSR_MSK, |
171 | val: sr << TFA989X_I2SREG_I2SSR); |
172 | } |
173 | |
174 | static const struct snd_soc_dai_ops tfa989x_dai_ops = { |
175 | .hw_params = tfa989x_hw_params, |
176 | }; |
177 | |
178 | static struct snd_soc_dai_driver tfa989x_dai = { |
179 | .name = "tfa989x-hifi", |
180 | .playback = { |
181 | .stream_name = "HiFi Playback", |
182 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
183 | .rates = SNDRV_PCM_RATE_8000_48000, |
184 | .rate_min = 8000, |
185 | .rate_max = 48000, |
186 | .channels_min = 1, |
187 | .channels_max = 2, |
188 | }, |
189 | .ops = &tfa989x_dai_ops, |
190 | }; |
191 | |
192 | static int tfa9890_init(struct regmap *regmap) |
193 | { |
194 | int ret; |
195 | |
196 | /* temporarily allow access to hidden registers */ |
197 | ret = regmap_write(map: regmap, TFA989X_HIDE_UNHIDE_KEY, val: 0x5a6b); |
198 | if (ret) |
199 | return ret; |
200 | |
201 | /* update PLL registers */ |
202 | ret = regmap_set_bits(map: regmap, reg: 0x59, bits: 0x3); |
203 | if (ret) |
204 | return ret; |
205 | |
206 | /* hide registers again */ |
207 | ret = regmap_write(map: regmap, TFA989X_HIDE_UNHIDE_KEY, val: 0x0000); |
208 | if (ret) |
209 | return ret; |
210 | |
211 | return regmap_write(map: regmap, TFA989X_CURRENTSENSE2, val: 0x7BE1); |
212 | } |
213 | |
214 | static const struct tfa989x_rev tfa9890_rev = { |
215 | .rev = TFA9890_REVISION, |
216 | .init = tfa9890_init, |
217 | }; |
218 | |
219 | static const struct reg_sequence tfa9895_reg_init[] = { |
220 | /* some other registers must be set for optimal amplifier behaviour */ |
221 | { TFA989X_BAT_PROT, 0x13ab }, |
222 | { TFA989X_AUDIO_CTR, 0x001f }, |
223 | |
224 | /* peak voltage protection is always on, but may be written */ |
225 | { TFA989X_SPKR_CALIBRATION, 0x3c4e }, |
226 | |
227 | /* TFA989X_SYSCTRL_DCA = 0 */ |
228 | { TFA989X_SYS_CTRL, 0x024d }, |
229 | { TFA989X_PWM_CONTROL, 0x0308 }, |
230 | { TFA989X_CURRENTSENSE4, 0x0e82 }, |
231 | }; |
232 | |
233 | static int tfa9895_init(struct regmap *regmap) |
234 | { |
235 | return regmap_multi_reg_write(map: regmap, regs: tfa9895_reg_init, |
236 | ARRAY_SIZE(tfa9895_reg_init)); |
237 | } |
238 | |
239 | static const struct tfa989x_rev tfa9895_rev = { |
240 | .rev = TFA9895_REVISION, |
241 | .init = tfa9895_init, |
242 | }; |
243 | |
244 | static int tfa9897_init(struct regmap *regmap) |
245 | { |
246 | int ret; |
247 | |
248 | /* Reduce slewrate by clearing iddqtestbst to avoid booster damage */ |
249 | ret = regmap_write(map: regmap, TFA989X_CURRENTSENSE3, val: 0x0300); |
250 | if (ret) |
251 | return ret; |
252 | |
253 | /* Enable clipping */ |
254 | ret = regmap_clear_bits(map: regmap, TFA989X_CURRENTSENSE4, bits: 0x1); |
255 | if (ret) |
256 | return ret; |
257 | |
258 | /* Set required TDM configuration */ |
259 | return regmap_write(map: regmap, reg: 0x14, val: 0x0); |
260 | } |
261 | |
262 | static const struct tfa989x_rev tfa9897_rev = { |
263 | .rev = TFA9897_REVISION, |
264 | .init = tfa9897_init, |
265 | }; |
266 | |
267 | /* |
268 | * Note: At the moment this driver bypasses the "CoolFlux DSP" built into the |
269 | * TFA989X amplifiers. Unfortunately, there seems to be absolutely |
270 | * no documentation for it - the public "short datasheets" do not provide |
271 | * any information about the DSP or available registers. |
272 | * |
273 | * Usually the TFA989X amplifiers are configured through proprietary userspace |
274 | * libraries. There are also some (rather complex) kernel drivers but even those |
275 | * rely on obscure firmware blobs for configuration (so-called "containers"). |
276 | * They seem to contain different "profiles" with tuned speaker settings, sample |
277 | * rates and volume steps (which would be better exposed as separate ALSA mixers). |
278 | * |
279 | * Bypassing the DSP disables volume control (and perhaps some speaker |
280 | * optimization?), but at least allows using the speaker without obscure |
281 | * kernel drivers and firmware. |
282 | * |
283 | * Ideally NXP (or now Goodix) should release proper documentation for these |
284 | * amplifiers so that support for the "CoolFlux DSP" can be implemented properly. |
285 | */ |
286 | static int tfa989x_dsp_bypass(struct regmap *regmap) |
287 | { |
288 | int ret; |
289 | |
290 | /* Clear CHSA to bypass DSP and take input from I2S 1 left channel */ |
291 | ret = regmap_clear_bits(map: regmap, TFA989X_I2SREG, TFA989X_I2SREG_CHSA_MSK); |
292 | if (ret) |
293 | return ret; |
294 | |
295 | /* Set DCDC compensation to off and speaker impedance to 8 ohm */ |
296 | ret = regmap_update_bits(map: regmap, TFA989X_I2S_SEL_REG, |
297 | TFA989X_I2S_SEL_REG_DCFG_MSK | |
298 | TFA989X_I2S_SEL_REG_SPKR_MSK, |
299 | TFA989X_I2S_SEL_REG_SPKR_MSK); |
300 | if (ret) |
301 | return ret; |
302 | |
303 | /* Set DCDC to follower mode and disable CoolFlux DSP */ |
304 | return regmap_clear_bits(map: regmap, TFA989X_SYS_CTRL, |
305 | BIT(TFA989X_SYS_CTRL_DCA) | |
306 | BIT(TFA989X_SYS_CTRL_CFE) | |
307 | BIT(TFA989X_SYS_CTRL_AMPC)); |
308 | } |
309 | |
310 | static void tfa989x_regulator_disable(void *data) |
311 | { |
312 | struct tfa989x *tfa989x = data; |
313 | |
314 | regulator_disable(regulator: tfa989x->vddd_supply); |
315 | } |
316 | |
317 | static int tfa989x_i2c_probe(struct i2c_client *i2c) |
318 | { |
319 | struct device *dev = &i2c->dev; |
320 | const struct tfa989x_rev *rev; |
321 | struct tfa989x *tfa989x; |
322 | struct regmap *regmap; |
323 | unsigned int val; |
324 | int ret; |
325 | |
326 | rev = device_get_match_data(dev); |
327 | if (!rev) { |
328 | dev_err(dev, "unknown device revision\n"); |
329 | return -ENODEV; |
330 | } |
331 | |
332 | tfa989x = devm_kzalloc(dev, size: sizeof(*tfa989x), GFP_KERNEL); |
333 | if (!tfa989x) |
334 | return -ENOMEM; |
335 | |
336 | tfa989x->rev = rev; |
337 | i2c_set_clientdata(client: i2c, data: tfa989x); |
338 | |
339 | tfa989x->vddd_supply = devm_regulator_get(dev, id: "vddd"); |
340 | if (IS_ERR(ptr: tfa989x->vddd_supply)) |
341 | return dev_err_probe(dev, err: PTR_ERR(ptr: tfa989x->vddd_supply), |
342 | fmt: "Failed to get vddd regulator\n"); |
343 | |
344 | if (tfa989x->rev->rev == TFA9897_REVISION) { |
345 | tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, con_id: "rcv", flags: GPIOD_OUT_LOW); |
346 | if (IS_ERR(ptr: tfa989x->rcv_gpiod)) |
347 | return PTR_ERR(ptr: tfa989x->rcv_gpiod); |
348 | } |
349 | |
350 | regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); |
351 | if (IS_ERR(ptr: regmap)) |
352 | return PTR_ERR(ptr: regmap); |
353 | |
354 | ret = regulator_enable(regulator: tfa989x->vddd_supply); |
355 | if (ret) { |
356 | dev_err(dev, "Failed to enable vddd regulator: %d\n", ret); |
357 | return ret; |
358 | } |
359 | |
360 | ret = devm_add_action_or_reset(dev, tfa989x_regulator_disable, tfa989x); |
361 | if (ret) |
362 | return ret; |
363 | |
364 | /* Bypass regcache for reset and init sequence */ |
365 | regcache_cache_bypass(map: regmap, enable: true); |
366 | |
367 | /* Dummy read to generate i2c clocks, required on some devices */ |
368 | regmap_read(map: regmap, TFA989X_REVISIONNUMBER, val: &val); |
369 | |
370 | ret = regmap_read(map: regmap, TFA989X_REVISIONNUMBER, val: &val); |
371 | if (ret) { |
372 | dev_err(dev, "failed to read revision number: %d\n", ret); |
373 | return ret; |
374 | } |
375 | |
376 | val &= TFA989X_REVISIONNUMBER_REV_MSK; |
377 | if (val != rev->rev) { |
378 | dev_err(dev, "invalid revision number, expected %#x, got %#x\n", |
379 | rev->rev, val); |
380 | return -ENODEV; |
381 | } |
382 | |
383 | ret = regmap_write(map: regmap, TFA989X_SYS_CTRL, BIT(TFA989X_SYS_CTRL_I2CR)); |
384 | if (ret) { |
385 | dev_err(dev, "failed to reset I2C registers: %d\n", ret); |
386 | return ret; |
387 | } |
388 | |
389 | ret = rev->init(regmap); |
390 | if (ret) { |
391 | dev_err(dev, "failed to initialize registers: %d\n", ret); |
392 | return ret; |
393 | } |
394 | |
395 | ret = tfa989x_dsp_bypass(regmap); |
396 | if (ret) { |
397 | dev_err(dev, "failed to enable DSP bypass: %d\n", ret); |
398 | return ret; |
399 | } |
400 | regcache_cache_bypass(map: regmap, enable: false); |
401 | |
402 | return devm_snd_soc_register_component(dev, component_driver: &tfa989x_component, |
403 | dai_drv: &tfa989x_dai, num_dai: 1); |
404 | } |
405 | |
406 | static const struct of_device_id tfa989x_of_match[] = { |
407 | { .compatible = "nxp,tfa9890", .data = &tfa9890_rev }, |
408 | { .compatible = "nxp,tfa9895", .data = &tfa9895_rev }, |
409 | { .compatible = "nxp,tfa9897", .data = &tfa9897_rev }, |
410 | { } |
411 | }; |
412 | MODULE_DEVICE_TABLE(of, tfa989x_of_match); |
413 | |
414 | static struct i2c_driver tfa989x_i2c_driver = { |
415 | .driver = { |
416 | .name = "tfa989x", |
417 | .of_match_table = tfa989x_of_match, |
418 | }, |
419 | .probe = tfa989x_i2c_probe, |
420 | }; |
421 | module_i2c_driver(tfa989x_i2c_driver); |
422 | |
423 | MODULE_DESCRIPTION("ASoC NXP/Goodix TFA989X (TFA1) driver"); |
424 | MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>"); |
425 | MODULE_LICENSE("GPL"); |
426 |
Definitions
- tfa989x_rev
- tfa989x
- tfa989x_writeable_reg
- tfa989x_volatile_reg
- tfa989x_regmap
- chsa_text
- chsa_enum
- chsa_mux
- tfa989x_dapm_widgets
- tfa989x_dapm_routes
- tfa989x_put_mode
- mode_text
- mode_enum
- tfa989x_mode_controls
- tfa989x_probe
- tfa989x_component
- tfa989x_rates
- tfa989x_find_sample_rate
- tfa989x_hw_params
- tfa989x_dai_ops
- tfa989x_dai
- tfa9890_init
- tfa9890_rev
- tfa9895_reg_init
- tfa9895_init
- tfa9895_rev
- tfa9897_init
- tfa9897_rev
- tfa989x_dsp_bypass
- tfa989x_regulator_disable
- tfa989x_i2c_probe
- tfa989x_of_match
Improve your Profiling and Debugging skills
Find out more