commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=22abb9b74bb13891e727d0b1cf57e888cfc02bca branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk
note: not ready for upstream Signed-off-by: Michael Hennerich <[email protected]> --- drivers/input/misc/Kconfig | 11 + drivers/input/misc/Makefile | 1 + drivers/input/misc/adux1001-vibra.c | 606 +++++++++++++++++++++++++++++++++++ include/linux/input/adux1001.h | 100 ++++++ 4 files changed, 718 insertions(+), 0 deletions(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index f9cf088..782a5db 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -467,4 +467,15 @@ config INPUT_XEN_KBDDEV_FRONTEND To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_ADUX1001_VIBRA + tristate "ADUX1001 Smart Electromagnetic Actuator Haptic Driver" + depends on I2C + select INPUT_FF_MEMLESS + help + This option enables support for the Analog Devices ADUX1001 + Smart Electromagnetic Actuator Haptic Driver. + + To compile this driver as a module, choose M here. The module will + be called adux1001-vibra. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index e3f7984..7161e59 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o +obj-$(CONFIG_INPUT_ADUX1001_VIBRA) += adux1001-vibra.o obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o diff --git a/drivers/input/misc/adux1001-vibra.c b/drivers/input/misc/adux1001-vibra.c new file mode 100644 index 0000000..dded58f --- /dev/null +++ b/drivers/input/misc/adux1001-vibra.c @@ -0,0 +1,606 @@ +/* + * ADUX1001 Smart Electromagnetic Actuator Haptic Driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/pm.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/pwm.h> + +#include <linux/input/adux1001.h> + +/* + * ADUX1001 Registers + */ +#define ADUX1001_CONTROL 0x01 /* Reset and interrupt commands R/W */ +#define ADUX1001_CONFIG 0x02 /* Operational mode bits and other + configuration settings R/W */ +#define ADUX1001_OUTPUT_CONFIG 0x03 /* Configure the output current and the + ARC closed loop gain. R/W */ +#define ADUX1001_OUTPUT_RATE 0x04 /* Output rate for I2C streaming and LLR or + I2C triggered arbitrary waveform R/W */ +#define ADUX1001_ARB_SEL 0x05 /* Specifies how many of the 10 ARB + registers to use for an LLR or I2C + arbitrary waveform R/W */ +#define ADUX1001_ARB0 0x06 /* Arbitrary waveform buffer 0 + amplitude and sign R/W */ +#define ADUX1001_ARB1 0x07 /* Arbitrary waveform buffer 1 + amplitude and sign R/W */ +#define ADUX1001_ARB2 0x08 /* Arbitrary waveform buffer 2 + amplitude and sign R/W */ +#define ADUX1001_ARB3 0x09 /* Arbitrary waveform buffer 3 + amplitude and sign R/W */ +#define ADUX1001_ARB4 0x0A /* Arbitrary waveform buffer 4 + amplitude and sign R/W */ +#define ADUX1001_ARB5 0x0B /* Arbitrary waveform buffer 5 + amplitude and sign R/W */ +#define ADUX1001_ARB6 0x0C /* Arbitrary waveform buffer 6 + amplitude and sign R/W */ +#define ADUX1001_ARB7 0x0D /* Arbitrary waveform buffer 7 + amplitude and sign R/W */ +#define ADUX1001_ARB8 0x0E /* Arbitrary waveform buffer 8 + amplitude and sign R/W */ +#define ADUX1001_ARB9 0x0F /* Arbitrary waveform buffer 9 + amplitude and sign R/W */ +#define ADUX1001_I2C_STREAM 0x10 /* I2C streaming buffer register R/W */ +#define ADUX1001_ACTIVE 0x11 /* Active commands, I2C mode output R/W */ +#define ADUX1001_CALIBRATE 0x12 /* Calibration configuration and + active command bits R/W */ +#define ADUX1001_SLRA_CAL0 0x13 /* Smart LRA mode calibration results + register 0. Maximum positive back EMF + value R/W */ +#define ADUX1001_SLRA_CAL1 0x14 /* Smart LRA mode calibration results + register 1. Maximum positive back EMF + value R/W */ +#define ADUX1001_SLRA_CAL2 0x15 /* Smart LRA mode calibration results + register 2. Maximum negative back EMF + value R/W */ +#define ADUX1001_SLRA_CAL3 0x16 /* Smart LRA mode calibration results + register 3. Maximum negative back EMF + value R/W */ +#define ADUX1001_SLRA_CAL4 0x17 /* Smart LRA mode calibration results + register 4. LRA resonance period R/W */ +#define ADUX1001_SLRA_CAL5 0x18 /* Smart LRA mode calibration results + register 5. LRA resonance period R/W */ +#define ADUX1001_STATUS 0x1C /* Status flags R */ +#define ADUX1001_VERSION 0x1D /* ADUX1001 silicon revision R */ + +/* + * ADUX1001 Bit masks + */ + +/* CONTROL, Register Address 0x01, Reset 0x00 */ +#define ADUX1001_RESET (1 << 1) +#define ADUX1001_I2C_INTERRUPT (1 << 0) + +/* CONFIG, Register Address 0x02, Reset 0x64 */ +#define ADUX1001_UVLO_EN(x) ((x) << 6) +#define ADUX1001_OCLO_EN(x) ((x) << 6) +#define ADUX1001_PWM_PRIORITY(x) ((x) << 3) +#define ADUX1001_INTERFACE_MODE(x) ((x) << 2) +#define ADUX1001_ACTUATOR_SEL(x) ((x) << 0) + +/* OUTPUT_CONFIG, Register Address 0x03, Reset 0x44 */ +#define ADUX1001_LOOP_GAIN(x) (((x) & 0xF) << 4) +#define ADUX1001_OUTPUT_CURRENT(x) (((x) & 0xF) << 0) + +/* OUTPUT_RATE, Register Address 0x04, Reset 0x01 */ +#define ADUX1001_LRA_OUTPUT_UNIT(x) ((x) << 7) +#define ADUX1001_OUTPUT_RATE_SEL(x) (((x) & 0x7F) << 0) + +/* ARB_SEL, Register Address 0x05, Reset 0x0A */ +#define ADUX1001_ARB_NUM_SEL(x) (((x) & 0xF) << 0) + +/* ACTIVE, Register Address 0x11, Reset 0x00 */ +#define ADUX1001_LLR_RESUME (1 << 2) +#define ADUX1001_I2C_MODE_SEL (1 << 1) +#define ADUX1001_I2C_MODE_ACTIVATE (1 << 0) + +/* CALIBRATE, Register Address 0x12, Reset 0x00 */ +#define ADUX1001_CAL_CYCLES(x) (((x) & 0x1F) << 1) +#define ADUX1001_CALIBRATE_EN (1 << 0) + +/* STATUS, Register Address 0x1C, Reset 0x20 */ +#define ADUX1001_FIFO_EMPTY (1 << 5) +#define ADUX1001_FIFO_FULL (1 << 4) +#define ADUX1001_LRA_FAULT (1 << 3) +#define ADUX1001_UVLO_FLAG (1 << 2) +#define ADUX1001_OCLO_FLAG (1 << 1) +#define ADUX1001_BUSY (1 << 0) + +/* VERSION, Register Address 0x1D, Reset 0x00 */ +#define ADUX1001_VERSION_MASK 0xF + +#define ADUX1001_POWERUP_DELAY_US 100 /* 100us */ +#define ADUX1001_MAX_PWM_PERIODE_NS 50000 +#define ADUX1001_MIN_PWM_PERIODE_NS 5000 + +struct adux1001_chip { + struct i2c_client *client; + struct adux1001_vibra_platform_data *pdata; + struct input_dev *input; + struct pwm_device *pwm; + struct work_struct work; + bool use_shutdown; + int period; + int duty; +}; + +static void adux1001_actuator_set(struct adux1001_chip *chip, unsigned duty) +{ + int ret; + + if (duty) { + ret = pwm_config(chip->pwm, + chip->duty, chip->period); + if (ret < 0) + dev_err(&chip->client->dev, + "pwm_config failed: duty %d period %d\n", + chip->duty, chip->period); + + ret = pwm_enable(chip->pwm); + if (ret < 0) + dev_err(&chip->client->dev, "pwm_enable failed\n"); + } else { + pwm_disable(chip->pwm); + } +} + +static int adux1001_calibrate(struct i2c_client *client) +{ + struct adux1001_chip *chip = i2c_get_clientdata(client); + const struct adux1001_vibra_platform_data *pdata = chip->pdata; + unsigned val, delay_ms; + int ret; + + val = ADUX1001_CAL_CYCLES(pdata->calibration_cycles) | + ADUX1001_CALIBRATE_EN; + + ret = i2c_smbus_write_byte_data(client, ADUX1001_CALIBRATE, val); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + /* assume default resonant period value (6.55ms) */ + delay_ms = pdata->calibration_cycles * 7; + + do { + msleep(delay_ms); + val = i2c_smbus_read_byte_data(client, ADUX1001_STATUS); + delay_ms = 4; + } while (val & ADUX1001_BUSY); + + if (val & (ADUX1001_LRA_FAULT | + ADUX1001_UVLO_FLAG | + ADUX1001_OCLO_FLAG)) { + dev_err(&client->dev, "LRA Fault\n"); + return -EFAULT; + } + + return 0; +} + +static int adux1001_read_calibdata(struct i2c_client *client, + struct adux1001_calib_data *calib_data) +{ + unsigned char *data = "" char *) calib_data; + int reg, ret; + + for (reg = ADUX1001_SLRA_CAL0; reg <= ADUX1001_SLRA_CAL5; reg++) { + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + data[reg - ADUX1001_SLRA_CAL0] = ret; + } + + return 0; +} + +static int adux1001_write_calibdata(struct i2c_client *client, + struct adux1001_calib_data *calib_data) +{ + unsigned char *data = "" char *) calib_data; + int reg, ret; + + for (reg = ADUX1001_SLRA_CAL0; reg <= ADUX1001_SLRA_CAL5; reg++) { + ret = i2c_smbus_write_byte_data(client, reg, + data[reg - ADUX1001_SLRA_CAL0]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int adux1001_setup(struct i2c_client *client) +{ + struct adux1001_chip *chip = i2c_get_clientdata(client); + const struct adux1001_vibra_platform_data *pdata = chip->pdata; + struct adux1001_calib_data calib_data; + int ret; + unsigned val, config; + + /* Get the adux1001 out of shutdown mode */ + if (chip->use_shutdown) { + gpio_set_value_cansleep(chip->pdata->gpio_shutdown, 1); + udelay(ADUX1001_POWERUP_DELAY_US); + } + + ret = i2c_smbus_write_byte_data(client, ADUX1001_CONTROL, + ADUX1001_RESET); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + + val = i2c_smbus_read_byte_data(client, ADUX1001_VERSION); + if ((val & ADUX1001_VERSION_MASK) <= 1) { + /* Workaround for Silicon Rev.0 */ + i2c_smbus_write_byte_data(client, 0x19, 0x47); + i2c_smbus_write_byte_data(client, 0x1A, 0xC0); + i2c_smbus_write_byte_data(client, 0x1B, 0x68); + } + + config = ADUX1001_UVLO_EN(!pdata->uvlo_dis) | + ADUX1001_OCLO_EN(!pdata->oclo_dis) | + ADUX1001_PWM_PRIORITY(pdata->pwm_priority) | + ADUX1001_INTERFACE_MODE(1) | + ADUX1001_ACTUATOR_SEL(pdata->actuator_is_erm); + + ret = i2c_smbus_write_byte_data(client, ADUX1001_CONFIG, config); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + + val = ADUX1001_LOOP_GAIN(pdata->loop_gain) | + ADUX1001_OUTPUT_CURRENT(pdata->max_output_current); + + ret = i2c_smbus_write_byte_data(client, ADUX1001_OUTPUT_CONFIG, val); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + + val = ADUX1001_LRA_OUTPUT_UNIT(pdata->lra_output_unit_1ms) | + ADUX1001_OUTPUT_RATE_SEL(pdata->output_rate); + + ret = i2c_smbus_write_byte_data(client, ADUX1001_OUTPUT_RATE, val); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + + if (pdata->get_calibdata) { + ret = pdata->get_calibdata(client, &calib_data); + if (ret < 0) { + /* First time calibration */ + adux1001_calibrate(client); + + if (pdata->store_calibdata) { + /* Get calibration values from registers */ + adux1001_read_calibdata(client, &calib_data); + + /* Have something else take core of storing + * them to none-volatile memory + */ + ret = pdata->store_calibdata(client, + &calib_data); + if (ret < 0) + dev_warn(&client->dev, + "failed to store_calibdata\n"); + } + } else { + /* get_calibdata returned success, + * use saved calibration data + */ + ret = adux1001_write_calibdata(client, &calib_data); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + } + } else { + adux1001_calibrate(client); + } + + /* Populate arbitrary waveform buffers, if available. + * Might be used in conjunction with the + * Low Latency Response (LLR) feature of the chip + */ + if (pdata->arb_wform_buffer_array && + (pdata->arb_wform_buffer_array_size > 0 && + pdata->arb_wform_buffer_array_size <= 10)) { + for (val = 0; val < pdata->arb_wform_buffer_array_size; val++) + ret = i2c_smbus_write_byte_data(client, + ADUX1001_ARB0 + val, + pdata->arb_wform_buffer_array[val]); + if (ret < 0) { + dev_err(&client->dev, + "i2c write failed\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, ADUX1001_ARB_SEL, + pdata->arb_wform_buffer_array_size); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + } + + config &= ~ADUX1001_INTERFACE_MODE(1); + config |= ADUX1001_INTERFACE_MODE(pdata->mode_sel & 1); + + ret = i2c_smbus_write_byte_data(client, ADUX1001_CONFIG, config); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + + return 0; +} + +static void adux1001_worker(struct work_struct *work) +{ + struct adux1001_chip *chip; + + chip = container_of(work, struct adux1001_chip, work); + adux1001_actuator_set(chip, chip->duty); +} + +static int adux1001_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct adux1001_chip *chip = input_get_drvdata(dev); + + chip->duty = (chip->period * effect->u.rumble.strong_magnitude) / + USHRT_MAX; + if (!chip->duty) + chip->duty = (chip->period * + (effect->u.rumble.weak_magnitude >> 1)) / + USHRT_MAX; + + schedule_work(&chip->work); + return 0; +} + +static int __devinit adux1001_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adux1001_chip *chip; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "i2c byte data not supported\n"); + return -EIO; + } + + if (!client->dev.platform_data) { + dev_err(&client->dev, "pdata is not available\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct adux1001_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->pdata = client->dev.platform_data; + chip->client = client; + i2c_set_clientdata(client, chip); + + if (chip->pdata->setup) { + ret = chip->pdata->setup(client, true); + if (ret < 0) { + dev_err(&client->dev, "setup callback failed!\n"); + goto err_free_mem; + } + } + + ret = gpio_is_valid(chip->pdata->gpio_shutdown); + if (ret) { + ret = gpio_request(chip->pdata->gpio_shutdown, id->name); + if (ret) { + dev_err(&client->dev, "gpio %d request failed\n", + chip->pdata->gpio_shutdown); + goto err_setup; + } + chip->use_shutdown = true; + + ret = gpio_direction_output(chip->pdata->gpio_shutdown, 0); + if (ret) { + dev_err(&client->dev, "gpio %d set direction failed\n", + chip->pdata->gpio_shutdown); + goto err_release_gpio; + } + } + + ret = adux1001_setup(chip->client); + if (ret < 0) { + dev_err(&client->dev, "device setup failed %d\n", ret); + goto err_release_gpio; + } + + /* Skip input device and pwm registration. Something else, such as the + * Immersion TouchSense kernel module controls the ADUX1001 PWM. + */ + if (chip->pdata->mode_sel == ADUX1001_PWM_MODE_EXT_ONLY) + return 0; + + + chip->pwm = pwm_request(chip->pdata->pwm_id, id->name); + if (IS_ERR(chip->pwm)) { + dev_err(&client->dev, "pwm request failed\n"); + ret = PTR_ERR(chip->pwm); + goto err_release_gpio; + } + + if (chip->pdata->pwm_periode_ns > ADUX1001_MAX_PWM_PERIODE_NS || + chip->pdata->pwm_periode_ns < ADUX1001_MIN_PWM_PERIODE_NS) { + dev_warn(&client->dev, "pwm_periode_ns out of spec\n"); + chip->period = 40000; /* 25kHz */ + } else { + chip->period = chip->pdata->pwm_periode_ns; + } + + INIT_WORK(&chip->work, adux1001_worker); + + chip->input = input_allocate_device(); + if (!chip->input) { + dev_err(&client->dev, "input device allocation failed\n"); + ret = -ENOMEM; + goto err_release_pwm ; + } + + input_set_drvdata(chip->input, chip); + chip->input->name = id->name; + chip->input->dev.parent = &client->dev; + chip->input->id.bustype = BUS_I2C; + + input_set_capability(chip->input, EV_FF, FF_RUMBLE); + + ret = input_ff_create_memless(chip->input, NULL, + adux1001_play_effect); + if (ret < 0) { + dev_err(&client->dev, "failed to create force feedback\n"); + goto err_free_idev; + } + + ret = input_register_device(chip->input); + if (ret < 0) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_ff_destroy; + } + + return 0; + +err_ff_destroy: + input_ff_destroy(chip->input); +err_free_idev: + input_free_device(chip->input); +err_release_pwm: + pwm_free(chip->pwm); +err_release_gpio: + if (chip->use_shutdown) + gpio_free(chip->pdata->gpio_shutdown); +err_setup: + if (chip->pdata->setup) + chip->pdata->setup(client, true); +err_free_mem: + kfree(chip); + + return ret; +} + +static int __devexit adux1001_remove(struct i2c_client *client) +{ + struct adux1001_chip *chip = i2c_get_clientdata(client); + + if (chip->pdata->mode_sel != ADUX1001_PWM_MODE_EXT_ONLY) { + cancel_work_sync(&chip->work); + input_unregister_device(chip->input); + adux1001_actuator_set(chip, false); + pwm_free(chip->pwm); + } + + i2c_smbus_write_byte_data(client, ADUX1001_CONTROL, + ADUX1001_RESET); + + if (chip->use_shutdown) + gpio_free(chip->pdata->gpio_shutdown); + + if (chip->pdata->setup) + chip->pdata->setup(client, false); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int adux1001_suspend(struct device *dev) +{ + struct adux1001_chip *chip = dev_get_drvdata(dev); + int ret; + + if (chip->pdata->mode_sel != ADUX1001_PWM_MODE_EXT_ONLY) { + cancel_work_sync(&chip->work); + adux1001_actuator_set(chip, 0); + } + + if (chip->use_shutdown) + gpio_set_value_cansleep(chip->pdata->gpio_shutdown, 0); + + if (chip->pdata->setup) { + ret = chip->pdata->setup(chip->client, false); + if (ret) { + dev_err(dev, "setup failed\n"); + return ret; + } + } + return 0; +} + +static int adux1001_resume(struct device *dev) +{ + struct adux1001_chip *chip = dev_get_drvdata(dev); + int ret; + + if (chip->pdata->setup) { + ret = chip->pdata->setup(chip->client, true); + if (ret) { + dev_err(dev, "setup failed\n"); + return ret; + } + } + + adux1001_setup(chip->client); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(adux1001_pm_ops, adux1001_suspend, adux1001_resume); + +static const struct i2c_device_id adux1001_id[] = { + {"adux1001", 0}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, adux1001_id); + +static struct i2c_driver adux1001_driver = { + .driver = { + .name = "adux1001", + .pm = &adux1001_pm_ops, + .owner = THIS_MODULE, + }, + .probe = adux1001_probe, + .remove = __devexit_p(adux1001_remove), + .id_table = adux1001_id, +}; + +static int __init adux1001_init(void) +{ + return i2c_add_driver(&adux1001_driver); +} +module_init(adux1001_init); + +static void __exit adux1001_exit(void) +{ + i2c_del_driver(&adux1001_driver); +} +module_exit(adux1001_exit); + +MODULE_AUTHOR("Michael Hennerich <[email protected]>"); +MODULE_DESCRIPTION("ADUX1001 Smart Electromagnetic Actuator Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/input/adux1001.h b/include/linux/input/adux1001.h new file mode 100644 index 0000000..33ba10d --- /dev/null +++ b/include/linux/input/adux1001.h @@ -0,0 +1,100 @@ +/* + * ADUX1001 Smart Electromagnetic Actuator Haptic Driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __LINUX_ADUX1001_H +#define __LINUX_ADUX1001_H + +struct adux1001_calib_data { + unsigned char slra_cal0; + unsigned char slra_cal1; + unsigned char slra_cal2; + unsigned char slra_cal3; + unsigned char slra_cal4; + unsigned char slra_cal5; +} __packed; + +struct adux1001_vibra_platform_data { + /* Actuator Selection: true = ERM, false = LRA */ + bool actuator_is_erm; + + /* true = Under voltage lockout circuitry disable */ + bool uvlo_dis; + + /* true = Over current lockout circuitry disable */ + bool oclo_dis; + + /* ADUX1001_PWM_MODE_EXT_ONLY: + * Some other kernel module such as the Immersion TouchSense controls + * the main PWM, no input force feedback device is created + */ +#define ADUX1001_PWM_MODE_EXT_ONLY 4 + + /* ADUX1001_PWM_MODE_INT_AND_EXT: + * An input force feedback device is created and controls + * one system PWM, another kernel module may control the second + * PWM, priority can be set by pwm_mode_sel + */ +#define ADUX1001_PWM_MODE_INT_AND_EXT 2 + + /* ADUX1001_I2C_MODE: + * TBD, using effects.type FF_PERIODIC in combination with + * periodic.waveform FF_CUSTOM we could play custom waveforms + * using I2C streaming mode + */ +#define ADUX1001_I2C_MODE 1 + + unsigned char mode_sel; + + /* PWM Selection/Priority */ +#define ADUX1001_PWMPRIO_PWM2EN_PWM1DIS 3 +#define ADUX1001_PWMPRIO_PWM1EN_PWM2DIS 2 +#define ADUX1001_PWMPRIO_PWM2HI_PWM1LO 1 +#define ADUX1001_PWMPRIO_PWM1HI_PWM2LO 0 + + unsigned char pwm_priority; /* 0..3 */ + + unsigned char loop_gain; /* 2..15 */ + + /* 0..8, from 50mA - 150mA in 12.5mA steps */ + unsigned char max_output_current; + + bool lra_output_unit_1ms; /* true = 1ms, false = LRA + * output unit is LRA Resonance + * Period/2 */ + + unsigned char output_rate; /* Rate = lra_output_unit * + * output_rate, 1..127 */ + + unsigned char calibration_cycles; /* 1..31 */ + + /* initial arbitrary waveform buffer */ + unsigned char *arb_wform_buffer_array; + unsigned char arb_wform_buffer_array_size; /* 1..10 */ + + /* system specific PWM identifier */ + int pwm_id; + /* PWM period in ns, range 5000..50000 (20-200kHz) */ + unsigned short pwm_periode_ns; + + /* system specific GPIO to control the SHUTDOWN pin, + * if not used set to -1 + */ + int gpio_shutdown; + + /* system specific setup callback */ + int (*setup)(struct i2c_client *client, + unsigned state); + /* get calibration callback function */ + int (*get_calibdata)(struct i2c_client *client, + struct adux1001_calib_data *); + /* store calibration callback function */ + int (*store_calibdata)(struct i2c_client *client, + struct adux1001_calib_data *); +}; + +#endif /* __LINUX_ADUX1001_H */
_______________________________________________ Linux-kernel-commits mailing list [email protected] https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits
