zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.43k stars 6.39k forks source link

Create an API for Haptic Driver Devices #67226

Closed rriveramcrus closed 1 week ago

rriveramcrus commented 8 months ago

Discussed in https://github.com/zephyrproject-rtos/zephyr/discussions/64942

Originally posted by **rriveramcrus** November 7, 2023 # Introduction Add a kernel device driver API for interacting with haptic driver devices. Draft PR: https://github.com/zephyrproject-rtos/zephyr/pull/66629 ## Problem description Haptic devices are integrated circuits that drive a linear resonant actuator (LRA) when an event occurs to provide the end user haptic feedback. A haptic event can be triggered via GPIO or through a control port. The haptic event sources can be simple PWM inputs, PCM inputs such as I2S/TDM, an integrated buzzer, tables of waveforms from ROM, or synthesized waveform tables in RAM. There is currently no purpose-built API in Zephyr for interacting with haptic driver device or facilitating haptic events. | Part Number | Input Sources | Feature set | | ------------- | ------------- | ----------- | | CS40L50 and CS40L26 | I2C/SPI, I2S | reference clock controls, integrated boost converter controls, Data interface controls , Brownout and temperature controls, calibration interface | | DRV2605 | Analog, I2C, PWM | MODE (a combination of source selection and calibration operation control) , ROM Bank Selection, Wait time between sequenced waveforms, Overdrive time offset, sustain time positive offset, sustain time negative offset, brake time offset, various ATH controls, various LRA drive controls, calibration and loop controls| | AW869x7 | I2C, Analog | Calibration controls, autobrake engine controls, ROM waveform selection, various LRA drive controls, integrated boost converter controls | The table above compare three LRA haptic driver devices from different vendors. Some overlap exists in the input sources and basic features, but advanced features are far from standardized in interface and nomenclature. ## Status Quo ### Zephyr OS No known subsystem catering to haptic driver devices. The input subsystem is focused on mice, keyboards, and other HIDs. The question of how to accomodate haptic LRA drivers has come up in the past (#22486). An embedded controller running Zephyr has various haptic feedback applications. ### Linux Kernel Haptic driver devices have been living in the Input Force Feedback subsystem as miscellaneous devices. This subsystem was not originally meant for haptic driver devices, but rather joysticks, mice, keyboards, etc. It would not be ideal to continue this development architecture in Zephyr and serves as an opportunity to put together something better for haptic driver devices. ## Proposed change ![haptics_api](https://github.com/zephyrproject-rtos/zephyr/assets/94382960/4f474dc4-8d7d-4d8a-aa43-fd3ec133d909) Functionally these devices are similar to audio codecs and the proposed *haptic_driver_api* takes inspiration from the *audio_codec_api*. The haptic waveform events are to be triggered through a playback state machine with three states and a corresponding handler for each state. ![haptic_driver_api-Playback Events drawio](https://github.com/zephyrproject-rtos/zephyr/assets/94382960/0ff262d9-15aa-476a-94db-c009c78c20b6) To begin haptic event playback the *start_event* handler is to be called. The device driver must begin playback of the haptic event that is presently configured. Once complete, the *stop_event* handler is called to stop playback of the haptic event. Haptic events in a system do have priority and the proposed API offers a *preempt_event* handler for preempting playback of an event. When starting or preempting an event, an event identifier is passed to the handler. This is useful for configuring the device to playback from the appropriate source, Configuration of the device at runtime is to handled as an extension of this API. ### Driver Examples The code snippets below serve as examples of how a client of the API would interface. ```c /* Zephyr device boot time data, read only */ struct cs40l26_haptics_config { struct i2c_dt_spec i2c; struct gpio_dt_spec gpio1; struct gpio_dt_spec gpio2; struct gpio_dt_spec gpio3; }; /* Zephyr runtime data, R/W */ struct cs40l26_haptics_data { uint32_t wf_index; uint8_t gpio_index; haptics_source_t configured_for; }; static const struct haptics_driver_api cs40l50_driver_api = { .start_output = &cs40l26_start_output, .stop_output = &cs40l26_stop_output, .preempt_output = &cs40l26_preempt_output, } ``` ```c static int cs40l26_start_output(const struct device *dev) { const struct cs40l26_haptics_config *cfg = dev->config; const struct cs40l26_haptics_data *data = dev->data; switch (data->configured_for) { case HAPTICS_SOURCE_ROM_BANK: return i2c_reg_write(&cfg->i2c, CS40L26_DSP_VIRTUAL1_MBOX_1, data->wf_index); case HAPTICS_SOURCE_GPIO_BANK: return cs40l26_set_gpios(dev); case HAPTICS_SOURCE_I2S: return cs40l26_i2s_en(dev); default: return -EINVAL; } return 0; } ```
henrikbrixandersen commented 8 months ago

