Loading...
Note: File does not exist in v3.1.
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 <james.ogletree@cirrus.com>
9 */
10
11#include <linux/bitfield.h>
12#include <linux/input.h>
13#include <linux/mfd/cs40l50.h>
14#include <linux/platform_device.h>
15#include <linux/pm_runtime.h>
16
17/* Wavetables */
18#define CS40L50_RAM_INDEX_START 0x1000000
19#define CS40L50_RAM_INDEX_END 0x100007F
20#define CS40L50_RTH_INDEX_START 0x1400000
21#define CS40L50_RTH_INDEX_END 0x1400001
22#define CS40L50_ROM_INDEX_START 0x1800000
23#define CS40L50_ROM_INDEX_END 0x180001A
24#define CS40L50_TYPE_PCM 8
25#define CS40L50_TYPE_PWLE 12
26#define CS40L50_PCM_ID 0x0
27#define CS40L50_OWT_CUSTOM_DATA_SIZE 2
28#define CS40L50_CUSTOM_DATA_MASK 0xFFFFU
29
30/* DSP */
31#define CS40L50_GPIO_BASE 0x2804140
32#define CS40L50_OWT_BASE 0x2805C34
33#define CS40L50_OWT_SIZE 0x2805C38
34#define CS40L50_OWT_NEXT 0x2805C3C
35#define CS40L50_EFFECTS_MAX 1
36
37/* GPIO */
38#define CS40L50_GPIO_NUM_MASK GENMASK(14, 12)
39#define CS40L50_GPIO_EDGE_MASK BIT(15)
40#define CS40L50_GPIO_MAPPING_NONE 0
41#define CS40L50_GPIO_DISABLE 0x1FF
42
43enum cs40l50_bank_type {
44 CS40L50_WVFRM_BANK_RAM,
45 CS40L50_WVFRM_BANK_ROM,
46 CS40L50_WVFRM_BANK_OWT,
47 CS40L50_WVFRM_BANK_NUM,
48};
49
50/* Describes an area in DSP memory populated by effects */
51struct cs40l50_bank {
52 enum cs40l50_bank_type type;
53 u32 base_index;
54 u32 max_index;
55};
56
57struct cs40l50_effect {
58 enum cs40l50_bank_type type;
59 struct list_head list;
60 u32 gpio_reg;
61 u32 index;
62 int id;
63};
64
65/* Describes haptic interface of loaded DSP firmware */
66struct cs40l50_vibra_dsp {
67 struct cs40l50_bank *banks;
68 u32 gpio_base_reg;
69 u32 owt_offset_reg;
70 u32 owt_size_reg;
71 u32 owt_base_reg;
72 u32 push_owt_cmd;
73 u32 delete_owt_cmd;
74 u32 stop_cmd;
75 int (*write)(struct device *dev, struct regmap *regmap, u32 val);
76};
77
78/* Describes configuration and state of haptic operations */
79struct cs40l50_vibra {
80 struct device *dev;
81 struct regmap *regmap;
82 struct input_dev *input;
83 struct workqueue_struct *vib_wq;
84 struct list_head effect_head;
85 struct cs40l50_vibra_dsp dsp;
86};
87
88struct cs40l50_work {
89 struct cs40l50_vibra *vib;
90 struct ff_effect *effect;
91 struct work_struct work;
92 s16 *custom_data;
93 int custom_len;
94 int count;
95 int error;
96};
97
98static struct cs40l50_bank cs40l50_banks[] = {
99 {
100 .type = CS40L50_WVFRM_BANK_RAM,
101 .base_index = CS40L50_RAM_INDEX_START,
102 .max_index = CS40L50_RAM_INDEX_END,
103 },
104 {
105 .type = CS40L50_WVFRM_BANK_ROM,
106 .base_index = CS40L50_ROM_INDEX_START,
107 .max_index = CS40L50_ROM_INDEX_END,
108 },
109 {
110 .type = CS40L50_WVFRM_BANK_OWT,
111 .base_index = CS40L50_RTH_INDEX_START,
112 .max_index = CS40L50_RTH_INDEX_END,
113 },
114};
115
116static struct cs40l50_vibra_dsp cs40l50_dsp = {
117 .banks = cs40l50_banks,
118 .gpio_base_reg = CS40L50_GPIO_BASE,
119 .owt_base_reg = CS40L50_OWT_BASE,
120 .owt_offset_reg = CS40L50_OWT_NEXT,
121 .owt_size_reg = CS40L50_OWT_SIZE,
122 .push_owt_cmd = CS40L50_OWT_PUSH,
123 .delete_owt_cmd = CS40L50_OWT_DELETE,
124 .stop_cmd = CS40L50_STOP_PLAYBACK,
125 .write = cs40l50_dsp_write,
126};
127
128static struct cs40l50_effect *cs40l50_find_effect(int id, struct list_head *effect_head)
129{
130 struct cs40l50_effect *effect;
131
132 list_for_each_entry(effect, effect_head, list)
133 if (effect->id == id)
134 return effect;
135
136 return NULL;
137}
138
139static int cs40l50_effect_bank_set(struct cs40l50_work *work_data,
140 struct cs40l50_effect *effect)
141{
142 s16 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK;
143
144 if (bank_type >= CS40L50_WVFRM_BANK_NUM) {
145 dev_err(work_data->vib->dev, "Invalid bank (%d)\n", bank_type);
146 return -EINVAL;
147 }
148
149 if (work_data->custom_len > CS40L50_OWT_CUSTOM_DATA_SIZE)
150 effect->type = CS40L50_WVFRM_BANK_OWT;
151 else
152 effect->type = bank_type;
153
154 return 0;
155}
156
157static int cs40l50_effect_index_set(struct cs40l50_work *work_data,
158 struct cs40l50_effect *effect)
159{
160 struct cs40l50_vibra *vib = work_data->vib;
161 struct cs40l50_effect *owt_effect;
162 u32 base_index, max_index;
163
164 base_index = vib->dsp.banks[effect->type].base_index;
165 max_index = vib->dsp.banks[effect->type].max_index;
166
167 effect->index = base_index;
168
169 switch (effect->type) {
170 case CS40L50_WVFRM_BANK_OWT:
171 list_for_each_entry(owt_effect, &vib->effect_head, list)
172 if (owt_effect->type == CS40L50_WVFRM_BANK_OWT)
173 effect->index++;
174 break;
175 case CS40L50_WVFRM_BANK_ROM:
176 case CS40L50_WVFRM_BANK_RAM:
177 effect->index += work_data->custom_data[1] & CS40L50_CUSTOM_DATA_MASK;
178 break;
179 default:
180 dev_err(vib->dev, "Bank type %d not supported\n", effect->type);
181 return -EINVAL;
182 }
183
184 if (effect->index > max_index || effect->index < base_index) {
185 dev_err(vib->dev, "Index out of bounds: %u\n", effect->index);
186 return -ENOSPC;
187 }
188
189 return 0;
190}
191
192static int cs40l50_effect_gpio_mapping_set(struct cs40l50_work *work_data,
193 struct cs40l50_effect *effect)
194{
195 u16 gpio_edge, gpio_num, button = work_data->effect->trigger.button;
196 struct cs40l50_vibra *vib = work_data->vib;
197
198 if (button) {
199 gpio_num = FIELD_GET(CS40L50_GPIO_NUM_MASK, button);
200 gpio_edge = FIELD_GET(CS40L50_GPIO_EDGE_MASK, button);
201 effect->gpio_reg = vib->dsp.gpio_base_reg + (gpio_num * 8) - gpio_edge;
202
203 return regmap_write(vib->regmap, effect->gpio_reg, button);
204 }
205
206 effect->gpio_reg = CS40L50_GPIO_MAPPING_NONE;
207
208 return 0;
209}
210
211struct cs40l50_owt_header {
212 u32 type;
213 u32 data_words;
214 u32 offset;
215} __packed;
216
217static int cs40l50_upload_owt(struct cs40l50_work *work_data)
218{
219 u8 *new_owt_effect_data __free(kfree) = NULL;
220 struct cs40l50_vibra *vib = work_data->vib;
221 size_t len = work_data->custom_len * 2;
222 struct cs40l50_owt_header header;
223 u32 offset, size;
224 int error;
225
226 error = regmap_read(vib->regmap, vib->dsp.owt_size_reg, &size);
227 if (error)
228 return error;
229
230 if ((size * sizeof(u32)) < sizeof(header) + len) {
231 dev_err(vib->dev, "No space in open wavetable for effect\n");
232 return -ENOSPC;
233 }
234
235 header.type = work_data->custom_data[0] == CS40L50_PCM_ID ? CS40L50_TYPE_PCM :
236 CS40L50_TYPE_PWLE;
237 header.offset = sizeof(header) / sizeof(u32);
238 header.data_words = len / sizeof(u32);
239
240 new_owt_effect_data = kmalloc(sizeof(header) + len, GFP_KERNEL);
241
242 memcpy(new_owt_effect_data, &header, sizeof(header));
243 memcpy(new_owt_effect_data + sizeof(header), work_data->custom_data, len);
244
245 error = regmap_read(vib->regmap, vib->dsp.owt_offset_reg, &offset);
246 if (error)
247 return error;
248
249 error = regmap_bulk_write(vib->regmap, vib->dsp.owt_base_reg +
250 (offset * sizeof(u32)), new_owt_effect_data,
251 sizeof(header) + len);
252 if (error)
253 return error;
254
255 error = vib->dsp.write(vib->dev, vib->regmap, vib->dsp.push_owt_cmd);
256 if (error)
257 return error;
258
259 return 0;
260}
261
262static void cs40l50_add_worker(struct work_struct *work)
263{
264 struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
265 struct cs40l50_vibra *vib = work_data->vib;
266 struct cs40l50_effect *effect;
267 bool is_new = false;
268 int error;
269
270 error = pm_runtime_resume_and_get(vib->dev);
271 if (error)
272 goto err_exit;
273
274 /* Update effect if already uploaded, otherwise create new effect */
275 effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
276 if (!effect) {
277 effect = kzalloc(sizeof(*effect), GFP_KERNEL);
278 if (!effect) {
279 error = -ENOMEM;
280 goto err_pm;
281 }
282
283 effect->id = work_data->effect->id;
284 is_new = true;
285 }
286
287 error = cs40l50_effect_bank_set(work_data, effect);
288 if (error)
289 goto err_free;
290
291 error = cs40l50_effect_index_set(work_data, effect);
292 if (error)
293 goto err_free;
294
295 error = cs40l50_effect_gpio_mapping_set(work_data, effect);
296 if (error)
297 goto err_free;
298
299 if (effect->type == CS40L50_WVFRM_BANK_OWT)
300 error = cs40l50_upload_owt(work_data);
301err_free:
302 if (is_new) {
303 if (error)
304 kfree(effect);
305 else
306 list_add(&effect->list, &vib->effect_head);
307 }
308err_pm:
309 pm_runtime_mark_last_busy(vib->dev);
310 pm_runtime_put_autosuspend(vib->dev);
311err_exit:
312 work_data->error = error;
313}
314
315static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect,
316 struct ff_effect *old)
317{
318 struct ff_periodic_effect *periodic = &effect->u.periodic;
319 struct cs40l50_vibra *vib = input_get_drvdata(dev);
320 struct cs40l50_work work_data;
321
322 if (effect->type != FF_PERIODIC || periodic->waveform != FF_CUSTOM) {
323 dev_err(vib->dev, "Type (%#X) or waveform (%#X) unsupported\n",
324 effect->type, periodic->waveform);
325 return -EINVAL;
326 }
327
328 work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data,
329 effect->u.periodic.custom_len,
330 sizeof(s16));
331 if (IS_ERR(work_data.custom_data))
332 return PTR_ERR(work_data.custom_data);
333
334 work_data.custom_len = effect->u.periodic.custom_len;
335 work_data.vib = vib;
336 work_data.effect = effect;
337 INIT_WORK_ONSTACK(&work_data.work, cs40l50_add_worker);
338
339 /* Push to the workqueue to serialize with playbacks */
340 queue_work(vib->vib_wq, &work_data.work);
341 flush_work(&work_data.work);
342 destroy_work_on_stack(&work_data.work);
343
344 kfree(work_data.custom_data);
345
346 return work_data.error;
347}
348
349static void cs40l50_start_worker(struct work_struct *work)
350{
351 struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
352 struct cs40l50_vibra *vib = work_data->vib;
353 struct cs40l50_effect *start_effect;
354
355 if (pm_runtime_resume_and_get(vib->dev) < 0)
356 goto err_free;
357
358 start_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
359 if (start_effect) {
360 while (--work_data->count >= 0) {
361 vib->dsp.write(vib->dev, vib->regmap, start_effect->index);
362 usleep_range(work_data->effect->replay.length,
363 work_data->effect->replay.length + 100);
364 }
365 } else {
366 dev_err(vib->dev, "Effect to play not found\n");
367 }
368
369 pm_runtime_mark_last_busy(vib->dev);
370 pm_runtime_put_autosuspend(vib->dev);
371err_free:
372 kfree(work_data);
373}
374
375static void cs40l50_stop_worker(struct work_struct *work)
376{
377 struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
378 struct cs40l50_vibra *vib = work_data->vib;
379
380 if (pm_runtime_resume_and_get(vib->dev) < 0)
381 return;
382
383 vib->dsp.write(vib->dev, vib->regmap, vib->dsp.stop_cmd);
384
385 pm_runtime_mark_last_busy(vib->dev);
386 pm_runtime_put_autosuspend(vib->dev);
387
388 kfree(work_data);
389}
390
391static int cs40l50_playback(struct input_dev *dev, int effect_id, int val)
392{
393 struct cs40l50_vibra *vib = input_get_drvdata(dev);
394 struct cs40l50_work *work_data;
395
396 work_data = kzalloc(sizeof(*work_data), GFP_ATOMIC);
397 if (!work_data)
398 return -ENOMEM;
399
400 work_data->vib = vib;
401
402 if (val > 0) {
403 work_data->effect = &dev->ff->effects[effect_id];
404 work_data->count = val;
405 INIT_WORK(&work_data->work, cs40l50_start_worker);
406 } else {
407 /* Stop the amplifier as device drives only one effect */
408 INIT_WORK(&work_data->work, cs40l50_stop_worker);
409 }
410
411 queue_work(vib->vib_wq, &work_data->work);
412
413 return 0;
414}
415
416static void cs40l50_erase_worker(struct work_struct *work)
417{
418 struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
419 struct cs40l50_effect *erase_effect, *owt_effect;
420 struct cs40l50_vibra *vib = work_data->vib;
421 int error;
422
423 error = pm_runtime_resume_and_get(vib->dev);
424 if (error)
425 goto err_exit;
426
427 erase_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
428 if (!erase_effect) {
429 dev_err(vib->dev, "Effect to erase not found\n");
430 error = -EINVAL;
431 goto err_pm;
432 }
433
434 if (erase_effect->gpio_reg != CS40L50_GPIO_MAPPING_NONE) {
435 error = regmap_write(vib->regmap, erase_effect->gpio_reg,
436 CS40L50_GPIO_DISABLE);
437 if (error)
438 goto err_pm;
439 }
440
441 if (erase_effect->type == CS40L50_WVFRM_BANK_OWT) {
442 error = vib->dsp.write(vib->dev, vib->regmap,
443 vib->dsp.delete_owt_cmd |
444 (erase_effect->index & 0xFF));
445 if (error)
446 goto err_pm;
447
448 list_for_each_entry(owt_effect, &vib->effect_head, list)
449 if (owt_effect->type == CS40L50_WVFRM_BANK_OWT &&
450 owt_effect->index > erase_effect->index)
451 owt_effect->index--;
452 }
453
454 list_del(&erase_effect->list);
455 kfree(erase_effect);
456err_pm:
457 pm_runtime_mark_last_busy(vib->dev);
458 pm_runtime_put_autosuspend(vib->dev);
459err_exit:
460 work_data->error = error;
461}
462
463static int cs40l50_erase(struct input_dev *dev, int effect_id)
464{
465 struct cs40l50_vibra *vib = input_get_drvdata(dev);
466 struct cs40l50_work work_data;
467
468 work_data.vib = vib;
469 work_data.effect = &dev->ff->effects[effect_id];
470
471 INIT_WORK_ONSTACK(&work_data.work, cs40l50_erase_worker);
472
473 /* Push to workqueue to serialize with playbacks */
474 queue_work(vib->vib_wq, &work_data.work);
475 flush_work(&work_data.work);
476 destroy_work_on_stack(&work_data.work);
477
478 return work_data.error;
479}
480
481static void cs40l50_remove_wq(void *data)
482{
483 flush_workqueue(data);
484 destroy_workqueue(data);
485}
486
487static int cs40l50_vibra_probe(struct platform_device *pdev)
488{
489 struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent);
490 struct cs40l50_vibra *vib;
491 int error;
492
493 vib = devm_kzalloc(pdev->dev.parent, sizeof(*vib), GFP_KERNEL);
494 if (!vib)
495 return -ENOMEM;
496
497 vib->dev = cs40l50->dev;
498 vib->regmap = cs40l50->regmap;
499 vib->dsp = cs40l50_dsp;
500
501 vib->input = devm_input_allocate_device(vib->dev);
502 if (!vib->input)
503 return -ENOMEM;
504
505 vib->input->id.product = cs40l50->devid;
506 vib->input->id.version = cs40l50->revid;
507 vib->input->name = "cs40l50_vibra";
508
509 input_set_drvdata(vib->input, vib);
510 input_set_capability(vib->input, EV_FF, FF_PERIODIC);
511 input_set_capability(vib->input, EV_FF, FF_CUSTOM);
512
513 error = input_ff_create(vib->input, CS40L50_EFFECTS_MAX);
514 if (error) {
515 dev_err(vib->dev, "Failed to create input device\n");
516 return error;
517 }
518
519 vib->input->ff->upload = cs40l50_add;
520 vib->input->ff->playback = cs40l50_playback;
521 vib->input->ff->erase = cs40l50_erase;
522
523 INIT_LIST_HEAD(&vib->effect_head);
524
525 vib->vib_wq = alloc_ordered_workqueue("vib_wq", WQ_HIGHPRI);
526 if (!vib->vib_wq)
527 return -ENOMEM;
528
529 error = devm_add_action_or_reset(vib->dev, cs40l50_remove_wq, vib->vib_wq);
530 if (error)
531 return error;
532
533 error = input_register_device(vib->input);
534 if (error)
535 return error;
536
537 return 0;
538}
539
540static const struct platform_device_id cs40l50_vibra_id_match[] = {
541 { "cs40l50-vibra", },
542 {}
543};
544MODULE_DEVICE_TABLE(platform, cs40l50_vibra_id_match);
545
546static struct platform_driver cs40l50_vibra_driver = {
547 .probe = cs40l50_vibra_probe,
548 .id_table = cs40l50_vibra_id_match,
549 .driver = {
550 .name = "cs40l50-vibra",
551 },
552};
553module_platform_driver(cs40l50_vibra_driver);
554
555MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
556MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
557MODULE_LICENSE("GPL");