| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 MediaTek Inc. |
| */ |
| |
| #include "tpd.h" |
| #include <linux/slab.h> |
| #include <linux/device.h> |
| #include <linux/miscdevice.h> |
| #include <linux/device.h> |
| #include <linux/slab.h> |
| #include <linux/uaccess.h> |
| #include <linux/fb.h> |
| #ifdef CONFIG_MTK_MT6306_GPIO_SUPPORT |
| #include <mtk_6306_gpio.h> |
| #endif |
| |
| #ifdef CONFIG_COMPAT |
| #include <linux/compat.h> |
| #endif |
| |
| #if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_50) \ |
| || defined(CONFIG_MTK_S3320_47) || defined(CONFIG_MTK_MIT200) \ |
| || defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \ |
| || defined(CONFIG_MTK_S7020) \ |
| || defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50) |
| #include <linux/input/mt.h> |
| #endif /* CONFIG_MTK_S3320 */ |
| /* for magnify velocity******************************************** */ |
| #define TOUCH_IOC_MAGIC 'A' |
| |
| #define TPD_GET_VELOCITY_CUSTOM_X _IO(TOUCH_IOC_MAGIC, 0) |
| #define TPD_GET_VELOCITY_CUSTOM_Y _IO(TOUCH_IOC_MAGIC, 1) |
| #define TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, 2, struct tpd_filter_t) |
| #ifdef CONFIG_COMPAT |
| #define COMPAT_TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, \ |
| 2, struct tpd_filter_t) |
| #endif |
| struct tpd_filter_t tpd_filter; |
| struct tpd_dts_info tpd_dts_data; |
| struct pinctrl *pinctrl1; |
| struct pinctrl_state *pins_default; |
| struct pinctrl_state *eint_as_int, *eint_output0, |
| *eint_output1, *rst_output0, *rst_output1; |
| const struct of_device_id touch_of_match[] = { |
| { .compatible = "goodix,touch", }, |
| {}, |
| }; |
| |
| void tpd_get_dts_info(void) |
| { |
| struct device_node *node1 = NULL; |
| int key_dim_local[16], i; |
| |
| node1 = of_find_matching_node(node1, touch_of_match); |
| if (node1) { |
| of_property_read_u32(node1, |
| "tpd-max-touch-num", &tpd_dts_data.touch_max_num); |
| of_property_read_u32(node1, |
| "use-tpd-button", &tpd_dts_data.use_tpd_button); |
| pr_debug("[tpd]use-tpd-button = %d\n", |
| tpd_dts_data.use_tpd_button); |
| if (of_property_read_u32_array(node1, "tpd-resolution", |
| tpd_dts_data.tpd_resolution, |
| ARRAY_SIZE(tpd_dts_data.tpd_resolution))) { |
| pr_debug("[tpd] resulution is %d %d", |
| tpd_dts_data.tpd_resolution[0], |
| tpd_dts_data.tpd_resolution[1]); |
| } |
| if (tpd_dts_data.use_tpd_button) { |
| of_property_read_u32(node1, |
| "tpd-key-num", &tpd_dts_data.tpd_key_num); |
| if (of_property_read_u32_array(node1, "tpd-key-local", |
| tpd_dts_data.tpd_key_local, |
| ARRAY_SIZE(tpd_dts_data.tpd_key_local))) |
| pr_debug("tpd-key-local: %d %d %d %d", |
| tpd_dts_data.tpd_key_local[0], |
| tpd_dts_data.tpd_key_local[1], |
| tpd_dts_data.tpd_key_local[2], |
| tpd_dts_data.tpd_key_local[3]); |
| if (of_property_read_u32_array(node1, |
| "tpd-key-dim-local", |
| key_dim_local, ARRAY_SIZE(key_dim_local))) { |
| memcpy(tpd_dts_data.tpd_key_dim_local, |
| key_dim_local, sizeof(key_dim_local)); |
| for (i = 0; i < 4; i++) { |
| pr_debug("[tpd]key[%d].key_x = %d\n", i, |
| tpd_dts_data |
| .tpd_key_dim_local[i] |
| .key_x); |
| pr_debug("[tpd]key[%d].key_y = %d\n", i, |
| tpd_dts_data |
| .tpd_key_dim_local[i] |
| .key_y); |
| pr_debug("[tpd]key[%d].key_W = %d\n", i, |
| tpd_dts_data |
| .tpd_key_dim_local[i] |
| .key_width); |
| pr_debug("[tpd]key[%d].key_H = %d\n", i, |
| tpd_dts_data |
| .tpd_key_dim_local[i] |
| .key_height); |
| } |
| } |
| } |
| of_property_read_u32(node1, "tpd-filter-enable", |
| &tpd_dts_data.touch_filter.enable); |
| if (tpd_dts_data.touch_filter.enable) { |
| of_property_read_u32(node1, |
| "tpd-filter-pixel-density", |
| &tpd_dts_data.touch_filter.pixel_density); |
| if (of_property_read_u32_array(node1, |
| "tpd-filter-custom-prameters", |
| (u32 *)tpd_dts_data.touch_filter.W_W, |
| ARRAY_SIZE(tpd_dts_data.touch_filter.W_W))) |
| pr_debug("get tpd-filter-custom-parameters"); |
| if (of_property_read_u32_array(node1, |
| "tpd-filter-custom-speed", |
| tpd_dts_data.touch_filter.VECLOCITY_THRESHOLD, |
| ARRAY_SIZE(tpd_dts_data |
| .touch_filter |
| .VECLOCITY_THRESHOLD))) |
| pr_debug("get tpd-filter-custom-speed"); |
| } |
| memcpy(&tpd_filter, |
| &tpd_dts_data.touch_filter, sizeof(tpd_filter)); |
| pr_debug("[tpd]tpd-filter-enable = %d, pixel_density = %d\n", |
| tpd_filter.enable, tpd_filter.pixel_density); |
| tpd_dts_data.tpd_use_ext_gpio = |
| of_property_read_bool(node1, "tpd-use-ext-gpio"); |
| of_property_read_u32(node1, |
| "tpd-rst-ext-gpio-num", |
| &tpd_dts_data.rst_ext_gpio_num); |
| |
| } else { |
| TPD_DMESG("can't find touch compatible custom node\n"); |
| } |
| } |
| |
| static DEFINE_MUTEX(tpd_set_gpio_mutex); |
| void tpd_gpio_as_int(int pin) |
| { |
| mutex_lock(&tpd_set_gpio_mutex); |
| TPD_DEBUG("[tpd]%s\n", __func__); |
| if (pin == 1) |
| pinctrl_select_state(pinctrl1, eint_as_int); |
| mutex_unlock(&tpd_set_gpio_mutex); |
| } |
| |
| void tpd_gpio_output(int pin, int level) |
| { |
| mutex_lock(&tpd_set_gpio_mutex); |
| TPD_DEBUG("%s pin = %d, level = %d\n", __func__, pin, level); |
| if (pin == 1) { |
| if (level) |
| pinctrl_select_state(pinctrl1, eint_output1); |
| else |
| pinctrl_select_state(pinctrl1, eint_output0); |
| } else { |
| if (tpd_dts_data.tpd_use_ext_gpio) { |
| #ifdef CONFIG_MTK_MT6306_GPIO_SUPPORT |
| mt6306_set_gpio_dir( |
| tpd_dts_data.rst_ext_gpio_num, 1); |
| mt6306_set_gpio_out( |
| tpd_dts_data.rst_ext_gpio_num, level); |
| #endif |
| } else { |
| if (level) |
| pinctrl_select_state(pinctrl1, rst_output1); |
| else |
| pinctrl_select_state(pinctrl1, rst_output0); |
| } |
| } |
| mutex_unlock(&tpd_set_gpio_mutex); |
| } |
| int tpd_get_gpio_info(struct platform_device *pdev) |
| { |
| int ret; |
| |
| TPD_DEBUG("[tpd %d] mt_tpd_pinctrl+++++++++++++++++\n", pdev->id); |
| pinctrl1 = devm_pinctrl_get(&pdev->dev); |
| if (IS_ERR(pinctrl1)) { |
| ret = PTR_ERR(pinctrl1); |
| dev_info(&pdev->dev, "fwq Cannot find pinctrl1!\n"); |
| return ret; |
| } |
| pins_default = pinctrl_lookup_state(pinctrl1, "default"); |
| if (IS_ERR(pins_default)) { |
| ret = PTR_ERR(pins_default); |
| TPD_DMESG("Cannot find pinctrl default %d!\n", ret); |
| } |
| eint_as_int = pinctrl_lookup_state(pinctrl1, "state_eint_as_int"); |
| if (IS_ERR(eint_as_int)) { |
| ret = PTR_ERR(eint_as_int); |
| TPD_DMESG("Cannot find pinctrl state_eint_as_int!\n"); |
| return ret; |
| } |
| eint_output0 = pinctrl_lookup_state(pinctrl1, "state_eint_output0"); |
| if (IS_ERR(eint_output0)) { |
| ret = PTR_ERR(eint_output0); |
| TPD_DMESG("Cannot find pinctrl state_eint_output0!\n"); |
| return ret; |
| } |
| eint_output1 = pinctrl_lookup_state(pinctrl1, "state_eint_output1"); |
| if (IS_ERR(eint_output1)) { |
| ret = PTR_ERR(eint_output1); |
| TPD_DMESG("Cannot find pinctrl state_eint_output1!\n"); |
| return ret; |
| } |
| if (tpd_dts_data.tpd_use_ext_gpio == false) { |
| rst_output0 = |
| pinctrl_lookup_state(pinctrl1, "state_rst_output0"); |
| if (IS_ERR(rst_output0)) { |
| ret = PTR_ERR(rst_output0); |
| TPD_DMESG("Cannot find pinctrl state_rst_output0!\n"); |
| return ret; |
| } |
| rst_output1 = |
| pinctrl_lookup_state(pinctrl1, "state_rst_output1"); |
| if (IS_ERR(rst_output1)) { |
| ret = PTR_ERR(rst_output1); |
| TPD_DMESG("Cannot find pinctrl state_rst_output1!\n"); |
| return ret; |
| } |
| } |
| TPD_DEBUG("[tpd%d] mt_tpd_pinctrl----------\n", pdev->id); |
| return 0; |
| } |
| |
| static int tpd_misc_open(struct inode *inode, struct file *file) |
| { |
| return nonseekable_open(inode, file); |
| } |
| |
| static int tpd_misc_release(struct inode *inode, struct file *file) |
| { |
| return 0; |
| } |
| |
| #ifdef CONFIG_COMPAT |
| static long tpd_compat_ioctl( |
| struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| long ret; |
| void __user *arg32 = compat_ptr(arg); |
| |
| if (!file->f_op || !file->f_op->unlocked_ioctl) |
| return -ENOTTY; |
| switch (cmd) { |
| case COMPAT_TPD_GET_FILTER_PARA: |
| if (arg32 == NULL) { |
| pr_info("invalid argument."); |
| return -EINVAL; |
| } |
| ret = file->f_op->unlocked_ioctl(file, TPD_GET_FILTER_PARA, |
| (unsigned long)arg32); |
| if (ret) { |
| pr_info("TPD_GET_FILTER_PARA unlocked_ioctl failed."); |
| return ret; |
| } |
| break; |
| default: |
| pr_info("tpd: unknown IOCTL: 0x%08x\n", cmd); |
| ret = -ENOIOCTLCMD; |
| break; |
| } |
| return ret; |
| } |
| #endif |
| static long tpd_unlocked_ioctl(struct file *file, |
| unsigned int cmd, unsigned long arg) |
| { |
| /* char strbuf[256]; */ |
| void __user *data; |
| |
| long err = 0; |
| |
| if (_IOC_DIR(cmd) & _IOC_READ) |
| err = !access_ok(VERIFY_WRITE, |
| (void __user *)arg, _IOC_SIZE(cmd)); |
| else if (_IOC_DIR(cmd) & _IOC_WRITE) |
| err = !access_ok(VERIFY_READ, |
| (void __user *)arg, _IOC_SIZE(cmd)); |
| if (err) { |
| pr_info("tpd: access error: %08X, (%2d, %2d)\n", |
| cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd)); |
| return -EFAULT; |
| } |
| |
| switch (cmd) { |
| case TPD_GET_VELOCITY_CUSTOM_X: |
| data = (void __user *)arg; |
| |
| if (data == NULL) { |
| err = -EINVAL; |
| break; |
| } |
| |
| if (copy_to_user(data, |
| &tpd_v_magnify_x, sizeof(tpd_v_magnify_x))) { |
| err = -EFAULT; |
| break; |
| } |
| |
| break; |
| |
| case TPD_GET_VELOCITY_CUSTOM_Y: |
| data = (void __user *)arg; |
| |
| if (data == NULL) { |
| err = -EINVAL; |
| break; |
| } |
| |
| if (copy_to_user(data, |
| &tpd_v_magnify_y, sizeof(tpd_v_magnify_y))) { |
| err = -EFAULT; |
| break; |
| } |
| |
| break; |
| case TPD_GET_FILTER_PARA: |
| data = (void __user *) arg; |
| |
| if (data == NULL) { |
| err = -EINVAL; |
| TPD_DMESG("GET_FILTER_PARA: data is null\n"); |
| break; |
| } |
| |
| if (copy_to_user(data, &tpd_filter, |
| sizeof(struct tpd_filter_t))) { |
| TPD_DMESG("GET_FILTER_PARA: copy data error\n"); |
| err = -EFAULT; |
| break; |
| } |
| break; |
| default: |
| pr_info("tpd: unknown IOCTL: 0x%08x\n", cmd); |
| err = -ENOIOCTLCMD; |
| break; |
| |
| } |
| |
| return err; |
| } |
| static struct work_struct touch_resume_work; |
| static struct workqueue_struct *touch_resume_workqueue; |
| static const struct file_operations tpd_fops = { |
| /* .owner = THIS_MODULE, */ |
| .open = tpd_misc_open, |
| .release = tpd_misc_release, |
| .unlocked_ioctl = tpd_unlocked_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = tpd_compat_ioctl, |
| #endif |
| }; |
| |
| /*---------------------------------------------------------------------------*/ |
| static struct miscdevice tpd_misc_device = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "touch", |
| .fops = &tpd_fops, |
| }; |
| |
| /* ********************************************** */ |
| /* #endif */ |
| |
| |
| /* function definitions */ |
| static int __init tpd_device_init(void); |
| static void __exit tpd_device_exit(void); |
| static int tpd_probe(struct platform_device *pdev); |
| static int tpd_remove(struct platform_device *pdev); |
| static struct work_struct tpd_init_work; |
| static struct workqueue_struct *tpd_init_workqueue; |
| static int tpd_suspend_flag; |
| int tpd_register_flag; |
| /* global variable definitions */ |
| struct tpd_device *tpd; |
| static struct tpd_driver_t tpd_driver_list[TP_DRV_MAX_COUNT]; /* = {0}; */ |
| |
| struct platform_device tpd_device = { |
| .name = TPD_DEVICE, |
| .id = -1, |
| }; |
| const struct dev_pm_ops tpd_pm_ops = { |
| .suspend = NULL, |
| .resume = NULL, |
| }; |
| static struct platform_driver tpd_driver = { |
| .remove = tpd_remove, |
| .shutdown = NULL, |
| .probe = tpd_probe, |
| .driver = { |
| .name = TPD_DEVICE, |
| .pm = &tpd_pm_ops, |
| .owner = THIS_MODULE, |
| .of_match_table = touch_of_match, |
| }, |
| }; |
| static struct tpd_driver_t *g_tpd_drv; |
| /* hh: use fb_notifier */ |
| static struct notifier_block tpd_fb_notifier; |
| /* use fb_notifier */ |
| static void touch_resume_workqueue_callback(struct work_struct *work) |
| { |
| TPD_DEBUG("GTP %s\n", __func__); |
| g_tpd_drv->resume(NULL); |
| tpd_suspend_flag = 0; |
| } |
| static int tpd_fb_notifier_callback( |
| struct notifier_block *self, |
| unsigned long event, void *data) |
| { |
| struct fb_event *evdata = NULL; |
| int blank; |
| int err = 0; |
| |
| TPD_DEBUG("%s\n", __func__); |
| |
| evdata = data; |
| /* If we aren't interested in this event, skip it immediately ... */ |
| if (event != FB_EVENT_BLANK) |
| return 0; |
| |
| blank = *(int *)evdata->data; |
| TPD_DMESG("fb_notify(blank=%d)\n", blank); |
| switch (blank) { |
| case FB_BLANK_UNBLANK: |
| TPD_DMESG("LCD ON Notify\n"); |
| if (g_tpd_drv && tpd_suspend_flag) { |
| err = queue_work(touch_resume_workqueue, |
| &touch_resume_work); |
| if (!err) { |
| TPD_DMESG("start resume_workqueue failed\n"); |
| return err; |
| } |
| } |
| break; |
| case FB_BLANK_POWERDOWN: |
| TPD_DMESG("LCD OFF Notify\n"); |
| if (g_tpd_drv && !tpd_suspend_flag) { |
| err = cancel_work_sync(&touch_resume_work); |
| if (!err) |
| TPD_DMESG("cancel resume_workqueue failed\n"); |
| g_tpd_drv->suspend(NULL); |
| } |
| tpd_suspend_flag = 1; |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| /* Add driver: if find TPD_TYPE_CAPACITIVE driver successfully, loading it */ |
| int tpd_driver_add(struct tpd_driver_t *tpd_drv) |
| { |
| int i; |
| |
| if (g_tpd_drv != NULL) { |
| TPD_DMESG("touch driver exist\n"); |
| return -1; |
| } |
| /* check parameter */ |
| if (tpd_drv == NULL) |
| return -1; |
| tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button; |
| /* R-touch */ |
| if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) { |
| tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name; |
| tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init; |
| tpd_driver_list[0].suspend = tpd_drv->suspend; |
| tpd_driver_list[0].resume = tpd_drv->resume; |
| tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button; |
| return 0; |
| } |
| for (i = 1; i < TP_DRV_MAX_COUNT; i++) { |
| /* add tpd driver into list */ |
| if (tpd_driver_list[i].tpd_device_name == NULL) { |
| tpd_driver_list[i].tpd_device_name = |
| tpd_drv->tpd_device_name; |
| tpd_driver_list[i].tpd_local_init = |
| tpd_drv->tpd_local_init; |
| tpd_driver_list[i].suspend = tpd_drv->suspend; |
| tpd_driver_list[i].resume = tpd_drv->resume; |
| tpd_driver_list[i].tpd_have_button = |
| tpd_drv->tpd_have_button; |
| tpd_driver_list[i].attrs = tpd_drv->attrs; |
| break; |
| } |
| if (strcmp(tpd_driver_list[i].tpd_device_name, |
| tpd_drv->tpd_device_name) == 0) |
| return 1; /* driver exist */ |
| } |
| |
| return 0; |
| } |
| |
| int tpd_driver_remove(struct tpd_driver_t *tpd_drv) |
| { |
| int i = 0; |
| /* check parameter */ |
| if (tpd_drv == NULL) |
| return -1; |
| for (i = 0; i < TP_DRV_MAX_COUNT; i++) { |
| /* find it */ |
| if (strcmp(tpd_driver_list[i].tpd_device_name, |
| tpd_drv->tpd_device_name) == 0) { |
| memset(&tpd_driver_list[i], 0, |
| sizeof(struct tpd_driver_t)); |
| break; |
| } |
| } |
| return 0; |
| } |
| |
| static void tpd_create_attributes(struct device *dev, struct tpd_attrs *attrs) |
| { |
| int num = attrs->num; |
| |
| for (; num > 0;) { |
| if (device_create_file(dev, attrs->attr[--num])) |
| pr_info("mtk_tpd: tpd create attributes file failed\n"); |
| } |
| } |
| |
| /* touch panel probe */ |
| static int tpd_probe(struct platform_device *pdev) |
| { |
| int touch_type = 1; /* 0:R-touch, 1: Cap-touch */ |
| int i = 0; |
| #ifndef CONFIG_CUSTOM_LCM_X |
| #ifdef CONFIG_LCM_WIDTH |
| unsigned long tpd_res_x = 0, tpd_res_y = 0; |
| int ret = 0; |
| #endif |
| #endif |
| |
| TPD_DMESG("enter %s, %d\n", __func__, __LINE__); |
| |
| if (misc_register(&tpd_misc_device)) |
| pr_info("mtk_tpd: tpd_misc_device register failed\n"); |
| tpd_get_gpio_info(pdev); |
| tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL); |
| if (tpd == NULL) |
| return -ENOMEM; |
| memset(tpd, 0, sizeof(struct tpd_device)); |
| |
| /* allocate input device */ |
| tpd->dev = input_allocate_device(); |
| if (tpd->dev == NULL) { |
| kfree(tpd); |
| return -ENOMEM; |
| } |
| /* TPD_RES_X = simple_strtoul(LCM_WIDTH, NULL, 0); */ |
| /* TPD_RES_Y = simple_strtoul(LCM_HEIGHT, NULL, 0); */ |
| |
| #ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION |
| if (strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) == 0 |
| || strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3) == 0) { |
| #ifdef CONFIG_MTK_FB |
| /*Fix build errors,as some projects cannot support these apis while bring up*/ |
| TPD_RES_Y = DISP_GetScreenWidth(); |
| TPD_RES_X = DISP_GetScreenHeight(); |
| #endif |
| } else |
| #endif |
| { |
| #ifdef CONFIG_CUSTOM_LCM_X |
| #ifndef CONFIG_FPGA_EARLY_PORTING |
| #if defined(CONFIG_MTK_FB) && defined(CONFIG_MTK_LCM) |
| /*Fix build errors,as some projects cannot support these apis while bring up*/ |
| TPD_RES_X = DISP_GetScreenWidth(); |
| TPD_RES_Y = DISP_GetScreenHeight(); |
| #else |
| /*for some projects, we do not use mtk framebuffer*/ |
| TPD_RES_X = tpd_dts_data.tpd_resolution[0]; |
| TPD_RES_Y = tpd_dts_data.tpd_resolution[1]; |
| #endif |
| #endif |
| #else |
| #ifdef CONFIG_LCM_WIDTH |
| ret = kstrtoul(CONFIG_LCM_WIDTH, 0, &tpd_res_x); |
| if (ret < 0) { |
| pr_info("Touch down get lcm_x failed"); |
| return ret; |
| } |
| TPD_RES_X = tpd_res_x; |
| ret = kstrtoul(CONFIG_LCM_HEIGHT, 0, &tpd_res_x); |
| if (ret < 0) { |
| pr_info("Touch down get lcm_y failed"); |
| return ret; |
| } |
| TPD_RES_Y = tpd_res_y; |
| #endif |
| #endif |
| } |
| |
| if (2560 == TPD_RES_X) |
| TPD_RES_X = 2048; |
| if (1600 == TPD_RES_Y) |
| TPD_RES_Y = 1536; |
| pr_debug("mtk_tpd: TPD_RES_X = %lu, TPD_RES_Y = %lu\n", |
| TPD_RES_X, TPD_RES_Y); |
| |
| tpd_mode = TPD_MODE_NORMAL; |
| tpd_mode_axis = 0; |
| tpd_mode_min = TPD_RES_Y / 2; |
| tpd_mode_max = TPD_RES_Y; |
| tpd_mode_keypad_tolerance = TPD_RES_X * TPD_RES_X / 1600; |
| /* struct input_dev dev initialization and registration */ |
| tpd->dev->name = TPD_DEVICE; |
| set_bit(EV_ABS, tpd->dev->evbit); |
| set_bit(EV_KEY, tpd->dev->evbit); |
| set_bit(ABS_X, tpd->dev->absbit); |
| set_bit(ABS_Y, tpd->dev->absbit); |
| set_bit(ABS_PRESSURE, tpd->dev->absbit); |
| #if !defined(CONFIG_MTK_S3320) && !defined(CONFIG_MTK_S3320_47)\ |
| && !defined(CONFIG_MTK_S3320_50) && !defined(CONFIG_MTK_MIT200) \ |
| && !defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \ |
| && !defined(CONFIG_MTK_S7020) \ |
| && !defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50) |
| set_bit(BTN_TOUCH, tpd->dev->keybit); |
| #endif /* CONFIG_MTK_S3320 */ |
| set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit); |
| |
| /* save dev for regulator_get() before tpd_local_init() */ |
| tpd->tpd_dev = &pdev->dev; |
| for (i = 1; i < TP_DRV_MAX_COUNT; i++) { |
| /* add tpd driver into list */ |
| if (tpd_driver_list[i].tpd_device_name != NULL) { |
| tpd_driver_list[i].tpd_local_init(); |
| /* msleep(1); */ |
| if (tpd_load_status == 1) { |
| TPD_DMESG("%s, tpd_driver_name=%s\n", __func__, |
| tpd_driver_list[i].tpd_device_name); |
| g_tpd_drv = &tpd_driver_list[i]; |
| break; |
| } |
| } |
| } |
| if (g_tpd_drv == NULL) { |
| if (tpd_driver_list[0].tpd_device_name != NULL) { |
| g_tpd_drv = &tpd_driver_list[0]; |
| /* touch_type:0: r-touch, 1: C-touch */ |
| touch_type = 0; |
| g_tpd_drv->tpd_local_init(); |
| TPD_DMESG("Generic touch panel driver\n"); |
| } else { |
| TPD_DMESG("no touch driver is loaded!!\n"); |
| return 0; |
| } |
| } |
| touch_resume_workqueue = create_singlethread_workqueue("touch_resume"); |
| INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback); |
| /* use fb_notifier */ |
| tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback; |
| if (fb_register_client(&tpd_fb_notifier)) |
| TPD_DMESG("register fb_notifier fail!\n"); |
| /* TPD_TYPE_CAPACITIVE handle */ |
| if (touch_type == 1) { |
| |
| set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit); |
| set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit); |
| set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit); |
| set_bit(ABS_MT_POSITION_X, tpd->dev->absbit); |
| set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit); |
| input_set_abs_params(tpd->dev, |
| ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0); |
| input_set_abs_params(tpd->dev, |
| ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0); |
| #if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) \ |
| || defined(CONFIG_MTK_S3320_50) || defined(CONFIG_MTK_MIT200) \ |
| || defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \ |
| || defined(CONFIG_MTK_S7020) \ |
| || defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50) |
| input_set_abs_params(tpd->dev, |
| ABS_MT_PRESSURE, 0, 255, 0, 0); |
| input_set_abs_params(tpd->dev, |
| ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0); |
| input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MINOR, 0, 15, 0, 0); |
| input_mt_init_slots(tpd->dev, 10, 0); |
| #else |
| input_set_abs_params(tpd->dev, |
| ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0); |
| input_set_abs_params(tpd->dev, |
| ABS_MT_TOUCH_MINOR, 0, 100, 0, 0); |
| #endif /* CONFIG_MTK_S3320 */ |
| TPD_DMESG("Cap touch panel driver\n"); |
| } |
| input_set_abs_params(tpd->dev, ABS_X, 0, TPD_RES_X, 0, 0); |
| input_set_abs_params(tpd->dev, ABS_Y, 0, TPD_RES_Y, 0, 0); |
| input_abs_set_res(tpd->dev, ABS_X, TPD_RES_X); |
| input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y); |
| input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0); |
| input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0); |
| |
| if (input_register_device(tpd->dev)) |
| TPD_DMESG("input_register_device failed.(tpd)\n"); |
| else |
| tpd_register_flag = 1; |
| if (g_tpd_drv->tpd_have_button) |
| tpd_button_init(); |
| |
| if (g_tpd_drv->attrs.num) |
| tpd_create_attributes(&pdev->dev, &g_tpd_drv->attrs); |
| |
| return 0; |
| } |
| static int tpd_remove(struct platform_device *pdev) |
| { |
| input_unregister_device(tpd->dev); |
| return 0; |
| } |
| |
| /* called when loaded into kernel */ |
| static void tpd_init_work_callback(struct work_struct *work) |
| { |
| TPD_DEBUG("MediaTek touch panel driver init\n"); |
| if (platform_driver_register(&tpd_driver) != 0) |
| TPD_DMESG("unable to register touch panel driver.\n"); |
| } |
| static int __init tpd_device_init(void) |
| { |
| int res = 0; |
| |
| tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd"); |
| INIT_WORK(&tpd_init_work, tpd_init_work_callback); |
| |
| res = queue_work(tpd_init_workqueue, &tpd_init_work); |
| if (!res) |
| pr_info("tpd : touch device init failed res:%d\n", res); |
| return 0; |
| } |
| /* should never be called */ |
| static void __exit tpd_device_exit(void) |
| { |
| TPD_DMESG("MediaTek touch panel driver exit\n"); |
| /* input_unregister_device(tpd->dev); */ |
| platform_driver_unregister(&tpd_driver); |
| } |
| |
| late_initcall(tpd_device_init); |
| module_exit(tpd_device_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("MediaTek touch panel driver"); |
| MODULE_AUTHOR("Kirby Wu<kirby.wu@mediatek.com>"); |