/* * driver/input/touchscreen/ft_touch.c * * Copyright(c) 2019 allwinner Technology Corp. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PM #include #endif #include "../init-input.h" #define FTS_META_REGS 3 #define FTS_ONE_TCH_LEN 6 #define FTS_MAX_POINTS 16 #define POINT_READ_BUF (FTS_META_REGS + FTS_ONE_TCH_LEN * FTS_MAX_POINTS) #define FTS_REG_CHIP_ID 0xA3 #define FTS_REG_FW_VER 0xA6 #define FTS_REG_FW_VENDOR_ID 0xA8 #define FTS_REG_POINT_RATE 0x88 #define FTS_PRESS 0x7F #define FTS_MAX_ID 0x0F #define FTS_TOUCH_X_H_POS 3 #define FTS_TOUCH_X_L_POS 4 #define FTS_TOUCH_Y_H_POS 5 #define FTS_TOUCH_Y_L_POS 6 #define FTS_TOUCH_PRE_POS 7 #define FTS_TOUCH_AREA_POS 8 #define FTS_TOUCH_POINT_NUM 2 #define FTS_TOUCH_EVENT_POS 3 #define FTS_TOUCH_ID_POS 5 #define FTS_TOUCH_DOWN 0 #define FTS_TOUCH_UP 1 #define FTS_TOUCH_CONTACT 2 static struct ctp_config_info config_info = { .input_type = CTP_TYPE, .name = NULL, .int_number = 0, }; struct ts_event { u16 au16_x[FTS_MAX_POINTS]; /* x coordinate */ u16 au16_y[FTS_MAX_POINTS]; /* y coordinate */ u16 pressure[FTS_MAX_POINTS]; /* touch event: 0 -- down; 1-- up; 2 -- contact */ u8 au8_touch_event[FTS_MAX_POINTS]; u8 au8_finger_id[FTS_MAX_POINTS]; /* touch ID */ u8 area[FTS_MAX_POINTS]; u8 touch_point; u8 point_num; }; struct ft6336_ts_data { int retry; int panel_type; uint8_t bad_data; char phys[32]; struct i2c_client *client; struct input_dev *input_dev; uint8_t use_irq; uint8_t use_shutdown; uint32_t gpio_shutdown; uint32_t gpio_irq; uint32_t screen_width; uint32_t screen_height; bool is_suspended; struct ts_event event; int touchs; struct hrtimer timer; struct work_struct work; int (*power)(struct ft6336_ts_data *ts, int on); }; static const char *f6x_ts_name = "ft6336"; static struct workqueue_struct *ft6x_wq; enum { DEBUG_INIT = 1U << 0, DEBUG_SUSPEND = 1U << 1, DEBUG_INT_INFO = 1U << 2, DEBUG_X_Y_INFO = 1U << 3, DEBUG_KEY_INFO = 1U << 4, DEBUG_WAKEUP_INFO = 1U << 5, DEBUG_OTHERS_INFO = 1U << 6, }; #define CTP_IRQ_MODE (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) #define CTP_NAME "ft6336" #define SCREEN_MAX_X (screen_max_x) #define SCREEN_MAX_Y (screen_max_y) #define PRESS_MAX (255) static u32 screen_max_x; static u32 screen_max_y; static u32 revert_x_flag; static u32 revert_y_flag; static u32 exchange_x_y_flag; static __u32 twi_id; /* Addresses to scan */ static const unsigned short normal_i2c[2] = {0x38, I2C_CLIENT_END}; static const int chip_id_value[3] = {0x13, 0x27, 0x28}; static uint8_t read_chip_value[3] = {0x0f, 0x7d, 0}; static struct i2c_client *i2c_connect_client; static void ft6x_init_events(struct work_struct *work); static void ft6336_resume_events(struct work_struct *work); static struct workqueue_struct *ft6x_init_wq; static struct workqueue_struct *ft6x_resume_wq; static DECLARE_WORK(ft6x_init_work, ft6x_init_events); static DECLARE_WORK(ft6336_resume_work, ft6336_resume_events); static struct ft6336_ts_data *ts_init; /* * Function: * Read data from the i2c slave device. * Input: * client:i2c device. * buf[0]:operate address. * buf[1]~buf[len]:read data buffer. * len:operate length. * Output: * numbers of i2c_msgs to transfer */ static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, uint16_t len) { struct i2c_msg msgs[2]; int ret = -1; msgs[0].flags = !I2C_M_RD; msgs[0].addr = client->addr; msgs[0].len = 1; msgs[0].buf = buf; msgs[1].flags = I2C_M_RD; msgs[1].addr = client->addr; msgs[1].len = len; msgs[1].buf = buf; ret = i2c_transfer(client->adapter, msgs, 2); return ret; } /* * Function: * write data to the i2c slave device. * Input: * client:i2c device. * buf[0]:operate address. * buf[1]~buf[len]:write data buffer. * len:operate length. * Output: * numbers of i2c_msgs to transfer. */ static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, uint16_t len) { struct i2c_msg msg; int ret = -1; msg.flags = !I2C_M_RD; msg.addr = client->addr; msg.len = len; msg.buf = data; ret = i2c_transfer(client->adapter, &msg, 1); return ret; } /** * ctp_detect - Device detection callback for automatic device creation * return value: * = 0; success; * < 0; err */ static int ctp_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; int i = 0; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_info("======return=====\n"); return -ENODEV; } if (twi_id == adapter->nr) { i2c_read_bytes(client, read_chip_value, 3); pr_debug("addr:0x%x,chip_id_value:0x%x 0x%x 0x%x\n", client->addr, read_chip_value[0], read_chip_value[1], read_chip_value[2]); while (chip_id_value[i++]) { if (read_chip_value[2] == chip_id_value[i - 1]) { strlcpy(info->type, CTP_NAME, I2C_NAME_SIZE); return 0; } } pr_err("%s:I2C connection might be something wrong !\n", __func__); return -ENODEV; } else return -ENODEV; } static ssize_t ft6336_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct input_dev *input = to_input_dev(dev); struct i2c_client *client = input_get_drvdata(input); ssize_t size; size = sprintf(buf, "%d\n", !pm_runtime_suspended(&client->dev)); return size; } static ssize_t ft6336_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long data; int error; struct input_dev *input = to_input_dev(dev); struct i2c_client *client = input_get_drvdata(input); error = kstrtoul(buf, 10, &data); if (error) return error; if (data == 0) pm_runtime_put_sync(&client->dev); else if (data == 1) pm_runtime_get_sync(&client->dev); return count; } static DEVICE_ATTR(enable, 0644, ft6336_enable_show, ft6336_enable_store); static struct attribute *ft6336_att_als[] = { &dev_attr_enable.attr, NULL }; static struct attribute_group ft6336_als_gr = { .attrs = ft6336_att_als }; /** * ctp_print_info - sysconfig print function * return value: * */ static void ctp_print_info(struct ctp_config_info info, int debug_level) { if (debug_level == DEBUG_INIT) { pr_debug("info.ctp_used:%d\n", info.ctp_used); pr_debug("info.twi_id:%d\n", info.twi_id); pr_debug("info.screen_max_x:%d\n", info.screen_max_x); pr_debug("info.screen_max_y:%d\n", info.screen_max_y); pr_debug("info.revert_x_flag:%d\n", info.revert_x_flag); pr_debug("info.revert_y_flag:%d\n", info.revert_y_flag); pr_debug("info.exchange_x_y_flag:%d\n", info.exchange_x_y_flag); pr_debug("info.irq_gpio_number:%d\n", info.irq_gpio.gpio); pr_debug("info.wakeup_gpio_number:%d\n", info.wakeup_gpio.gpio); } } /** * ctp_wakeup - function */ static int ctp_wakeup(int status, int ms) { pr_debug("***CTP*** %s:status:%d, ms = %d\n", __func__, status, ms); if (status == 0) { char pin_name[32]; __u32 config; /* wakeup_gpio set output */ memset(pin_name, 0, sizeof(pin_name)); sunxi_gpio_to_name(config_info.wakeup_gpio.gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 1); pin_config_set(SUNXI_PINCTRL, pin_name, config); /* wakeup_gpio set pull */ memset(pin_name, 0, sizeof(pin_name)); sunxi_gpio_to_name(config_info.wakeup_gpio.gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 1); pin_config_set(SUNXI_PINCTRL, pin_name, config); /* irq_gpio set pull */ memset(pin_name, 0, sizeof(pin_name)); sunxi_gpio_to_name(config_info.irq_gpio.gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 1); pin_config_set(SUNXI_PINCTRL, pin_name, config); if (ms == 0) __gpio_set_value(config_info.wakeup_gpio.gpio, 0); else { __gpio_set_value(config_info.wakeup_gpio.gpio, 0); msleep(ms); __gpio_set_value(config_info.wakeup_gpio.gpio, 1); } } if (status == 1) { if (ms == 0) __gpio_set_value(config_info.wakeup_gpio.gpio, 1); else { __gpio_set_value(config_info.wakeup_gpio.gpio, 1); msleep(ms); __gpio_set_value(config_info.wakeup_gpio.gpio, 0); } } usleep_range(5000, 6000); return 0; } /* * Function: * GTP initialize function. * Input: * ts: i2c client private struct. * Output: * Executive outcomes.1---succeed. */ static int ft6336_init_panel(struct ft6336_ts_data *ts) { u8 buf[8] = {0}; buf[0] = FTS_REG_POINT_RATE; i2c_write_bytes(ts->client, buf, 2); buf[0] = FTS_REG_POINT_RATE; i2c_read_bytes(ts->client, buf, 5); pr_debug(" ft report rate (addr:0x%x) is %dHz.\n", FTS_REG_POINT_RATE, buf[0] * 10); memset(buf, 0, sizeof(buf)); buf[0] = FTS_REG_FW_VER; i2c_write_bytes(ts->client, buf, 2); buf[0] = FTS_REG_FW_VER; i2c_read_bytes(ts->client, buf, 5); pr_debug(" ft Firmware version (addr:0x%x) is 0x%x 0x%x 0x%x 0x%x\n", FTS_REG_FW_VER, buf[0], buf[1], buf[2], buf[3]); memset(buf, 0, sizeof(buf)); buf[0] = FTS_REG_CHIP_ID; i2c_write_bytes(ts->client, buf, 2); buf[0] = FTS_REG_CHIP_ID; i2c_read_bytes(ts->client, buf, 5); pr_debug(" ft chip id is (addr:0x%x) is 0x%x\n", FTS_REG_CHIP_ID, buf[0]); return 1; } static s32 ft6336_ts_version(struct ft6336_ts_data *ts) { u8 buf[8]; buf[0] = FTS_REG_CHIP_ID; buf[1] = 0x2; i2c_read_bytes(ts->client, buf, 5); pr_debug("FTS_REG_CHIP_ID:%02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4]); return 1; } static void ft_ts_work_func(struct work_struct *work) { static u8 buf[POINT_READ_BUF] = {0}; s32 ret = -1; struct ft6336_ts_data *ts = NULL; struct ts_event *event = NULL; u8 pointid = FTS_MAX_ID; int uppoint = 0; int touchs = 0; int i = 0; pr_debug("===enter %s===\n", __func__); ts = container_of(work, struct ft6336_ts_data, work); event = &ts->event; memset(buf, 0, sizeof(buf)); ret = i2c_read_bytes(ts->client, buf, POINT_READ_BUF); if (ret <= 0) { pr_err("%s:I2C read error!", __func__); goto exit_work_func; } memset(event, 0, sizeof(struct ts_event)); event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; event->touch_point = 0; for (i = 0; i < FTS_MAX_POINTS; i++) { pointid = (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4; if (pointid >= FTS_MAX_ID) break; event->touch_point++; event->au16_x[i] = (s16)(buf[FTS_TOUCH_X_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F) << 8 | (s16) buf[FTS_TOUCH_X_L_POS + FTS_ONE_TCH_LEN * i]; event->au16_y[i] = (s16)(buf[FTS_TOUCH_Y_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F) << 8 | (s16) buf[FTS_TOUCH_Y_L_POS + FTS_ONE_TCH_LEN * i]; event->au8_touch_event[i] = buf[FTS_TOUCH_EVENT_POS + FTS_ONE_TCH_LEN * i] >> 6; event->au8_finger_id[i] = (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4; event->area[i] = (buf[FTS_TOUCH_AREA_POS + FTS_ONE_TCH_LEN * i]) >> 4; event->pressure[i] = (s16) buf[FTS_TOUCH_PRE_POS + FTS_ONE_TCH_LEN * i]; if (0 == event->area[i]) event->area[i] = 0x09; if (0 == event->pressure[i]) event->pressure[i] = 0x3f; if ((event->au8_touch_event[i] == FTS_TOUCH_DOWN || event->au8_touch_event[i] == FTS_TOUCH_CONTACT) && (event->point_num == 0)) { goto exit_work_func; } } for (i = 0; i < event->touch_point; i++) { input_mt_slot(ts->input_dev, event->au8_finger_id[i]); if (event->au8_touch_event[i] == FTS_TOUCH_DOWN || event->au8_touch_event[i] == FTS_TOUCH_CONTACT) { input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, event->area[i]); input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); input_report_abs(ts->input_dev, ABS_MT_PRESSURE, event->pressure[i]); input_report_abs(ts->input_dev, ABS_MT_POSITION_X, event->au16_x[i]); input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, event->au16_y[i]); touchs |= BIT(event->au8_finger_id[i]); ts->touchs |= BIT(event->au8_finger_id[i]); } else { uppoint++; input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); ts->touchs &= ~BIT(event->au8_finger_id[i]); } } if (unlikely(ts->touchs ^ touchs)) { for (i = 0; i < FTS_MAX_POINTS; i++) { if (BIT(i) & (ts->touchs ^ touchs)) { uppoint++; input_mt_slot(ts->input_dev, i); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); } } } ts->touchs = touchs; if (event->touch_point == uppoint || event->point_num == 0) input_report_key(ts->input_dev, BTN_TOUCH, 0); else input_report_key(ts->input_dev, BTN_TOUCH, event->touch_point > 0); input_sync(ts->input_dev); exit_work_func: enable_irq(gpio_to_irq(config_info.int_number)); return; } static irqreturn_t ft6336_ts_irq_hanbler(int irq, void *dev_id) { struct ft6336_ts_data *ts = (struct ft6336_ts_data *)dev_id; disable_irq_nosync(gpio_to_irq(config_info.int_number)); pr_debug("==========------TS Interrupt-----============\n"); queue_work(ft6x_wq, &ts->work); return IRQ_HANDLED; } static int ft6336_ts_power(struct ft6336_ts_data *ts, int on) { s32 ret = -1; switch (on) { case 0: ret = 1; break; case 1: ctp_wakeup(0, 2); usleep_range(10000, 11000); ret = ft6336_init_panel(ts); if (ret != 1) { pr_err("init panel fail!\n"); return -1; } usleep_range(10000, 11000); ret = 1; break; default: pr_err("%s: Cant't support this command.", f6x_ts_name); ret = -EINVAL; break; } return ret; } static void ft6336_resume_events(struct work_struct *work) { int ret; if (ts_init->is_suspended == false) { ctp_wakeup(0, 2); usleep_range(10000, 11000); } else if (ts_init->power) { ret = ts_init->power(ts_init, 1); if (ret < 0) pr_err("%s power on failed\n", f6x_ts_name); } ret = input_set_int_enable(&(config_info.input_type), 1); ts_init->is_suspended = false; if (ret < 0) pr_err("%s irq enable failed\n", f6x_ts_name); } static int ft6336_ts_suspend(struct device *dev) { int ret; struct ft6336_ts_data *ts = dev_get_drvdata(dev); if (pm_runtime_suspended(dev)) return 0; if (ts->is_suspended == false) { flush_workqueue(ft6x_resume_wq); ret = input_set_int_enable(&(config_info.input_type), 0); if (ret < 0) pr_err("%s irq disable failed\n", f6x_ts_name); ret = cancel_work_sync(&ts->work); flush_workqueue(ft6x_wq); if (ts->power) { ret = ts->power(ts, 0); if (ret < 0) dev_err(dev, " power off failed\n"); } ts->is_suspended = true; } return 0; } static int ft6336_ts_resume(struct device *dev) { struct ft6336_ts_data *ts = dev_get_drvdata(dev); if (pm_runtime_suspended(dev)) return 0; if (ts->is_suspended == true) queue_work(ft6x_resume_wq, &ft6336_resume_work); return 0; } static void ft6x_init_events(struct work_struct *work) { int ret = 0; ret = ft6336_init_panel(ts_init); if (!ret) pr_err("init panel fail!\n"); else pr_debug("init panel succeed!\n"); config_info.dev = &(ts_init->input_dev->dev); ret = input_request_int(&(config_info.input_type), ft6336_ts_irq_hanbler, CTP_IRQ_MODE, ts_init); if (ret) pr_info("ft6336_probe: request irq failed\n"); } static int ft6336_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ft6336_ts_data *ts; int ret = 0; pr_debug("=============FT6336 Probe==================\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "System need I2C function.\n"); ret = -ENODEV; goto err_check_functionality_failed; } ts = kzalloc(sizeof(*ts), GFP_KERNEL); if (ts == NULL) { ret = -ENOMEM; goto err_alloc_data_failed; } i2c_connect_client = client; /* used by Guitar Updating */ INIT_WORK(&ts->work, ft_ts_work_func); ts->client = client; i2c_set_clientdata(ts->client, ts); ts->input_dev = input_allocate_device(); if (ts->input_dev == NULL) { ret = -ENOMEM; dev_dbg(&client->dev, "Failed to allocate input device\n"); goto err_input_dev_alloc_failed; } ts->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); set_bit(BTN_TOUCH, ts->input_dev->keybit); set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); ts->input_dev->absbit[0] = BIT_MASK(ABS_MT_TRACKING_ID) | BIT_MASK(ABS_MT_TOUCH_MAJOR) | BIT_MASK(ABS_MT_WIDTH_MAJOR) | BIT_MASK(ABS_MT_POSITION_X) | BIT_MASK(ABS_MT_POSITION_Y); input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 0x0f, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, FTS_MAX_POINTS, 0, 0); sprintf(ts->phys, "input/ft6336-ts"); ts->input_dev->name = CTP_NAME; ts->input_dev->phys = ts->phys; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0xDEAD; ts->input_dev->id.product = 0xBEEF; ts->input_dev->id.version = 0x1105; ts->is_suspended = false; ret = input_register_device(ts->input_dev); if (ret) { dev_err(&client->dev, "Unable to register %s input device\n", ts->input_dev->name); goto err_input_register_device_failed; } input_set_drvdata(ts->input_dev, client); ret = sysfs_create_group(&ts->input_dev->dev.kobj, &ft6336_als_gr); if (ret < 0) { dev_err(&client->dev, "ft6336: sysfs_create_group err\n"); goto err_input_register_device_failed; } ft6x_wq = create_singlethread_workqueue("ft_wq"); if (!ft6x_wq) { pr_err("Creat %s workqueue failed.\n", f6x_ts_name); return -ENOMEM; } flush_workqueue(ft6x_wq); ts->power = ft6336_ts_power; ts_init = ts; ft6x_init_wq = create_singlethread_workqueue("ft6x_init"); if (ft6x_init_wq == NULL) { pr_err("create ft6x_wq fail!\n"); return -ENOMEM; } ft6x_resume_wq = create_singlethread_workqueue("ft6336_resume"); if (ft6x_resume_wq == NULL) { pr_err("create ft6x_resume_wq fail!\n"); return -ENOMEM; } queue_work(ft6x_init_wq, &ft6x_init_work); ft6336_ts_version(ts); pm_runtime_set_active(&client->dev); pm_runtime_get(&client->dev); pm_runtime_enable(&client->dev); pr_debug("========Probe Ok================\n"); return 0; err_input_register_device_failed: input_free_device(ts->input_dev); err_input_dev_alloc_failed: i2c_set_clientdata(client, NULL); err_alloc_data_failed: err_check_functionality_failed: return ret; } static int ft6336_ts_remove(struct i2c_client *client) { struct ft6336_ts_data *ts = i2c_get_clientdata(client); dev_notice(&client->dev, "The driver is removing...\n"); input_free_int(&(config_info.input_type), ts_init); cancel_work_sync(&ft6x_init_work); cancel_work_sync(&ft6336_resume_work); flush_workqueue(ft6x_wq); if (ft6x_resume_wq) destroy_workqueue(ft6x_resume_wq); if (ft6x_init_wq) destroy_workqueue(ft6x_init_wq); if (ft6x_wq) destroy_workqueue(ft6x_wq); pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); sysfs_remove_group(&ts->input_dev->dev.kobj, &ft6336_als_gr); input_unregister_device(ts->input_dev); i2c_set_clientdata(ts->client, NULL); kfree(ts); return 0; } static const struct i2c_device_id ft6336_ts_id[] = { {CTP_NAME, 0}, {}, }; MODULE_DEVICE_TABLE(i2c, ft6336_ts_id); static const struct dev_pm_ops ft6336_pm_ops = { .suspend = ft6336_ts_suspend, .resume = ft6336_ts_resume, }; #define FT6336_PM_OPS (&ft6336_pm_ops) static struct i2c_driver ft6336_ts_driver = { .class = I2C_CLASS_HWMON, .probe = ft6336_ts_probe, .remove = ft6336_ts_remove, .id_table = ft6336_ts_id, .driver = { .name = CTP_NAME, .owner = THIS_MODULE, .pm = FT6336_PM_OPS, }, .detect = ctp_detect, .address_list = normal_i2c, }; static int ctp_get_system_config(void) { ctp_print_info(config_info, DEBUG_INIT); twi_id = config_info.twi_id; screen_max_x = config_info.screen_max_x; screen_max_y = config_info.screen_max_y; revert_x_flag = config_info.revert_x_flag; revert_y_flag = config_info.revert_y_flag; exchange_x_y_flag = config_info.exchange_x_y_flag; if ((screen_max_x == 0) || (screen_max_y == 0)) { pr_err("%s:read config error!\n", __func__); return 0; } return 1; } static int __init ft_ts_init(void) { int ret = -1; pr_debug("*********************************************\n"); if (!input_sensor_startup(&(config_info.input_type))) { ret = input_sensor_init(&(config_info.input_type)); if (ret != 0) { pr_err("%s:ctp_ops.input_sensor_init err.\n", __func__); goto init_err; } input_set_power_enable(&(config_info.input_type), 1); } else { pr_err("%s: input_ctp_startup err.\n", __func__); return 0; } if (config_info.ctp_used == 0) { pr_info("*** ctp_used set to 0 !\n"); pr_info("*** if use ctp, please put the sys_config.fex ctp_used set to 1.\n"); ret = 0; goto init_err; } if (!ctp_get_system_config()) { pr_err("%s:read config fail!\n", __func__); goto init_err; } ctp_wakeup(0, 2); ret = i2c_add_driver(&ft6336_ts_driver); if (ret) goto init_err; pr_debug("*********************************************\n"); return ret; init_err: input_set_power_enable(&(config_info.input_type), 0); return ret; } static void __exit ft_ts_exit(void) { i2c_del_driver(&ft6336_ts_driver); input_sensor_free(&(config_info.input_type)); input_set_power_enable(&(config_info.input_type), 0); } late_initcall(ft_ts_init); module_exit(ft_ts_exit); MODULE_DESCRIPTION("ft Touchscreen Driver"); MODULE_LICENSE("GPL v2");