Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Mike Rapoport | 2247 | 94.41% | 1 | 5.56% |
Mark Brown | 40 | 1.68% | 1 | 5.56% |
Javier Martinez Canillas | 32 | 1.34% | 1 | 5.56% |
Sven Van Asbroeck | 24 | 1.01% | 1 | 5.56% |
Aniroop Mathur | 8 | 0.34% | 1 | 5.56% |
Dmitry Torokhov | 8 | 0.34% | 2 | 11.11% |
Jonathan Cameron | 4 | 0.17% | 1 | 5.56% |
Rusty Russell | 3 | 0.13% | 1 | 5.56% |
Uwe Kleine-König | 3 | 0.13% | 3 | 16.67% |
Shailendra Verma | 3 | 0.13% | 1 | 5.56% |
Linus Torvalds (pre-git) | 2 | 0.08% | 1 | 5.56% |
Erick Archer | 2 | 0.08% | 1 | 5.56% |
Axel Lin | 2 | 0.08% | 1 | 5.56% |
Linus Torvalds | 1 | 0.04% | 1 | 5.56% |
Lucas De Marchi | 1 | 0.04% | 1 | 5.56% |
Total | 2380 | 18 |
/* * Synaptics touchpad with I2C interface * * Copyright (C) 2009 Compulab, Ltd. * Mike Rapoport <mike@compulab.co.il> * Igor Grinberg <grinberg@compulab.co.il> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. */ #include <linux/module.h> #include <linux/i2c.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/slab.h> #include <linux/pm.h> #define DRIVER_NAME "synaptics_i2c" /* maximum product id is 15 characters */ #define PRODUCT_ID_LENGTH 15 #define REGISTER_LENGTH 8 /* * after soft reset, we should wait for 1 ms * before the device becomes operational */ #define SOFT_RESET_DELAY_US 3000 /* and after hard reset, we should wait for max 500ms */ #define HARD_RESET_DELAY_MS 500 /* Registers by SMBus address */ #define PAGE_SEL_REG 0xff #define DEVICE_STATUS_REG 0x09 /* Registers by RMI address */ #define DEV_CONTROL_REG 0x0000 #define INTERRUPT_EN_REG 0x0001 #define ERR_STAT_REG 0x0002 #define INT_REQ_STAT_REG 0x0003 #define DEV_COMMAND_REG 0x0004 #define RMI_PROT_VER_REG 0x0200 #define MANUFACT_ID_REG 0x0201 #define PHYS_INT_VER_REG 0x0202 #define PROD_PROPERTY_REG 0x0203 #define INFO_QUERY_REG0 0x0204 #define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1) #define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2) #define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3) #define PRODUCT_ID_REG0 0x0210 #define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1) #define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2) #define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3) #define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4) #define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5) #define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6) #define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7) #define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8) #define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9) #define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10) #define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11) #define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12) #define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13) #define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14) #define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15) #define DATA_REG0 0x0400 #define ABS_PRESSURE_REG 0x0401 #define ABS_MSB_X_REG 0x0402 #define ABS_LSB_X_REG (ABS_MSB_X_REG + 1) #define ABS_MSB_Y_REG 0x0404 #define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1) #define REL_X_REG 0x0406 #define REL_Y_REG 0x0407 #define DEV_QUERY_REG0 0x1000 #define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1) #define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2) #define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3) #define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4) #define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5) #define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6) #define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7) #define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8) #define GENERAL_2D_CONTROL_REG 0x1041 #define SENSOR_SENSITIVITY_REG 0x1044 #define SENS_MAX_POS_MSB_REG 0x1046 #define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1) /* Register bits */ /* Device Control Register Bits */ #define REPORT_RATE_1ST_BIT 6 /* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */ #define F10_ABS_INT_ENA 0 #define F10_REL_INT_ENA 1 #define F20_INT_ENA 2 /* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */ #define F10_ABS_INT_REQ 0 #define F10_REL_INT_REQ 1 #define F20_INT_REQ 2 /* Device Status Register Bits (DEVICE_STATUS_REG) */ #define STAT_CONFIGURED 6 #define STAT_ERROR 7 /* Device Command Register Bits (DEV_COMMAND_REG) */ #define RESET_COMMAND 0x01 #define REZERO_COMMAND 0x02 /* Data Register 0 Bits (DATA_REG0) */ #define GESTURE 3 /* Device Query Registers Bits */ /* DEV_QUERY_REG3 */ #define HAS_PALM_DETECT 1 #define HAS_MULTI_FING 2 #define HAS_SCROLLER 4 #define HAS_2D_SCROLL 5 /* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */ #define NO_DECELERATION 1 #define REDUCE_REPORTING 3 #define NO_FILTER 5 /* Function Masks */ /* Device Control Register Masks (DEV_CONTROL_REG) */ #define REPORT_RATE_MSK 0xc0 #define SLEEP_MODE_MSK 0x07 /* Device Sleep Modes */ #define FULL_AWAKE 0x0 #define NORMAL_OP 0x1 #define LOW_PWR_OP 0x2 #define VERY_LOW_PWR_OP 0x3 #define SENS_SLEEP 0x4 #define SLEEP_MOD 0x5 #define DEEP_SLEEP 0x6 #define HIBERNATE 0x7 /* Interrupt Register Mask */ /* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */ #define INT_ENA_REQ_MSK 0x07 #define INT_ENA_ABS_MSK 0x01 #define INT_ENA_REL_MSK 0x02 #define INT_ENA_F20_MSK 0x04 /* Device Status Register Masks (DEVICE_STATUS_REG) */ #define CONFIGURED_MSK 0x40 #define ERROR_MSK 0x80 /* Data Register 0 Masks */ #define FINGER_WIDTH_MSK 0xf0 #define GESTURE_MSK 0x08 #define SENSOR_STATUS_MSK 0x07 /* * MSB Position Register Masks * ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG | * DEV_QUERY_REG3 | DEV_QUERY_REG5 */ #define MSB_POSITION_MSK 0x1f /* Device Query Registers Masks */ /* DEV_QUERY_REG2 */ #define NUM_EXTRA_POS_MSK 0x07 /* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */ #define THREAD_IRQ_SLEEP_SECS 2 #define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC) /* * When in Polling mode and no data received for NO_DATA_THRES msecs * reduce the polling rate to NO_DATA_SLEEP_MSECS */ #define NO_DATA_THRES (MSEC_PER_SEC) #define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) /* Control touchpad's No Deceleration option */ static bool no_decel = true; module_param(no_decel, bool, 0644); MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); /* Control touchpad's Reduced Reporting option */ static bool reduce_report; module_param(reduce_report, bool, 0644); MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)"); /* Control touchpad's No Filter option */ static bool no_filter; module_param(no_filter, bool, 0644); MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); /* * touchpad Attention line is Active Low and Open Drain, * therefore should be connected to pulled up line * and the irq configuration should be set to Falling Edge Trigger */ /* Control IRQ / Polling option */ static bool polling_req; module_param(polling_req, bool, 0444); MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); /* Control Polling Rate */ static int scan_rate = 80; module_param(scan_rate, int, 0644); MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80"); /* The main device structure */ struct synaptics_i2c { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; int no_data_count; int no_decel_param; int reduce_report_param; int no_filter_param; int scan_rate_param; int scan_ms; }; static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate) { touch->scan_ms = MSEC_PER_SEC / scan_rate; touch->scan_rate_param = scan_rate; } /* * Driver's initial design makes no race condition possible on i2c bus, * so there is no need in any locking. * Keep it in mind, while playing with the code. */ static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg) { int ret; ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); if (ret == 0) ret = i2c_smbus_read_byte_data(client, reg & 0xff); return ret; } static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val) { int ret; ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); if (ret == 0) ret = i2c_smbus_write_byte_data(client, reg & 0xff, val); return ret; } static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) { int ret; ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8); if (ret == 0) ret = i2c_smbus_read_word_data(client, reg & 0xff); return ret; } static int synaptics_i2c_config(struct i2c_client *client) { int ret, control; u8 int_en; /* set Report Rate to Device Highest (>=80) and Sleep to normal */ ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1); if (ret) return ret; /* set Interrupt Disable to Func20 / Enable to Func10) */ int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK; ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en); if (ret) return ret; control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG); /* No Deceleration */ control |= no_decel ? 1 << NO_DECELERATION : 0; /* Reduced Reporting */ control |= reduce_report ? 1 << REDUCE_REPORTING : 0; /* No Filter */ control |= no_filter ? 1 << NO_FILTER : 0; ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control); if (ret) return ret; return 0; } static int synaptics_i2c_reset_config(struct i2c_client *client) { int ret; /* Reset the Touchpad */ ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND); if (ret) { dev_err(&client->dev, "Unable to reset device\n"); } else { usleep_range(SOFT_RESET_DELAY_US, SOFT_RESET_DELAY_US + 100); ret = synaptics_i2c_config(client); if (ret) dev_err(&client->dev, "Unable to config device\n"); } return ret; } static int synaptics_i2c_check_error(struct i2c_client *client) { int status, ret = 0; status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) & (CONFIGURED_MSK | ERROR_MSK); if (status != CONFIGURED_MSK) ret = synaptics_i2c_reset_config(client); return ret; } static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) { struct input_dev *input = touch->input; int xy_delta, gesture; s32 data; s8 x_delta, y_delta; /* Deal with spontaneous resets and errors */ if (synaptics_i2c_check_error(touch->client)) return false; /* Get Gesture Bit */ data = synaptics_i2c_reg_get(touch->client, DATA_REG0); gesture = (data >> GESTURE) & 0x1; /* * Get Relative axes. we have to get them in one shot, * so we get 2 bytes starting from REL_X_REG. */ xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff; /* Separate X from Y */ x_delta = xy_delta & 0xff; y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff; /* Report the button event */ input_report_key(input, BTN_LEFT, gesture); /* Report the deltas */ input_report_rel(input, REL_X, x_delta); input_report_rel(input, REL_Y, -y_delta); input_sync(input); return xy_delta || gesture; } static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) { struct synaptics_i2c *touch = dev_id; mod_delayed_work(system_wq, &touch->dwork, 0); return IRQ_HANDLED; } static void synaptics_i2c_check_params(struct synaptics_i2c *touch) { bool reset = false; if (scan_rate != touch->scan_rate_param) set_scan_rate(touch, scan_rate); if (no_decel != touch->no_decel_param) { touch->no_decel_param = no_decel; reset = true; } if (no_filter != touch->no_filter_param) { touch->no_filter_param = no_filter; reset = true; } if (reduce_report != touch->reduce_report_param) { touch->reduce_report_param = reduce_report; reset = true; } if (reset) synaptics_i2c_reset_config(touch->client); } /* Control the Device polling rate / Work Handler sleep time */ static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, bool have_data) { unsigned long delay, nodata_count_thres; if (polling_req) { delay = touch->scan_ms; if (have_data) { touch->no_data_count = 0; } else { nodata_count_thres = NO_DATA_THRES / touch->scan_ms; if (touch->no_data_count < nodata_count_thres) touch->no_data_count++; else delay = NO_DATA_SLEEP_MSECS; } return msecs_to_jiffies(delay); } else { delay = msecs_to_jiffies(THREAD_IRQ_SLEEP_MSECS); return round_jiffies_relative(delay); } } /* Work Handler */ static void synaptics_i2c_work_handler(struct work_struct *work) { bool have_data; struct synaptics_i2c *touch = container_of(work, struct synaptics_i2c, dwork.work); unsigned long delay; synaptics_i2c_check_params(touch); have_data = synaptics_i2c_get_input(touch); delay = synaptics_i2c_adjust_delay(touch, have_data); /* * While interrupt driven, there is no real need to poll the device. * But touchpads are very sensitive, so there could be errors * related to physical environment and the attention line isn't * necessarily asserted. In such case we can lose the touchpad. * We poll the device once in THREAD_IRQ_SLEEP_SECS and * if error is detected, we try to reset and reconfigure the touchpad. */ mod_delayed_work(system_wq, &touch->dwork, delay); } static int synaptics_i2c_open(struct input_dev *input) { struct synaptics_i2c *touch = input_get_drvdata(input); int ret; ret = synaptics_i2c_reset_config(touch->client); if (ret) return ret; if (polling_req) mod_delayed_work(system_wq, &touch->dwork, msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; } static void synaptics_i2c_close(struct input_dev *input) { struct synaptics_i2c *touch = input_get_drvdata(input); if (!polling_req) synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); cancel_delayed_work_sync(&touch->dwork); /* Save some power */ synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); } static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) { struct input_dev *input = touch->input; input->name = touch->client->name; input->phys = touch->client->adapter->name; input->id.bustype = BUS_I2C; input->id.version = synaptics_i2c_word_get(touch->client, INFO_QUERY_REG0); input->dev.parent = &touch->client->dev; input->open = synaptics_i2c_open; input->close = synaptics_i2c_close; input_set_drvdata(input, touch); /* Register the device as mouse */ __set_bit(EV_REL, input->evbit); __set_bit(REL_X, input->relbit); __set_bit(REL_Y, input->relbit); /* Register device's buttons and keys */ __set_bit(EV_KEY, input->evbit); __set_bit(BTN_LEFT, input->keybit); } static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) { struct synaptics_i2c *touch; touch = kzalloc(sizeof(*touch), GFP_KERNEL); if (!touch) return NULL; touch->client = client; touch->no_decel_param = no_decel; touch->scan_rate_param = scan_rate; set_scan_rate(touch, scan_rate); INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); return touch; } static int synaptics_i2c_probe(struct i2c_client *client) { int ret; struct synaptics_i2c *touch; touch = synaptics_i2c_touch_create(client); if (!touch) return -ENOMEM; ret = synaptics_i2c_reset_config(client); if (ret) goto err_mem_free; if (client->irq < 1) polling_req = true; touch->input = input_allocate_device(); if (!touch->input) { ret = -ENOMEM; goto err_mem_free; } synaptics_i2c_set_input_params(touch); if (!polling_req) { dev_dbg(&touch->client->dev, "Requesting IRQ: %d\n", touch->client->irq); ret = request_irq(touch->client->irq, synaptics_i2c_irq, IRQ_TYPE_EDGE_FALLING, DRIVER_NAME, touch); if (ret) { dev_warn(&touch->client->dev, "IRQ request failed: %d, " "falling back to polling\n", ret); polling_req = true; synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); } } if (polling_req) dev_dbg(&touch->client->dev, "Using polling at rate: %d times/sec\n", scan_rate); /* Register the device in input subsystem */ ret = input_register_device(touch->input); if (ret) { dev_err(&client->dev, "Input device register failed: %d\n", ret); goto err_input_free; } i2c_set_clientdata(client, touch); return 0; err_input_free: input_free_device(touch->input); err_mem_free: kfree(touch); return ret; } static void synaptics_i2c_remove(struct i2c_client *client) { struct synaptics_i2c *touch = i2c_get_clientdata(client); if (!polling_req) free_irq(client->irq, touch); input_unregister_device(touch->input); kfree(touch); } static int synaptics_i2c_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct synaptics_i2c *touch = i2c_get_clientdata(client); cancel_delayed_work_sync(&touch->dwork); /* Save some power */ synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP); return 0; } static int synaptics_i2c_resume(struct device *dev) { int ret; struct i2c_client *client = to_i2c_client(dev); struct synaptics_i2c *touch = i2c_get_clientdata(client); ret = synaptics_i2c_reset_config(client); if (ret) return ret; mod_delayed_work(system_wq, &touch->dwork, msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend, synaptics_i2c_resume); static const struct i2c_device_id synaptics_i2c_id_table[] = { { "synaptics_i2c" }, { } }; MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); #ifdef CONFIG_OF static const struct of_device_id synaptics_i2c_of_match[] = { { .compatible = "synaptics,synaptics_i2c", }, { }, }; MODULE_DEVICE_TABLE(of, synaptics_i2c_of_match); #endif static struct i2c_driver synaptics_i2c_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(synaptics_i2c_of_match), .pm = pm_sleep_ptr(&synaptics_i2c_pm), }, .probe = synaptics_i2c_probe, .remove = synaptics_i2c_remove, .id_table = synaptics_i2c_id_table, }; module_i2c_driver(synaptics_i2c_driver); MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1