From c71cce98584119c7d0a65c0d57695048727dff0c Mon Sep 17 00:00:00 2001 From: linjh Date: Thu, 2 Jun 2011 12:28:56 +0800 Subject: [PATCH] phonepad: add lcd hsd07pfw1; tp d70_l3188a --- arch/arm/configs/rk29_phonepadsdk_defconfig | 9 +- arch/arm/mach-rk29/board-rk29phonepadsdk.c | 19 + drivers/input/touchscreen/Kconfig | 3 + drivers/input/touchscreen/Makefile | 2 + drivers/input/touchscreen/goodix_touch.c | 902 +++++++++++++++++++ drivers/video/display/screen/Kconfig | 2 + drivers/video/display/screen/Makefile | 1 + drivers/video/display/screen/lcd_hsd07pfw1.c | 77 ++ include/linux/goodix_queue.h | 120 +++ include/linux/goodix_touch.h | 77 ++ 10 files changed, 1207 insertions(+), 5 deletions(-) create mode 100755 drivers/input/touchscreen/goodix_touch.c create mode 100755 drivers/video/display/screen/lcd_hsd07pfw1.c create mode 100755 include/linux/goodix_queue.h create mode 100755 include/linux/goodix_touch.h diff --git a/arch/arm/configs/rk29_phonepadsdk_defconfig b/arch/arm/configs/rk29_phonepadsdk_defconfig index 37ffb0994648..c8865f3cd5e5 100755 --- a/arch/arm/configs/rk29_phonepadsdk_defconfig +++ b/arch/arm/configs/rk29_phonepadsdk_defconfig @@ -1050,12 +1050,10 @@ CONFIG_INPUT_TOUCHSCREEN=y # CONFIG_HANNSTAR_P1003 is not set # CONFIG_ATMEL_MXT224 is not set # CONFIG_SINTEK_3FA16 is not set -CONFIG_EETI_EGALAX=y -CONFIG_EETI_EGALAX_MAX_X=1087 -CONFIG_EETI_EGALAX_MAX_Y=800 -# CONFIG_EETI_EGALAX_DEBUG is not set +# CONFIG_EETI_EGALAX is not set # CONFIG_TOUCHSCREEN_IT7260 is not set # CONFIG_TOUCHSCREEN_GT801_IIC is not set +CONFIG_D70_L3188A=y # CONFIG_INPUT_MISC is not set CONFIG_INPUT_GPIO=y @@ -1442,7 +1440,8 @@ CONFIG_DISPLAY_SUPPORT=y # CONFIG_LCD_HL070VM4AU is not set # CONFIG_LCD_HSD070IDW1 is not set # CONFIG_LCD_RGB_TFT480800_25_E is not set -CONFIG_LCD_HSD100PXN=y +# CONFIG_LCD_HSD100PXN is not set +CONFIG_LCD_HSD07PFW1=y # CONFIG_LCD_B101AW06 is not set # CONFIG_LCD_LS035Y8DX02A is not set # CONFIG_LCD_A060SE02 is not set diff --git a/arch/arm/mach-rk29/board-rk29phonepadsdk.c b/arch/arm/mach-rk29/board-rk29phonepadsdk.c index 97345b2f3ad1..10afc5c52c3c 100644 --- a/arch/arm/mach-rk29/board-rk29phonepadsdk.c +++ b/arch/arm/mach-rk29/board-rk29phonepadsdk.c @@ -57,6 +57,8 @@ #endif #include "../../../drivers/input/touchscreen/xpt2046_cbn_ts.h" +#include + #ifdef CONFIG_VIDEO_RK29 /*---------------- Camera Sensor Macro Define Begin ------------------------*/ /*---------------- Camera Sensor Configuration Macro Begin ------------------------*/ @@ -447,6 +449,14 @@ static struct eeti_egalax_platform_data eeti_egalax_info = { .disp_on_value = TOUCH_SCREEN_DISPLAY_VALUE, }; #endif + +#if defined (CONFIG_D70_L3188A) +struct goodix_i2c_rmi_platform_data d70_l3188a_info = { + .shutdown_pin = RK29_PIN4_PD5, + .irq_pin = RK29_PIN0_PA2, +}; +#endif + /*MMA8452 gsensor*/ #if defined (CONFIG_GS_MMA8452) #define MMA8452_INT_PIN RK29_PIN0_PA3 @@ -699,6 +709,15 @@ static struct i2c_board_info __initdata board_i2c2_devices[] = { .platform_data = &eeti_egalax_info, }, #endif +#if defined (CONFIG_D70_L3188A) + { + .type = "goodix-ts", + .addr = 0x55, + .flags = 0, + .irq = RK29_PIN0_PA2, + .platform_data = &d70_l3188a_info, + }, +#endif }; #endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 90678fb41d6a..7e5f9e692722 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -752,4 +752,7 @@ config TOUCHSCREEN_IT7260 config TOUCHSCREEN_GT801_IIC tristate "GT801_IIC based touchscreens" depends on I2C2_RK29 +config D70_L3188A + tristate "D70-L3188A based touchscreens" + depends on I2C2_RK29 endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d5c52e43a8a4..7680d487fe67 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -55,4 +55,6 @@ obj-$(CONFIG_EETI_EGALAX) += eeti_egalax_i2c.o obj-$(CONFIG_ATMEL_MXT224) += atmel_maxtouch.o obj-$(CONFIG_TOUCHSCREEN_GT801_IIC) += gt801_ts.o obj-$(CONFIG_TOUCHSCREEN_ILI2102_IIC) += ili2102_ts.o +obj-$(CONFIG_D70_L3188A) += goodix_touch.o + diff --git a/drivers/input/touchscreen/goodix_touch.c b/drivers/input/touchscreen/goodix_touch.c new file mode 100755 index 000000000000..57797bbd45f2 --- /dev/null +++ b/drivers/input/touchscreen/goodix_touch.c @@ -0,0 +1,902 @@ +/*--------------------------------------------------------------------------------------------------------- + * driver/input/touchscreen/goodix_touch.c + * + * Copyright(c) 2010 Goodix Technology Corp. All rights reserved. + * Author: Eltonny + * Date: 2010.11.11 + * + *---------------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +//#include +//#include +#include +#include +#include +#include +#include + +#ifndef GUITAR_GT80X +#error The code does not match the hardware version. +#endif + +static struct workqueue_struct *goodix_wq; + +/******************************************** +* 管理当前手指状态的伪队列,对当前手指根据时间顺序排序 +* 适用于Guitar小屏 */ +static struct point_queue finger_list; //record the fingers list +/*************************************************/ + +const char *rk29_ts_name = "Goodix TouchScreen of GT80X"; +/*used by guitar_update module */ +struct i2c_client * i2c_connect_client = NULL; +EXPORT_SYMBOL(i2c_connect_client); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void goodix_ts_early_suspend(struct early_suspend *h); +static void goodix_ts_late_resume(struct early_suspend *h); +#endif + +/*read the gt80x register ,used i2c bus*/ +static int gt80x_read_regs(struct i2c_client *client, u8 reg, u8 buf[], unsigned len) +{ + int ret; + ret =i2c_master_reg8_recv(client, reg, buf, len, 200*1000); + if(ret < 0) + dev_err(&client->dev,"i2c_read fail =%d\n",ret); + return ret; +} +/* set the gt80x registe,used i2c bus*/ +static int gt80x_write_regs(struct i2c_client *client, u8 reg, u8 const buf[], unsigned short len) +{ + int ret; + ret = i2c_master_reg8_send(client,reg, buf, len, 200*1000); + if (ret < 0) { + dev_err(&client->dev,"i2c_write fail =%d\n",ret); + } + return ret; +} +#if 0 +/******************************************************* +功能: + 读取从机数据 + 每个读操作用两条i2c_msg组成,第1条消息用于发送从机地址, + 第2条用于发送读取地址和取回数据;每条消息前发送起始信号 +参数: + client: i2c设备,包含设备地址 + buf[0]: 首字节为读取地址 + buf[1]~buf[len]:数据缓冲区 + len: 读取数据长度 +return: + 执行消息数 +*********************************************************/ +/*Function as i2c_master_send */ +static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int 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[0]; + //接收数据 + msgs[1].flags=I2C_M_RD;//读消息 + msgs[1].addr=client->addr; + msgs[1].len=len-1; + msgs[1].buf=&buf[1]; + + ret=i2c_transfer(client->adapter,msgs,2); + return ret; +} + +/******************************************************* +功能: + 向从机写数据 +参数: + client: i2c设备,包含设备地址 + buf[0]: 首字节为写地址 + buf[1]~buf[len]:数据缓冲区 + len: 数据长度 +return: + 执行消息数 +*******************************************************/ +/*Function as i2c_master_send */ +static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int 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; +} +#endif +/******************************************************* +功能: + Guitar初始化函数,用于发送配置信息,获取版本信息 +参数: + ts: client私有数据结构体 +return: + 执行结果码,0表示正常执行 +*******************************************************/ +static int goodix_init_panel(struct goodix_ts_data *ts) +{ + int i, ret = -1; + u8 start_reg = 0x30; + u8 buf[53]; + uint8_t config_info[53] = { + 0x13,0x05,0x04,0x28,0x02,0x14,0x14,0x10,0x50,0xBA, + 0x14,0x00,0x1E,0x00,0x01,0x23,0x45,0x67,0x89,0xAB, + 0xCD,0xE1,0x00,0x00,0x00,0x00,0x4D,0xC1,0x20,0x01, + 0x01,0x83,0x50,0x3C,0x1E,0xB4,0x00,0x33,0x2C,0x01, + 0xEC,0x3C,0x64,0x32,0x71,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x01 + }; + + ret = gt80x_write_regs(ts->client, start_reg, config_info, 53); + if (ret < 0) { + printk("download gt80x firmware err %d\n", ret); + goto error_i2c_transfer; + } + msleep(10); +#if 0 + ret = gt80x_read_regs(ts->client, start_reg, buf, 53); + if (ret < 0) + { + printk("\n--%s--Read Register values error !!!\n",__FUNCTION__); + } + + for (i = 0; i < 53; i++) + { + if (buf[i] != config_info[i]) + { + dev_err(&ts->client->dev,"may be i2c errorat reg_add[%d] = %#x current value = %#x expect value = %#x\n", + i, 0x30 + i, buf[i], config_info[i]); + break; + } + } +#endif + return 0; + +error_i2c_transfer: + return ret; +} +/*读取GT80X的版本号并打印*/ +static int goodix_read_version(struct goodix_ts_data *ts) +{ + int ret; + uint8_t reg1 = 0x69; + uint8_t reg2 = 0x6a; + //uint8_t version[2]={0x69,0xff}; //command of reading Guitar's version + uint8_t version[1]={0xff}; //command of reading Guitar's version + //uint8_t version_data[41]; //store touchscreen version infomation + uint8_t version_data[40]; //store touchscreen version infomation + memset(version_data, 0 , sizeof(version_data)); + //version_data[0]=0x6A; + //ret=i2c_write_bytes(ts->client,version,2); + ret = gt80x_write_regs(ts->client, reg1, version, 1); + if (ret < 0) + goto error_i2c_version; + msleep(16); + + //ret=i2c_read_bytes(ts->client,version_data, 40); + ret = gt80x_read_regs(ts->client, reg2, version_data, 40); + if (ret < 0) + goto error_i2c_version; + //dev_info(&ts->client->dev," Guitar Version: %s\n", &version_data[1]); + printk("%s: Guitar Version: %s\n", __func__, version_data); + + version[0] = 0x00; //cancel the command + //i2c_write_bytes(ts->client, version, 2); + ret = gt80x_write_regs(ts->client, reg1, version, 1); + return 0; + +error_i2c_version: + return ret; +} + +/******************************************************* +功能: + 触摸屏工作函数 + 由中断触发,接受1组坐标数据,校验后再分析输出 +参数: + ts: client私有数据结构体 +return: + 执行结果码,0表示正常执行 +********************************************************/ +static void goodix_ts_work_func(struct work_struct *work) +{ + static uint8_t finger_bit = 0; //last time fingers' state + uint8_t read_position = 0; + uint8_t point_data[35] = {0}; + uint8_t finger = 0; //record which finger is changed + int ret = -1; + int count = 0; + int check_sum = 0; + //unsigned char start_reg = 0x00; + struct goodix_ts_data *ts = container_of(work, struct goodix_ts_data, work); + struct goodix_i2c_rmi_platform_data *pdata = ts->client->dev.platform_data; + +//#ifdef SHUTDOWN_PORT + if (pdata && pdata->shutdown_pin) { + //if (gpio_get_value(SHUTDOWN_PORT)) + if (gpio_get_value(pdata->shutdown_pin)) + { + //printk(KERN_ALERT "Guitar stop working.The data is invalid. \n"); + goto NO_ACTION; + } + } +//#endif + + //if i2c transfer is failed, let it restart less than 10 times + if (ts->retry > 9) { + if(!ts->use_irq && (ts->timer.state != HRTIMER_STATE_INACTIVE)) + hrtimer_cancel(&ts->timer); + dev_info(&(ts->client->dev), "Because of transfer error, %s stop working.\n",rk29_ts_name); + return ; + } + if(ts->bad_data) + msleep(16); + + ret = gt80x_read_regs(ts->client, point_data[0], &point_data[1], 34); + if(ret <= 0) + { + //dev_err(&(ts->client->dev),"I2C transfer error. Number:%d\n ", ret); + ts->bad_data = 1; + ts->retry++; + if(ts->power) + { + ts->power(ts, 0); + ts->power(ts, 1); + } + else + { + goodix_init_panel(ts); + msleep(500); + } + goto XFER_ERROR; + } + ts->bad_data = 0; + + //The bit indicate which fingers pressed down + switch (point_data[1] & 0x1f) + { + case 0: + case 1: + for (count = 1; count < 8; count++) + check_sum += (int)point_data[count]; + if ((check_sum % 256) != point_data[8]) + goto XFER_ERROR; + break; + case 2: + case 3: + for ( count = 1; count < 13; count++) + check_sum += (int)point_data[count]; + if ((check_sum % 256) != point_data[13]) + goto XFER_ERROR; + break; + default: //(point_data[1]& 0x1f) > 3 + for (count = 1; count < 34; count++) + check_sum += (int)point_data[count]; + if ((check_sum % 256) != point_data[34]) + goto XFER_ERROR; + } + + point_data[1] &= 0x1f; + finger = finger_bit ^ point_data[1]; + if (finger == 0 && point_data[1] == 0) + goto NO_ACTION; //no fingers and no action + else if(finger == 0) //the same as last time + goto BIT_NO_CHANGE; + //check which point(s) DOWN or UP + for (count = 0; (finger != 0) && (count < MAX_FINGER_NUM); count++) + { + if ((finger & 0x01) == 1) //current bit is 1, so NO.postion finger is change + { + if (((finger_bit >> count) & 0x01) ==1 ) //NO.postion finger is UP + set_up_point(&finger_list, count); + else + add_point(&finger_list, count); + } + finger >>= 1; + } + +BIT_NO_CHANGE: + for(count = 0; count < finger_list.length; count++) + { + if(finger_list.pointer[count].state == FLAG_UP) + { + finger_list.pointer[count].x = finger_list.pointer[count].y = 0; + finger_list.pointer[count].pressure = 0; + continue; + } + + if(finger_list.pointer[count].num < 3) + read_position = finger_list.pointer[count].num*5 + 3; + else if (finger_list.pointer[count].num == 4) + read_position = 29; + + if(finger_list.pointer[count].num != 3) + { + finger_list.pointer[count].x = (unsigned int) (point_data[read_position]<<8) + (unsigned int)( point_data[read_position+1]); + finger_list.pointer[count].y = (unsigned int)(point_data[read_position+2]<<8) + (unsigned int) (point_data[read_position+3]); + finger_list.pointer[count].pressure = (unsigned int) (point_data[read_position+4]); + } + else + { + finger_list.pointer[count].x = (unsigned int) (point_data[18]<<8) + (unsigned int)( point_data[25]); + finger_list.pointer[count].y = (unsigned int)(point_data[26]<<8) + (unsigned int) (point_data[27]); + finger_list.pointer[count].pressure = (unsigned int) (point_data[28]); + } + + // 将触摸屏的坐标映射到LCD坐标上. 触摸屏短边为X轴,LCD坐标一般长边为X轴,可能需要调整原点位置 + finger_list.pointer[count].x = (TOUCH_MAX_WIDTH - finger_list.pointer[count].x)*SCREEN_MAX_WIDTH/TOUCH_MAX_WIDTH;//y + finger_list.pointer[count].y = finger_list.pointer[count].y*SCREEN_MAX_HEIGHT/TOUCH_MAX_HEIGHT ; //x + gt80xy_swap(finger_list.pointer[count].x, finger_list.pointer[count].y); + + //printk("%s: dx = %d dy = %d\n", __func__, + // finger_list.pointer[count].x, finger_list.pointer[count].y); + + } + +#ifndef GOODIX_MULTI_TOUCH + if(finger_list.pointer[0].state == FLAG_DOWN) + { + input_report_abs(ts->input_dev, ABS_X, finger_list.pointer[0].x); + input_report_abs(ts->input_dev, ABS_Y, finger_list.pointer[0].y); + } + input_report_abs(ts->input_dev, ABS_PRESSURE, pressure[0]); + input_report_key(ts->input_dev, BTN_TOUCH, finger_list.pointer[0].state); +#else + + /* ABS_MT_TOUCH_MAJOR is used as ABS_MT_PRESSURE in android. */ + for(count = 0; count < (finger_list.length); count++) + { + if(finger_list.pointer[count].state == FLAG_DOWN) + { + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, finger_list.pointer[count].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, finger_list.pointer[count].y); + } + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, finger_list.pointer[count].pressure); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, finger_list.pointer[count].pressure); + input_mt_sync(ts->input_dev); + } + #if 0 + read_position = finger_list.length-1; + if(finger_list.length > 0 && finger_list.pointer[read_position].state == FLAG_DOWN) + { + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, finger_list.pointer[read_position].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, finger_list.pointer[read_position].y); + } + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, finger_list.pointer[read_position].pressure); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, finger_list.pointer[read_position].pressure); + input_mt_sync(ts->input_dev); + #endif +#endif + input_sync(ts->input_dev); + + del_point(&finger_list); + finger_bit=point_data[1]; + +XFER_ERROR: +NO_ACTION: + if(ts->use_irq) + enable_irq(ts->client->irq); + +} + +/******************************************************* +功能: + 计时器响应函数 + 由计时器触发,调度触摸屏工作函数运行;之后重新计时 +参数: + timer:函数关联的计时器 +return: + 计时器工作模式,HRTIMER_NORESTART表示不需要自动重启 +********************************************************/ +static enum hrtimer_restart goodix_ts_timer_func(struct hrtimer *timer) +{ + struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer); + queue_work(goodix_wq, &ts->work); + if(ts->timer.state != HRTIMER_STATE_INACTIVE) + hrtimer_start(&ts->timer, ktime_set(0, 16000000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +/******************************************************* +功能: + 中断响应函数 + 由中断触发,调度触摸屏处理函数运行 +参数: + timer:函数关联的计时器 +return: + 计时器工作模式,HRTIMER_NORESTART表示不需要自动重启 +********************************************************/ +//#if defined(INT_PORT) +static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) +{ + struct goodix_ts_data *ts = dev_id; + + disable_irq_nosync(ts->client->irq); + queue_work(goodix_wq, &ts->work); + + return IRQ_HANDLED; +} +//#endif + +/******************************************************* +功能: + GT80X的电源管理 +参数: + on:设置GT80X运行模式,0为进入Sleep模式 +return: + 是否设置成功,小于0表示设置失败 +********************************************************/ +//#if defined(SHUTDOWN_PORT) +static int goodix_ts_power(struct goodix_ts_data * ts, int on) +{ + int ret = -1; + struct goodix_i2c_rmi_platform_data *pdata = NULL; + + if(ts == NULL || (ts && !ts->use_shutdown)) + return -1; + + pdata = ts->client->dev.platform_data; + + switch(on) + { + case 0: + gpio_set_value(pdata->shutdown_pin, 1); + msleep(5); + if(gpio_get_value(pdata->shutdown_pin)) //has been suspend + ret = 0; + break; + case 1: + gpio_set_value(pdata->shutdown_pin, 0); + msleep(5); + if(gpio_get_value(pdata->shutdown_pin)) //has been suspend + ret = -1; + else + { + ret = goodix_init_panel(ts); + if(!ret) + msleep(500); + } + break; + //default:printk("Command ERROR.\n"); + } + printk(on?"Set Guitar's Shutdown LOW\n":"Set Guitar's Shutdown HIGH\n"); + return 0; +} +//#endif + +/******************************************************* +功能: + 触摸屏探测函数 + 在注册驱动时调用(要求存在对应的client); + 用于IO,中断等资源申请;设备注册;触摸屏初始化等工作 +参数: + client:待驱动的设备结构体 + id:设备ID +return: + 执行结果码,0表示正常执行 +********************************************************/ +static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct goodix_ts_data *ts; + int ret = 0; + int retry=0; + int count=0; + u8 test_reg = 0x30; + u8 test_buf[] = {0x01}; + + struct goodix_i2c_rmi_platform_data *pdata; + pdata = client->dev.platform_data; + + dev_dbg(&client->dev,"Install touchscreen driver for guitar.\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; + } + //Check I2C function +//#ifdef SHUTDOWN_PORT + if (pdata && pdata->shutdown_pin) { + //ret = gpio_request(SHUTDOWN_PORT, "TS_SHUTDOWN"); //Request IO + ret = gpio_request(pdata->shutdown_pin, "TS_SHUTDOWN"); //Request IO + if (ret < 0) + { + //printk(KERN_ALERT "Failed to request GPIO:%d, ERRNO:%d\n",(int)SHUTDOWN_PORT,ret); + printk(KERN_ALERT "Failed to request GPIO:%d, ERRNO:%d\n",(int)pdata->shutdown_pin,ret); + goto err_gpio_request; + } + //gpio_direction_output(SHUTDOWN_PORT, 0); //Touchscreen is waiting to wakeup + //ret = gpio_get_value(SHUTDOWN_PORT); + gpio_direction_output(pdata->shutdown_pin, 0); //Touchscreen is waiting to wakeup + ret = gpio_get_value(pdata->shutdown_pin); + if (ret) + { + printk(KERN_ALERT "Cannot set touchscreen to work.\n"); + goto err_i2c_failed; + } + } +//#endif + + i2c_connect_client = client; //used by Guitar Updating. + msleep(16); + + for (retry = 0; retry < 5; retry++) + { + ret = gt80x_write_regs(client, test_reg, test_buf, 1); //Test i2c. + if (ret > 0) + break; + } + if (ret < 0) + { + dev_err(&client->dev, "Warnning: I2C connection might be something wrong!\n"); + goto err_i2c_failed; + } + +//#ifdef SHUTDOWN_PORT + if (pdata && pdata->shutdown_pin) { + ts->use_shutdown = 1; + //gpio_set_value(SHUTDOWN_PORT, 1); + gpio_set_value(pdata->shutdown_pin, 1); + }//suspend +//#endif + + INIT_WORK(&ts->work, goodix_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + //pdata = client->dev.platform_data; + + 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_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; +#ifndef GOODIX_MULTI_TOUCH + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + // | BIT_MASK(ABS_MT_TOUCH_MAJOR)| BIT_MASK(ABS_MT_WIDTH_MAJOR) + // BIT_MASK(ABS_MT_POSITION_X) | + // BIT_MASK(ABS_MT_POSITION_Y); // for android + + input_set_abs_params(ts->input_dev, ABS_X, 0, SCREEN_MAX_HEIGHT, 0, 0); + input_set_abs_params(ts->input_dev, ABS_Y, 0, SCREEN_MAX_WIDTH, 0, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0); +#else + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_HEIGHT, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_WIDTH, 0, 0); +#endif + + sprintf(ts->phys, "input/ts)"); + ts->input_dev->name = rk29_ts_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 = 10427; + + finger_list.length = 0; + ret = input_register_device(ts->input_dev); + if (ret) { + dev_err(&client->dev,"Probe: Unable to register %s input device\n", ts->input_dev->name); + goto err_input_register_device_failed; + } + + ts->use_irq = 0; + ts->retry = 0; + ts->bad_data = 0; +//#if defined(INT_PORT) + //client->irq=TS_INT; + //gpio_set_value(TS_INT, 0); + + if (pdata && pdata->irq_pin) { + client->irq = gpio_to_irq(client->irq); + if (client->irq) + { + //ret = gpio_request(INT_PORT, "TS_INT"); //Request IO + ret = gpio_request(pdata->irq_pin, "TS_INT"); //Request IO + if (ret < 0) + { + //dev_err(&client->dev, "Failed to request GPIO:%d, ERRNO:%d\n",(int)INT_PORT,ret); + dev_err(&client->dev, "Failed to request GPIO:%d, ERRNO:%d\n", (int)pdata->irq_pin, ret); + goto err_int_request_failed; + } + //ret = s3c_gpio_cfgpin(INT_PORT, INT_CFG); //Set IO port function + ret = request_irq(client->irq, goodix_ts_irq_handler , IRQ_TYPE_EDGE_RISING, + client->name, ts); + if (ret != 0) { + dev_err(&client->dev,"Can't allocate touchscreen's interrupt!ERRNO:%d\n", ret); + //gpio_direction_input(INT_PORT); + //gpio_free(INT_PORT); + gpio_direction_input(pdata->irq_pin); + gpio_free(pdata->irq_pin); + goto err_int_request_failed; + } + else + { + //disable_irq(TS_INT); + disable_irq(client->irq); + ts->use_irq = 1; + dev_dbg(&client->dev,"Reques EIRQ %d succesd on GPIO:%d\n",client->irq, pdata->irq_pin); + } + } + } +//#endif + +err_int_request_failed: + if (!ts->use_irq) + { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = goodix_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } + +//#ifdef SHUTDOWN_PORT + if (pdata && pdata->shutdown_pin) { + //gpio_set_value(SHUTDOWN_PORT, 0); + gpio_set_value(pdata->shutdown_pin, 0); + msleep(10); + ts->power = goodix_ts_power; + } +//#endif + for(count = 0; count < 3; count++) + { + ret = goodix_init_panel(ts); + if(ret != 0) //Initiall failed + continue; + else + { + if(ts->use_irq) + enable_irq(client->irq); + //enable_irq(TS_INT); + break; + } + } + if(ret != 0) { + ts->bad_data = 1; + goto err_init_godix_ts; + } + goodix_read_version(ts); + msleep(500); + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = goodix_ts_early_suspend; + ts->early_suspend.resume = goodix_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + dev_info(&client->dev,"Start %s in %s mode\n", + ts->input_dev->name, ts->use_irq ? "Interrupt" : "Polling"); + return 0; + +err_init_godix_ts: + if(ts->use_irq) + { + //free_irq(TS_INT,ts); + free_irq(client->irq,ts); +//#if defined(INT_PORT) + if (pdata && pdata->irq_pin) + gpio_free(pdata->irq_pin); +//#endif + } + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: + i2c_set_clientdata(client, NULL); +err_i2c_failed: +//#ifdef SHUTDOWN_PORT + if (pdata && pdata->shutdown_pin) { + //gpio_direction_input(SHUTDOWN_PORT); + //gpio_free(SHUTDOWN_PORT); + gpio_direction_input(pdata->shutdown_pin); + gpio_free(pdata->shutdown_pin); + } +//#endif +err_gpio_request: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + + +/******************************************************* +功能: + 驱动资源释放 +参数: + client:设备结构体 +return: + 执行结果码,0表示正常执行 +********************************************************/ +static int goodix_ts_remove(struct i2c_client *client) +{ + struct goodix_ts_data *ts = i2c_get_clientdata(client); + struct goodix_i2c_rmi_platform_data *pdata; + pdata = client->dev.platform_data; +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + if (ts->use_irq) + { + free_irq(client->irq, ts); +//#if defined(INT_PORT) + if (pdata && pdata->irq_pin) + gpio_free(pdata->irq_pin); + //gpio_free(INT_PORT); +//#endif + } + else + hrtimer_cancel(&ts->timer); + +//#ifdef SHUTDOWN_PORT + if (pdata && pdata->shutdown_pin) { + if(ts->use_shutdown) + { + //gpio_direction_input(SHUTDOWN_PORT); + //gpio_free(SHUTDOWN_PORT); + gpio_direction_input(pdata->shutdown_pin); + gpio_free(pdata->shutdown_pin); + } + } +//#endif + dev_notice(&client->dev,"The driver is removing...\n"); + i2c_set_clientdata(client, NULL); + input_unregister_device(ts->input_dev); + if(ts->input_dev) + kfree(ts->input_dev); + kfree(ts); + return 0; +} + +//停用设备 +static int goodix_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->use_irq) + disable_irq(client->irq); + else if(ts->timer.state) + hrtimer_cancel(&ts->timer); + ret = cancel_work_sync(&ts->work); + if(ret && ts->use_irq) + enable_irq(client->irq); + if (ts->power) { + ret = ts->power(ts,0); + if (ret < 0) + printk(KERN_ERR "%s power off failed\n", rk29_ts_name); + } + return 0; +} +//重新唤醒 +static int goodix_ts_resume(struct i2c_client *client) +{ + int ret; + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(ts, 1); + if (ret < 0) + printk(KERN_ERR "%s power on failed\n", rk29_ts_name); + } + + if (ts->use_irq) + enable_irq(client->irq); + else + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void goodix_ts_early_suspend(struct early_suspend *h) +{ + struct goodix_ts_data *ts; + ts = container_of(h, struct goodix_ts_data, early_suspend); + goodix_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void goodix_ts_late_resume(struct early_suspend *h) +{ + struct goodix_ts_data *ts; + ts = container_of(h, struct goodix_ts_data, early_suspend); + goodix_ts_resume(ts->client); +} +#endif + +//可用于该驱动的 设备名—设备ID 列表 +//only one client +static const struct i2c_device_id goodix_ts_id[] = { + { GOODIX_I2C_NAME, 0 }, + { } +}; + +//设备驱动结构体 +static struct i2c_driver goodix_ts_driver = { + .probe = goodix_ts_probe, + .remove = goodix_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = goodix_ts_suspend, + .resume = goodix_ts_resume, +#endif + .id_table = goodix_ts_id, + .driver = { + .name = GOODIX_I2C_NAME, + .owner = THIS_MODULE, + }, +}; + +/******************************************************* +功能: + 驱动加载函数 +return: + 执行结果码,0表示正常执行 +********************************************************/ +static int __devinit goodix_ts_init(void) +{ + int ret; + //printk(KERN_DEBUG "%s is installing...\n", rk29_ts_name); + goodix_wq = create_workqueue("goodix_wq"); + if (!goodix_wq) { + printk(KERN_ALERT "Creat workqueue faiked\n"); + return -ENOMEM; + + } + ret=i2c_add_driver(&goodix_ts_driver); + return ret; +} + +/******************************************************* +功能: + 驱动卸载函数 +参数: + client:设备结构体 +********************************************************/ +static void __exit goodix_ts_exit(void) +{ + printk(KERN_DEBUG "%s is exiting...\n", rk29_ts_name); + i2c_del_driver(&goodix_ts_driver); + if (goodix_wq) + destroy_workqueue(goodix_wq); +} + +late_initcall(goodix_ts_init); +module_exit(goodix_ts_exit); + +MODULE_DESCRIPTION("Goodix Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/display/screen/Kconfig b/drivers/video/display/screen/Kconfig index cc508268fe47..0dea8eb9d0db 100755 --- a/drivers/video/display/screen/Kconfig +++ b/drivers/video/display/screen/Kconfig @@ -17,6 +17,8 @@ config LCD_RGB_TFT480800_25_E bool "RGB TFT480800_25_E" config LCD_HSD100PXN bool "RGB Hannstar HSD100PXN(1024X768)" +config LCD_HSD07PFW1 + bool "RGB Hannstar HSD07PFW1(1024X600)" config LCD_B101AW06 bool "RGB Hannstar B101AW06(1024X600)" config LCD_RGB_TFT480800_25_E diff --git a/drivers/video/display/screen/Makefile b/drivers/video/display/screen/Makefile index 2909467dec26..f75d34a0f3b4 100755 --- a/drivers/video/display/screen/Makefile +++ b/drivers/video/display/screen/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_LCD_CPTCLAA038LA31XE) += lcd_CPTclaa038la31xe.o obj-$(CONFIG_LCD_HX8357) += lcd_hx8357.o obj-$(CONFIG_LCD_HSD100PXN) += lcd_hsd100pxn.o +obj-$(CONFIG_LCD_HSD07PFW1) += lcd_hsd07pfw1.o obj-$(CONFIG_LCD_B101AW06) += lcd_B101AW06.o obj-$(CONFIG_LCD_NT35510) += lcd_nt35510.o obj-$(CONFIG_LCD_ILI9803_CPT4_3) += lcd_ili9803_cpt4_3.o diff --git a/drivers/video/display/screen/lcd_hsd07pfw1.c b/drivers/video/display/screen/lcd_hsd07pfw1.c new file mode 100755 index 000000000000..20686a796876 --- /dev/null +++ b/drivers/video/display/screen/lcd_hsd07pfw1.c @@ -0,0 +1,77 @@ +#include +#include +#include "../../rk29_fb.h" +#include +#include +#include +#include "screen.h" + + +/* Base */ +#define OUT_TYPE SCREEN_RGB + +//#define OUT_FACE OUT_D888_P666 +#define OUT_FACE OUT_P888 +#define OUT_CLK 45000000 // 65000000 +#define LCDC_ACLK 312000000//312000000 //29 lcdc axi DMA ƵÂÊ + +/* Timing */ +#define H_PW 3 +#define H_BP 176 +#define H_VD 1024 +#define H_FP 0 + +#define V_PW 1 +#define V_BP 25 +#define V_VD 600 +#define V_FP 0 + +#define LCD_WIDTH 1024 +#define LCD_HEIGHT 600 +/* Other */ +#define DCLK_POL 0 +#define SWAP_RB 0 + +void set_lcd_info(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info ) +{ + /* screen type & face */ + screen->type = OUT_TYPE; + screen->face = OUT_FACE; + + /* Screen size */ + screen->x_res = H_VD; + screen->y_res = V_VD; + + screen->width = LCD_WIDTH; + screen->height = LCD_HEIGHT; + + /* Timing */ + screen->lcdc_aclk = LCDC_ACLK; + screen->pixclock = OUT_CLK; + screen->left_margin = H_BP; + screen->right_margin = H_FP; + screen->hsync_len = H_PW; + screen->upper_margin = V_BP; + screen->lower_margin = V_FP; + screen->vsync_len = V_PW; + + /* Pin polarity */ + screen->pin_hsync = 0; + screen->pin_vsync = 0; + screen->pin_den = 0; + screen->pin_dclk = DCLK_POL; + + /* Swap rule */ + screen->swap_rb = SWAP_RB; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; +} + + + diff --git a/include/linux/goodix_queue.h b/include/linux/goodix_queue.h new file mode 100755 index 000000000000..85b599af5b58 --- /dev/null +++ b/include/linux/goodix_queue.h @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------------------- + * kernel/include/linux/goodix_queue.h + * + * Copyright(c) 2010 Goodix Technology Corp. All rights reserved. + * Author: Eltonny + * Date: 2010.11.11 + * + *---------------------------------------------------------------------------------------------------------*/ + +/* 用于管理手指序列的伪队列操作函数, + * 适用于Goodix的Guitar小屏驱动 + * 调整手指上报顺序以避免出现手指ID交换现象 + * 在大屏驱动中,该功能将被调整 + */ +#ifndef _LINUX_GOODIX_QUEUE_H +#define _LINUX_GOODIX_QUEUE_H +#include + +struct point_node +{ + uint8_t num; + uint8_t state; + uint8_t pressure; + unsigned int x; + unsigned int y; +}; + +struct point_queue +{ + int length; + struct point_node pointer[MAX_FINGER_NUM]; +}; + + +/******************************************************* +功能: + 删除手指队列中松键的手指 +参数: + point_list +********************************************************/ +static void del_point(struct point_queue *point_list) +{ + int count = point_list->length-1; + int position; + for(; count >= 0; count--) //note: must search from tail to head + if(point_list->pointer[count].state == FLAG_UP) + { + if(point_list->length == 0 ) + return ; + position = count; + for(; position < MAX_FINGER_NUM -1; position++) + point_list->pointer[position] = point_list->pointer[position+1]; + point_list->length--; + } +} + +/******************************************************* +功能: + 在队列尾中加入新增的手指 +参数: + point_list + num:手指标号 +return: + 是否成功增加手指 +********************************************************/ +static int add_point(struct point_queue *point_list, int num) +{ + if(point_list->length >= MAX_FINGER_NUM || num < 0 ) + return -1; + point_list->pointer[point_list->length].num = num; + point_list->pointer[point_list->length].state = FLAG_DOWN; + point_list->length++; + return 0; +} + +/******************************************************* +功能: + 查找指定标号的手指位置 +参数: + point_list + num:手指标号 +return: + 返回找到的手指在队列中的位置 +********************************************************/ +static int search_point(struct point_queue *point_list, int num) +{ + int count = 0; + if(point_list->length <= 0 || num < 0 || num > MAX_FINGER_NUM) + return -1; //no data + for(; count < point_list->length; count++) + if(point_list->pointer[count].num == num) + return count; + else continue; + return -1; +} + +/******************************************************* +功能: + 查找松键的手指并设置标志位为FLAG_UP +参数: + point_list + num:手指标号 +return: + 是否成功设置手指标志位 +********************************************************/ +static int set_up_point(struct point_queue *point_list, int num) +{ + int number = 0; + if(point_list->length <= 0 || num < 0 || num > MAX_FINGER_NUM) + return -1; //no data + number = search_point(point_list, num); + if(num >= 0) + { + point_list->pointer[number].state = FLAG_UP; + return 0; + } + return -1; +} + +#endif /* _LINUX_GOODIX_QUEUE_H */ diff --git a/include/linux/goodix_touch.h b/include/linux/goodix_touch.h new file mode 100755 index 000000000000..989154317f30 --- /dev/null +++ b/include/linux/goodix_touch.h @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------------------- + * kernel/include/linux/goodix_touch.h + * + * Copyright(c) 2010 Goodix Technology Corp. All rights reserved. + * Author: Eltonny + * Date: 2010.11.11 + * + *---------------------------------------------------------------------------------------------------------*/ + +#ifndef _LINUX_GOODIX_TOUCH_H +#define _LINUX_GOODIX_TOUCH_H + +#include +#include +#include +#include + +#define GOODIX_I2C_NAME "goodix-ts" +#define GUITAR_GT80X +//触摸屏的分辨率 +#define TOUCH_MAX_HEIGHT 7680 +#define TOUCH_MAX_WIDTH 5120 +//显示屏的分辨率,根据具体平台更改,与触摸屏映射坐标相关 +#define SCREEN_MAX_HEIGHT 1024 +#define SCREEN_MAX_WIDTH 600 + +//#define SHUTDOWN_PORT RK29_PIN4_PD5 //SHUTDOWN管脚号 +//#define INT_PORT RK29_PIN0_PA2 //Int IO port + +#if 0 +#ifdef INT_PORT + #define TS_INT gpio_to_irq(INT_PORT) //Interrupt Number,EINT18 as 119 + //#define INT_CFG S3C_GPIO_SFN(3) //IO configer,EINT type +#else + #define TS_INT 0 +#endif +#endif + +#define FLAG_UP 0 +#define FLAG_DOWN 1 + +#define GOODIX_MULTI_TOUCH +#ifndef GOODIX_MULTI_TOUCH + #define MAX_FINGER_NUM 1 +#else + #define MAX_FINGER_NUM 2 //最大支持手指数(<=5) +#endif +#undef GOODIX_TS_DEBUG + +#define gt80xy_swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0) + +struct goodix_ts_data { + uint16_t addr; + struct i2c_client *client; + struct input_dev *input_dev; + uint8_t use_irq; + uint8_t use_shutdown; + struct hrtimer timer; + struct work_struct work; + char phys[32]; + int bad_data; + int retry; + + struct early_suspend early_suspend; + int (*power)(struct goodix_ts_data * ts, int on); +}; + +struct goodix_i2c_rmi_platform_data { + uint32_t version; /* Use this entry for panels with */ + //该结构体用于管理设备平台资源 + //预留,用于之后的功能扩展 + + unsigned shutdown_pin; + unsigned irq_pin; +}; + +#endif /* _LINUX_GOODIX_TOUCH_H */ -- 2.34.1