Lines Matching +full:haptic +full:- +full:driver
1 // SPDX-License-Identifier: GPL-2.0
3 * CS40L50 Advanced Haptic Driver with waveform memory,
4 * integrated DSP, and closed-loop algorithms
65 /* Describes haptic interface of loaded DSP firmware */
78 /* Describes configuration and state of haptic operations */
133 if (effect->id == id) in cs40l50_find_effect()
142 s16 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK; in cs40l50_effect_bank_set()
145 dev_err(work_data->vib->dev, "Invalid bank (%d)\n", bank_type); in cs40l50_effect_bank_set()
146 return -EINVAL; in cs40l50_effect_bank_set()
149 if (work_data->custom_len > CS40L50_OWT_CUSTOM_DATA_SIZE) in cs40l50_effect_bank_set()
150 effect->type = CS40L50_WVFRM_BANK_OWT; in cs40l50_effect_bank_set()
152 effect->type = bank_type; in cs40l50_effect_bank_set()
160 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_effect_index_set()
164 base_index = vib->dsp.banks[effect->type].base_index; in cs40l50_effect_index_set()
165 max_index = vib->dsp.banks[effect->type].max_index; in cs40l50_effect_index_set()
167 effect->index = base_index; in cs40l50_effect_index_set()
169 switch (effect->type) { in cs40l50_effect_index_set()
171 list_for_each_entry(owt_effect, &vib->effect_head, list) in cs40l50_effect_index_set()
172 if (owt_effect->type == CS40L50_WVFRM_BANK_OWT) in cs40l50_effect_index_set()
173 effect->index++; in cs40l50_effect_index_set()
177 effect->index += work_data->custom_data[1] & CS40L50_CUSTOM_DATA_MASK; in cs40l50_effect_index_set()
180 dev_err(vib->dev, "Bank type %d not supported\n", effect->type); in cs40l50_effect_index_set()
181 return -EINVAL; in cs40l50_effect_index_set()
184 if (effect->index > max_index || effect->index < base_index) { in cs40l50_effect_index_set()
185 dev_err(vib->dev, "Index out of bounds: %u\n", effect->index); in cs40l50_effect_index_set()
186 return -ENOSPC; in cs40l50_effect_index_set()
195 u16 gpio_edge, gpio_num, button = work_data->effect->trigger.button; in cs40l50_effect_gpio_mapping_set()
196 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_effect_gpio_mapping_set()
201 effect->gpio_reg = vib->dsp.gpio_base_reg + (gpio_num * 8) - gpio_edge; in cs40l50_effect_gpio_mapping_set()
203 return regmap_write(vib->regmap, effect->gpio_reg, button); in cs40l50_effect_gpio_mapping_set()
206 effect->gpio_reg = CS40L50_GPIO_MAPPING_NONE; in cs40l50_effect_gpio_mapping_set()
220 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_upload_owt()
221 size_t len = work_data->custom_len * 2; in cs40l50_upload_owt()
226 error = regmap_read(vib->regmap, vib->dsp.owt_size_reg, &size); in cs40l50_upload_owt()
231 dev_err(vib->dev, "No space in open wavetable for effect\n"); in cs40l50_upload_owt()
232 return -ENOSPC; in cs40l50_upload_owt()
235 header.type = work_data->custom_data[0] == CS40L50_PCM_ID ? CS40L50_TYPE_PCM : in cs40l50_upload_owt()
243 memcpy(new_owt_effect_data + sizeof(header), work_data->custom_data, len); in cs40l50_upload_owt()
245 error = regmap_read(vib->regmap, vib->dsp.owt_offset_reg, &offset); in cs40l50_upload_owt()
249 error = regmap_bulk_write(vib->regmap, vib->dsp.owt_base_reg + in cs40l50_upload_owt()
255 error = vib->dsp.write(vib->dev, vib->regmap, vib->dsp.push_owt_cmd); in cs40l50_upload_owt()
265 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_add_worker()
270 error = pm_runtime_resume_and_get(vib->dev); in cs40l50_add_worker()
275 effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head); in cs40l50_add_worker()
279 error = -ENOMEM; in cs40l50_add_worker()
283 effect->id = work_data->effect->id; in cs40l50_add_worker()
299 if (effect->type == CS40L50_WVFRM_BANK_OWT) in cs40l50_add_worker()
306 list_add(&effect->list, &vib->effect_head); in cs40l50_add_worker()
309 pm_runtime_mark_last_busy(vib->dev); in cs40l50_add_worker()
310 pm_runtime_put_autosuspend(vib->dev); in cs40l50_add_worker()
312 work_data->error = error; in cs40l50_add_worker()
318 struct ff_periodic_effect *periodic = &effect->u.periodic; in cs40l50_add()
322 if (effect->type != FF_PERIODIC || periodic->waveform != FF_CUSTOM) { in cs40l50_add()
323 dev_err(vib->dev, "Type (%#X) or waveform (%#X) unsupported\n", in cs40l50_add()
324 effect->type, periodic->waveform); in cs40l50_add()
325 return -EINVAL; in cs40l50_add()
328 work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data, in cs40l50_add()
329 effect->u.periodic.custom_len, in cs40l50_add()
334 work_data.custom_len = effect->u.periodic.custom_len; in cs40l50_add()
340 queue_work(vib->vib_wq, &work_data.work); in cs40l50_add()
352 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_start_worker()
355 if (pm_runtime_resume_and_get(vib->dev) < 0) in cs40l50_start_worker()
358 start_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head); in cs40l50_start_worker()
360 while (--work_data->count >= 0) { in cs40l50_start_worker()
361 vib->dsp.write(vib->dev, vib->regmap, start_effect->index); in cs40l50_start_worker()
362 usleep_range(work_data->effect->replay.length, in cs40l50_start_worker()
363 work_data->effect->replay.length + 100); in cs40l50_start_worker()
366 dev_err(vib->dev, "Effect to play not found\n"); in cs40l50_start_worker()
369 pm_runtime_mark_last_busy(vib->dev); in cs40l50_start_worker()
370 pm_runtime_put_autosuspend(vib->dev); in cs40l50_start_worker()
378 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_stop_worker()
380 if (pm_runtime_resume_and_get(vib->dev) < 0) in cs40l50_stop_worker()
383 vib->dsp.write(vib->dev, vib->regmap, vib->dsp.stop_cmd); in cs40l50_stop_worker()
385 pm_runtime_mark_last_busy(vib->dev); in cs40l50_stop_worker()
386 pm_runtime_put_autosuspend(vib->dev); in cs40l50_stop_worker()
398 return -ENOMEM; in cs40l50_playback()
400 work_data->vib = vib; in cs40l50_playback()
403 work_data->effect = &dev->ff->effects[effect_id]; in cs40l50_playback()
404 work_data->count = val; in cs40l50_playback()
405 INIT_WORK(&work_data->work, cs40l50_start_worker); in cs40l50_playback()
408 INIT_WORK(&work_data->work, cs40l50_stop_worker); in cs40l50_playback()
411 queue_work(vib->vib_wq, &work_data->work); in cs40l50_playback()
420 struct cs40l50_vibra *vib = work_data->vib; in cs40l50_erase_worker()
423 error = pm_runtime_resume_and_get(vib->dev); in cs40l50_erase_worker()
427 erase_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head); in cs40l50_erase_worker()
429 dev_err(vib->dev, "Effect to erase not found\n"); in cs40l50_erase_worker()
430 error = -EINVAL; in cs40l50_erase_worker()
434 if (erase_effect->gpio_reg != CS40L50_GPIO_MAPPING_NONE) { in cs40l50_erase_worker()
435 error = regmap_write(vib->regmap, erase_effect->gpio_reg, in cs40l50_erase_worker()
441 if (erase_effect->type == CS40L50_WVFRM_BANK_OWT) { in cs40l50_erase_worker()
442 error = vib->dsp.write(vib->dev, vib->regmap, in cs40l50_erase_worker()
443 vib->dsp.delete_owt_cmd | in cs40l50_erase_worker()
444 (erase_effect->index & 0xFF)); in cs40l50_erase_worker()
448 list_for_each_entry(owt_effect, &vib->effect_head, list) in cs40l50_erase_worker()
449 if (owt_effect->type == CS40L50_WVFRM_BANK_OWT && in cs40l50_erase_worker()
450 owt_effect->index > erase_effect->index) in cs40l50_erase_worker()
451 owt_effect->index--; in cs40l50_erase_worker()
454 list_del(&erase_effect->list); in cs40l50_erase_worker()
457 pm_runtime_mark_last_busy(vib->dev); in cs40l50_erase_worker()
458 pm_runtime_put_autosuspend(vib->dev); in cs40l50_erase_worker()
460 work_data->error = error; in cs40l50_erase_worker()
469 work_data.effect = &dev->ff->effects[effect_id]; in cs40l50_erase()
474 queue_work(vib->vib_wq, &work_data.work); in cs40l50_erase()
489 struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent); in cs40l50_vibra_probe()
493 vib = devm_kzalloc(pdev->dev.parent, sizeof(*vib), GFP_KERNEL); in cs40l50_vibra_probe()
495 return -ENOMEM; in cs40l50_vibra_probe()
497 vib->dev = cs40l50->dev; in cs40l50_vibra_probe()
498 vib->regmap = cs40l50->regmap; in cs40l50_vibra_probe()
499 vib->dsp = cs40l50_dsp; in cs40l50_vibra_probe()
501 vib->input = devm_input_allocate_device(vib->dev); in cs40l50_vibra_probe()
502 if (!vib->input) in cs40l50_vibra_probe()
503 return -ENOMEM; in cs40l50_vibra_probe()
505 vib->input->id.product = cs40l50->devid; in cs40l50_vibra_probe()
506 vib->input->id.version = cs40l50->revid; in cs40l50_vibra_probe()
507 vib->input->name = "cs40l50_vibra"; in cs40l50_vibra_probe()
509 input_set_drvdata(vib->input, vib); in cs40l50_vibra_probe()
510 input_set_capability(vib->input, EV_FF, FF_PERIODIC); in cs40l50_vibra_probe()
511 input_set_capability(vib->input, EV_FF, FF_CUSTOM); in cs40l50_vibra_probe()
513 error = input_ff_create(vib->input, CS40L50_EFFECTS_MAX); in cs40l50_vibra_probe()
515 dev_err(vib->dev, "Failed to create input device\n"); in cs40l50_vibra_probe()
519 vib->input->ff->upload = cs40l50_add; in cs40l50_vibra_probe()
520 vib->input->ff->playback = cs40l50_playback; in cs40l50_vibra_probe()
521 vib->input->ff->erase = cs40l50_erase; in cs40l50_vibra_probe()
523 INIT_LIST_HEAD(&vib->effect_head); in cs40l50_vibra_probe()
525 vib->vib_wq = alloc_ordered_workqueue("vib_wq", WQ_HIGHPRI); in cs40l50_vibra_probe()
526 if (!vib->vib_wq) in cs40l50_vibra_probe()
527 return -ENOMEM; in cs40l50_vibra_probe()
529 error = devm_add_action_or_reset(vib->dev, cs40l50_remove_wq, vib->vib_wq); in cs40l50_vibra_probe()
533 error = input_register_device(vib->input); in cs40l50_vibra_probe()
541 { "cs40l50-vibra", },
549 .driver = {
550 .name = "cs40l50-vibra",
555 MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");