]> Git Repo - J-linux.git/blob - drivers/mfd/cs40l50-core.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / mfd / cs40l50-core.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * CS40L50 Advanced Haptic Driver with waveform memory,
4  * integrated DSP, and closed-loop algorithms
5  *
6  * Copyright 2024 Cirrus Logic, Inc.
7  *
8  * Author: James Ogletree <[email protected]>
9  */
10
11 #include <linux/firmware/cirrus/cs_dsp.h>
12 #include <linux/firmware/cirrus/wmfw.h>
13 #include <linux/mfd/core.h>
14 #include <linux/mfd/cs40l50.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/regulator/consumer.h>
17
18 static const struct mfd_cell cs40l50_devs[] = {
19         { .name = "cs40l50-codec", },
20         { .name = "cs40l50-vibra", },
21 };
22
23 const struct regmap_config cs40l50_regmap = {
24         .reg_bits =             32,
25         .reg_stride =           4,
26         .val_bits =             32,
27         .reg_format_endian =    REGMAP_ENDIAN_BIG,
28         .val_format_endian =    REGMAP_ENDIAN_BIG,
29 };
30 EXPORT_SYMBOL_GPL(cs40l50_regmap);
31
32 static const char * const cs40l50_supplies[] = {
33         "vdd-io",
34 };
35
36 static const struct regmap_irq cs40l50_reg_irqs[] = {
37         REGMAP_IRQ_REG(CS40L50_DSP_QUEUE_IRQ, CS40L50_IRQ1_INT_2_OFFSET,
38                        CS40L50_DSP_QUEUE_MASK),
39         REGMAP_IRQ_REG(CS40L50_AMP_SHORT_IRQ, CS40L50_IRQ1_INT_1_OFFSET,
40                        CS40L50_AMP_SHORT_MASK),
41         REGMAP_IRQ_REG(CS40L50_TEMP_ERR_IRQ, CS40L50_IRQ1_INT_8_OFFSET,
42                        CS40L50_TEMP_ERR_MASK),
43         REGMAP_IRQ_REG(CS40L50_BST_UVP_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
44                        CS40L50_BST_UVP_MASK),
45         REGMAP_IRQ_REG(CS40L50_BST_SHORT_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
46                        CS40L50_BST_SHORT_MASK),
47         REGMAP_IRQ_REG(CS40L50_BST_ILIMIT_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
48                        CS40L50_BST_ILIMIT_MASK),
49         REGMAP_IRQ_REG(CS40L50_UVLO_VDDBATT_IRQ, CS40L50_IRQ1_INT_10_OFFSET,
50                        CS40L50_UVLO_VDDBATT_MASK),
51         REGMAP_IRQ_REG(CS40L50_GLOBAL_ERROR_IRQ, CS40L50_IRQ1_INT_18_OFFSET,
52                        CS40L50_GLOBAL_ERROR_MASK),
53 };
54
55 static struct regmap_irq_chip cs40l50_irq_chip = {
56         .name =         "cs40l50",
57         .status_base =  CS40L50_IRQ1_INT_1,
58         .mask_base =    CS40L50_IRQ1_MASK_1,
59         .ack_base =     CS40L50_IRQ1_INT_1,
60         .num_regs =     22,
61         .irqs =         cs40l50_reg_irqs,
62         .num_irqs =     ARRAY_SIZE(cs40l50_reg_irqs),
63         .runtime_pm =   true,
64 };
65
66 int cs40l50_dsp_write(struct device *dev, struct regmap *regmap, u32 val)
67 {
68         int i, ret;
69         u32 ack;
70
71         /* Device NAKs if hibernating, so optionally retry */
72         for (i = 0; i < CS40L50_DSP_TIMEOUT_COUNT; i++) {
73                 ret = regmap_write(regmap, CS40L50_DSP_QUEUE, val);
74                 if (!ret)
75                         break;
76
77                 usleep_range(CS40L50_DSP_POLL_US, CS40L50_DSP_POLL_US + 100);
78         }
79
80         /* If the write never took place, no need to check for the ACK */
81         if (i == CS40L50_DSP_TIMEOUT_COUNT) {
82                 dev_err(dev, "Timed out writing %#X to DSP: %d\n", val, ret);
83                 return ret;
84         }
85
86         ret = regmap_read_poll_timeout(regmap, CS40L50_DSP_QUEUE, ack, !ack,
87                                        CS40L50_DSP_POLL_US,
88                                        CS40L50_DSP_POLL_US * CS40L50_DSP_TIMEOUT_COUNT);
89         if (ret)
90                 dev_err(dev, "DSP failed to ACK %#X: %d\n", val, ret);
91
92         return ret;
93 }
94 EXPORT_SYMBOL_GPL(cs40l50_dsp_write);
95
96 static const struct cs_dsp_region cs40l50_dsp_regions[] = {
97         { .type = WMFW_HALO_PM_PACKED, .base = CS40L50_PMEM_0 },
98         { .type = WMFW_HALO_XM_PACKED, .base = CS40L50_XMEM_PACKED_0 },
99         { .type = WMFW_HALO_YM_PACKED, .base = CS40L50_YMEM_PACKED_0 },
100         { .type = WMFW_ADSP2_XM, .base = CS40L50_XMEM_UNPACKED24_0 },
101         { .type = WMFW_ADSP2_YM, .base = CS40L50_YMEM_UNPACKED24_0 },
102 };
103
104 static const struct reg_sequence cs40l50_internal_vamp_config[] = {
105         { CS40L50_BST_LPMODE_SEL, CS40L50_DCM_LOW_POWER },
106         { CS40L50_BLOCK_ENABLES2, CS40L50_OVERTEMP_WARN },
107 };
108
109 static const struct reg_sequence cs40l50_irq_mask_override[] = {
110         { CS40L50_IRQ1_MASK_2, CS40L50_IRQ_MASK_2_OVERRIDE },
111         { CS40L50_IRQ1_MASK_20, CS40L50_IRQ_MASK_20_OVERRIDE },
112 };
113
114 static int cs40l50_wseq_init(struct cs40l50 *cs40l50)
115 {
116         struct cs_dsp *dsp = &cs40l50->dsp;
117
118         cs40l50->wseqs[CS40L50_STANDBY].ctl = cs_dsp_get_ctl(dsp, "STANDBY_SEQUENCE",
119                                                              WMFW_ADSP2_XM,
120                                                              CS40L50_PM_ALGO);
121         if (!cs40l50->wseqs[CS40L50_STANDBY].ctl) {
122                 dev_err(cs40l50->dev, "Control not found for standby sequence\n");
123                 return -ENOENT;
124         }
125
126         cs40l50->wseqs[CS40L50_ACTIVE].ctl = cs_dsp_get_ctl(dsp, "ACTIVE_SEQUENCE",
127                                                             WMFW_ADSP2_XM,
128                                                             CS40L50_PM_ALGO);
129         if (!cs40l50->wseqs[CS40L50_ACTIVE].ctl) {
130                 dev_err(cs40l50->dev, "Control not found for active sequence\n");
131                 return -ENOENT;
132         }
133
134         cs40l50->wseqs[CS40L50_PWR_ON].ctl = cs_dsp_get_ctl(dsp, "PM_PWR_ON_SEQ",
135                                                             WMFW_ADSP2_XM,
136                                                             CS40L50_PM_ALGO);
137         if (!cs40l50->wseqs[CS40L50_PWR_ON].ctl) {
138                 dev_err(cs40l50->dev, "Control not found for power-on sequence\n");
139                 return -ENOENT;
140         }
141
142         return cs_dsp_wseq_init(&cs40l50->dsp, cs40l50->wseqs, ARRAY_SIZE(cs40l50->wseqs));
143 }
144
145 static int cs40l50_dsp_config(struct cs40l50 *cs40l50)
146 {
147         int ret;
148
149         /* Configure internal V_AMP supply */
150         ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_internal_vamp_config,
151                                      ARRAY_SIZE(cs40l50_internal_vamp_config));
152         if (ret)
153                 return ret;
154
155         ret = cs_dsp_wseq_multi_write(&cs40l50->dsp, &cs40l50->wseqs[CS40L50_PWR_ON],
156                                       cs40l50_internal_vamp_config, CS_DSP_WSEQ_FULL,
157                                       ARRAY_SIZE(cs40l50_internal_vamp_config), false);
158         if (ret)
159                 return ret;
160
161         /* Override firmware defaults for IRQ masks */
162         ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_irq_mask_override,
163                                      ARRAY_SIZE(cs40l50_irq_mask_override));
164         if (ret)
165                 return ret;
166
167         return cs_dsp_wseq_multi_write(&cs40l50->dsp, &cs40l50->wseqs[CS40L50_PWR_ON],
168                                        cs40l50_irq_mask_override, CS_DSP_WSEQ_FULL,
169                                        ARRAY_SIZE(cs40l50_irq_mask_override), false);
170 }
171
172 static int cs40l50_dsp_post_run(struct cs_dsp *dsp)
173 {
174         struct cs40l50 *cs40l50 = container_of(dsp, struct cs40l50, dsp);
175         int ret;
176
177         ret = cs40l50_wseq_init(cs40l50);
178         if (ret)
179                 return ret;
180
181         ret = cs40l50_dsp_config(cs40l50);
182         if (ret) {
183                 dev_err(cs40l50->dev, "Failed to configure DSP: %d\n", ret);
184                 return ret;
185         }
186
187         ret = devm_mfd_add_devices(cs40l50->dev, PLATFORM_DEVID_NONE, cs40l50_devs,
188                                    ARRAY_SIZE(cs40l50_devs), NULL, 0, NULL);
189         if (ret)
190                 dev_err(cs40l50->dev, "Failed to add child devices: %d\n", ret);
191
192         return ret;
193 }
194
195 static const struct cs_dsp_client_ops client_ops = {
196         .post_run = cs40l50_dsp_post_run,
197 };
198
199 static void cs40l50_dsp_remove(void *data)
200 {
201         cs_dsp_remove(data);
202 }
203
204 static int cs40l50_dsp_init(struct cs40l50 *cs40l50)
205 {
206         int ret;
207
208         cs40l50->dsp.num = 1;
209         cs40l50->dsp.type = WMFW_HALO;
210         cs40l50->dsp.dev = cs40l50->dev;
211         cs40l50->dsp.regmap = cs40l50->regmap;
212         cs40l50->dsp.base = CS40L50_CORE_BASE;
213         cs40l50->dsp.base_sysinfo = CS40L50_SYS_INFO_ID;
214         cs40l50->dsp.mem = cs40l50_dsp_regions;
215         cs40l50->dsp.num_mems = ARRAY_SIZE(cs40l50_dsp_regions);
216         cs40l50->dsp.no_core_startstop = true;
217         cs40l50->dsp.client_ops = &client_ops;
218
219         ret = cs_dsp_halo_init(&cs40l50->dsp);
220         if (ret)
221                 return ret;
222
223         return devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_remove,
224                                         &cs40l50->dsp);
225 }
226
227 static int cs40l50_reset_dsp(struct cs40l50 *cs40l50)
228 {
229         int ret;
230
231         mutex_lock(&cs40l50->lock);
232
233         if (cs40l50->dsp.running)
234                 cs_dsp_stop(&cs40l50->dsp);
235
236         if (cs40l50->dsp.booted)
237                 cs_dsp_power_down(&cs40l50->dsp);
238
239         ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_SHUTDOWN);
240         if (ret)
241                 goto err_mutex;
242
243         ret = cs_dsp_power_up(&cs40l50->dsp, cs40l50->fw, "cs40l50.wmfw",
244                               cs40l50->bin, "cs40l50.bin", "cs40l50");
245         if (ret)
246                 goto err_mutex;
247
248         ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_SYSTEM_RESET);
249         if (ret)
250                 goto err_mutex;
251
252         ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_PREVENT_HIBER);
253         if (ret)
254                 goto err_mutex;
255
256         ret = cs_dsp_run(&cs40l50->dsp);
257 err_mutex:
258         mutex_unlock(&cs40l50->lock);
259
260         return ret;
261 }
262
263 static void cs40l50_dsp_power_down(void *data)
264 {
265         cs_dsp_power_down(data);
266 }
267
268 static void cs40l50_dsp_stop(void *data)
269 {
270         cs_dsp_stop(data);
271 }
272
273 static void cs40l50_dsp_bringup(const struct firmware *bin, void *context)
274 {
275         struct cs40l50 *cs40l50 = context;
276         u32 nwaves;
277         int ret;
278
279         /* Wavetable is optional; bringup DSP regardless */
280         cs40l50->bin = bin;
281
282         ret = cs40l50_reset_dsp(cs40l50);
283         if (ret) {
284                 dev_err(cs40l50->dev, "Failed to reset DSP: %d\n", ret);
285                 goto err_fw;
286         }
287
288         ret = regmap_read(cs40l50->regmap, CS40L50_NUM_WAVES, &nwaves);
289         if (ret)
290                 goto err_fw;
291
292         dev_info(cs40l50->dev, "%u RAM effects loaded\n", nwaves);
293
294         /* Add teardown actions for first-time bringup */
295         ret = devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_power_down,
296                                        &cs40l50->dsp);
297         if (ret) {
298                 dev_err(cs40l50->dev, "Failed to add power down action: %d\n", ret);
299                 goto err_fw;
300         }
301
302         ret = devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_stop, &cs40l50->dsp);
303         if (ret)
304                 dev_err(cs40l50->dev, "Failed to add stop action: %d\n", ret);
305 err_fw:
306         release_firmware(cs40l50->bin);
307         release_firmware(cs40l50->fw);
308 }
309
310 static void cs40l50_request_firmware(const struct firmware *fw, void *context)
311 {
312         struct cs40l50 *cs40l50 = context;
313         int ret;
314
315         if (!fw) {
316                 dev_err(cs40l50->dev, "No firmware file found\n");
317                 return;
318         }
319
320         cs40l50->fw = fw;
321
322         ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_WT,
323                                       cs40l50->dev, GFP_KERNEL, cs40l50,
324                                       cs40l50_dsp_bringup);
325         if (ret) {
326                 dev_err(cs40l50->dev, "Failed to request %s: %d\n", CS40L50_WT, ret);
327                 release_firmware(cs40l50->fw);
328         }
329 }
330
331 struct cs40l50_irq {
332         const char *name;
333         int virq;
334 };
335
336 static struct cs40l50_irq cs40l50_irqs[] = {
337         { "DSP", },
338         { "Global", },
339         { "Boost UVLO", },
340         { "Boost current limit", },
341         { "Boost short", },
342         { "Boost undervolt", },
343         { "Overtemp", },
344         { "Amp short", },
345 };
346
347 static const struct reg_sequence cs40l50_err_rls[] = {
348         { CS40L50_ERR_RLS, CS40L50_GLOBAL_ERR_RLS_SET },
349         { CS40L50_ERR_RLS, CS40L50_GLOBAL_ERR_RLS_CLEAR },
350 };
351
352 static irqreturn_t cs40l50_hw_err(int irq, void *data)
353 {
354         struct cs40l50 *cs40l50 = data;
355         int ret = 0, i;
356
357         mutex_lock(&cs40l50->lock);
358
359         /* Log hardware interrupt and execute error release sequence */
360         for (i = 1; i < ARRAY_SIZE(cs40l50_irqs); i++) {
361                 if (cs40l50_irqs[i].virq == irq) {
362                         dev_err(cs40l50->dev, "%s error\n", cs40l50_irqs[i].name);
363                         ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_err_rls,
364                                                      ARRAY_SIZE(cs40l50_err_rls));
365                         break;
366                 }
367         }
368
369         mutex_unlock(&cs40l50->lock);
370         return IRQ_RETVAL(!ret);
371 }
372
373 static irqreturn_t cs40l50_dsp_queue(int irq, void *data)
374 {
375         struct cs40l50 *cs40l50 = data;
376         u32 rd_ptr, val, wt_ptr;
377         int ret = 0;
378
379         mutex_lock(&cs40l50->lock);
380
381         /* Read from DSP queue, log, and update read pointer */
382         while (!ret) {
383                 ret = regmap_read(cs40l50->regmap, CS40L50_DSP_QUEUE_WT, &wt_ptr);
384                 if (ret)
385                         break;
386
387                 ret = regmap_read(cs40l50->regmap, CS40L50_DSP_QUEUE_RD, &rd_ptr);
388                 if (ret)
389                         break;
390
391                 /* Check if queue is empty */
392                 if (wt_ptr == rd_ptr)
393                         break;
394
395                 ret = regmap_read(cs40l50->regmap, rd_ptr, &val);
396                 if (ret)
397                         break;
398
399                 dev_dbg(cs40l50->dev, "DSP payload: %#X", val);
400
401                 rd_ptr += sizeof(u32);
402
403                 if (rd_ptr > CS40L50_DSP_QUEUE_END)
404                         rd_ptr = CS40L50_DSP_QUEUE_BASE;
405
406                 ret = regmap_write(cs40l50->regmap, CS40L50_DSP_QUEUE_RD, rd_ptr);
407         }
408
409         mutex_unlock(&cs40l50->lock);
410
411         return IRQ_RETVAL(!ret);
412 }
413
414 static int cs40l50_irq_init(struct cs40l50 *cs40l50)
415 {
416         int ret, i, virq;
417
418         ret = devm_regmap_add_irq_chip(cs40l50->dev, cs40l50->regmap, cs40l50->irq,
419                                        IRQF_ONESHOT | IRQF_SHARED, 0,
420                                        &cs40l50_irq_chip, &cs40l50->irq_data);
421         if (ret) {
422                 dev_err(cs40l50->dev, "Failed adding IRQ chip\n");
423                 return ret;
424         }
425
426         for (i = 0; i < ARRAY_SIZE(cs40l50_irqs); i++) {
427                 virq = regmap_irq_get_virq(cs40l50->irq_data, i);
428                 if (virq < 0) {
429                         dev_err(cs40l50->dev, "Failed getting virq for %s\n",
430                                 cs40l50_irqs[i].name);
431                         return virq;
432                 }
433
434                 cs40l50_irqs[i].virq = virq;
435
436                 /* Handle DSP and hardware interrupts separately */
437                 ret = devm_request_threaded_irq(cs40l50->dev, virq, NULL,
438                                                 i ? cs40l50_hw_err : cs40l50_dsp_queue,
439                                                 IRQF_ONESHOT | IRQF_SHARED,
440                                                 cs40l50_irqs[i].name, cs40l50);
441                 if (ret) {
442                         return dev_err_probe(cs40l50->dev, ret,
443                                              "Failed requesting %s IRQ\n",
444                                              cs40l50_irqs[i].name);
445                 }
446         }
447
448         return 0;
449 }
450
451 static int cs40l50_get_model(struct cs40l50 *cs40l50)
452 {
453         int ret;
454
455         ret = regmap_read(cs40l50->regmap, CS40L50_DEVID, &cs40l50->devid);
456         if (ret)
457                 return ret;
458
459         if (cs40l50->devid != CS40L50_DEVID_A)
460                 return -EINVAL;
461
462         ret = regmap_read(cs40l50->regmap, CS40L50_REVID, &cs40l50->revid);
463         if (ret)
464                 return ret;
465
466         if (cs40l50->revid < CS40L50_REVID_B0)
467                 return -EINVAL;
468
469         dev_dbg(cs40l50->dev, "Cirrus Logic CS40L50 rev. %02X\n", cs40l50->revid);
470
471         return 0;
472 }
473
474 static int cs40l50_pm_runtime_setup(struct device *dev)
475 {
476         int ret;
477
478         pm_runtime_set_autosuspend_delay(dev, CS40L50_AUTOSUSPEND_MS);
479         pm_runtime_use_autosuspend(dev);
480         pm_runtime_get_noresume(dev);
481         ret = pm_runtime_set_active(dev);
482         if (ret)
483                 return ret;
484
485         return devm_pm_runtime_enable(dev);
486 }
487
488 int cs40l50_probe(struct cs40l50 *cs40l50)
489 {
490         struct device *dev = cs40l50->dev;
491         int ret;
492
493         mutex_init(&cs40l50->lock);
494
495         cs40l50->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
496         if (IS_ERR(cs40l50->reset_gpio))
497                 return dev_err_probe(dev, PTR_ERR(cs40l50->reset_gpio),
498                                      "Failed getting reset GPIO\n");
499
500         ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(cs40l50_supplies),
501                                              cs40l50_supplies);
502         if (ret)
503                 return dev_err_probe(dev, ret, "Failed getting supplies\n");
504
505         /* Ensure minimum reset pulse width */
506         usleep_range(CS40L50_RESET_PULSE_US, CS40L50_RESET_PULSE_US + 100);
507
508         gpiod_set_value_cansleep(cs40l50->reset_gpio, 0);
509
510         /* Wait for control port to be ready */
511         usleep_range(CS40L50_CP_READY_US, CS40L50_CP_READY_US + 100);
512
513         ret = cs40l50_get_model(cs40l50);
514         if (ret)
515                 return dev_err_probe(dev, ret, "Failed to get part number\n");
516
517         ret = cs40l50_dsp_init(cs40l50);
518         if (ret)
519                 return dev_err_probe(dev, ret, "Failed to initialize DSP\n");
520
521         ret = cs40l50_pm_runtime_setup(dev);
522         if (ret)
523                 return dev_err_probe(dev, ret, "Failed to initialize runtime PM\n");
524
525         ret = cs40l50_irq_init(cs40l50);
526         if (ret)
527                 return ret;
528
529         ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_FW,
530                                       dev, GFP_KERNEL, cs40l50, cs40l50_request_firmware);
531         if (ret)
532                 return dev_err_probe(dev, ret, "Failed to request %s\n", CS40L50_FW);
533
534         pm_runtime_mark_last_busy(dev);
535         pm_runtime_put_autosuspend(dev);
536
537         return 0;
538 }
539 EXPORT_SYMBOL_GPL(cs40l50_probe);
540
541 int cs40l50_remove(struct cs40l50 *cs40l50)
542 {
543         gpiod_set_value_cansleep(cs40l50->reset_gpio, 1);
544
545         return 0;
546 }
547 EXPORT_SYMBOL_GPL(cs40l50_remove);
548
549 static int cs40l50_runtime_suspend(struct device *dev)
550 {
551         struct cs40l50 *cs40l50 = dev_get_drvdata(dev);
552
553         return regmap_write(cs40l50->regmap, CS40L50_DSP_QUEUE, CS40L50_ALLOW_HIBER);
554 }
555
556 static int cs40l50_runtime_resume(struct device *dev)
557 {
558         struct cs40l50 *cs40l50 = dev_get_drvdata(dev);
559
560         return cs40l50_dsp_write(dev, cs40l50->regmap, CS40L50_PREVENT_HIBER);
561 }
562
563 EXPORT_GPL_DEV_PM_OPS(cs40l50_pm_ops) = {
564         RUNTIME_PM_OPS(cs40l50_runtime_suspend, cs40l50_runtime_resume, NULL)
565 };
566
567 MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
568 MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <[email protected]>");
569 MODULE_LICENSE("GPL");
570 MODULE_IMPORT_NS("FW_CS_DSP");
This page took 0.058389 seconds and 4 git commands to generate.