CC: @fabiobaltieri

fabiobaltieri commented 7 months ago

Hi Marcus, thanks for the proposal. I've been peeking at some datasheets and I'm trying to understand if there's even enough commonalities between haptic controllers of different vendors to justify a subsystem or if maybe this should live as misc device driver that supports multiple p/n from the same vendor that are designed around a common model.

Do you have any insight on that? I'm thinking it may help to add some details and higlight how the API would cover various devices, maybe comparing the CS40L26 (or another device from CL) to a DRV2605L (it seems to be a popular part number in the QMK world, just got myself one to play with :-)).

At first sight from me I think it may make sense, there's certainly overlap on the start/stop output APIs, but the interesting part is how the whole configuration and property (is there a difference?) may pan out. Could you expand on those?

rriveramcrus commented 7 months ago

Hey @fabiobaltieri, Thanks for taking a look! I added a comparison table in the Problem Description section. The playback controls and input sources are very similar across the board but as you mentioned the controls set through properties can really differ.

The intention with the configure would be to atomically configure the haptic driver for a desired input source. If it is a I2S input source the would mean configure clocks and a data interface. If it is ROM input source there's little to do besides make sure the command is valid.

The intention of the properties was more so for facilitating LRA calibration, configuring the integrated boost converter, setting protections, etc. I think some basic properties can be enumerated in the haptics API but it may be wise to allow it to be extended beyond that with vendor specific properties in a separate library.

An unknown for me is how to handle waveform synthesis. Should I just expose the various controls of the hardware synthesizer as properties or should the API try define and take in standardize waveform instructions, parse them, and issue them in the configuration handler? I think it's more of a question of what is more Zephyr-like.

Ricardo

fabiobaltieri commented 7 months ago

Right but the configuration part of those devices I reckon is

So I'm thinking that it may not be much sense to try and enumerate a ton of properties where they are implemented in groups, but rather just implement the basic common stuff (start/stop effect, not sure what else) and then have the configuration either handled in the driver init or with a per-driver extension that can be shared between a group of drivers.

The playback part I'd imagine being the more intersting part, not sure what to do about it but for i2s I'd imagine it's just use the i2s subsystem, for RAM wavefrom maybe just a simple write_samples API?

rriveramcrus commented 7 months ago

So I'm thinking that it may not be much sense to try and enumerate a ton of properties where they are implemented in groups, but rather just implement the basic common stuff (start/stop effect, not sure what else) and then have the configuration either handled in the driver init or with a per-driver extension that can be shared between a group of drivers.

I am okay with this. Are there any vendor specific extensions you recommend to draw inspiration from? I would like to rewrite the draft PR to be closer to this vision to make the rest of the discussion a bit easier.

fabiobaltieri commented 7 months ago

@rriveramcrus do the subsystem api with the common stuff and a sample driver using driver specific APIs for configuration, there's few of those in code base, for example https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor/ccs811.h.

raveslave commented 5 months ago

hi rriveram, great suggestion & overview

suggest to also add upstream events to the haptic_driver_api that can be used for driver IC's offering protection features like over temp, current monitoring and various diagnostics events / alerts.

these could be tied to real irq inputs or soft irq's based on some condition. also neat for zephyr shell when working with tuning and bring-up

carlescufi commented 3 months ago

@fabiobaltieri @rriveramcrus should we bring this up in the next Architecture WG meeting?

rriveramcrus commented 3 months ago

Sounds good to me! Next Tuesday at 8AM PT/17h CET, correct?

rriveramcrus commented 3 months ago

Actually if we could push to the following week, June 11th, that would work better.