1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // tegra210_peq.c - Tegra210 PEQ driver |
4 | // |
5 | // Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/device.h> |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <linux/regmap.h> |
16 | #include <sound/core.h> |
17 | #include <sound/pcm.h> |
18 | #include <sound/pcm_params.h> |
19 | #include <sound/soc.h> |
20 | |
21 | #include "tegra210_ope.h" |
22 | #include "tegra210_peq.h" |
23 | |
24 | static const struct reg_default tegra210_peq_reg_defaults[] = { |
25 | { TEGRA210_PEQ_CFG, 0x00000013}, |
26 | { TEGRA210_PEQ_CFG_RAM_CTRL, 0x00004000}, |
27 | { TEGRA210_PEQ_CFG_RAM_SHIFT_CTRL, 0x00004000}, |
28 | }; |
29 | |
30 | static const u32 biquad_init_gains[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH] = { |
31 | 1495012349, /* Pre-gain */ |
32 | |
33 | /* Gains : b0, b1, a0, a1, a2 */ |
34 | 536870912, -1073741824, 536870912, 2143508246, -1069773768, /* Band-0 */ |
35 | 134217728, -265414508, 131766272, 2140402222, -1071252997, /* Band-1 */ |
36 | 268435456, -233515765, -33935948, 1839817267, -773826124, /* Band-2 */ |
37 | 536870912, -672537913, 139851540, 1886437554, -824433167, /* Band-3 */ |
38 | 268435456, -114439279, 173723964, 205743566, 278809729, /* Band-4 */ |
39 | 1, 0, 0, 0, 0, /* Band-5 */ |
40 | 1, 0, 0, 0, 0, /* Band-6 */ |
41 | 1, 0, 0, 0, 0, /* Band-7 */ |
42 | 1, 0, 0, 0, 0, /* Band-8 */ |
43 | 1, 0, 0, 0, 0, /* Band-9 */ |
44 | 1, 0, 0, 0, 0, /* Band-10 */ |
45 | 1, 0, 0, 0, 0, /* Band-11 */ |
46 | |
47 | 963423114, /* Post-gain */ |
48 | }; |
49 | |
50 | static const u32 biquad_init_shifts[TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH] = { |
51 | 23, /* Pre-shift */ |
52 | 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, /* Shift for bands */ |
53 | 28, /* Post-shift */ |
54 | }; |
55 | |
56 | static s32 biquad_coeff_buffer[TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH]; |
57 | |
58 | static void tegra210_peq_read_ram(struct regmap *regmap, unsigned int reg_ctrl, |
59 | unsigned int reg_data, unsigned int ram_offset, |
60 | unsigned int *data, size_t size) |
61 | { |
62 | unsigned int val; |
63 | unsigned int i; |
64 | |
65 | val = ram_offset & TEGRA210_PEQ_RAM_CTRL_RAM_ADDR_MASK; |
66 | val |= TEGRA210_PEQ_RAM_CTRL_ADDR_INIT_EN; |
67 | val |= TEGRA210_PEQ_RAM_CTRL_SEQ_ACCESS_EN; |
68 | val |= TEGRA210_PEQ_RAM_CTRL_RW_READ; |
69 | |
70 | regmap_write(map: regmap, reg: reg_ctrl, val); |
71 | |
72 | /* |
73 | * Since all ahub non-io modules work under same ahub clock it is not |
74 | * necessary to check ahub read busy bit after every read. |
75 | */ |
76 | for (i = 0; i < size; i++) |
77 | regmap_read(map: regmap, reg: reg_data, val: &data[i]); |
78 | } |
79 | |
80 | static void tegra210_peq_write_ram(struct regmap *regmap, unsigned int reg_ctrl, |
81 | unsigned int reg_data, unsigned int ram_offset, |
82 | unsigned int *data, size_t size) |
83 | { |
84 | unsigned int val; |
85 | unsigned int i; |
86 | |
87 | val = ram_offset & TEGRA210_PEQ_RAM_CTRL_RAM_ADDR_MASK; |
88 | val |= TEGRA210_PEQ_RAM_CTRL_ADDR_INIT_EN; |
89 | val |= TEGRA210_PEQ_RAM_CTRL_SEQ_ACCESS_EN; |
90 | val |= TEGRA210_PEQ_RAM_CTRL_RW_WRITE; |
91 | |
92 | regmap_write(map: regmap, reg: reg_ctrl, val); |
93 | |
94 | for (i = 0; i < size; i++) |
95 | regmap_write(map: regmap, reg: reg_data, val: data[i]); |
96 | } |
97 | |
98 | static int tegra210_peq_get(struct snd_kcontrol *kcontrol, |
99 | struct snd_ctl_elem_value *ucontrol) |
100 | { |
101 | struct soc_mixer_control *mc = |
102 | (struct soc_mixer_control *)kcontrol->private_value; |
103 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
104 | struct tegra210_ope *ope = snd_soc_component_get_drvdata(c: cmpnt); |
105 | unsigned int mask = (1 << fls(x: mc->max)) - 1; |
106 | unsigned int val; |
107 | |
108 | regmap_read(map: ope->peq_regmap, reg: mc->reg, val: &val); |
109 | |
110 | ucontrol->value.integer.value[0] = (val >> mc->shift) & mask; |
111 | |
112 | if (!mc->invert) |
113 | return 0; |
114 | |
115 | ucontrol->value.integer.value[0] = |
116 | mc->max - ucontrol->value.integer.value[0]; |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static int tegra210_peq_put(struct snd_kcontrol *kcontrol, |
122 | struct snd_ctl_elem_value *ucontrol) |
123 | { |
124 | struct soc_mixer_control *mc = |
125 | (struct soc_mixer_control *)kcontrol->private_value; |
126 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
127 | struct tegra210_ope *ope = snd_soc_component_get_drvdata(c: cmpnt); |
128 | unsigned int mask = (1 << fls(x: mc->max)) - 1; |
129 | bool change = false; |
130 | unsigned int val; |
131 | |
132 | val = (ucontrol->value.integer.value[0] & mask); |
133 | |
134 | if (mc->invert) |
135 | val = mc->max - val; |
136 | |
137 | val = val << mc->shift; |
138 | |
139 | regmap_update_bits_check(map: ope->peq_regmap, reg: mc->reg, mask: (mask << mc->shift), |
140 | val, change: &change); |
141 | |
142 | return change ? 1 : 0; |
143 | } |
144 | |
145 | static int tegra210_peq_ram_get(struct snd_kcontrol *kcontrol, |
146 | struct snd_ctl_elem_value *ucontrol) |
147 | { |
148 | struct tegra_soc_bytes *params = (void *)kcontrol->private_value; |
149 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
150 | struct tegra210_ope *ope = snd_soc_component_get_drvdata(c: cmpnt); |
151 | u32 i, reg_ctrl = params->soc.base; |
152 | u32 reg_data = reg_ctrl + cmpnt->val_bytes; |
153 | s32 *data = (s32 *)biquad_coeff_buffer; |
154 | |
155 | pm_runtime_get_sync(dev: cmpnt->dev); |
156 | |
157 | tegra210_peq_read_ram(regmap: ope->peq_regmap, reg_ctrl, reg_data, |
158 | ram_offset: params->shift, data, size: params->soc.num_regs); |
159 | |
160 | pm_runtime_put_sync(dev: cmpnt->dev); |
161 | |
162 | for (i = 0; i < params->soc.num_regs; i++) |
163 | ucontrol->value.integer.value[i] = (long)data[i]; |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static int tegra210_peq_ram_put(struct snd_kcontrol *kcontrol, |
169 | struct snd_ctl_elem_value *ucontrol) |
170 | { |
171 | struct tegra_soc_bytes *params = (void *)kcontrol->private_value; |
172 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); |
173 | struct tegra210_ope *ope = snd_soc_component_get_drvdata(c: cmpnt); |
174 | u32 i, reg_ctrl = params->soc.base; |
175 | u32 reg_data = reg_ctrl + cmpnt->val_bytes; |
176 | s32 *data = (s32 *)biquad_coeff_buffer; |
177 | |
178 | for (i = 0; i < params->soc.num_regs; i++) |
179 | data[i] = (s32)ucontrol->value.integer.value[i]; |
180 | |
181 | pm_runtime_get_sync(dev: cmpnt->dev); |
182 | |
183 | tegra210_peq_write_ram(regmap: ope->peq_regmap, reg_ctrl, reg_data, |
184 | ram_offset: params->shift, data, size: params->soc.num_regs); |
185 | |
186 | pm_runtime_put_sync(dev: cmpnt->dev); |
187 | |
188 | return 1; |
189 | } |
190 | |
191 | static int tegra210_peq_param_info(struct snd_kcontrol *kcontrol, |
192 | struct snd_ctl_elem_info *uinfo) |
193 | { |
194 | struct soc_bytes *params = (void *)kcontrol->private_value; |
195 | |
196 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
197 | uinfo->value.integer.min = INT_MIN; |
198 | uinfo->value.integer.max = INT_MAX; |
199 | uinfo->count = params->num_regs; |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | #define TEGRA210_PEQ_GAIN_PARAMS_CTRL(chan) \ |
205 | TEGRA_SOC_BYTES_EXT("PEQ Channel-" #chan " Biquad Gain Params", \ |
206 | TEGRA210_PEQ_CFG_RAM_CTRL, \ |
207 | TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH, \ |
208 | (TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH * chan), 0xffffffff, \ |
209 | tegra210_peq_ram_get, tegra210_peq_ram_put, \ |
210 | tegra210_peq_param_info) |
211 | |
212 | #define TEGRA210_PEQ_SHIFT_PARAMS_CTRL(chan) \ |
213 | TEGRA_SOC_BYTES_EXT("PEQ Channel-" #chan " Biquad Shift Params", \ |
214 | TEGRA210_PEQ_CFG_RAM_SHIFT_CTRL, \ |
215 | TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH, \ |
216 | (TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH * chan), 0x1f, \ |
217 | tegra210_peq_ram_get, tegra210_peq_ram_put, \ |
218 | tegra210_peq_param_info) |
219 | |
220 | static const struct snd_kcontrol_new tegra210_peq_controls[] = { |
221 | SOC_SINGLE_EXT("PEQ Active" , TEGRA210_PEQ_CFG, |
222 | TEGRA210_PEQ_CFG_MODE_SHIFT, 1, 0, |
223 | tegra210_peq_get, tegra210_peq_put), |
224 | |
225 | SOC_SINGLE_EXT("PEQ Biquad Stages" , TEGRA210_PEQ_CFG, |
226 | TEGRA210_PEQ_CFG_BIQUAD_STAGES_SHIFT, |
227 | TEGRA210_PEQ_MAX_BIQUAD_STAGES - 1, 0, |
228 | tegra210_peq_get, tegra210_peq_put), |
229 | |
230 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(0), |
231 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(1), |
232 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(2), |
233 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(3), |
234 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(4), |
235 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(5), |
236 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(6), |
237 | TEGRA210_PEQ_GAIN_PARAMS_CTRL(7), |
238 | |
239 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(0), |
240 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(1), |
241 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(2), |
242 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(3), |
243 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(4), |
244 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(5), |
245 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(6), |
246 | TEGRA210_PEQ_SHIFT_PARAMS_CTRL(7), |
247 | }; |
248 | |
249 | static bool tegra210_peq_wr_reg(struct device *dev, unsigned int reg) |
250 | { |
251 | switch (reg) { |
252 | case TEGRA210_PEQ_SOFT_RESET: |
253 | case TEGRA210_PEQ_CG: |
254 | case TEGRA210_PEQ_CFG ... TEGRA210_PEQ_CFG_RAM_SHIFT_DATA: |
255 | return true; |
256 | default: |
257 | return false; |
258 | } |
259 | } |
260 | |
261 | static bool tegra210_peq_rd_reg(struct device *dev, unsigned int reg) |
262 | { |
263 | if (tegra210_peq_wr_reg(dev, reg)) |
264 | return true; |
265 | |
266 | switch (reg) { |
267 | case TEGRA210_PEQ_STATUS: |
268 | return true; |
269 | default: |
270 | return false; |
271 | } |
272 | } |
273 | |
274 | static bool tegra210_peq_volatile_reg(struct device *dev, unsigned int reg) |
275 | { |
276 | switch (reg) { |
277 | case TEGRA210_PEQ_SOFT_RESET: |
278 | case TEGRA210_PEQ_STATUS: |
279 | case TEGRA210_PEQ_CFG_RAM_CTRL ... TEGRA210_PEQ_CFG_RAM_SHIFT_DATA: |
280 | return true; |
281 | default: |
282 | return false; |
283 | } |
284 | } |
285 | |
286 | static bool tegra210_peq_precious_reg(struct device *dev, unsigned int reg) |
287 | { |
288 | switch (reg) { |
289 | case TEGRA210_PEQ_CFG_RAM_DATA: |
290 | case TEGRA210_PEQ_CFG_RAM_SHIFT_DATA: |
291 | return true; |
292 | default: |
293 | return false; |
294 | } |
295 | } |
296 | |
297 | static const struct regmap_config tegra210_peq_regmap_config = { |
298 | .name = "peq" , |
299 | .reg_bits = 32, |
300 | .reg_stride = 4, |
301 | .val_bits = 32, |
302 | .max_register = TEGRA210_PEQ_CFG_RAM_SHIFT_DATA, |
303 | .writeable_reg = tegra210_peq_wr_reg, |
304 | .readable_reg = tegra210_peq_rd_reg, |
305 | .volatile_reg = tegra210_peq_volatile_reg, |
306 | .precious_reg = tegra210_peq_precious_reg, |
307 | .reg_defaults = tegra210_peq_reg_defaults, |
308 | .num_reg_defaults = ARRAY_SIZE(tegra210_peq_reg_defaults), |
309 | .cache_type = REGCACHE_FLAT, |
310 | }; |
311 | |
312 | void tegra210_peq_restore(struct regmap *regmap, u32 *biquad_gains, |
313 | u32 *biquad_shifts) |
314 | { |
315 | unsigned int i; |
316 | |
317 | for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) { |
318 | tegra210_peq_write_ram(regmap, TEGRA210_PEQ_CFG_RAM_CTRL, |
319 | TEGRA210_PEQ_CFG_RAM_DATA, |
320 | ram_offset: (i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH), |
321 | data: biquad_gains, |
322 | TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH); |
323 | |
324 | tegra210_peq_write_ram(regmap, |
325 | TEGRA210_PEQ_CFG_RAM_SHIFT_CTRL, |
326 | TEGRA210_PEQ_CFG_RAM_SHIFT_DATA, |
327 | ram_offset: (i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH), |
328 | data: biquad_shifts, |
329 | TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH); |
330 | |
331 | } |
332 | } |
333 | |
334 | void tegra210_peq_save(struct regmap *regmap, u32 *biquad_gains, |
335 | u32 *biquad_shifts) |
336 | { |
337 | unsigned int i; |
338 | |
339 | for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) { |
340 | tegra210_peq_read_ram(regmap, |
341 | TEGRA210_PEQ_CFG_RAM_CTRL, |
342 | TEGRA210_PEQ_CFG_RAM_DATA, |
343 | ram_offset: (i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH), |
344 | data: biquad_gains, |
345 | TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH); |
346 | |
347 | tegra210_peq_read_ram(regmap, |
348 | TEGRA210_PEQ_CFG_RAM_SHIFT_CTRL, |
349 | TEGRA210_PEQ_CFG_RAM_SHIFT_DATA, |
350 | ram_offset: (i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH), |
351 | data: biquad_shifts, |
352 | TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH); |
353 | } |
354 | } |
355 | |
356 | int tegra210_peq_component_init(struct snd_soc_component *cmpnt) |
357 | { |
358 | struct tegra210_ope *ope = snd_soc_component_get_drvdata(c: cmpnt); |
359 | unsigned int i; |
360 | |
361 | pm_runtime_get_sync(dev: cmpnt->dev); |
362 | regmap_update_bits(map: ope->peq_regmap, TEGRA210_PEQ_CFG, |
363 | TEGRA210_PEQ_CFG_MODE_MASK, |
364 | val: 0 << TEGRA210_PEQ_CFG_MODE_SHIFT); |
365 | regmap_update_bits(map: ope->peq_regmap, TEGRA210_PEQ_CFG, |
366 | TEGRA210_PEQ_CFG_BIQUAD_STAGES_MASK, |
367 | val: (TEGRA210_PEQ_BIQUAD_INIT_STAGE - 1) << |
368 | TEGRA210_PEQ_CFG_BIQUAD_STAGES_SHIFT); |
369 | |
370 | /* Initialize PEQ AHUB RAM with default params */ |
371 | for (i = 0; i < TEGRA210_PEQ_MAX_CHANNELS; i++) { |
372 | |
373 | /* Set default gain params */ |
374 | tegra210_peq_write_ram(regmap: ope->peq_regmap, |
375 | TEGRA210_PEQ_CFG_RAM_CTRL, |
376 | TEGRA210_PEQ_CFG_RAM_DATA, |
377 | ram_offset: (i * TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH), |
378 | data: (u32 *)&biquad_init_gains, |
379 | TEGRA210_PEQ_GAIN_PARAM_SIZE_PER_CH); |
380 | |
381 | /* Set default shift params */ |
382 | tegra210_peq_write_ram(regmap: ope->peq_regmap, |
383 | TEGRA210_PEQ_CFG_RAM_SHIFT_CTRL, |
384 | TEGRA210_PEQ_CFG_RAM_SHIFT_DATA, |
385 | ram_offset: (i * TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH), |
386 | data: (u32 *)&biquad_init_shifts, |
387 | TEGRA210_PEQ_SHIFT_PARAM_SIZE_PER_CH); |
388 | |
389 | } |
390 | |
391 | pm_runtime_put_sync(dev: cmpnt->dev); |
392 | |
393 | snd_soc_add_component_controls(component: cmpnt, controls: tegra210_peq_controls, |
394 | ARRAY_SIZE(tegra210_peq_controls)); |
395 | |
396 | return 0; |
397 | } |
398 | |
399 | int tegra210_peq_regmap_init(struct platform_device *pdev) |
400 | { |
401 | struct device *dev = &pdev->dev; |
402 | struct tegra210_ope *ope = dev_get_drvdata(dev); |
403 | struct device_node *child; |
404 | struct resource mem; |
405 | void __iomem *regs; |
406 | int err; |
407 | |
408 | child = of_get_child_by_name(node: dev->of_node, name: "equalizer" ); |
409 | if (!child) |
410 | return -ENODEV; |
411 | |
412 | err = of_address_to_resource(dev: child, index: 0, r: &mem); |
413 | of_node_put(node: child); |
414 | if (err < 0) { |
415 | dev_err(dev, "fail to get PEQ resource\n" ); |
416 | return err; |
417 | } |
418 | |
419 | mem.flags = IORESOURCE_MEM; |
420 | regs = devm_ioremap_resource(dev, res: &mem); |
421 | if (IS_ERR(ptr: regs)) |
422 | return PTR_ERR(ptr: regs); |
423 | ope->peq_regmap = devm_regmap_init_mmio(dev, regs, |
424 | &tegra210_peq_regmap_config); |
425 | if (IS_ERR(ptr: ope->peq_regmap)) { |
426 | dev_err(dev, "regmap init failed\n" ); |
427 | return PTR_ERR(ptr: ope->peq_regmap); |
428 | } |
429 | |
430 | regcache_cache_only(map: ope->peq_regmap, enable: true); |
431 | |
432 | return 0; |
433 | } |
434 | |