/* * VTL CTP driver * * Copyright (C) 2013 VTL Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtl_ts.h" #include "chip.h" #include "apk.h" #include "tp_fw.h" #define TS_THREAD_PRIO 90 static DECLARE_WAIT_QUEUE_HEAD(waiter); //static struct task_struct *ts_thread = NULL; static unsigned char thread_syn_flag =0; // **************************************************************************** // Globel or static variables // **************************************************************************** static struct ts_driver g_driver; static int vtl_first_init_flag = 1; struct ts_info g_ts = { .driver = &g_driver, .debug = DEBUG_ENABLE, }; struct ts_info *pg_ts = &g_ts; static struct i2c_device_id vtl_ts_id[] = { { DRIVER_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c,vtl_ts_id); /* static struct i2c_board_info i2c_info[] = { { I2C_BOARD_INFO(DRIVER_NAME, 0x01), .platform_data = NULL, }, }; */ // **************************************************************************** // Function declaration // **************************************************************************** unsigned char *gtpfw; static int vtl_ts_config(struct ts_info *ts) { struct device *dev = &ts->driver->client->dev; //struct ts_config_info *ts_config_info; int err; struct device_node *np = dev->of_node; enum of_gpio_flags rst_flags; unsigned long irq_flags; int val; gtpfw = tp_fw; DEBUG(); /* ts config */ ts->config_info.touch_point_number = TOUCH_POINT_NUM; if(dev->platform_data !=NULL) { return -1; } else { if (of_property_read_u32(np, "screen_max_x", &val)) { dev_err(&ts->driver->client->dev, "no screen_max_x defined\n"); return -EINVAL; } ts->config_info.screen_max_x = val; if (of_property_read_u32(np, "screen_max_y", &val)) { dev_err(&ts->driver->client->dev, "no screen_max_y defined\n"); return -EINVAL; } ts->config_info.screen_max_y = val; if (of_property_read_u32(np, "xy_swap", &val)) { val = 0; } ts->config_info.xy_swap = val; if (of_property_read_u32(np, "x_reverse", &val)) { val = 0; } ts->config_info.x_reverse = val; if (of_property_read_u32(np, "y_reverse", &val)) { val = 0; } ts->config_info.y_reverse = val; if (of_property_read_u32(np, "x_mul", &val)) { val = 1; } ts->config_info.x_mul = val; if (of_property_read_u32(np, "y_mul", &val)) { val = 1; } ts->config_info.y_mul = val; if (of_property_read_u32(np, "bin_ver", &val)) { val = 0; } ts->config_info.bin_ver = val; printk("--->>> vtl_ts : xy_swap %d, x_reverse %d, y_reverse %d, x_mul %d, y_mul %d, bin_ver %d\n", ts->config_info.xy_swap, ts->config_info.x_reverse, ts->config_info.y_reverse, ts->config_info.x_mul, ts->config_info.y_mul, ts->config_info.bin_ver); printk("the screen_x is %d , screen_y is %d \n",ts->config_info.screen_max_x,ts->config_info.screen_max_y); ts->config_info.irq_gpio_number = of_get_named_gpio_flags(np, "irq_gpio_number", 0, (enum of_gpio_flags *)&irq_flags); ts->config_info.rst_gpio_number = of_get_named_gpio_flags(np, "rst_gpio_number", 0, &rst_flags); } ts->config_info.irq_number = gpio_to_irq(ts->config_info.irq_gpio_number);/* IRQ config*/ err = gpio_request(ts->config_info.rst_gpio_number, "vtl_ts_rst"); if ( err ) { return -EIO; } gpio_direction_output(ts->config_info.rst_gpio_number, 1); //gpio_set_value(ts->config_info.rst_gpio_number, 1); return 0; } struct ts_info * vtl_ts_get_object(void) { DEBUG(); return pg_ts; } #if 0 static void vtl_ts_free_gpio(void) { struct ts_info *ts; ts =pg_ts; DEBUG(); gpio_free(ts->config_info.rst_gpio_number); } #endif void vtl_ts_hw_reset(void) { struct ts_info *ts; ts =pg_ts; DEBUG(); //gpio_set_value(ts->config_info.rst_gpio_number, 1); //msleep(10); gpio_set_value(ts->config_info.rst_gpio_number, 0); msleep(50); gpio_set_value(ts->config_info.rst_gpio_number, 1); //msleep(250); msleep(5); chip_solfware_reset(ts->driver->client);//20140306 } static void vtl_ts_wakeup(void) { struct ts_info *ts; ts =pg_ts; DEBUG(); gpio_set_value(ts->config_info.rst_gpio_number, 0); //msleep(50); msleep(20); gpio_set_value(ts->config_info.rst_gpio_number, 1); msleep(5); chip_solfware_reset(ts->driver->client);//20140306 } static irqreturn_t vtl_ts_irq(int irq, void *dev) { struct ts_info *ts; ts =pg_ts; DEBUG(); disable_irq_nosync(ts->config_info.irq_number);// Disable ts interrupt thread_syn_flag=1; wake_up_interruptible(&waiter); return IRQ_HANDLED; } static int vtl_ts_read_xy_data(struct ts_info *ts) { struct i2c_msg msgs; int ret; DEBUG(); msgs.addr = ts->driver->client->addr; msgs.flags = 0x01; // 0x00: write 0x01:read msgs.len = sizeof(ts->xy_data.buf); msgs.buf = ts->xy_data.buf; // msgs.scl_rate = TS_I2C_SPEED; ///only for rockchip platform ret = i2c_transfer( ts->driver->client->adapter, &msgs, 1); if(ret != 1){ printk("___%s:i2c read xy_data err___\n",__func__); return -1; } return 0; #if 0 ret = vtl_ts_i2c_read(client,client->addr,ts->xy_data.buf,sizeof(ts->xy_data.buf)); if(ret){ printk("___%s:i2c read err___\n",__func__); return -1; } return 0; #endif } static void vtl_ts_report_xy_coord(struct ts_info *ts) { int id; int sync; int x, y; unsigned int press; unsigned char touch_point_number; static unsigned int release = 0; struct input_dev *input_dev; union ts_xy_data *xy_data; DEBUG(); xy_data = &ts->xy_data; input_dev = ts->driver->input_dev; touch_point_number = ts->config_info.touch_point_number; /* report points */ sync = 0; press = 0; for ( id = 0; id point[id].xhi != 0xFF) && (xy_data->point[id].yhi != 0xFF) && ( (xy_data->point[id].status == 1) || (xy_data->point[id].status == 2))) { /* printk("--->>> vtl_ts report: xy_swap %d, x_reverse %d, y_reverse %d, x_mul %d, y_mul %d, bin_ver %d\n", ts->config_info.xy_swap, ts->config_info.x_reverse, ts->config_info.y_reverse, ts->config_info.x_mul, ts->config_info.y_mul, ts->config_info.bin_ver); */ if (ts->config_info.xy_swap == 1) { x = (xy_data->point[id].yhi<<4)|(xy_data->point[id].ylo&0xF); y = (xy_data->point[id].xhi<<4)|(xy_data->point[id].xlo&0xF); } else { x = (xy_data->point[id].xhi<<4)|(xy_data->point[id].xlo&0xF); y = (xy_data->point[id].yhi<<4)|(xy_data->point[id].ylo&0xF); } if (ts->config_info.x_reverse) x = ts->config_info.screen_max_x - x; if (ts->config_info.y_reverse) y = ts->config_info.screen_max_y - y; x = ts->config_info.x_mul*x; y = ts->config_info.x_mul*y; //#if(DEBUG_ENABLE) //if((ts->debug)||(DEBUG_ENABLE)){ if(ts->debug){ printk("id = %d,status = %d,X = %d,Y = %d\n",xy_data->point[id].id,xy_data->point[id].status,x,y); //XY_DEBUG(xy_data->point[id].id,xy_data->point[id].status,x,y); } //#endif input_mt_slot(input_dev, xy_data->point[id].id - 1); input_report_abs(input_dev, ABS_MT_TRACKING_ID, xy_data->point[id].id-1); //input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true); input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 1); input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 1); press |= 0x01 << (xy_data->point[id].id - 1); sync = 1; } } release &= (release ^ press);//release point flag for ( id = 0; id < touch_point_number; id++ ) //up { if ( release & (0x01<config_info.ctp_used) { vtl_first_init_flag = 0; disable_irq(ts->config_info.irq_number); chip_enter_sleep_mode(); for(i=0;iconfig_info.touch_point_number;i++) { input_mt_slot(ts->driver->input_dev,i); input_report_abs(ts->driver->input_dev, ABS_MT_TRACKING_ID, -1); //input_mt_report_slot_state(ts->driver->input_dev, MT_TOOL_FINGER, false); } input_sync(ts->driver->input_dev); } return 0; } int vtl_ts_resume(struct i2c_client *client) { struct ts_info *ts; unsigned char i; ts =pg_ts; DEBUG(); if(ts->config_info.ctp_used) { /* Hardware reset */ //vtl_ts_hw_reset(); vtl_ts_wakeup(); for(i=0;iconfig_info.touch_point_number;i++) { input_mt_slot(ts->driver->input_dev,i); input_report_abs(ts->driver->input_dev, ABS_MT_TRACKING_ID, -1); //input_mt_report_slot_state(ts->driver->input_dev, MT_TOOL_FINGER, false); } input_sync(ts->driver->input_dev); if(vtl_first_init_flag==0) enable_irq(ts->config_info.irq_number); } return 0; } static int vtl_ts_early_suspend(struct tp_device *tp) { struct ts_info *ts; ts =pg_ts; DEBUG(); vtl_ts_suspend(ts->driver->client, PMSG_SUSPEND); return 0; } static int vtl_ts_early_resume(struct tp_device *tp) { struct ts_info *ts; ts =pg_ts; DEBUG(); vtl_ts_resume(ts->driver->client); return 0; } static void vtl_ts_remove(struct i2c_client *client) { struct ts_info *ts; ts =pg_ts; DEBUG(); free_irq(ts->config_info.irq_number, ts); gpio_free(ts->config_info.rst_gpio_number); //vtl_ts_free_gpio(); //#ifdef CONFIG_HAS_EARLYSUSPEND //unregister_early_suspend(&ts->driver->early_suspend); tp_unregister_fb(&ts->tp); //#endif if(ts->driver->input_dev != NULL) { input_unregister_device(ts->driver->input_dev); } if ( ts->driver->proc_entry != NULL ){ remove_proc_entry(DRIVER_NAME, NULL); } if(ts->driver->ts_thread != NULL) { printk("___kthread stop start___\n"); thread_syn_flag=1; wake_up_interruptible(&waiter); kthread_stop(ts->driver->ts_thread); ts->driver->ts_thread = NULL; printk("___kthread stop end___\n"); } } static int vtl_ts_init_input_dev(struct ts_info *ts) { struct input_dev *input_dev; struct device *dev; int err; DEBUG(); dev = &ts->driver->client->dev; /* allocate input device */ ts->driver->input_dev = input_allocate_device(); if ( ts->driver->input_dev == NULL ) { dev_err(dev, "Unable to allocate input device for device %s.\n", DRIVER_NAME); return -1; } input_dev = ts->driver->input_dev; input_dev->name = DRIVER_NAME; input_dev->id.bustype = BUS_I2C; input_dev->id.vendor = 0xaaaa; input_dev->id.product = 0x5555; input_dev->id.version = 0x0001; /* config input device */ __set_bit(EV_SYN, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_ABS, input_dev->evbit); //set_bit(BTN_TOUCH, input_dev->keybit);//20130923 //set_bit(ABS_MT_POSITION_X, input_dev->absbit);//20130923 //set_bit(ABS_MT_POSITION_Y, input_dev->absbit);//20130923 __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); input_mt_init_slots(input_dev, TOUCH_POINT_NUM,0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ts->config_info.screen_max_x, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ts->config_info.screen_max_y, 0, 0); input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,ts->config_info.touch_point_number, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); /* register input device */ err = input_register_device(input_dev); if ( err ) { input_free_device(ts->driver->input_dev); ts->driver->input_dev = NULL; dev_err(dev, "Unable to register input device for device %s.\n", DRIVER_NAME); return -1; } return 0; } static int vtl_ts_handler(void *data) { int ret; struct device *dev; struct ts_info *ts; //struct sched_param param = { .sched_priority = TS_THREAD_PRIO}; DEBUG(); //sched_setscheduler(current, SCHED_RR, ¶m); ts = (struct ts_info *)data; dev = &ts->driver->client->dev; /* Request platform resources (gpio/interrupt pins) */ ret = vtl_ts_config(ts); if(ret){ dev_err(dev, "VTL touch screen config Failed.\n"); goto ERR_TS_CONFIG; } vtl_ts_hw_reset(); ret = chip_init(); if(ret){ dev_err(dev, "vtl ts chip init failed.\n"); goto ERR_CHIP_INIT; } /*init input dev*/ ret = vtl_ts_init_input_dev(ts); if(ret){ dev_err(dev, "init input dev failed.\n"); goto ERR_INIT_INPUT; } /* Create Proc Entry File */ #if 0 ts->driver->proc_entry = create_proc_entry(DRIVER_NAME, 0666/*S_IFREG | S_IRUGO | S_IWUSR*/, NULL); if ( ts->driver->proc_entry == NULL ) { dev_err(dev, "Failed creating proc dir entry file.\n"); goto ERR_PROC_ENTRY; } else{ ts->driver->proc_entry->proc_fops = &apk_fops; } #endif /* register early suspend */ //#ifdef CONFIG_HAS_EARLYSUSPEND //ts->driver->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; //ts->driver->early_suspend.suspend = vtl_ts_early_suspend; //ts->driver->early_suspend.resume = vtl_ts_early_resume; //register_early_suspend(&ts->driver->early_suspend); //#endif /* Init irq */ ret = request_irq(ts->config_info.irq_number, vtl_ts_irq, IRQF_TRIGGER_FALLING, DRIVER_NAME, ts); if ( ret ) { dev_err(dev, "Unable to request irq for device %s.\n", DRIVER_NAME); goto ERR_IRQ_REQ; } //disable_irq(pg_ts->config_info.irq_number); ts->config_info.ctp_used =1; ts->tp.tp_resume = vtl_ts_early_resume; ts->tp.tp_suspend = vtl_ts_early_suspend; tp_register_fb(&ts->tp); while (!kthread_should_stop())//while(1) { //set_current_state(TASK_INTERRUPTIBLE); wait_event_interruptible(waiter, thread_syn_flag); thread_syn_flag = 0; //set_current_state(TASK_RUNNING); //printk("__state = %x_%x_\n",current->state,ts->driver->ts_thread->state); ret = vtl_ts_read_xy_data(ts); if(!ret){ vtl_ts_report_xy_coord(ts); } else { printk("____read xy_data error___\n"); } // Enable ts interrupt enable_irq(pg_ts->config_info.irq_number); } printk("vtl_ts_Kthread exit,%s(%d)\n",__func__,__LINE__); return 0; ERR_IRQ_REQ: //#ifdef CONFIG_HAS_EARLYSUSPEND //unregister_early_suspend(&ts->driver->early_suspend); //#endif tp_unregister_fb(&ts->tp); if ( ts->driver->proc_entry ){ remove_proc_entry(DRIVER_NAME, NULL); ts->driver->proc_entry = NULL; } /* ERR_PROC_ENTRY: */ if(ts->driver->input_dev){ input_unregister_device(ts->driver->input_dev); ts->driver->input_dev = NULL; } ERR_INIT_INPUT: ERR_CHIP_INIT: gpio_free(ts->config_info.rst_gpio_number); ERR_TS_CONFIG: ts->config_info.ctp_used =0; printk("vtl_ts_Kthread exit,%s(%d)\n",__func__,__LINE__); //do_exit(0); return 0; } int vtl_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err = -1; struct ts_info *ts; struct device *dev; DEBUG(); ts = pg_ts; ts->driver->client = client; dev = &ts->driver->client->dev; /* Check I2C Functionality */ err = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); if ( !err ) { dev_err(dev, "Check I2C Functionality Failed.\n"); return ENODEV; } //ts->driver->ts_thread = kthread_run(vtl_ts_handler, NULL, DRIVER_NAME); ts->driver->ts_thread = kthread_run(vtl_ts_handler, ts, DRIVER_NAME); if (IS_ERR(ts->driver->ts_thread)) { err = PTR_ERR(ts->driver->ts_thread); ts->driver->ts_thread = NULL; dev_err(dev, "failed to create kernel thread: %d\n", err); return -1; //goto ERR_CREATE_TS_THREAD; } printk("___%s() end____ \n", __func__); return 0; } static struct of_device_id vtl_ts_dt_ids[] = { { .compatible = "ct,vtl_ts" }, { } }; struct i2c_driver vtl_ts_driver = { .driver = { .owner = THIS_MODULE, .name = DRIVER_NAME, .of_match_table = of_match_ptr(vtl_ts_dt_ids), }, .id_table = vtl_ts_id, .probe = vtl_ts_probe, //#ifndef CONFIG_HAS_EARLYSUSPEND //.suspend = vtl_ts_suspend, //.resume = vtl_ts_resume, //#endif .remove = vtl_ts_remove, }; int __init vtl_ts_init(void) { DEBUG(); return i2c_add_driver(&vtl_ts_driver); } void __exit vtl_ts_exit(void) { DEBUG(); i2c_del_driver(&vtl_ts_driver); } module_init(vtl_ts_init); module_exit(vtl_ts_exit); MODULE_AUTHOR("yangdechu@vtl.com.cn"); MODULE_DESCRIPTION("VTL touchscreen driver for rockchip,V1.0"); MODULE_LICENSE("GPL");