| /* drivers/input/misc/ots_pat9126/pat9126_linux_driver.c |
| * |
| * Copyright (c) 2016, The Linux Foundation. All rights reserved. |
| * |
| */ |
| |
| #include <linux/input.h> |
| #include <linux/pm.h> |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/of_gpio.h> |
| #include <linux/delay.h> |
| #include <linux/regulator/consumer.h> |
| #include "pat9126.h" |
| #include <linux/fs.h> |
| #include <linux/file.h> |
| #include <linux/mm.h> |
| #include <asm/uaccess.h> |
| #include <linux/notifier.h> |
| #include <linux/reboot.h> |
| #include <linux/time.h> |
| #include <linux/rtc.h> |
| #include <linux/string.h> |
| #include <linux/soc/qcom/panel_event_notifier.h> |
| |
| #define WAKE_SENSITIVITY 1 |
| #define PIXART_PAT9126_SLOP 13 |
| #define SENSITIVITY_LEN 4 /* range "0" - "255" */ |
| #define DISPLAY_BLANKED 0 |
| #define DISPLAY_UNBLANKED 1 |
| |
| struct pixart_pat9126_data { |
| struct i2c_client *client; |
| struct input_dev *input; |
| int irq; |
| u32 irq_flags; |
| struct device *pat9126_device; |
| u32 press_keycode; |
| bool press_en; |
| bool inverse_x; |
| bool inverse_y; |
| u32 vdd_active_load; |
| u32 vld_active_load; |
| u32 vdd_sleep_load; |
| u32 vld_sleep_load; |
| int state; |
| int display_mode; |
| u8 crown_sensitivity; |
| u8 current_sensitivity; |
| u8 slop; |
| bool wake_handled; |
| struct work_struct work; |
| struct workqueue_struct *workqueue; |
| struct delayed_work polling_work; |
| struct delayed_work resume_work; |
| struct drm_panel *active_panel; |
| void *notifier_cookie; |
| struct mutex mtx; |
| struct regulator *vdd_supply; |
| struct regulator *vld_supply; |
| }; |
| |
| #define PAT9126_STATE_SUSPEND 0 |
| #define PAT9126_STATE_ON 1 |
| #define PAT9126_STATE_RESUMING 2 |
| |
| struct rw_reg_info { |
| char flag; /*R/W char*/ |
| long w_addr; |
| long r_addr; |
| long r_data; |
| }; |
| |
| struct rw_reg_info pat9126_reg_info; |
| |
| /* Declaration of suspend and resume functions */ |
| static void pat9126_drm_panel_notifier_callback(enum panel_event_notifier_tag tag, |
| struct panel_event_notification *notification, void *data); |
| |
| static int pat9126_display_suspend(struct device *dev); |
| static int pat9126_display_resume(struct device *dev); |
| |
| static int pat9126_set_loads_active(struct pixart_pat9126_data *data) |
| { |
| int ret; |
| |
| ret = regulator_set_load(data->vdd_supply, data->vdd_active_load); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to set vdd regulator load\n"); |
| return ret; |
| } |
| |
| ret = regulator_set_load(data->vld_supply, data->vld_active_load); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to set vld regulator load\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int pat9126_set_loads_sleep(struct pixart_pat9126_data *data) |
| { |
| int ret; |
| |
| ret = regulator_set_load(data->vdd_supply, data->vdd_sleep_load); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to set vdd regulator load\n"); |
| return ret; |
| } |
| |
| ret = regulator_set_load(data->vld_supply, data->vld_sleep_load); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to set vld regulator load\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int pat9126_enable_regulators(struct pixart_pat9126_data *data) |
| { |
| int ret; |
| |
| ret = regulator_enable(data->vdd_supply); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to enable vdd regulator\n"); |
| return ret; |
| } |
| |
| ret = regulator_enable(data->vld_supply); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to enable vld regulator\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static void pat9126_disable_regulators(struct pixart_pat9126_data *data) |
| { |
| int ret; |
| |
| ret = regulator_disable(data->vld_supply); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to disable vld regulator\n"); |
| } |
| |
| ret = regulator_disable(data->vdd_supply); |
| if (ret) { |
| pr_err("[PAT9126]: Failed to disable vdd regulator\n"); |
| } |
| } |
| |
| static int pat9126_write(struct i2c_client *client, u8 addr, u8 data) |
| { |
| u8 buf[BUF_SIZE]; |
| struct device *dev = &client->dev; |
| |
| buf[0] = addr; |
| buf[1] = data; |
| |
| /* Returns negative errno, or else the number of bytes written. */ |
| if (i2c_master_send(client, buf, BUF_SIZE) < 0) { |
| dev_err(dev, "%s Failed: writing to reg 0x%x\n", __func__, addr); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int pat9126_read(struct i2c_client *client, u8 addr, u8 *data) |
| { |
| struct device *dev = &client->dev; |
| struct i2c_msg msg[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .len = 1, |
| .buf = &addr, |
| }, { |
| .addr = client->addr, |
| .flags = 1, |
| .len = 1, |
| .buf = data, |
| } |
| }; |
| |
| if (i2c_transfer(client->adapter, msg, 2) != 2) { |
| dev_err(dev, "%s Failed: reading reg 0x%x\n", __func__, |
| addr); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int pat9126_write_verified(struct i2c_client *client, u8 address, u8 data) |
| { |
| int i, ret; |
| u8 read_value; |
| |
| for (i = 0; i < PAT9150_I2C_RETRY; i++) { |
| ret = pat9126_write(client, address, data); |
| if (ret < 0) |
| return ret; |
| ret = pat9126_read(client, address, &read_value); |
| if (ret < 0) |
| return ret; |
| |
| if (read_value == data) |
| return 0; |
| |
| msleep(1); |
| } |
| |
| return -EIO; |
| } |
| |
| void delay(int ms) |
| { |
| msleep(ms); |
| } |
| |
| int pat9126_sensor_init(struct i2c_client *client) |
| { |
| u8 id = 0; |
| int ret = 0; |
| |
| /* |
| * Read sensor_pid in address 0x00 to check if the |
| * serial link is valid, read value should be 0x31. |
| */ |
| pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &id); |
| if (id != PIXART_PAT9126_SENSOR_ID) { |
| pr_err("[PAT9126]: PID mismatch (id = 0x%x)", id); |
| return (-1); |
| } |
| |
| /* |
| * PAT9126 sensor recommended settings: |
| * switch to bank0, not allowed to perform pat9126_write_verified |
| */ |
| pat9126_write(client, PIXART_PAT9126_SELECT_BANK_REG, |
| PIXART_PAT9126_BANK0); |
| /* |
| * software reset (i.e. set bit7 to 1). |
| * It will reset to 0 automatically |
| * so perform OTS_RegWriteRead is not allowed. |
| */ |
| pat9126_write(client, PIXART_PAT9126_CONFIG_REG, |
| PIXART_PAT9126_RESET); |
| |
| /* delay 1ms */ |
| usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1); |
| |
| /* disable write protect */ |
| if (pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG, |
| PIXART_PAT9126_DISABLE_WRITE_PROTECT) < 0) |
| return (-1); |
| /* set X-axis resolution (depends on application) */ |
| if (pat9126_write_verified(client, PIXART_PAT9126_SET_CPI_RES_X_REG, |
| PIXART_PAT9126_CPI_RESOLUTION_X) < 0) |
| return (-1); |
| /* set Y-axis resolution (depends on application) */ |
| if (pat9126_write_verified(client, PIXART_PAT9126_SET_CPI_RES_Y_REG, |
| PIXART_PAT9126_CPI_RESOLUTION_Y) < 0) |
| return (-1); |
| /* set 12-bit X/Y data format (depends on application) */ |
| if (pat9126_write_verified(client, PIXART_PAT9126_ORIENTATION_REG, |
| PIXART_PAT9126_MOTION_DATA_LENGTH) < 0) |
| return (-1); |
| /* ONLY for VDD=VDDA=1.7~1.9V: for power saving */ |
| if (pat9126_write_verified(client, PIXART_PAT9126_VOLTAGE_SEGMENT_SEL_REG, |
| PIXART_PAT9126_LOW_VOLTAGE_SEGMENT) < 0) |
| return (-1); |
| |
| pat9126_write_verified(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, |
| PIXART_PAT9126_SENSOR_SET_MODE2); |
| |
| pat9126_write_verified(client, PIXART_PAT9126_AE_ENABLE, PIXART_PAT9126_AE_ENABLE_VAL); |
| pat9126_write_verified(client, PIXART_PAT9126_NY_MIN, PIXART_PAT9126_NY_MIN_VAL); |
| |
| ret = pat9126_enable_mot(client); |
| if (ret < 0) { |
| pr_err("[PAT9126]: Enable Motion FAIL."); |
| } |
| |
| /* enable write protect */ |
| pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG, |
| PIXART_PAT9126_ENABLE_WRITE_PROTECT); |
| return ret; |
| } |
| |
| int pat9126_disable_mot(struct i2c_client *client, int16_t detect_freq) |
| { |
| uint8_t tmp_1 = 0; |
| uint8_t sensor_pid = 0; |
| |
| pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &sensor_pid); |
| if (sensor_pid != PIXART_PAT9126_SENSOR_ID) { |
| return (-1); |
| } |
| |
| pat9126_write_verified(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, |
| PIXART_PAT9126_SENSOR_DEFAULT_MODE2); // Set motion to open drain |
| pat9126_read(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, &tmp_1); |
| pr_debug("[PAT9126]: Open drain mode motion: 0x%2x. \n", tmp_1); |
| |
| pat9126_write_verified(client, PIXART_PAT9126_SLEEP2_MODE_FREQ_REG, |
| detect_freq); |
| |
| pat9126_write(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, |
| PIXART_PAT9126_FORCE_ENTER_SLEEP2_MODE); |
| |
| return 0; |
| } |
| |
| int pat9126_enable_mot(struct i2c_client *client) |
| { |
| uint8_t tmp_1 = 0; |
| uint8_t sensor_pid = 0; |
| |
| pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &sensor_pid); |
| if (sensor_pid != PIXART_PAT9126_SENSOR_ID) { |
| return (-1); |
| } |
| |
| pat9126_write_verified(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, |
| PIXART_PAT9126_SENSOR_SET_MODE2); // Set motion to drive mode |
| pat9126_read(client, PIXART_PAT9126_SENSOR_MODE_SELECT_REG, &tmp_1); |
| pr_debug("[PAT9126]: Drive mode motion: 0x%2x. \n", tmp_1); |
| |
| /*Read Register for Pulling Up Motion IRQ*/ |
| pat9126_read(client, PIXART_PAT9126_MOTION_STATUS_REG, &tmp_1); |
| pat9126_read(client, PIXART_PAT9126_DELTA_X_LO_REG, &tmp_1); |
| pat9126_read(client, PIXART_PAT9126_DELTA_Y_LO_REG, &tmp_1); |
| pat9126_read(client, PIXART_PAT9126_DELTA_XY_HI_REG, &tmp_1); |
| |
| delay(1); /* delay 1ms */ |
| |
| pat9126_write_verified(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, |
| PIXART_PAT9126_WAKEUP_MODE); |
| pat9126_read(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, &tmp_1); |
| pr_debug("[PAT9126]: Enable sleep1 and disable sleep2 mode: 0x%2x. \n", tmp_1); |
| return 0; |
| } |
| |
| int pat9126_enable_mot_write_protected(struct i2c_client *client) { |
| int result; |
| |
| pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG, |
| PIXART_PAT9126_DISABLE_WRITE_PROTECT); |
| |
| result = pat9126_enable_mot(client); |
| |
| pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG, |
| PIXART_PAT9126_ENABLE_WRITE_PROTECT); |
| |
| if (result) { |
| pr_err("[PAT9126] %s: failed to enable sensor\n", __func__); |
| } |
| |
| return result; |
| } |
| |
| int pat9126_disable_mot_write_protected(struct i2c_client *client) { |
| int result; |
| |
| pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG, |
| PIXART_PAT9126_DISABLE_WRITE_PROTECT); |
| |
| result = pat9126_disable_mot(client, PIXART_PAT9126_SLEEP_MODE_FREQ_1024MS); |
| |
| pat9126_write_verified(client, PIXART_PAT9126_WRITE_PROTECT_REG, |
| PIXART_PAT9126_ENABLE_WRITE_PROTECT); |
| |
| if (result) { |
| pr_err("[PAT9126] %s: failed to disable sensor\n", __func__); |
| } |
| |
| return result; |
| } |
| |
| /* Read motion */ |
| void pat9126_read_motion(struct i2c_client *client, int16_t *dx16, int16_t *dy16) |
| { |
| /* Be sure to use unsigned values for the low bytes to |
| avoid unintended sign extension (b/234361169) */ |
| uint8_t deltaX_l = 0, deltaY_l = 0, deltaXY_h = 0; |
| int16_t deltaX_h = 0, deltaY_h = 0; |
| uint8_t motion = 0; |
| |
| pat9126_read(client, PIXART_PAT9126_MOTION_STATUS_REG, &motion); |
| pr_debug("[pat9126]: Motion BIT: 0x%2x\n", motion); |
| |
| if (motion & PIXART_PAT9126_VALID_MOTION_DATA) { |
| pat9126_read(client, PIXART_PAT9126_DELTA_X_LO_REG, &deltaX_l); |
| pat9126_read(client, PIXART_PAT9126_DELTA_Y_LO_REG, &deltaY_l); |
| pat9126_read(client, PIXART_PAT9126_DELTA_XY_HI_REG, &deltaXY_h); |
| |
| deltaX_h = (deltaXY_h & 0xF0); |
| deltaX_h <<= 4; |
| /* 12-bit data convert to 16-bit */ |
| if (deltaX_h & 0x800) |
| deltaX_h |= 0xf000; |
| |
| deltaY_h = (deltaXY_h & 0xF); |
| deltaY_h <<= 8; |
| /* 12-bit data convert to 16-bit */ |
| if (deltaY_h & 0x800) |
| deltaY_h |= 0xf000; |
| } |
| |
| *dx16 = deltaX_h | (int16_t) deltaX_l; |
| *dy16 = deltaY_h | (int16_t) deltaY_l; |
| } |
| |
| static void pat9126_work_handler(struct work_struct *work) |
| { |
| int8_t slop = 0; |
| int16_t delta_x = 0, delta_y = 0; |
| struct delayed_work *dw = to_delayed_work(work); |
| struct pixart_pat9126_data *data = \ |
| container_of(dw, struct pixart_pat9126_data, polling_work); |
| struct input_dev *ipdev = data->input; |
| struct device *dev = &data->client->dev; |
| ktime_t timestamp = ktime_set(0, 0); |
| |
| mutex_lock(&data->mtx); |
| |
| if (data->state == PAT9126_STATE_RESUMING) { |
| pr_warn("[PAT9126] %s: work handler run before resume complete\n", __func__); |
| goto end_work_handler; |
| } |
| |
| /* check if MOTION bit is set or not */ |
| pat9126_read_motion(data->client, &delta_x, &delta_y); |
| timestamp = ktime_get(); |
| dev_dbg(dev, "delta_x: %d, delta_y: %d\n", delta_x, delta_y); |
| |
| /* Inverse x depending upon the device orientation */ |
| delta_x = (data->inverse_x) ? -delta_x : delta_x; |
| /* Inverse y depending upon the device orientation */ |
| delta_y = (data->inverse_y) ? -delta_y : delta_y; |
| |
| dev_dbg(dev, "delta_x = 0x%2x, delta_y = 0x%2x\n", |
| delta_x, delta_y); |
| |
| if (delta_x != 0) { |
| /* Send delta_x as REL_WHEEL for rotation */ |
| input_set_timestamp(ipdev, timestamp); |
| slop = (delta_x < 0) ? -(data->slop) : data->slop; |
| if (WAKE_SENSITIVITY == data->current_sensitivity && |
| !data->wake_handled) { |
| input_report_rel(ipdev, REL_WHEEL, slop); |
| data->wake_handled = true; |
| pr_info("[PAT9126] WHEEL distance : %d\n", slop); |
| } |
| else { |
| input_report_rel(ipdev, REL_WHEEL, delta_x); |
| pr_info("[PAT9126] WHEEL distance : %d\n", delta_x); |
| } |
| input_sync(ipdev); |
| } |
| |
| end_work_handler: |
| enable_irq(data->client->irq); |
| mutex_unlock(&data->mtx); |
| } |
| |
| static irqreturn_t pat9126_irq(int irq, void *dev_data) |
| { |
| struct pixart_pat9126_data *data = dev_data; |
| bool result = false; |
| |
| disable_irq_nosync(irq); |
| if (!work_pending(&data->work)) { |
| result = schedule_delayed_work(&data->polling_work, msecs_to_jiffies(10)); |
| if (result == false) { |
| /* queue_work fail */ |
| pr_err("%s:queue_work fail.\n",__func__); |
| } |
| } else { |
| /* work pending */ |
| pr_err("%s:queue_work pending.\n",__func__); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void pat9126_sensitivity_write(struct pixart_pat9126_data *data, |
| u8 sensitivity) { |
| struct i2c_client *client = data->client; |
| |
| pr_info("[PAT9126] set sensitivity: %d\n", sensitivity); |
| pat9126_write(client, PIXART_PAT9126_SET_CPI_RES_X_REG, sensitivity); |
| data->current_sensitivity = sensitivity; |
| } |
| |
| static ssize_t pat9126_sensitivity_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| if (!kstrtou8(buf, 0, &data->crown_sensitivity)) { |
| pat9126_sensitivity_write(data, data->crown_sensitivity); |
| } |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_sensitivity_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) { |
| int count = 0; |
| uint8_t tmp; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_SET_CPI_RES_X_REG, &tmp); |
| count += sprintf(buf, "0x%2x\n", tmp); |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_slop_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| if (!kstrtou8(buf, 0, &data->slop)) { |
| return count; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_slop_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) { |
| int count = 0; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| count += sprintf(buf, "0x%2x\n", data->slop); |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_id_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) { |
| int count = 0; |
| uint8_t maj, min; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_PRODUCT_ID1_REG, &maj); |
| pat9126_read(client, PIXART_PAT9126_PRODUCT_ID2_REG, &min); |
| count += sprintf(buf, "0x%2x 0x%2x\n", maj, min); |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_max_sleep_level_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) { |
| u8 tmp, sleep_mode; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, &sleep_mode); |
| |
| if (kstrtou8(buf, 0, &tmp)) { |
| return count; |
| } |
| |
| dev_dbg(dev, "Max sleep level: %u\n", tmp); |
| |
| sleep_mode &= ~SLEEP_MODES_ENABLED_MASK; |
| |
| if (tmp == 0) { |
| sleep_mode |= SLEEP_MODES_ENABLED_ZERO; |
| } else if (tmp == 1) { |
| sleep_mode |= SLEEP_MODES_ENABLED_ONE; |
| } else if (tmp == 2) { |
| sleep_mode |= SLEEP_MODES_ENABLED_TWO; |
| } |
| |
| pat9126_write(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, sleep_mode); |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_max_sleep_level_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) { |
| int count = 0; |
| uint8_t sleep_mode; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, &sleep_mode); |
| |
| switch (sleep_mode & SLEEP_MODES_ENABLED_MASK) { |
| case SLEEP_MODES_ENABLED_TWO: |
| count += sprintf(buf, "2\n"); |
| break; |
| case SLEEP_MODES_ENABLED_ONE: |
| count += sprintf(buf, "1\n"); |
| break; |
| default: |
| /* Note: this case covers '01' and '00' which both |
| correspond to zero sleep modes */ |
| count += sprintf(buf, "0\n"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_sleep_level_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) { |
| u8 tmp, sleep_mode; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, &sleep_mode); |
| |
| if (kstrtou8(buf, 0, &tmp)) { |
| return count; |
| } |
| |
| dev_dbg(dev, "Setting sleep level: %u\n", tmp); |
| |
| if (tmp == 0) { |
| sleep_mode &= ~SLEEP_MODE_MASK; |
| sleep_mode |= SLEEP_MODE_WAKE; |
| } else if (tmp == 1) { |
| /* Can't switch to sleep mode #1 if disabled */ |
| if ((sleep_mode & SLEEP_MODES_ENABLED_MASK) != SLEEP_MODES_ENABLED_ZERO) { |
| sleep_mode &= ~SLEEP_MODE_MASK; |
| sleep_mode |= SLEEP_MODE_ONE; |
| } |
| } else if (tmp == 2) { |
| /* Can't switch to sleep mode #2 if disabled */ |
| if ((sleep_mode & SLEEP_MODES_ENABLED_MASK) == SLEEP_MODES_ENABLED_TWO) { |
| sleep_mode &= ~SLEEP_MODE_MASK; |
| sleep_mode |= SLEEP_MODE_TWO; |
| } |
| } |
| |
| pat9126_write(client, PIXART_PAT9126_SLEEP_MODE_SELECT_REG, sleep_mode); |
| |
| return count; |
| } |
| |
| static int pat9126_pd_write(struct device *dev, u8 val) { |
| u8 config; |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_CONFIG_REG, &config); |
| config &= ~POWER_DOWN_ENABLE_BIT; |
| |
| if (val) { |
| config |= POWER_DOWN_ENABLE_BIT; |
| } |
| |
| pat9126_write(client, PIXART_PAT9126_CONFIG_REG, config); |
| return 0; |
| } |
| |
| static ssize_t pat9126_pd_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| u8 tmp; |
| |
| if (!kstrtou8(buf, 0, &tmp)) { |
| dev_dbg(dev, "power down: %d\n", (tmp ? 1 : 0)); |
| |
| if (!tmp) { |
| pat9126_set_loads_active(data); |
| } |
| |
| pat9126_pd_write(dev, tmp); |
| |
| if (tmp) { |
| pat9126_set_loads_sleep(data); |
| } |
| } else { |
| dev_warn(dev, "failed to parse sysfs arg: '%s'\n", buf); |
| } |
| |
| return count; |
| } |
| |
| static ssize_t pat9126_pd_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) { |
| int count = 0; |
| uint8_t tmp; |
| |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| |
| pat9126_read(client, PIXART_PAT9126_CONFIG_REG, &tmp); |
| tmp = ((POWER_DOWN_ENABLE_BIT & tmp) ? 1 : 0); |
| count += sprintf(buf, "0x%x\n", tmp); |
| |
| return count; |
| } |
| |
| static void pat9126_complete_resume(struct work_struct *work) { |
| struct delayed_work *dw = to_delayed_work(work); |
| struct pixart_pat9126_data *data = |
| container_of(dw, struct pixart_pat9126_data, resume_work); |
| int ret = 0; |
| |
| mutex_lock(&data->mtx); |
| |
| if (data->state != PAT9126_STATE_RESUMING) { |
| if (data->state == PAT9126_STATE_ON) { |
| pr_warn("[PAT9126] %s: Already resumed\n", __func__); |
| } else { |
| // If suspend is called before resume is complete |
| // resume will be aborted |
| pr_info("[PAT9126] %s: Resume aborted\n", __func__); |
| } |
| |
| goto end_complete_resume; |
| } |
| |
| ret = pat9126_set_loads_active(data); |
| if (ret) { |
| goto end_complete_resume; |
| } |
| |
| pat9126_enable_mot_write_protected(data->client); |
| |
| data->state = PAT9126_STATE_ON; |
| |
| end_complete_resume: |
| mutex_unlock(&data->mtx); |
| } |
| |
| static DEVICE_ATTR |
| (crown_sensitivity, S_IRUGO | S_IWUSR | S_IWGRP, pat9126_sensitivity_show, pat9126_sensitivity_store); |
| |
| static DEVICE_ATTR |
| (id, S_IRUGO, pat9126_id_show, NULL); |
| |
| static DEVICE_ATTR |
| (max_sleep_level, S_IRUGO | S_IWUSR | S_IWGRP, pat9126_max_sleep_level_show, pat9126_max_sleep_level_store); |
| |
| static DEVICE_ATTR |
| (sleep_level, S_IWUSR | S_IWGRP, NULL, pat9126_sleep_level_store); |
| |
| static DEVICE_ATTR |
| (pd, S_IRUGO | S_IWUSR | S_IWGRP, pat9126_pd_show, pat9126_pd_store); |
| |
| static DEVICE_ATTR |
| (slop, S_IRUGO | S_IWUSR | S_IWGRP, pat9126_slop_show, pat9126_slop_store); |
| |
| static struct attribute *pat9126_attr_list[] = { |
| &dev_attr_crown_sensitivity.attr, |
| &dev_attr_id.attr, |
| &dev_attr_max_sleep_level.attr, |
| &dev_attr_sleep_level.attr, |
| &dev_attr_pd.attr, |
| &dev_attr_slop.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group pat9126_attr_grp = { |
| .attrs = pat9126_attr_list, |
| }; |
| |
| static int pat9126_parse_dt(struct device *dev, |
| struct pixart_pat9126_data *data) |
| { |
| struct device_node *np = dev->of_node; |
| u32 temp_val; |
| int ret; |
| |
| data->inverse_x = of_property_read_bool(np, "pixart,inverse-x"); |
| data->inverse_y = of_property_read_bool(np, "pixart,inverse-y"); |
| data->press_en = of_property_read_bool(np, "pixart,press-enabled"); |
| if (data->press_en) { |
| ret = of_property_read_u32(np, "pixart,press-keycode", |
| &temp_val); |
| if (!ret) { |
| data->press_keycode = temp_val; |
| } else { |
| dev_err(dev, "Unable to parse press-keycode\n"); |
| return ret; |
| } |
| } |
| |
| ret = of_property_read_u32(np, "pixart,vdd-active-load", |
| &temp_val); |
| if (!ret) { |
| data->vdd_active_load = temp_val; |
| } else { |
| dev_err(dev, "Unable to parse vdd-active-load\n"); |
| return ret; |
| } |
| |
| ret = of_property_read_u32(np, "pixart,vld-active-load", |
| &temp_val); |
| if (!ret) { |
| data->vld_active_load = temp_val; |
| } else { |
| dev_err(dev, "Unable to parse vld-active-load\n"); |
| return ret; |
| } |
| |
| ret = of_property_read_u32(np, "pixart,vdd-sleep-load", |
| &temp_val); |
| if (!ret) { |
| data->vdd_sleep_load = temp_val; |
| } else { |
| dev_err(dev, "Unable to parse vdd-sleep-load\n"); |
| return ret; |
| } |
| |
| ret = of_property_read_u32(np, "pixart,vld-sleep-load", |
| &temp_val); |
| if (!ret) { |
| data->vld_sleep_load = temp_val; |
| } else { |
| dev_err(dev, "Unable to parse vld-sleep-load\n"); |
| return ret; |
| } |
| |
| data->vld_supply = devm_regulator_get(dev, "pixart,vld"); |
| if (IS_ERR(data->vld_supply)) { |
| ret = PTR_ERR(data->vld_supply); |
| dev_err(dev, "get vld regulator failed, ret=%d\n", ret); |
| return ret; |
| } |
| |
| data->vdd_supply = devm_regulator_get(dev, "pixart,vdd"); |
| if (IS_ERR(data->vdd_supply)) { |
| ret = PTR_ERR(data->vdd_supply); |
| dev_err(dev, "get vdd regulator failed, ret=%d\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int pat9126_register_panel_notifier(struct pixart_pat9126_data *data) |
| { |
| struct device_node *np = data->client->dev.of_node; |
| struct device_node *pnode; |
| struct drm_panel *panel; |
| void *cookie = NULL; |
| int i, count, rc; |
| |
| count = of_count_phandle_with_args(np, "display-panels", NULL); |
| if (count <= 0) |
| return 0; |
| |
| for (i = 0; i < count; i++) { |
| pnode = of_parse_phandle(np, "display-panels", i); |
| if (!pnode) |
| return -ENODEV; |
| |
| panel = of_drm_find_panel(pnode); |
| of_node_put(pnode); |
| if (!IS_ERR(panel)) { |
| data->active_panel = panel; |
| break; |
| } |
| } |
| |
| if (!data->active_panel) { |
| rc = PTR_ERR(panel); |
| if (rc != -EPROBE_DEFER) |
| pr_err("failed to find active panel, rc=%d\n", rc); |
| |
| return rc; |
| } |
| |
| cookie = panel_event_notifier_register( |
| PANEL_EVENT_NOTIFICATION_PRIMARY, |
| PANEL_EVENT_NOTIFIER_CLIENT_SECONDARY_TOUCH, |
| data->active_panel, |
| &pat9126_drm_panel_notifier_callback, |
| data); |
| if (IS_ERR(cookie)) { |
| rc = PTR_ERR(cookie); |
| pr_err("failed to register panel event notifier, rc=%d\n", rc); |
| return rc; |
| } |
| |
| pr_debug("register panel notifier successful\n"); |
| data->notifier_cookie = cookie; |
| return 0; |
| } |
| |
| static void pat9126_unregister_panel_notifier(struct pixart_pat9126_data *data) |
| { |
| if (data->notifier_cookie) |
| panel_event_notifier_unregister(data->notifier_cookie); |
| } |
| |
| static int pixart_input_open(struct input_dev *dev) { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) input_get_drvdata(dev); |
| |
| mutex_lock(&data->mtx); |
| |
| if (data->display_mode == DISPLAY_UNBLANKED) { |
| // Display On |
| pat9126_sensitivity_write(data, data->crown_sensitivity); |
| pat9126_enable_mot_write_protected(data->client); |
| data->state = PAT9126_STATE_ON; |
| } else { |
| // Display Suspended |
| pat9126_sensitivity_write(data, WAKE_SENSITIVITY); |
| enable_irq_wake(data->client->irq); |
| pat9126_disable_mot_write_protected(data->client); |
| data->state = PAT9126_STATE_SUSPEND; |
| data->wake_handled = false; |
| } |
| |
| mutex_unlock(&data->mtx); |
| |
| return 0; |
| } |
| |
| static void pixart_input_close(struct input_dev *dev) { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) input_get_drvdata(dev); |
| |
| pr_info("[PAT9126]: disabling wake on irq %d", data->client->irq); |
| mutex_lock(&data->mtx); |
| |
| if (data->state == PAT9126_STATE_SUSPEND) { |
| // Wake irq was enabled in pat9126_display_suspend so we need to disable the irq |
| disable_irq_wake(data->client->irq); |
| } else { |
| // Wake irq wasn't enabled in pat9126_display_suspend nor was the device disabled |
| // so we need to put the pat9126 device into sleep2 mode |
| pat9126_disable_mot_write_protected(data->client); |
| } |
| |
| mutex_unlock(&data->mtx); |
| } |
| |
| static int pat9126_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int ret = 0; |
| struct pixart_pat9126_data *data; |
| struct input_dev *input; |
| struct device *dev = &client->dev; |
| |
| struct device_node *dp = NULL; |
| dp = client->dev.of_node; |
| |
| ret = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); |
| if (ret < 0) { |
| dev_err(dev, "I2C not supported\n"); |
| return -ENXIO; |
| } |
| |
| data = devm_kzalloc(dev, sizeof(struct pixart_pat9126_data), |
| GFP_KERNEL); |
| if (!data) |
| return -ENOMEM; |
| ret = pat9126_parse_dt(dev, data); |
| if (ret) { |
| dev_err(dev, "DT parsing failed, errno:%d\n", ret); |
| return ret; |
| } |
| data->client = client; |
| |
| input = devm_input_allocate_device(dev); |
| if (!input) { |
| dev_err(dev, "Failed to alloc input device\n"); |
| return -ENOMEM; |
| } |
| |
| input_set_capability(input, EV_REL, REL_WHEEL); |
| if (data->press_en) |
| input_set_capability(input, EV_KEY, data->press_keycode); |
| |
| i2c_set_clientdata(client, data); |
| input_set_drvdata(input, data); |
| input->name = PAT9126_DEV_NAME; |
| input->phys = PAT9126_DEV_PHYS; |
| input->open = pixart_input_open; |
| input->close = pixart_input_close; |
| |
| data->input = input; |
| ret = input_register_device(data->input); |
| if (ret < 0) { |
| dev_err(dev, "Failed to register input device\n"); |
| return ret; |
| } |
| |
| ret = pinctrl_pm_select_default_state(dev); |
| if (ret < 0) |
| dev_err(dev, "Could not set pin to active state %d\n", ret); |
| |
| ret = pat9126_set_loads_active(data); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = pat9126_enable_regulators(data); |
| if (ret < 0) { |
| goto err_regulators; |
| } |
| |
| usleep_range(DELAY_BETWEEN_REG_US, DELAY_BETWEEN_REG_US + 1); |
| |
| /* |
| * Initialize pixart sensor after some delay, when vdd |
| * regulator is enabled |
| */ |
| if (pat9126_sensor_init(data->client) < 0) { |
| ret = -ENODEV; |
| dev_err(dev, "Failed to initialize sensor %d\n", ret); |
| goto err_regulators; |
| } |
| |
| mutex_init(&data->mtx); |
| data->state = PAT9126_STATE_ON; |
| data->display_mode = DISPLAY_UNBLANKED; |
| data->crown_sensitivity = PIXART_PAT9126_CPI_RESOLUTION_X; |
| data->current_sensitivity = PIXART_PAT9126_CPI_RESOLUTION_X; |
| data->slop = PIXART_PAT9126_SLOP; |
| data->wake_handled = false; |
| |
| ret = pat9126_register_panel_notifier(data); |
| if (ret) { |
| dev_err(dev, "register panel_notifier failed, errno:%d\n", ret); |
| goto err_regulators; |
| } |
| |
| INIT_DELAYED_WORK(&data->polling_work, pat9126_work_handler); |
| INIT_DELAYED_WORK(&data->resume_work, pat9126_complete_resume); |
| |
| pr_err("[PAT9126]: Probe Enable Irq. \n"); |
| ret = devm_request_threaded_irq(dev, client->irq, NULL, pat9126_irq, |
| IRQF_ONESHOT | IRQF_TRIGGER_LOW, |
| "pixart_pat9126_irq", data); |
| |
| if (ret) { |
| dev_err(dev, "Req irq %d failed, errno:%d\n", client->irq, ret); |
| goto err_request_threaded_irq; |
| } |
| |
| ret = sysfs_create_group(&client->dev.kobj, &pat9126_attr_grp); |
| if (ret) { |
| dev_err(dev, "Failed to create sysfs group, errno:%d\n", ret); |
| goto err_sysfs_create; |
| } |
| |
| return 0; |
| |
| err_sysfs_create: |
| disable_irq(client->irq); |
| err_request_threaded_irq: |
| cancel_work_sync(&data->work); |
| destroy_workqueue(data->workqueue); |
| err_regulators: |
| pat9126_disable_regulators(data); |
| |
| return ret; |
| } |
| |
| static int pat9126_i2c_remove(struct i2c_client *client) |
| { |
| int ret = 0; |
| struct device *dev = &client->dev; |
| struct pixart_pat9126_data *data = i2c_get_clientdata(client); |
| |
| dev_dbg(dev, "%s\n", __func__); |
| if (data) { |
| pat9126_unregister_panel_notifier(data); |
| pat9126_disable_regulators(data); |
| } |
| |
| return ret; |
| } |
| |
| static void pat9126_drm_panel_notifier_callback(enum panel_event_notifier_tag tag, |
| struct panel_event_notification *notification, void *data) |
| { |
| struct pixart_pat9126_data *pat9126 = data; |
| |
| if (!notification) { |
| return; |
| } |
| |
| switch (notification->notif_type) { |
| case DRM_PANEL_EVENT_UNBLANK: |
| if (!notification->notif_data.early_trigger) { |
| pat9126_display_resume(&pat9126->client->dev); |
| pat9126->display_mode = DISPLAY_UNBLANKED; |
| } |
| break; |
| |
| case DRM_PANEL_EVENT_BLANK: |
| case DRM_PANEL_EVENT_BLANK_LP: |
| if (notification->notif_data.early_trigger) { |
| pat9126_display_suspend(&pat9126->client->dev); |
| pat9126->display_mode = DISPLAY_BLANKED; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return; |
| } |
| |
| static int pat9126_display_suspend(struct device *dev) |
| { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| mutex_lock(&data->mtx); |
| |
| pr_debug("[PAT9126] %s\n", __func__); |
| |
| if (data->input->users == 0) { |
| goto end_suspend; |
| } |
| |
| if (data->state != PAT9126_STATE_ON) { |
| if (data->state == PAT9126_STATE_SUSPEND) { |
| pr_warn("[PAT9126] %s: Redundant call to suspend\n", __func__); |
| } else { |
| // PAT9126_STATE_RESUMING |
| pr_info("[PAT9126] %s: Aborting resume\n", __func__); |
| } |
| |
| goto end_suspend; |
| } |
| |
| /* Set low sensitivity */ |
| pat9126_sensitivity_write(data, WAKE_SENSITIVITY); |
| data->wake_handled = false; |
| |
| enable_irq_wake(data->client->irq); |
| pat9126_disable_mot_write_protected(data->client); |
| pat9126_set_loads_sleep(data); |
| |
| end_suspend: |
| data->state = PAT9126_STATE_SUSPEND; |
| mutex_unlock(&data->mtx); |
| return 0; |
| } |
| |
| static int pat9126_display_resume(struct device *dev) |
| { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| pr_debug("[PAT9126] %s\n", __func__); |
| |
| mutex_lock(&data->mtx); |
| if (data->state != PAT9126_STATE_SUSPEND) { |
| pr_warn("[PAT9126] %s: Redundant call to resume\n", __func__); |
| mutex_unlock(&data->mtx); |
| return 0; |
| } |
| |
| /* Skip if input device is closed */ |
| if (data->input->users == 0) { |
| mutex_unlock(&data->mtx); |
| return 0; |
| } |
| |
| /* Set regular sensitivity */ |
| pat9126_sensitivity_write(data, data->crown_sensitivity); |
| |
| disable_irq_wake(data->client->irq); |
| data->state = PAT9126_STATE_RESUMING; |
| mutex_unlock(&data->mtx); |
| |
| if (!schedule_delayed_work(&data->resume_work, msecs_to_jiffies(10))) { |
| pr_err("[PAT9126] %s: Failed to schedule delayed resume\n", __func__); |
| |
| // Note that we must release the mutex (as above) before |
| // calling this function. |
| pat9126_complete_resume(&data->resume_work.work); |
| } |
| |
| return 0; |
| } |
| |
| static int pat9126_pm_suspend_late(struct device *dev) |
| { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| mutex_lock(&data->mtx); |
| |
| return 0; |
| } |
| |
| static int pat9126_pm_resume_early(struct device *dev) |
| { |
| struct pixart_pat9126_data *data = |
| (struct pixart_pat9126_data *) dev_get_drvdata(dev); |
| |
| mutex_unlock(&data->mtx); |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id pat9126_device_id[] = { |
| {PAT9126_DEV_NAME, 0}, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(i2c, pat9126_device_id); |
| |
| static const struct dev_pm_ops pat9126_pm_ops = { |
| .suspend_late = pat9126_pm_suspend_late, |
| .resume_early = pat9126_pm_resume_early |
| }; |
| |
| static const struct of_device_id pixart_pat9126_match_table[] = { |
| { .compatible = "pixart,pat9126",}, |
| { }, |
| }; |
| |
| static struct i2c_driver pat9126_i2c_driver = { |
| .driver = { |
| .name = PAT9126_DEV_NAME, |
| .owner = THIS_MODULE, |
| .pm = &pat9126_pm_ops, |
| .of_match_table = pixart_pat9126_match_table, |
| }, |
| .probe = pat9126_i2c_probe, |
| .remove = pat9126_i2c_remove, |
| .id_table = pat9126_device_id, |
| }; |
| module_i2c_driver(pat9126_i2c_driver); |
| |
| MODULE_AUTHOR("pixart"); |
| MODULE_DESCRIPTION("pixart pat9126 driver"); |
| MODULE_LICENSE("GPL"); |