This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM335x Starter Kit Capacitive Touch Screen

Other Parts Discussed in Thread: DA8XX

Hi,

I am trying to integrate a capacitive touch screen display - NHD-5.0-800480TF-ATXI#-CTP with the starter kit under linux.

I have successfully modified the boardconfig file to get the LCD display right. Unfortunately I am having trouble with the capacitive touch screen.

The touch controller is FT5X06 and I have found some driver files, that I hope can be adapted here.

It would be great if anyone could give me pointers in integrating this, and this could serve as a solution to others trying the same.

I have adapted  the driver file and managed to compile a new kernel image. But during the driver probe, it is unable to read anything from the touch controller.(Error -121 I2C Read Reg)

I have tried to manually detect this chip at address 0x38 using i2cdetect -r 1. With the driver not added in kernel, Nothing is detected at this address. When the driver is added, a "UU" is shown at this address.

A device is registered successfully as /devices/virtual/input/input2

I am attaching the driver files and the board config file.

Thanks a lot.

Ram

7750.ft5x06_ts.c
/* 
 * drivers/input/touchscreen/ft5x0x_ts.c
 *
 * FocalTech ft5x0x TouchScreen driver. 
 *
 * Copyright (c) 2010  Focal tech Ltd.
 *
 * 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.
 *
 * VERSION      	DATE			AUTHOR
 *    1.0		  2010-01-05			WenFS
 *
 * note: only support multitouch	Wenfs 2010-10-01
 */



#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/ft5x06_ts.h>

#ifdef CONFIG_HAS_EARLYSUSPEND
	#include <linux/earlysuspend.h>
#endif

#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
//#include <asm/jzsoc.h>

static struct i2c_client *this_client;
static struct ft5x0x_ts_platform_data *pdata;

#define CONFIG_FT5X0X_MULTITOUCH 1

struct ts_event {
	u16	x1;
	u16	y1;
	u16	x2;
	u16	y2;
	u16	x3;
	u16	y3;
	u16	x4;
	u16	y4;
	u16	x5;
	u16	y5;
	u16	pressure;
    u8  touch_point;
};

struct ft5x0x_ts_data {
	struct input_dev	*input_dev;
	struct ts_event		event;
	struct work_struct 	pen_event_work;
	struct workqueue_struct *ts_workqueue;
#ifdef CONFIG_HAS_EARLYSUSPEND
	struct early_suspend	early_suspend;
#endif  //CONFIG_HAS_EARLYSUSPEND
};

/***********************************************************************************************
Name	:	ft5x0x_i2c_rxdata 

Input	:	*rxdata
                     *length

Output	:	ret

function	:	

***********************************************************************************************/
static int ft5x0x_i2c_rxdata(char *rxdata, int length)
{
	int ret;

	struct i2c_msg msgs[] = {
		{
			.addr	= this_client->addr,
			.flags	= 0,
			.len	= 1,
			.buf	= rxdata,
		},
		{
			.addr	= this_client->addr,
			.flags	= I2C_M_RD,
			.len	= length,
			.buf	= rxdata,
		},
	};

    //msleep(1);
	ret = i2c_transfer(this_client->adapter, msgs, 2);
	if (ret < 0)
		pr_err("msg %s i2c read error: %d\n", __func__, ret);
	
	return ret;
}
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static int ft5x0x_i2c_txdata(char *txdata, int length)
{
	int ret;

	struct i2c_msg msg[] = {
		{
			.addr	= this_client->addr,
			.flags	= 0,
			.len	= length,
			.buf	= txdata,
		},
	};

   	//msleep(1);
	ret = i2c_transfer(this_client->adapter, msg, 1);
	if (ret < 0)
		pr_err("%s i2c write error: %d\n", __func__, ret);

	return ret;
}
/***********************************************************************************************
Name	:	 ft5x0x_write_reg

Input	:	addr -- address
                     para -- parameter

Output	:	

function	:	write register of ft5x0x

***********************************************************************************************/
static int ft5x0x_write_reg(u8 addr, u8 para)
{
    u8 buf[3];
    int ret = -1;

    buf[0] = addr;
    buf[1] = para;
    ret = ft5x0x_i2c_txdata(buf, 2);
    if (ret < 0) {
        pr_err("write reg failed! %#x ret: %d", buf[0], ret);
        return -1;
    }
    
    return 0;
}


/***********************************************************************************************
Name	:	ft5x0x_read_reg 

Input	:	addr
                     pdata

Output	:	

function	:	read register of ft5x0x

***********************************************************************************************/
static int ft5x0x_read_reg(u8 addr, u8 *pdata)
{
	int ret;
	u8 buf[2] = {0};

	buf[0] = addr;
	struct i2c_msg msgs[] = {
		{
			.addr	= this_client->addr,
			.flags	= 0,
			.len	= 1,
			.buf	= buf,
		},
		{
			.addr	= this_client->addr,
			.flags	= I2C_M_RD,
			.len	= 1,
			.buf	= buf,
		},
	};

    //msleep(1);
	ret = i2c_transfer(this_client->adapter, msgs, 2);
	if (ret < 0)
		pr_err("msg %s i2c read error: %d\n", __func__, ret);

	*pdata = buf[0];
	return ret;
  
}


/***********************************************************************************************
Name	:	 ft5x0x_read_fw_ver

Input	:	 void
                     

Output	:	 firmware version 	

function	:	 read TP firmware version

***********************************************************************************************/
static unsigned char ft5x0x_read_fw_ver(void)
{
	unsigned char ver;
	ft5x0x_read_reg(FT5X0X_REG_FIRMID, &ver);
	return(ver);
}


#define CONFIG_SUPPORT_FTS_CTP_UPG


#ifdef CONFIG_SUPPORT_FTS_CTP_UPG

typedef enum
{
    ERR_OK,
    ERR_MODE,
    ERR_READID,
    ERR_ERASE,
    ERR_STATUS,
    ERR_ECC,
    ERR_DL_ERASE_FAIL,
    ERR_DL_PROGRAM_FAIL,
    ERR_DL_VERIFY_FAIL
}E_UPGRADE_ERR_TYPE;

typedef unsigned char         FTS_BYTE;     //8 bit
typedef unsigned short        FTS_WORD;    //16 bit
typedef unsigned int          FTS_DWRD;    //16 bit
typedef unsigned char         FTS_BOOL;    //8 bit

#define FTS_NULL                0x0
#define FTS_TRUE                0x01
#define FTS_FALSE              0x0

#define I2C_CTPM_ADDRESS       0x70


void delay_qt_ms(unsigned long  w_ms)
{
    unsigned long i;
    unsigned long j;

    for (i = 0; i < w_ms; i++)
    {
        for (j = 0; j < 1000; j++)
        {
            udelay(1);
        }
    }
}


/*
[function]: 
    callback: read data from ctpm by i2c interface,implemented by special user;
[parameters]:
    bt_ctpm_addr[in]    :the address of the ctpm;
    pbt_buf[out]        :data buffer;
    dw_lenth[in]        :the length of the data buffer;
[return]:
    FTS_TRUE     :success;
    FTS_FALSE    :fail;
*/
FTS_BOOL i2c_read_interface(FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)
{
    int ret;
    
    ret=i2c_master_recv(this_client, pbt_buf, dw_lenth);

    if(ret<=0)
    {
        printk("[TSP]i2c_read_interface error\n");
        return FTS_FALSE;
    }
  
    return FTS_TRUE;
}

/*
[function]: 
    callback: write data to ctpm by i2c interface,implemented by special user;
[parameters]:
    bt_ctpm_addr[in]    :the address of the ctpm;
    pbt_buf[in]        :data buffer;
    dw_lenth[in]        :the length of the data buffer;
[return]:
    FTS_TRUE     :success;
    FTS_FALSE    :fail;
*/
FTS_BOOL i2c_write_interface(FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)
{
    int ret;
    ret=i2c_master_send(this_client, pbt_buf, dw_lenth);
    if(ret<=0)
    {
        printk("[TSP]i2c_write_interface error line = %d, ret = %d\n", __LINE__, ret);
        return FTS_FALSE;
    }

    return FTS_TRUE;
}

/*
[function]: 
    send a command to ctpm.
[parameters]:
    btcmd[in]        :command code;
    btPara1[in]    :parameter 1;    
    btPara2[in]    :parameter 2;    
    btPara3[in]    :parameter 3;    
    num[in]        :the valid input parameter numbers, if only command code needed and no parameters followed,then the num is 1;    
[return]:
    FTS_TRUE    :success;
    FTS_FALSE    :io fail;
*/
FTS_BOOL cmd_write(FTS_BYTE btcmd,FTS_BYTE btPara1,FTS_BYTE btPara2,FTS_BYTE btPara3,FTS_BYTE num)
{
    FTS_BYTE write_cmd[4] = {0};

    write_cmd[0] = btcmd;
    write_cmd[1] = btPara1;
    write_cmd[2] = btPara2;
    write_cmd[3] = btPara3;
    return i2c_write_interface(I2C_CTPM_ADDRESS, write_cmd, num);
}

/*
[function]: 
    write data to ctpm , the destination address is 0.
[parameters]:
    pbt_buf[in]    :point to data buffer;
    bt_len[in]        :the data numbers;    
[return]:
    FTS_TRUE    :success;
    FTS_FALSE    :io fail;
*/
FTS_BOOL byte_write(FTS_BYTE* pbt_buf, FTS_DWRD dw_len)
{
    
    return i2c_write_interface(I2C_CTPM_ADDRESS, pbt_buf, dw_len);
}

/*
[function]: 
    read out data from ctpm,the destination address is 0.
[parameters]:
    pbt_buf[out]    :point to data buffer;
    bt_len[in]        :the data numbers;    
[return]:
    FTS_TRUE    :success;
    FTS_FALSE    :io fail;
*/
FTS_BOOL byte_read(FTS_BYTE* pbt_buf, FTS_BYTE bt_len)
{
    return i2c_read_interface(I2C_CTPM_ADDRESS, pbt_buf, bt_len);
}


/*
[function]: 
    burn the FW to ctpm.
[parameters]:(ref. SPEC)
    pbt_buf[in]    :point to Head+FW ;
    dw_lenth[in]:the length of the FW + 6(the Head length);    
    bt_ecc[in]    :the ECC of the FW
[return]:
    ERR_OK        :no error;
    ERR_MODE    :fail to switch to UPDATE mode;
    ERR_READID    :read id fail;
    ERR_ERASE    :erase chip fail;
    ERR_STATUS    :status error;
    ERR_ECC        :ecc error.
*/


#define    FTS_PACKET_LENGTH        128

static unsigned char CTPM_FW[]=
{
#include "ft_app.i"
};

E_UPGRADE_ERR_TYPE  fts_ctpm_fw_upgrade(FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)
{
    FTS_BYTE reg_val[2] = {0};
    FTS_DWRD i = 0;

    FTS_DWRD  packet_number;
    FTS_DWRD  j;
    FTS_DWRD  temp;
    FTS_DWRD  lenght;
    FTS_BYTE  packet_buf[FTS_PACKET_LENGTH + 6];
    FTS_BYTE  auc_i2c_write_buf[10];
    FTS_BYTE bt_ecc;
    int      i_ret;

    /*********Step 1:Reset  CTPM *****/
    /*write 0xaa to register 0xfc*/
    ft5x0x_write_reg(0xfc,0xaa);
    delay_qt_ms(50);
     /*write 0x55 to register 0xfc*/
    ft5x0x_write_reg(0xfc,0x55);
    printk("[TSP] Step 1: Reset CTPM test\n");
   
    delay_qt_ms(30);   


    /*********Step 2:Enter upgrade mode *****/
    auc_i2c_write_buf[0] = 0x55;
    auc_i2c_write_buf[1] = 0xaa;
    do
    {
        i ++;
        i_ret = ft5x0x_i2c_txdata(auc_i2c_write_buf, 2);
        delay_qt_ms(5);
    }while(i_ret <= 0 && i < 5 );

    /*********Step 3:check READ-ID***********************/        
    cmd_write(0x90,0x00,0x00,0x00,4);
    byte_read(reg_val,2);
    if (reg_val[0] == 0x79 && reg_val[1] == 0x3)
    {
        printk("[TSP] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",reg_val[0],reg_val[1]);
    }
    else
    {
        return ERR_READID;
        //i_is_new_protocol = 1;
    }

     /*********Step 4:erase app*******************************/
    cmd_write(0x61,0x00,0x00,0x00,1);
   
    delay_qt_ms(1500);
    printk("[TSP] Step 4: erase. \n");

    /*********Step 5:write firmware(FW) to ctpm flash*********/
    bt_ecc = 0;
    printk("[TSP] Step 5: start upgrade. \n");
    dw_lenth = dw_lenth - 8;
    packet_number = (dw_lenth) / FTS_PACKET_LENGTH;
    packet_buf[0] = 0xbf;
    packet_buf[1] = 0x00;
    for (j=0;j<packet_number;j++)
    {
        temp = j * FTS_PACKET_LENGTH;
        packet_buf[2] = (FTS_BYTE)(temp>>8);
        packet_buf[3] = (FTS_BYTE)temp;
        lenght = FTS_PACKET_LENGTH;
        packet_buf[4] = (FTS_BYTE)(lenght>>8);
        packet_buf[5] = (FTS_BYTE)lenght;

        for (i=0;i<FTS_PACKET_LENGTH;i++)
        {
            packet_buf[6+i] = pbt_buf[j*FTS_PACKET_LENGTH + i]; 
            bt_ecc ^= packet_buf[6+i];
        }
        
        byte_write(&packet_buf[0],FTS_PACKET_LENGTH + 6);
        delay_qt_ms(FTS_PACKET_LENGTH/6 + 1);
        if ((j * FTS_PACKET_LENGTH % 1024) == 0)
        {
              printk("[TSP] upgrade the 0x%x th byte.\n", ((unsigned int)j) * FTS_PACKET_LENGTH);
        }
    }

    if ((dw_lenth) % FTS_PACKET_LENGTH > 0)
    {
        temp = packet_number * FTS_PACKET_LENGTH;
        packet_buf[2] = (FTS_BYTE)(temp>>8);
        packet_buf[3] = (FTS_BYTE)temp;

        temp = (dw_lenth) % FTS_PACKET_LENGTH;
        packet_buf[4] = (FTS_BYTE)(temp>>8);
        packet_buf[5] = (FTS_BYTE)temp;

        for (i=0;i<temp;i++)
        {
            packet_buf[6+i] = pbt_buf[ packet_number*FTS_PACKET_LENGTH + i]; 
            bt_ecc ^= packet_buf[6+i];
        }

        byte_write(&packet_buf[0],temp+6);    
        delay_qt_ms(20);
    }

    //send the last six byte
    for (i = 0; i<6; i++)
    {
        temp = 0x6ffa + i;
        packet_buf[2] = (FTS_BYTE)(temp>>8);
        packet_buf[3] = (FTS_BYTE)temp;
        temp =1;
        packet_buf[4] = (FTS_BYTE)(temp>>8);
        packet_buf[5] = (FTS_BYTE)temp;
        packet_buf[6] = pbt_buf[ dw_lenth + i]; 
        bt_ecc ^= packet_buf[6];

        byte_write(&packet_buf[0],7);  
        delay_qt_ms(20);
    }

    /*********Step 6: read out checksum***********************/
    /*send the opration head*/
    cmd_write(0xcc,0x00,0x00,0x00,1);
    byte_read(reg_val,1);
    printk("[TSP] Step 6:  ecc read 0x%x, new firmware 0x%x. \n", reg_val[0], bt_ecc);
    if(reg_val[0] != bt_ecc)
    {
        return ERR_ECC;
    }

    /*********Step 7: reset the new FW***********************/
    cmd_write(0x07,0x00,0x00,0x00,1);

    return ERR_OK;
}


int fts_ctpm_fw_upgrade_with_i_file(void)
{
   FTS_BYTE*     pbt_buf = FTS_NULL;
   int i_ret;
    
    //=========FW upgrade========================*/
   pbt_buf = CTPM_FW;
   /*call the upgrade function*/
   i_ret =  fts_ctpm_fw_upgrade(pbt_buf,sizeof(CTPM_FW));
   if (i_ret != 0)
   {
       //error handling ...
       //TBD
   }

   return i_ret;
}

unsigned char fts_ctpm_get_upg_ver(void)
{
    unsigned int ui_sz;
    ui_sz = sizeof(CTPM_FW);
    if (ui_sz > 2)
    {
        return CTPM_FW[ui_sz - 2];
    }
    else
    {
        //TBD, error handling?
        return 0xff; //default value
    }
}

#endif


/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static void ft5x0x_ts_release(void)
{
	struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
#ifdef CONFIG_FT5X0X_MULTITOUCH	
	input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0);
#else
	input_report_abs(data->input_dev, ABS_PRESSURE, 0);
	input_report_key(data->input_dev, BTN_TOUCH, 0);
#endif
	input_sync(data->input_dev);
}

static int ft5x0x_read_data(void)
{
	struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
	struct ts_event *event = &data->event;
//	u8 buf[14] = {0};
	u8 buf[32] = {0};
	int ret = -1;

#ifdef CONFIG_FT5X0X_MULTITOUCH
//	ret = ft5x0x_i2c_rxdata(buf, 13);
	ret = ft5x0x_i2c_rxdata(buf, 31);
#else
    ret = ft5x0x_i2c_rxdata(buf, 7);
#endif
    if (ret < 0) {
		printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
		return ret;
	}

	memset(event, 0, sizeof(struct ts_event));
//	event->touch_point = buf[2] & 0x03;// 0000 0011
	event->touch_point = buf[2] & 0x07;// 000 0111

    if (event->touch_point == 0) {
        ft5x0x_ts_release();
        return 1; 
    }

#ifdef CONFIG_FT5X0X_MULTITOUCH
    switch (event->touch_point) {
		case 5:
			event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
			event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
		case 4:
			event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
			event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
		case 3:
			event->x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
			event->y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
		case 2:
			event->x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];
			event->y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];
		case 1:
			event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
			event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
            break;
		default:
		    return -1;
	}
#else
    if (event->touch_point == 1) {
    	event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
		event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
    }
#endif
    event->pressure = 200;

	dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
		event->x1, event->y1, event->x2, event->y2);
	//printk("%d (%d, %d), (%d, %d)\n", event->touch_point, event->x1, event->y1, event->x2, event->y2);

    return 0;
}
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static void ft5x0x_report_value(void)
{
	struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
	struct ts_event *event = &data->event;
	u8 uVersion;

//		printk("==ft5x0x_report_value =\n");
#ifdef CONFIG_FT5X0X_MULTITOUCH
	switch(event->touch_point) {
		case 5:
			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x5);
			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y5);
			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
			input_mt_sync(data->input_dev);
//			printk("===x2 = %d,y2 = %d ====\n",event->x2,event->y2);
		case 4:
			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x4);
			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y4);
			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
			input_mt_sync(data->input_dev);
//			printk("===x2 = %d,y2 = %d ====\n",event->x2,event->y2);
		case 3:
			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x3);
			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y3);
			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
			input_mt_sync(data->input_dev);
//			printk("===x2 = %d,y2 = %d ====\n",event->x2,event->y2);
		case 2:
			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x2);
			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y2);
			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
			input_mt_sync(data->input_dev);
//			printk("===x2 = %d,y2 = %d ====\n",event->x2,event->y2);
		case 1:
			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x1);
			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y1);
			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
			input_mt_sync(data->input_dev);
			printk("===x1 = %d,y1 = %d ====\n",event->x1,event->y1);

		default:
//			printk("==touch_point default =\n");
			break;
	}
#else	/* CONFIG_FT5X0X_MULTITOUCH*/
	if (event->touch_point == 1) {
		input_report_abs(data->input_dev, ABS_X, event->x1);
		input_report_abs(data->input_dev, ABS_Y, event->y1);
		input_report_abs(data->input_dev, ABS_PRESSURE, event->pressure);
	}
	input_report_key(data->input_dev, BTN_TOUCH, 1);
#endif	/* CONFIG_FT5X0X_MULTITOUCH*/
	input_sync(data->input_dev);

	dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
		event->x1, event->y1, event->x2, event->y2);
}	/*end ft5x0x_report_value*/
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static void ft5x0x_ts_pen_irq_work(struct work_struct *work)
{
	int ret = -1;
//	printk("==work 1=\n");
	ret = ft5x0x_read_data();	
	if (ret == 0) {	
		ft5x0x_report_value();
	}
//	else printk("data package read error\n");
//	printk("==work 2=\n");
//    	msleep(1);
    enable_irq(this_client->irq);
//	enable_irq(IRQ_EINT(6));
}
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)
{
	struct ft5x0x_ts_data *ft5x0x_ts = dev_id;
    	disable_irq(this_client->irq);		
//	disable_irq(IRQ_EINT(6));
//	printk("==int=\n");
	if (!work_pending(&ft5x0x_ts->pen_event_work)) {
		queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work);
	}

	return IRQ_HANDLED;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static void ft5x0x_ts_suspend(struct early_suspend *handler)
{
//	struct ft5x0x_ts_data *ts;
//	ts =  container_of(handler, struct ft5x0x_ts_data, early_suspend);

	printk("==ft5x0x_ts_suspend=\n");
//	disable_irq(this_client->irq);
//	disable_irq(IRQ_EINT(6));
//	cancel_work_sync(&ts->pen_event_work);
//	flush_workqueue(ts->ts_workqueue);
	// ==set mode ==, 
//    	ft5x0x_set_reg(FT5X0X_REG_PMODE, PMODE_HIBERNATE);
}
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static void ft5x0x_ts_resume(struct early_suspend *handler)
{
	printk("==ft5x0x_ts_resume=\n");
	// wake the mode
//	__gpio_as_output(GPIO_FT5X0X_WAKE);		
//	__gpio_clear_pin(GPIO_FT5X0X_WAKE);		//set wake = 0,base on system
//	 msleep(100);
//	__gpio_set_pin(GPIO_FT5X0X_WAKE);			//set wake = 1,base on system
//	msleep(100);
//	enable_irq(this_client->irq);
//	enable_irq(IRQ_EINT(6));
}
#endif  //CONFIG_HAS_EARLYSUSPEND
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static int 
ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct ft5x0x_ts_data *ft5x0x_ts;
	struct input_dev *input_dev;
	int err = 0;
	unsigned char ver = 0;
	unsigned char uc_reg_value; 
	
	printk("==ft5x0x_ts_probe=\n");
	
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		err = -ENODEV;
		goto exit_check_functionality_failed;
	}

	printk("==kzalloc=\n");
	ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);
	if (!ft5x0x_ts)	{
		err = -ENOMEM;
		goto exit_alloc_data_failed;
	}


	this_client = client;
	i2c_set_clientdata(client, ft5x0x_ts);


	INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);

	ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));
	if (!ft5x0x_ts->ts_workqueue) {
		err = -ESRCH;
		goto exit_create_singlethread;
	}

//	pdata = client->dev.platform_data;
//	if (pdata == NULL) {
//		dev_err(&client->dev, "%s: platform data is null\n", __func__);
//		goto exit_platform_data_null;
//	}
	
//	printk("==request_irq=\n");
	err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED, "ft5x0x_ts", ft5x0x_ts);
//	err = request_irq(IRQ_EINT(6), ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING, "ft5x0x_ts", ft5x0x_ts);
	if (err < 0) {
		dev_err(&client->dev, "ft5x0x_probe: request irq failed\n");
		goto exit_irq_request_failed;
	}

//	__gpio_as_irq_fall_edge(pdata->intr);		//
	disable_irq(this_client->irq);
//	disable_irq(IRQ_EINT(6));

//	printk("==input_allocate_device=\n");
	input_dev = input_allocate_device();
	if (!input_dev) {
		err = -ENOMEM;
		dev_err(&client->dev, "failed to allocate input device\n");
		goto exit_input_dev_alloc_failed;
	}
	
	ft5x0x_ts->input_dev = input_dev;

#ifdef CONFIG_FT5X0X_MULTITOUCH
	set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
	set_bit(ABS_MT_POSITION_X, input_dev->absbit);
	set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
	set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);

	input_set_abs_params(input_dev,
			     ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
	input_set_abs_params(input_dev,
			     ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
	input_set_abs_params(input_dev,
			     ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
	input_set_abs_params(input_dev,
			     ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
#else
	set_bit(ABS_X, input_dev->absbit);
	set_bit(ABS_Y, input_dev->absbit);
	set_bit(ABS_PRESSURE, input_dev->absbit);
	set_bit(BTN_TOUCH, input_dev->keybit);

	input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
	input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
	input_set_abs_params(input_dev,
			     ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);
#endif

	set_bit(EV_ABS, input_dev->evbit);
	set_bit(EV_KEY, input_dev->evbit);

	input_dev->name		= FT5X0X_NAME;		//dev_name(&client->dev)
	err = input_register_device(input_dev);
	if (err) {
		dev_err(&client->dev,
		"ft5x0x_ts_probe: failed to register input device: %s\n",
		dev_name(&client->dev));
		goto exit_input_register_device_failed;
	}

#ifdef CONFIG_HAS_EARLYSUSPEND
	printk("==register_early_suspend =\n");
	ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;
	ft5x0x_ts->early_suspend.resume	= ft5x0x_ts_resume;
	register_early_suspend(&ft5x0x_ts->early_suspend);
#endif

    msleep(50);
    //get some register information
    uc_reg_value = ft5x0x_read_fw_ver();
    printk("[FST] FT55x0x Firmware version = 0x%x\n", uc_reg_value);
    uc_reg_value = ft5x0x_read_reg(FT5X0X_REG_FT5201ID, &ver);
    printk("[FST] FT55x0x FiT5201ID = 0x%x\n", uc_reg_value);
    uc_reg_value = ft5x0x_read_reg(FT5X0X_REG_ERR, &ver);
    printk("[FST] FT55x0x ERR = 0x%x\n", uc_reg_value);


//    fts_ctpm_fw_upgrade_with_i_file();


    
//wake the CTPM
//	__gpio_as_output(GPIO_FT5X0X_WAKE);		
//	__gpio_clear_pin(GPIO_FT5X0X_WAKE);		//set wake = 0,base on system
//	 msleep(100);
//	__gpio_set_pin(GPIO_FT5X0X_WAKE);			//set wake = 1,base on system
//	msleep(100);
//	ft5x0x_set_reg(0x88, 0x05); //5, 6,7,8
//	ft5x0x_set_reg(0x80, 30);
//	msleep(50);
    	enable_irq(this_client->irq);
//    enable_irq(IRQ_EINT(6));

	printk("==probe over =\n");
    return 0;

exit_input_register_device_failed:
	input_free_device(input_dev);
exit_input_dev_alloc_failed:
	free_irq(client->irq, ft5x0x_ts);
//	free_irq(IRQ_EINT(6), ft5x0x_ts);
exit_irq_request_failed:
exit_platform_data_null:
	cancel_work_sync(&ft5x0x_ts->pen_event_work);
	destroy_workqueue(ft5x0x_ts->ts_workqueue);
exit_create_singlethread:
	printk("==singlethread error =\n");
	i2c_set_clientdata(client, NULL);
	kfree(ft5x0x_ts);
exit_alloc_data_failed:
exit_check_functionality_failed:
	return err;
}
/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static int __devexit ft5x0x_ts_remove(struct i2c_client *client)
{
	printk("==ft5x0x_ts_remove=\n");
	struct ft5x0x_ts_data *ft5x0x_ts = i2c_get_clientdata(client);
#ifdef CONFIG_HAS_EARLYSUSPEND
	unregister_early_suspend(&ft5x0x_ts->early_suspend);
#endif
	free_irq(client->irq, ft5x0x_ts);
//	free_irq(IRQ_EINT(6), ft5x0x_ts);
	input_unregister_device(ft5x0x_ts->input_dev);
	kfree(ft5x0x_ts);
	cancel_work_sync(&ft5x0x_ts->pen_event_work);
	destroy_workqueue(ft5x0x_ts->ts_workqueue);
	i2c_set_clientdata(client, NULL);
	return 0;
}

static const struct i2c_device_id ft5x0x_ts_id[] = {
	{ FT5X0X_NAME, 0 },{ }
};


MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);

static struct i2c_driver ft5x0x_ts_driver = {
	.probe		= ft5x0x_ts_probe,
	.remove		= __devexit_p(ft5x0x_ts_remove),
	.id_table	= ft5x0x_ts_id,
	.driver	= {
		.name	= FT5X0X_NAME,
		.owner	= THIS_MODULE,
	},
};

/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static int __init ft5x0x_ts_init(void)
{
	int ret;
	printk("==ft5x0x_ts_init==\n");
	ret = i2c_add_driver(&ft5x0x_ts_driver);
	printk("ret=%d\n",ret);
	return ret;
//	return i2c_add_driver(&ft5x0x_ts_driver);
}

/***********************************************************************************************
Name	:	 

Input	:	
                     

Output	:	

function	:	

***********************************************************************************************/
static void __exit ft5x0x_ts_exit(void)
{
	printk("==ft5x0x_ts_exit==\n");
	i2c_del_driver(&ft5x0x_ts_driver);
}

module_init(ft5x0x_ts_init);
module_exit(ft5x0x_ts_exit);

MODULE_AUTHOR("<wenfs@Focaltech-systems.com>");
MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");
MODULE_LICENSE("GPL");

8231.board-am335xevm.c

7357.ft5x06_ts.h

  • Hi Shiram,
     
    There is a wakeup signal - LCD_CAP_TOUCH_WAKE, going to the capacitive TS. Have you checked the state of this signal?
  • Hi Biser,

    Thanks for the quick reply.

    No, I did not check this pins state. The driver I got from https://gitorious.org/ft5x06-driver/ ignores this pin.

    Also i really dont know how the interrupt is handled here. I am just beginning to understand these things. As of now WAKE and INT is ignored.

    But for start, during the ID register read in the driver's probe function, I get this read error -121 message.

    Kernel fault code documentation pins it to a possible clock stretching issue. (http://www.mjmwired.net/kernel/Documentation/i2c/fault-codes#122)

    This bus is also shared by an EEPROM, accelerometer, power management ic and an audio ic as far as i can tell and regsitered at 100KHz.

    Thanks a lot.

    Ram

  • shriram said:
    But for start, during the ID register read in the driver's probe function, I get this read error -121 message.
    Kernel fault code documentation pins it to a possible clock stretching issue. (http://www.mjmwired.net/kernel/Documentation/i2c/fault-codes#122)
     
    There is an Errata 1.0.26 that might be related to this (http://www.ti.com/lit/er/sprz360d/sprz360d.pdf).
  • Hi Biser,

    Just measured the signals with an Oscilloscope. There is no ACK coming from the controller. I see the 3.3V.

    I tried also probing with i2cget, i2cdump and i2cdetect. I always get read failed.

    I am probing at address 0x38 and I dont get a response at all. I can see that the SDA and SCL are properly driven from the processor side, only no ACK during the 9th clock. SDA stays high at this 9th clock.

    I am quite stumped now and wondering how to proceed !

    Ram

  • Try driving the WAKE signal high.
  • Biser Gatchev-XID said:
    Try driving the WAKE signal high.

    Yes ! This helped me talk with the Controller. I am able to read the ft5x06 registers.

    Next I am trying to integrate this into the driver.

    Once the driver is regsitered properly, will it automatically be used as the default touchscreen input ?

    Thanks a lot.

    Ram

  • Good to hear that! I can't help you with the software, unfortunately. You may need to disable the default TSC driver. If you experience problems post on this thread, I will ask somebody from the software team to help.
  • Hi Biser,

    Thanks a lot. Now I have added a pullup resistor in the WAKE pin and the ft5x06_ts driver registers without any errors. 

    I am stil unable to get the touchscreen to work. I will detail my configuration below. If anyone from software team could guide me with this, I would be thankful.

    /proc/bus/input/devices : lists ft5x06_ts as :

    BUS=0000 Vendor=0000 Product=0000 Version=0000

    Name="ft5x06_ts"

    phys=

    systs=/devices/virtual/input/input1

    Uniq=

    Handlers=event1

    PROP=0

    EV=b

    KEY=0

    ABS=650000 0

    I modified the matrixgui service script in init.d to export  TSLIB_TSDEVICE=/dev/input/event1.

    There are no modifications in driver code and board config since i uploaded first in this thread.

    Thanks a lot

    Ram

  • Hi shriram,

    Here is the output of cat /proc/bus/input/devices when using the default TI touchscreen driver:

    I: Bus=0000 Vendor=0000 Product=0000 Version=0000
    N: Name="ti-tsc"
    P: Phys=
    S: Sysfs=/devices/platform/omap/ti_tscadc/tsc/input/input2
    U: Uniq=
    H: Handlers=mouse0 event2
    B: PROP=0
    B: EV=b
    B: KEY=400 0 0 0 0 0 0 0 0 0 0
    B: ABS=1000003

    Your output seems ok, except for that you have no mouse handler, but I don't think this is it.

    Please try:

    cat /dev/input/event1

    and see if you get any gibberish output in the console.

    Best regards,
    Miroslav

  • Hi Miroslav,

    Thanks for the fast reply.

    cat /dev/input/event1 did not give out any data ( I also tried touching the screen many times).

    I tried "evtest /dev/input/event1" and get

    driver version 1.0.1

    device id: bus 0x0 vendor 0x0 product 0x0 version 0x0

    device name : "ft5x06_ts"

    supported events:

    Event type 0 (Sync)

    Event type 1 (key)

    Event type 3 (Absolute)

     Event code 48 (Touch Major) Value 0 Min 0 Max 255

     Event code 50 (width Major) Value 0 Min 0 Max 200

     Event code 53 (Position X) Value 0 Min 0 Max 800

     Event code 54 (Position Y) Value 0 Min 0 Max 480

    Testing ... (interrupt to exit)

    and then nothing else. (Also tried here touching the screen many times).

    I inserted some printk inside the driver function "ft5x0x_ts_pen_irq_work" as it is being assigned in INIT_WORK, but these statements do not get printed at all in /var/log/messages. The last ft5x06 related message here is from the probe function.

    Also "ps" does not reveal any worker thread related to this driver.

    Thanks a lot.

    Ram

  • Hi,

    Did you check if your touch controller outputs interrupts when you touch the screen?

    Best regards,
    Miroslav

  • Hi Miroslav,

    Yes, the INT pin is normaly high and goes to '0' when the screen is touched. I verified this with oscilloscope.

    But this pin is not configured at all either in the driver or in the board config.

    I found this driver code in https://gitorious.org/ft5x06-driver/  and adapted according to the following post here http://e2e.ti.com/support/arm/sitara_arm/f/791/p/254237/890635.aspx#890635.

    As far as I can understand, the interrupts are disabled in driver as of now, and as long as ft5x0x_ts_pen_irq_work gets called regularly, interrupt need not be read. But I am a complete novice here.

    I have uploaded my adapted driver in the first post in this thread.

    Thanks a lot.

    Ram

  • Hi Miroslav,

    I have modified the board config file to configure captouch pins as below

    /* pinmux for captouch device */
    static struct pinmux_config gpio_captouch_mux[] = {
    {"gpmc_clk.gpio2_1", OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},
    {"gpmc_csn3.gpio2_0", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT},
    {NULL, 0},
    };

    and i added an entry for this to be called during starterkit initialization.

    In the touch driver, there is this interrupt req statement

    err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING, FT5X0X_NAME, ft5x0x_ts);

    I think client->irq does not point to the correct pin or to any pin at all. How can i specify here the interrupt pin defined in the board config file ?

    Thanks a lot for your help.

    Ram.

  • Hi Ram,

    The client->irq argument does not pass a pin number, but an interrupt line number. You have to be sure that you are registering the interrupt handler to the correct interrupt line used by the touch screen driver. If you don't know the interrupt line number, you have to find a way to autodetect it. This topic is well covered in chapter 10 of the Linux Device Drivers e-book.

    Basically, these are your choices (all of them are described in the pdf file I linked):

    1. Your device "announces" the interrupt line it is going to use (in some status register, etc.).
    2. Kernel-assisted probing (Linux kernel provides some basic functions).
    3. Do-it-yourself probing (explained in the pdf).

    Sample code is provided in the pdf.

    You can, however, check if client->irq is wrong. If you are able to insmod your module, do a "cat /proc/interrupts" and check if there is a registered handler with name FT5X0X_NAME. Check if the interrupts count changes when you press the touch screen. If it does, then the interrupt handler has been properly registered.

    Best regards,
    Miroslav

  • Hi Miroslav,

    I have successfully registered the interrupt line. The appropriate function is being called in the driver, everytime the screen is touched. Just for others who might find this useful, I added the following code to get the interrupt line

    err = gpio_request(GPIO_TO_PIN(2,0),FT5X0X_NAME);
    gpio_direction_input(GPIO_TO_PIN(2,0));
    gpio_export(GPIO_TO_PIN(2,0),true);
    ct_irq_num = gpio_to_irq(GPIO_TO_PIN(2,0));

    err = request_irq(ct_irq_num, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING, FT5X0X_NAME, ft5x0x_ts);//IRQ_EINT(6)
    if (err < 0) {
    dev_err(&client->dev, "ft5x0x_probe: request irq failed\n");
    printk("==request_irq failed=\n");//64 irq line should be
    goto exit_irq_request_failed;
    }

    This corresponding pin has to be set as input in pinmux in the boardconfig file as described in my previous post.

    Now I can see the events in /dev/input/event1 and I can see the screen coordinates being read by the driver with the kernel debug messages i inserted in the interrupt handler.

    I have set export TSLIB_TSDEVICE=/dev/input/event1 in the /etc/init.d/matrix-gui-2.0 file. But still the touch is not used by the GUI.

    How do I proceed from here ? How to integrate this driver to be recognised as the default touch device ?

    Thanks a lot.

    Ram

  • Hi Miroslav,

    Some more updates.  My device is registered in /dev/input/event1. I replaced touchscreen0 with this and tried ts_calibrate. I got the message that this device is not a touchscreen and does not support ABS_X and ABS_Y events. 

    After googling around i found that after adding this code snippet in probe function, in driver,

    set_bit(EV_KEY, input_dev->evbit);
    set_bit(ABS_X, input_dev->absbit);
    set_bit(ABS_Y, input_dev->absbit);
    set_bit(BTN_TOUCH, input_dev->keybit);

    ts_calibrate does not complain anymore.

    But it does not work either. It is still waiting for the first touch. But driver is giving an input_report_abs,.. and input_sync for every touch with the right coordinates.

    I tried ts_test and the program reports a touch event , but with x and y values always zero (0), regardless of where I touch in the screen. The cursor move to the top left corner in the first touch and stays there.

    What am I Missing here ? I found posts with similar problem, but no concrete sollution posted from what I understand.

    Thanks a lot.

    Ram

  • Hi Ram,

    Does ts_print work?

    Best regards,
    Miroslav

  • Hi Miroslav,

    I was trying out a lot of things and am now able to get SINGLE Touch working.

    1. I Recompiled tslib from master git repository

    2. I did not define multitouch in touch driver and compiled it only for single touch.

    With this latest tslib, I have the following scenario

    * If I compile the driver with multitouch, only /device/input/event1 is reporting values whenever screen is touched. ts_calibrate, print and test does not work. tslib tools complain that ABS and KEY events are not registered.(but proc/bus/input/devices says otherwise)

    * If I compile with singletouch, I am able to use touch screen with matrix GUI, qt apps and the tslib utils (single touch only).

    I have no idea what is missing with multi-touch.

    A gist of what driver does for each touch point in a multitouch config

    input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
    input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x1);
    input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y1);
    input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
    input_mt_sync(data->input_dev);

    input_sync(data->input_dev);

    Thanks a lot.

    Ram

  • I can tell you that stock Tslib and Qt4 embedded do not support the linux multi-touch interface...however, it should still work if you only provide 1 touch at a time.  I hacked Qt, Tslib, and our touchscreen driver (atmel_mxt_ts) a few years back to force gesture data through the otherwise unused pressure value so I could access said information in Qt (using the button mask).

    Qt5 has a mostly-working linux multi-touch mouse plugin, but I have yet to try it on the AM335x.

    I think, however, that your driver is likely outputting the multi-touch in the proper format.  Do you get good data from evtest when you have the driver compiled for multi-touch (I'm not familiar with that touchscreen driver)?

  • David Paden said:
    I think, however, that your driver is likely outputting the multi-touch in the proper format.  Do you get good data from evtest when you have the driver compiled for multi-touch (I'm not familiar with that touchscreen driver)?

    Hi David,

    I think you are right.  evtest reports the following supported events

    Input driver version is 1.0.1
    Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0
    Input device name: "ft5x0x_ts"
    Supported events:
    Event type 0 (Sync)
    Event type 1 (Key)
    Event code 330 (Touch)
    Event type 3 (Absolute)
    Event code 0 (X)
    Value 503
    Min 0
    Max 800
    Event code 1 (Y)
    Value 259
    Min 0
    Max 480
    Event code 24 (Pressure)
    Value 0
    Min 0
    Max 255
    Event code 48 (Touch Major)
    Value 0
    Min 0
    Max 255
    Event code 50 (Width Major)
    Value 0
    Min 0
    Max 200
    Event code 53 (Position X)
    Value 0
    Min 0
    Max 800
    Event code 54 (Position Y)
    Value 0
    Min 0
    Max 480

    It reports X, Y and then Postion X and Position Y. I guess this is because I modified the driver to report ABS_X,Y and ABS_MT_POSITION_X,Y.

    In the absense of ABS_X&Y touch is not working.(i.e.) tslib utils does not recognise any touch events (always read zero).  Thats why I compiled all touch reports together (single and multitouch) in the driver as a test.

    From the driver side the following input_report s are sent after a touch interrupt

    ABS_MT_TOUCH_MAJOR, ABS_MT_WIDTH_MAJOR, 

    ABS_MT_POSITION_X & ABS_MT_POSITION_Y for each touch point (X1,Y1...X4,Y4)

    ABS_X,ABS_Y,ABS_PRESSURE, BTN_TOUCH,

    For each MT report a input_mt_sync is called. An input_sync at the very end after reporting ABS_X,Y and PRESSURE.

    In probe function in driver :

    set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
    set_bit(ABS_MT_POSITION_X, input_dev->absbit);
    set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
    set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);

    set_bit(ABS_X, input_dev->absbit);
    set_bit(ABS_Y, input_dev->absbit);
    set_bit(ABS_PRESSURE, input_dev->absbit);
    set_bit(BTN_TOUCH, input_dev->keybit);

    set_bit(EV_ABS, input_dev->evbit);
    set_bit(EV_KEY, input_dev->evbit);

    does this look right to you ?

    Thanks a lot

    Ram

  • I finally understood whats going on and what is needed.

    It was all clear once I looked at tslib plugins/input-raw.c

    It first expects ABS_X, ABS_Y, BTN_TOUCH,EV_ABS & EV_KEY before it even recognises a device as touchscreen.

    Then if EV_SYN is defined it accepts ABS_MT_POSITIONX & Y else accepts only ABS_X & Y.

    So I modified my driver accordingly and am able to use touchscreen by reporting just ABS_MT_POSITION_X & Y.

    As David pointed out, multitouch support is not just out of the box now.

    I found this post http://www.ptrackapp.com/apclassys-notes/embedded-linux-multitouch/ and I hope this brings me on track with multitouch.

    As per this post, I need another llibrary TUIO to bring multitouch support.

    Is there is any other way ?

    I will post all my code here once I get multitouch working.

    In the mean-time any pointers are greatly appreciated.

    Thanks a lot

    Ram

  • Hallo Everyone,

    I am posting my final working capacitive touchscreen driver for NHD-5.0-800480TF-ATXI#-CTP attached to the AM335x starter kit. I have not integrated the CAPTOUCH_WAKE pin to be held high from the driver. I did this directly in hardware with a pull up resistor. The respective pinmux configurations must be initialized in the boardconfig file. I have also posted my board config file for reference. Please cross check the file before using as it is. It has also been modification for custom hardware not supplied with the starter kit.

    A dummy empty file ft_app.i is require for the driver to compile.

    You might have to recompile tslib from its latest source and copy the libraries to the board.

    This driver has been tested with Qt4.8. Single touch works with supplied TI SDK and Qt. For multitouch in Qt 4.8, please follow the link   http://www.ptrackapp.com/apclassys-notes/embedded-linux-multitouch/ . Qt5 might have integrated multitouch. I am yet to test this. To setup Qt5 this link might be useful http://tigraphics.blogspot.de/2013/01/qt-500-final-dec-2012-available-on-ti.html

    Thanks a lot for your help everyone.

    Ram

    2350.ft5x06_ts.h

    1362.ft5x06_ts.c
    /* 
     * drivers/input/touchscreen/ft5x0x_ts.c
     *
     * FocalTech ft5x0x TouchScreen driver. 
     *
     * Copyright (c) 2010  Focal tech Ltd.
     *
     * 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.
     *
     * VERSION      	DATE			AUTHOR
     *    1.0		  2010-01-05			WenFS
     *
     * note: only support multitouch	Wenfs 2010-10-01
     */
    
    
    
    #include <linux/i2c.h>
    #include <linux/input.h>
    
    #ifdef CONFIG_HAS_EARLYSUSPEND
    	#include <linux/earlysuspend.h>
    #endif
    
    #include <linux/interrupt.h>
    #include <linux/delay.h>
    #include <linux/slab.h>
    #include <linux/module.h>
    #include <linux/gpio.h>
    #include <linux/input/mt.h>
    #include <linux/input/ft5x06_ts.h>
    //#include <asm/jzsoc.h>
    
    static struct i2c_client *this_client;
    static struct ft5x0x_ts_platform_data *pdata;
    
    #define CONFIG_FT5X0X_MULTITOUCH 1
    
    /* Convert GPIO signal to GPIO pin number */
    #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))
    
    static int ct_irq_num;
    
    struct ts_event {
    	u16	x1;
    	u16	y1;
    	u16	x2;
    	u16	y2;
    	u16	x3;
    	u16	y3;
    	u16	x4;
    	u16	y4;
    	u16	x5;
    	u16	y5;
    	u16	pressure;
        u8  touch_point;
    };
    
    struct ft5x0x_ts_data {
    	struct input_dev	*input_dev;
    	struct ts_event		event;
    	struct work_struct 	pen_event_work;
    	struct workqueue_struct *ts_workqueue;
    #ifdef CONFIG_HAS_EARLYSUSPEND
    	struct early_suspend	early_suspend;
    #endif  //CONFIG_HAS_EARLYSUSPEND
    };
    
    /***********************************************************************************************
    Name	:	ft5x0x_i2c_rxdata 
    
    Input	:	*rxdata
                         *length
    
    Output	:	ret
    
    function	:	
    
    ***********************************************************************************************/
    static int ft5x0x_i2c_rxdata(char *rxdata, int length)
    {
    	int ret;
    
    	struct i2c_msg msgs[] = {
    		{
    			.addr	= this_client->addr,
    			.flags	= 0,
    			.len	= 1,
    			.buf	= rxdata,
    		},
    		{
    			.addr	= this_client->addr,
    			.flags	= I2C_M_RD,
    			.len	= length,
    			.buf	= rxdata,
    		},
    	};
    
        //msleep(1);
    	ret = i2c_transfer(this_client->adapter, msgs, 2);
    	if (ret < 0)
    		pr_err("msg %s i2c read error: %d\n", __func__, ret);
    	
    	return ret;
    }
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static int ft5x0x_i2c_txdata(char *txdata, int length)
    {
    	int ret;
    
    	struct i2c_msg msg[] = {
    		{
    			.addr	= this_client->addr,
    			.flags	= 0,
    			.len	= length,
    			.buf	= txdata,
    		},
    	};
    
       	//msleep(1);
    	ret = i2c_transfer(this_client->adapter, msg, 1);
    	if (ret < 0)
    		pr_err("%s i2c write error: %d\n", __func__, ret);
    
    	return ret;
    }
    /***********************************************************************************************
    Name	:	 ft5x0x_write_reg
    
    Input	:	addr -- address
                         para -- parameter
    
    Output	:	
    
    function	:	write register of ft5x0x
    
    ***********************************************************************************************/
    static int ft5x0x_write_reg(u8 addr, u8 para)
    {
        u8 buf[3];
        int ret = -1;
    
        buf[0] = addr;
        buf[1] = para;
        ret = ft5x0x_i2c_txdata(buf, 2);
        if (ret < 0) {
            pr_err("write reg failed! %#x ret: %d", buf[0], ret);
            return -1;
        }
        
        return 0;
    }
    
    
    /***********************************************************************************************
    Name	:	ft5x0x_read_reg 
    
    Input	:	addr
                         pdata
    
    Output	:	
    
    function	:	read register of ft5x0x
    
    ***********************************************************************************************/
    static int ft5x0x_read_reg(u8 addr, u8 *pdata)
    {
    	int ret;
    	u8 buf[2] = {0};
    
    	buf[0] = addr;
    	struct i2c_msg msgs[] = {
    		{
    			.addr	= this_client->addr,
    			.flags	= 0,
    			.len	= 1,
    			.buf	= buf,
    		},
    		{
    			.addr	= this_client->addr,
    			.flags	= I2C_M_RD,
    			.len	= 1,
    			.buf	= buf,
    		},
    	};
    
        //msleep(1);
    	ret = i2c_transfer(this_client->adapter, msgs, 2);
    	if (ret < 0)
    		pr_err("msg %s i2c read error: %d\n", __func__, ret);
    
    	*pdata = buf[0]; 
    
    	//Modification to add delay between two operations for clock stretching issue. --Shriram
    	/*ret = i2c_transfer(this_client->adapter, &msgs[0], 1);
    	if (ret < 0)
    		pr_err("msg %s i2c read error - write addr error: %d\n", __func__, ret);
    	ret = i2c_transfer(this_client->adapter, &msgs[1], 1);
    	if (ret < 0)
    		pr_err("msg %s i2c read error - write suc, read reg error: %d\n", __func__, ret);
    	*pdata = buf[0];*/
    
    	return ret;
      
    }
    
    
    /***********************************************************************************************
    Name	:	 ft5x0x_read_fw_ver
    
    Input	:	 void
                         
    
    Output	:	 firmware version 	
    
    function	:	 read TP firmware version
    
    ***********************************************************************************************/
    static unsigned char ft5x0x_read_fw_ver(void)
    {
    	unsigned char ver;
    	ft5x0x_read_reg(FT5X0X_REG_FIRMID, &ver);
    	return(ver);
    }
    
    
    #define CONFIG_SUPPORT_FTS_CTP_UPG
    
    
    #ifdef CONFIG_SUPPORT_FTS_CTP_UPG
    
    typedef enum
    {
        ERR_OK,
        ERR_MODE,
        ERR_READID,
        ERR_ERASE,
        ERR_STATUS,
        ERR_ECC,
        ERR_DL_ERASE_FAIL,
        ERR_DL_PROGRAM_FAIL,
        ERR_DL_VERIFY_FAIL
    }E_UPGRADE_ERR_TYPE;
    
    typedef unsigned char         FTS_BYTE;     //8 bit
    typedef unsigned short        FTS_WORD;    //16 bit
    typedef unsigned int          FTS_DWRD;    //16 bit
    typedef unsigned char         FTS_BOOL;    //8 bit
    
    #define FTS_NULL                0x0
    #define FTS_TRUE                0x01
    #define FTS_FALSE              0x0
    
    #define I2C_CTPM_ADDRESS       0x70
    
    
    void delay_qt_ms(unsigned long  w_ms)
    {
        unsigned long i;
        unsigned long j;
    
        for (i = 0; i < w_ms; i++)
        {
            for (j = 0; j < 1000; j++)
            {
                udelay(1);
            }
        }
    }
    
    
    /*
    [function]: 
        callback: read data from ctpm by i2c interface,implemented by special user;
    [parameters]:
        bt_ctpm_addr[in]    :the address of the ctpm;
        pbt_buf[out]        :data buffer;
        dw_lenth[in]        :the length of the data buffer;
    [return]:
        FTS_TRUE     :success;
        FTS_FALSE    :fail;
    */
    FTS_BOOL i2c_read_interface(FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)
    {
        int ret;
        
        ret=i2c_master_recv(this_client, pbt_buf, dw_lenth);
    
        if(ret<=0)
        {
            printk("[TSP]i2c_read_interface error\n");
            return FTS_FALSE;
        }
      
        return FTS_TRUE;
    }
    
    /*
    [function]: 
        callback: write data to ctpm by i2c interface,implemented by special user;
    [parameters]:
        bt_ctpm_addr[in]    :the address of the ctpm;
        pbt_buf[in]        :data buffer;
        dw_lenth[in]        :the length of the data buffer;
    [return]:
        FTS_TRUE     :success;
        FTS_FALSE    :fail;
    */
    FTS_BOOL i2c_write_interface(FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)
    {
        int ret;
        ret=i2c_master_send(this_client, pbt_buf, dw_lenth);
        if(ret<=0)
        {
            printk("[TSP]i2c_write_interface error line = %d, ret = %d\n", __LINE__, ret);
            return FTS_FALSE;
        }
    
        return FTS_TRUE;
    }
    
    /*
    [function]: 
        send a command to ctpm.
    [parameters]:
        btcmd[in]        :command code;
        btPara1[in]    :parameter 1;    
        btPara2[in]    :parameter 2;    
        btPara3[in]    :parameter 3;    
        num[in]        :the valid input parameter numbers, if only command code needed and no parameters followed,then the num is 1;    
    [return]:
        FTS_TRUE    :success;
        FTS_FALSE    :io fail;
    */
    FTS_BOOL cmd_write(FTS_BYTE btcmd,FTS_BYTE btPara1,FTS_BYTE btPara2,FTS_BYTE btPara3,FTS_BYTE num)
    {
        FTS_BYTE write_cmd[4] = {0};
    
        write_cmd[0] = btcmd;
        write_cmd[1] = btPara1;
        write_cmd[2] = btPara2;
        write_cmd[3] = btPara3;
        return i2c_write_interface(I2C_CTPM_ADDRESS, write_cmd, num);
    }
    
    /*
    [function]: 
        write data to ctpm , the destination address is 0.
    [parameters]:
        pbt_buf[in]    :point to data buffer;
        bt_len[in]        :the data numbers;    
    [return]:
        FTS_TRUE    :success;
        FTS_FALSE    :io fail;
    */
    FTS_BOOL byte_write(FTS_BYTE* pbt_buf, FTS_DWRD dw_len)
    {
        
        return i2c_write_interface(I2C_CTPM_ADDRESS, pbt_buf, dw_len);
    }
    
    /*
    [function]: 
        read out data from ctpm,the destination address is 0.
    [parameters]:
        pbt_buf[out]    :point to data buffer;
        bt_len[in]        :the data numbers;    
    [return]:
        FTS_TRUE    :success;
        FTS_FALSE    :io fail;
    */
    FTS_BOOL byte_read(FTS_BYTE* pbt_buf, FTS_BYTE bt_len)
    {
        return i2c_read_interface(I2C_CTPM_ADDRESS, pbt_buf, bt_len);
    }
    
    
    /*
    [function]: 
        burn the FW to ctpm.
    [parameters]:(ref. SPEC)
        pbt_buf[in]    :point to Head+FW ;
        dw_lenth[in]:the length of the FW + 6(the Head length);    
        bt_ecc[in]    :the ECC of the FW
    [return]:
        ERR_OK        :no error;
        ERR_MODE    :fail to switch to UPDATE mode;
        ERR_READID    :read id fail;
        ERR_ERASE    :erase chip fail;
        ERR_STATUS    :status error;
        ERR_ECC        :ecc error.
    */
    
    
    #define    FTS_PACKET_LENGTH        128
    
    static unsigned char CTPM_FW[]=
    {
    #include "ft_app.i"
    };
    
    E_UPGRADE_ERR_TYPE  fts_ctpm_fw_upgrade(FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)
    {
        FTS_BYTE reg_val[2] = {0};
        FTS_DWRD i = 0;
    
        FTS_DWRD  packet_number;
        FTS_DWRD  j;
        FTS_DWRD  temp;
        FTS_DWRD  lenght;
        FTS_BYTE  packet_buf[FTS_PACKET_LENGTH + 6];
        FTS_BYTE  auc_i2c_write_buf[10];
        FTS_BYTE bt_ecc;
        int      i_ret;
    
        /*********Step 1:Reset  CTPM *****/
        /*write 0xaa to register 0xfc*/
        ft5x0x_write_reg(0xfc,0xaa);
        delay_qt_ms(50);
         /*write 0x55 to register 0xfc*/
        ft5x0x_write_reg(0xfc,0x55);
        printk("[TSP] Step 1: Reset CTPM test\n");
       
        delay_qt_ms(30);   
    
    
        /*********Step 2:Enter upgrade mode *****/
        auc_i2c_write_buf[0] = 0x55;
        auc_i2c_write_buf[1] = 0xaa;
        do
        {
            i ++;
            i_ret = ft5x0x_i2c_txdata(auc_i2c_write_buf, 2);
            delay_qt_ms(5);
        }while(i_ret <= 0 && i < 5 );
    
        /*********Step 3:check READ-ID***********************/        
        cmd_write(0x90,0x00,0x00,0x00,4);
        byte_read(reg_val,2);
        if (reg_val[0] == 0x79 && reg_val[1] == 0x3)
        {
            printk("[TSP] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",reg_val[0],reg_val[1]);
        }
        else
        {
            return ERR_READID;
            //i_is_new_protocol = 1;
        }
    
         /*********Step 4:erase app*******************************/
        cmd_write(0x61,0x00,0x00,0x00,1);
       
        delay_qt_ms(1500);
        printk("[TSP] Step 4: erase. \n");
    
        /*********Step 5:write firmware(FW) to ctpm flash*********/
        bt_ecc = 0;
        printk("[TSP] Step 5: start upgrade. \n");
        dw_lenth = dw_lenth - 8;
        packet_number = (dw_lenth) / FTS_PACKET_LENGTH;
        packet_buf[0] = 0xbf;
        packet_buf[1] = 0x00;
        for (j=0;j<packet_number;j++)
        {
            temp = j * FTS_PACKET_LENGTH;
            packet_buf[2] = (FTS_BYTE)(temp>>8);
            packet_buf[3] = (FTS_BYTE)temp;
            lenght = FTS_PACKET_LENGTH;
            packet_buf[4] = (FTS_BYTE)(lenght>>8);
            packet_buf[5] = (FTS_BYTE)lenght;
    
            for (i=0;i<FTS_PACKET_LENGTH;i++)
            {
                packet_buf[6+i] = pbt_buf[j*FTS_PACKET_LENGTH + i]; 
                bt_ecc ^= packet_buf[6+i];
            }
            
            byte_write(&packet_buf[0],FTS_PACKET_LENGTH + 6);
            delay_qt_ms(FTS_PACKET_LENGTH/6 + 1);
            if ((j * FTS_PACKET_LENGTH % 1024) == 0)
            {
                  printk("[TSP] upgrade the 0x%x th byte.\n", ((unsigned int)j) * FTS_PACKET_LENGTH);
            }
        }
    
        if ((dw_lenth) % FTS_PACKET_LENGTH > 0)
        {
            temp = packet_number * FTS_PACKET_LENGTH;
            packet_buf[2] = (FTS_BYTE)(temp>>8);
            packet_buf[3] = (FTS_BYTE)temp;
    
            temp = (dw_lenth) % FTS_PACKET_LENGTH;
            packet_buf[4] = (FTS_BYTE)(temp>>8);
            packet_buf[5] = (FTS_BYTE)temp;
    
            for (i=0;i<temp;i++)
            {
                packet_buf[6+i] = pbt_buf[ packet_number*FTS_PACKET_LENGTH + i]; 
                bt_ecc ^= packet_buf[6+i];
            }
    
            byte_write(&packet_buf[0],temp+6);    
            delay_qt_ms(20);
        }
    
        //send the last six byte
        for (i = 0; i<6; i++)
        {
            temp = 0x6ffa + i;
            packet_buf[2] = (FTS_BYTE)(temp>>8);
            packet_buf[3] = (FTS_BYTE)temp;
            temp =1;
            packet_buf[4] = (FTS_BYTE)(temp>>8);
            packet_buf[5] = (FTS_BYTE)temp;
            packet_buf[6] = pbt_buf[ dw_lenth + i]; 
            bt_ecc ^= packet_buf[6];
    
            byte_write(&packet_buf[0],7);  
            delay_qt_ms(20);
        }
    
        /*********Step 6: read out checksum***********************/
        /*send the opration head*/
        cmd_write(0xcc,0x00,0x00,0x00,1);
        byte_read(reg_val,1);
        printk("[TSP] Step 6:  ecc read 0x%x, new firmware 0x%x. \n", reg_val[0], bt_ecc);
        if(reg_val[0] != bt_ecc)
        {
            return ERR_ECC;
        }
    
        /*********Step 7: reset the new FW***********************/
        cmd_write(0x07,0x00,0x00,0x00,1);
    
        return ERR_OK;
    }
    
    
    int fts_ctpm_fw_upgrade_with_i_file(void)
    {
       FTS_BYTE*     pbt_buf = FTS_NULL;
       int i_ret;
        
        //=========FW upgrade========================*/
       pbt_buf = CTPM_FW;
       /*call the upgrade function*/
       i_ret =  fts_ctpm_fw_upgrade(pbt_buf,sizeof(CTPM_FW));
       if (i_ret != 0)
       {
           //error handling ...
           //TBD
       }
    
       return i_ret;
    }
    
    unsigned char fts_ctpm_get_upg_ver(void)
    {
        unsigned int ui_sz;
        ui_sz = sizeof(CTPM_FW);
        if (ui_sz > 2)
        {
            return CTPM_FW[ui_sz - 2];
        }
        else
        {
            //TBD, error handling?
            return 0xff; //default value
        }
    }
    
    #endif
    
    
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static void ft5x0x_ts_release(void)
    {
    	struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
    #ifdef CONFIG_FT5X0X_MULTITOUCH
    	input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0);
    	input_mt_sync(data->input_dev);
    #else
    	input_report_abs(data->input_dev, ABS_PRESSURE, 0);
    #endif
    	input_report_key(data->input_dev, BTN_TOUCH, 0);
    	input_sync(data->input_dev);
    }
    
    static int ft5x0x_read_data(void)
    {
    	struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
    	struct ts_event *event = &data->event;
    //	u8 buf[14] = {0};
    	u8 buf[32] = {0};
    	int ret = -1;
    
    #ifdef CONFIG_FT5X0X_MULTITOUCH
    //	ret = ft5x0x_i2c_rxdata(buf, 13);
    	ret = ft5x0x_i2c_rxdata(buf, 31);
    #else
        ret = ft5x0x_i2c_rxdata(buf, 7);
    #endif
        if (ret < 0) {
    		printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
    		return ret;
    	}
    
    	memset(event, 0, sizeof(struct ts_event));
    //	event->touch_point = buf[2] & 0x03;// 0000 0011
    	event->touch_point = buf[2] & 0x07;// 000 0111
    
        if (event->touch_point == 0) {
            ft5x0x_ts_release();
            return 1; 
        }
    
    #ifdef CONFIG_FT5X0X_MULTITOUCH
        switch (event->touch_point) {
    		case 5:
    			event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
    			event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
    		case 4:
    			event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
    			event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
    		case 3:
    			event->x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
    			event->y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
    		case 2:
    			event->x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];
    			event->y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];
    		case 1:
    			event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
    			event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
                break;
    		default:
    		    return -1;
    	}
    #else
        if (event->touch_point == 1) {
        	event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
    		event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
        }
    #endif
        event->pressure = 200;
    
    	dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
    		event->x1, event->y1, event->x2, event->y2);
    	//printk("%d (%d, %d), (%d, %d)\n", event->touch_point, event->x1, event->y1, event->x2, event->y2);
    
        return 0;
    }
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static void ft5x0x_report_value(void)
    {
    	struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
    	struct ts_event *event = &data->event;
    	u8 uVersion;
    
    		//printk("==ft5x0x_report_value =\n");
    #ifdef CONFIG_FT5X0X_MULTITOUCH
    	switch(event->touch_point) {
    		case 5:
    			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x5);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y5);
    			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
    			input_mt_sync(data->input_dev);
    			//printk("===x5 = %d,y5 = %d ====\n",event->x2,event->y2);
    		case 4:
    			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x4);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y4);
    			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
    			input_mt_sync(data->input_dev);
    			//printk("===x4 = %d,y4 = %d ====\n",event->x2,event->y2);
    		case 3:
    			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x3);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y3);
    			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
    			input_mt_sync(data->input_dev);
    			//printk("===x3 = %d,y3 = %d ====\n",event->x2,event->y2);
    		case 2:
    			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x2);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y2);
    			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
    			input_mt_sync(data->input_dev);
    			//printk("===x2 = %d,y2 = %d ====\n",event->x2,event->y2);
    		case 1:
    			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x1);
    			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y1);
    			input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
    			input_mt_sync(data->input_dev);
    			//printk("===x1 = %d,y1 = %d ====\n",event->x1,event->y1);
    
    		default:
    			//printk("==touch_point default =\n");
    			break;
    	}
    #else	/* CONFIG_FT5X0X_MULTITOUCH*/
    	if (event->touch_point == 1) {
    		input_report_abs(data->input_dev, ABS_X, event->x1);
    		input_report_abs(data->input_dev, ABS_Y, event->y1);
    		input_report_abs(data->input_dev, ABS_PRESSURE, event->pressure);
    	}
    #endif	/* CONFIG_FT5X0X_MULTITOUCH*/
    	input_report_key(data->input_dev, BTN_TOUCH, 1);
    	input_sync(data->input_dev);
    
    	dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
    		event->x1, event->y1, event->x2, event->y2);
    }	/*end ft5x0x_report_value*/
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static void ft5x0x_ts_pen_irq_work(struct work_struct *work)
    {
    	int ret = -1;
    	//printk("==work 1=\n");
    	ret = ft5x0x_read_data();	
    	if (ret == 0) {	
    		ft5x0x_report_value();
    	}
    	//else printk("data package read error\n");
    	//printk("==work 2=\n");
        	msleep(1);
    //    enable_irq(this_client->irq);
    //	enable_irq(IRQ_EINT(6));
    }
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)
    {
    	struct ft5x0x_ts_data *ft5x0x_ts = dev_id;
    //    	disable_irq(this_client->irq);		
    //	disable_irq(IRQ_EINT(6));
    	//printk("==int=");
    	if (!work_pending(&ft5x0x_ts->pen_event_work)) {
    		queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work);
    	}
    
    	return IRQ_HANDLED;
    }
    #ifdef CONFIG_HAS_EARLYSUSPEND
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static void ft5x0x_ts_suspend(struct early_suspend *handler)
    {
    //	struct ft5x0x_ts_data *ts;
    //	ts =  container_of(handler, struct ft5x0x_ts_data, early_suspend);
    
    	printk("==ft5x0x_ts_suspend=\n");
    //	disable_irq(this_client->irq);
    //	disable_irq(IRQ_EINT(6));
    //	cancel_work_sync(&ts->pen_event_work);
    //	flush_workqueue(ts->ts_workqueue);
    	// ==set mode ==, 
    //    	ft5x0x_set_reg(FT5X0X_REG_PMODE, PMODE_HIBERNATE);
    }
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static void ft5x0x_ts_resume(struct early_suspend *handler)
    {
    	printk("==ft5x0x_ts_resume=\n");
    	// wake the mode
    //	__gpio_as_output(GPIO_FT5X0X_WAKE);		
    //	__gpio_clear_pin(GPIO_FT5X0X_WAKE);		//set wake = 0,base on system
    //	 msleep(100);
    //	__gpio_set_pin(GPIO_FT5X0X_WAKE);			//set wake = 1,base on system
    //	msleep(100);
    //	enable_irq(this_client->irq);
    //	enable_irq(IRQ_EINT(6));
    }
    #endif  //CONFIG_HAS_EARLYSUSPEND
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static int 
    ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
    	struct ft5x0x_ts_data *ft5x0x_ts;
    	struct input_dev *input_dev;
    	int err = 0;
    	unsigned char ver = 0;
    	unsigned char uc_reg_value;
    	ct_irq_num=0;
    	
    	printk("==ft5x0x_ts_probe=\n");
    	
    	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    		err = -ENODEV;
    		goto exit_check_functionality_failed;
    	}
    
    	printk("==kzalloc=\n");
    	ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);
    	if (!ft5x0x_ts)	{
    		err = -ENOMEM;
    		goto exit_alloc_data_failed;
    	}
    
    
    	this_client = client;
    	i2c_set_clientdata(client, ft5x0x_ts);
    
    
    	INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);
    
    	ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));//dev_name(&client->dev));
    	if (!ft5x0x_ts->ts_workqueue) {
    		err = -ESRCH;
    		goto exit_create_singlethread;
    	}
    
    	err = gpio_request(GPIO_TO_PIN(2,0),FT5X0X_NAME);
    	gpio_direction_input(GPIO_TO_PIN(2,0));
    	gpio_export(GPIO_TO_PIN(2,0),true);
    	ct_irq_num = gpio_to_irq(GPIO_TO_PIN(2,0));
    
    //	pdata = client->dev.platform_data;
    //	if (pdata == NULL) {
    //		dev_err(&client->dev, "%s: platform data is null\n", __func__);
    //		goto exit_platform_data_null;
    //	}
    	
    //	printk("==request_irq=\n");64 irq line
    //	err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED, FT5X0X_NAME, ft5x0x_ts);//"ft5x0x_ts" instead of FT5.._NAME before --shriram
    	err = request_irq(ct_irq_num, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING, FT5X0X_NAME, ft5x0x_ts);//IRQ_EINT(6)
    	if (err < 0) {
    		dev_err(&client->dev, "ft5x0x_probe: request irq failed\n");
    		printk("==request_irq failed=\n");//64 irq line should be
    		goto exit_irq_request_failed;
    	}
    
    //	__gpio_as_irq_fall_edge(pdata->intr);		//
    	disable_irq(ct_irq_num);
    //	disable_irq(IRQ_EINT(6));
    
    //	printk("==input_allocate_device=\n");
    	input_dev = input_allocate_device();
    	if (!input_dev) {
    		err = -ENOMEM;
    		dev_err(&client->dev, "failed to allocate input device\n");
    		goto exit_input_dev_alloc_failed;
    	}
    	
    	ft5x0x_ts->input_dev = input_dev;
    
    #ifdef CONFIG_FT5X0X_MULTITOUCH
    	set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
    	set_bit(ABS_MT_POSITION_X, input_dev->absbit);
    	set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
    	set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
    
    	input_set_abs_params(input_dev,
    			     ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
    	input_set_abs_params(input_dev,
    			     ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
    	input_set_abs_params(input_dev,
    			     ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
    	input_set_abs_params(input_dev,
    			     ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
    #else
    	set_bit(ABS_X, input_dev->absbit);
    	set_bit(ABS_Y, input_dev->absbit);
    	set_bit(ABS_PRESSURE, input_dev->absbit);
    	set_bit(BTN_TOUCH, input_dev->keybit);
    
    	input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
    	input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
    	input_set_abs_params(input_dev, ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);
    #endif
    
    	//Define ABS_X&Y for tslib as it expects that
    	set_bit(ABS_X, input_dev->absbit);
    	set_bit(ABS_Y, input_dev->absbit);
    	set_bit(BTN_TOUCH, input_dev->keybit);
    
    	set_bit(EV_SYN, input_dev->evbit);
    	set_bit(EV_ABS, input_dev->evbit);
    	set_bit(EV_KEY, input_dev->evbit);
    
    //	 err = input_mt_init_slots(input_dev, 4);//4 is max support points
    //	 if (err) {
    //			 dev_err(&client->dev, "Unable to init MT slots.\n");
    //			 printk("Unable to init MT Slots.\n");
    //			 //goto err_free_mem;
    //	 }
    
    	input_dev->name		= FT5X0X_NAME;		//dev_name(&client->dev)
    	err = input_register_device(input_dev);
    	if (err) {
    		dev_err(&client->dev,
    		"ft5x0x_ts_probe: failed to register input device: %s\n",
    		dev_name(&client->dev));
    		goto exit_input_register_device_failed;
    	}
    
    #ifdef CONFIG_HAS_EARLYSUSPEND
    	printk("==register_early_suspend =\n");
    	ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
    	ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;
    	ft5x0x_ts->early_suspend.resume	= ft5x0x_ts_resume;
    	register_early_suspend(&ft5x0x_ts->early_suspend);
    #endif
    
        msleep(50);
        //get some register information
        uc_reg_value = ft5x0x_read_fw_ver();
        printk("[FST] FT55x0x Firmware version = 0x%x\n", uc_reg_value);
        /*uc_reg_value = ft5x0x_read_reg(FT5X0X_REG_FT5201ID, &ver);
        printk("[FST] FT55x0x FiT5201ID = 0x%x\n", uc_reg_value);
        uc_reg_value = ft5x0x_read_reg(FT5X0X_REG_ERR, &ver);
        printk("[FST] FT55x0x ERR = 0x%x\n", uc_reg_value);*/
    
    
    //    fts_ctpm_fw_upgrade_with_i_file();
    
    //wake the CTPM
    //	__gpio_as_output(GPIO_FT5X0X_WAKE);		
    //	__gpio_clear_pin(GPIO_FT5X0X_WAKE);		//set wake = 0,base on system
    //	 msleep(100);
    //	__gpio_set_pin(GPIO_FT5X0X_WAKE);			//set wake = 1,base on system
    //	msleep(100);
    //	ft5x0x_set_reg(0x88, 0x05); //5, 6,7,8
    //	ft5x0x_set_reg(0x80, 30);
    //	msleep(50);
        	enable_irq(ct_irq_num);
    //    enable_irq(IRQ_EINT(6));
    
    	printk("==probe over =\n");
        return 0;
    
    exit_input_register_device_failed:
    	input_free_device(input_dev);
    exit_input_dev_alloc_failed:
    	free_irq(ct_irq_num, ft5x0x_ts);
    //	free_irq(IRQ_EINT(6), ft5x0x_ts);
    exit_irq_request_failed:
    exit_platform_data_null:
    	cancel_work_sync(&ft5x0x_ts->pen_event_work);
    	destroy_workqueue(ft5x0x_ts->ts_workqueue);
    exit_create_singlethread:
    	printk("==singlethread error =\n");
    	i2c_set_clientdata(client, NULL);
    	kfree(ft5x0x_ts);
    exit_alloc_data_failed:
    exit_check_functionality_failed:
    	return err;
    }
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static int __devexit ft5x0x_ts_remove(struct i2c_client *client)
    {
    	printk("==ft5x0x_ts_remove=\n");
    	struct ft5x0x_ts_data *ft5x0x_ts = i2c_get_clientdata(client);
    #ifdef CONFIG_HAS_EARLYSUSPEND
    	unregister_early_suspend(&ft5x0x_ts->early_suspend);
    #endif
    	free_irq(ct_irq_num, ft5x0x_ts);
    	gpio_free(GPIO_TO_PIN(2,0));
    //	free_irq(IRQ_EINT(6), ft5x0x_ts);
    	input_unregister_device(ft5x0x_ts->input_dev);
    	kfree(ft5x0x_ts);
    	cancel_work_sync(&ft5x0x_ts->pen_event_work);
    	destroy_workqueue(ft5x0x_ts->ts_workqueue);
    	i2c_set_clientdata(client, NULL);
    	return 0;
    }
    
    static const struct i2c_device_id ft5x0x_ts_id[] = {
    	{ FT5X0X_NAME, 0 },{ }
    };
    
    
    MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);
    
    static struct i2c_driver ft5x0x_ts_driver = {
    	.probe		= ft5x0x_ts_probe,
    	.remove		= __devexit_p(ft5x0x_ts_remove),
    	.id_table	= ft5x0x_ts_id,
    	.driver	= {
    		.name	= FT5X0X_NAME,
    		.owner	= THIS_MODULE,
    	},
    };
    
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static int __init ft5x0x_ts_init(void)
    {
    	int ret;
    	printk("==ft5x0x_ts_init==\n");
    	ret = i2c_add_driver(&ft5x0x_ts_driver);
    	printk("ret=%d\n",ret);
    	return ret;
    //	return i2c_add_driver(&ft5x0x_ts_driver);
    }
    
    /***********************************************************************************************
    Name	:	 
    
    Input	:	
                         
    
    Output	:	
    
    function	:	
    
    ***********************************************************************************************/
    static void __exit ft5x0x_ts_exit(void)
    {
    	printk("==ft5x0x_ts_exit==\n");
    	i2c_del_driver(&ft5x0x_ts_driver);
    }
    
    module_init(ft5x0x_ts_init);
    module_exit(ft5x0x_ts_exit);
    
    MODULE_AUTHOR("<wenfs@Focaltech-systems.com>");
    MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");
    MODULE_LICENSE("GPL");
    

    6378.board-am335xevm.c

  • Hi Ram,

    Did you get the ft5x06 to working? I am trying it on 3.8 kernel. How did you register this device as input and shown in /input

    thanks

    siva.

  • Hi

    Can you post the resources on how you were able to make the LCD display worked.


    Thanks. 

  • Hi,

    I have posted here my complete source code. 

    This is the final version that I currently use. ftx5x06 driver posted here (look in this post for the verified answer), should register the device as input. Please make sure that I2c works and the interrupt is configured properly (pinmux + hardware pullups).

    I have no other sources to post.

    If you could tell me your problem in detail (your driver source + dmesg output minimum), I will try to help you. May be your display model + rough schematic would also be useful.

    Ram

  • I am using the linux kernel 3.8. I doesn't have the board-am335x-evm.c because it uses a device tree method for the add on hardware. Bu I think I can manage to do a device tree definition that contains the pinmux settings. However What I am not sure is if I set the correct LCD pinmux, does it readily make the LCD display work? Or do I have to download and port a separate driver for the LCD display? I'm focusing on the display for now, the touch panel will follow once I can make the display work.

    Thanks.




    Kit 

  • Hi Kit,

    To make the display work, you have to make sure that

    1. pinmux is correct (check from hardware + software side)

    2. timings should be adjusted properly. If you use a newhaven display, just changing the resolution will do the trick. Otherwise you have to manually adapt the timings. The timing information is easy to get/calculate from the datasheet.

    As far as i Know, this is all you need.

    Ram

  • Well, thank you. I am not using the newhaven display. I'll check the timing in the datasheet. Where can I adjust the resolution? I have check you board file. You did not connect the bits16-24. It is the bits 0 and 1 (least significant) of each RGB data right?

  • I did not connect 16-24 because I want to use these lines for other purposes. And a 16 bit color was enough for me. You can ofcourse include the full 24 bits.

    Please have a look at this link

    http://processors.wiki.ti.com/index.php/AM335x_LCD_Controller_Driver's_Guide

    You can try your timing + resolution options from console using fbset.

    Every change to do with your lcd panel (other than pinmux) can be done in da8xx-fb.c

  • Oh I see. Thank you very much. I checked my datasheet and it matches the timing in your LCD's datasheet. I'm going to test this once I finished the the circuit. 

     

  • Last question about the display. How did you connect the 40 pins LCD to the AM335x LCD pins?
    did you follow the LCD4 cape pinning?

    I found this in the datasheet of you LCD:

    40-pin           Symbol      Function description
    LCD
    5-12              [R0-R7]      MPU Red Data Signals
    13-20            [G0-G7]      MPU Green Data Signals
    21-28            [B0-B7]       MPU Blue Data Signals

    Since you only connected 16 pins, how did you map the 16 LCD pins (LCD_data0-LCD_data15) to pins 5-28 of the 40 pin LCD FPC cable?

    As of now this is what is on my mind:
    LCD_data0   -> R3
    LCD_data1   -> R4
    LCD_data2   -> R5
    LCD_data3   -> R6
    LCD_data4   -> R7
    LCD_data5   -> G2
    LCD_data6   -> G3
    LCD_data7   -> G4
    LCD_data8   -> G5
    LCD_data9   -> G6
    LCD_data10 -> G7
    LCD_data11 -> B3
    LCD_data12 -> B4
    LCD_data13 -> B5
    LCD_data14 -> B6
    LCD_data15 -> B7

    Thanks in advance.


    Kit 

  • Hi Kit,

    Please refer to this app  note. i think you are right!!

    http://www.ti.com/lit/er/sprz360e/sprz360e.pdf


    Thanks,

    siva.

  • Hi siva,

    Thanks for all the help. I think I can go on now.


    Regards,

    Kit 

  • Hello Kit,

    Did you manage to get the FT5x06 touchscreen to work with the 3.8 kernel? If so, what driver did you use?

    Thanks

  • Not yet. I am still designing the circuit to interface it to beaglebone. I am going to fabricate it manually today and hoping that I can manage to etch the 0.5mm pins. I got delayed because the supplier supplied a different LCD from the datasheet they gave me so I got the wrong components. I will be using the drivers shriram posted. I will be focusing first on making the display work first. Then maybe by next week, the touch interface.

  • I managed to get the driver to work on the 3.8 kernel by slightly modifying it, it maps the input device perfectly and is usable. However I'm getting a lot of read timeouts on the i2c bus:

    omap_i2c 4802a000.i2c: timeout waiting for bus ready
    msg ft5x0x_i2c_rxdata i2c read error: -110
    ft5x0x_read_data read_data i2c_rxdata failed: -110
    msg ft5x0x_i2c_rxdata i2c read error: -121
    ft5x0x_read_data read_data i2c_rxdata failed: -121

    The timeouts cause the driver being unable to fetch the positions and 'freezing' touch input. Any ideas why these timeouts occur?

  • does time out occur for other devices in the same i2c line ? If so, check for proper pullup in sda and scl lines. May be some other device is creating problem.

    Does switching to a higher I2C bus speed help ? I changed the 100KHz default to 400KHz.

  • There are no other devices on the bus. Switching to a higher bus speed reduces the amount of timeouts but does not solve it unfortunately.

    I tried a different touchscreen with a FT5316 controller and used the same driver, this does not give any i2c timeouts and works perfectly.

  • I also made the driver work with linux 3.8 in beaglebone black last night. I did not notice the timeouts. How do you check for the timeouts?

  • dmesg will give you the latest errors. You should also be able to see it in console if its a i2c_timeout error.

    pr_err("msg %s i2c read error: %d\n", __func__, ret);

    Result from the above statement from driver is also present in dmesg.

    Also there must be noticeable touch misses / performance issues.

  • Kit, can I test the driver you are currently using?

  • shriram,

    Thanks for all the contribution so far. 

    I am currently building for the android OS and running to problems using this display. It just display some veritcal lines.

    I tried to change the uEnv.txt for the android version for higher resolution:

    video=800x480-16@60

    I notice that you said, if you are not using anything else beside NewHaven's display then it should work seemlessly and I only need to change the resolution. Can I ask where you are changing the resolution? 

    Also where should I start modifying to incorporate your code in the Android build.

    Thanks!

  • Hi Minh Van Ly,

    Since I am with linux sdk6, I still use 3.2 kernel and no device tree yet. So I am working with board config file and the following details are for linux. I have not worked with android.But I hope it is something similar for android.

    In the board config file : TFC_S9700RTWV35TR_01B_pdata should work for NHD 5" display. The data specs are identical between these two displays.

    struct da8xx_lcdc_platform_data TFC_S9700RTWV35TR_01B_pdata = {
    	.manu_name		= "ThreeFive",
    	.controller_data	= &lcd_cfg,
    	.type			= "TFC_S9700RTWV35TR_01B",
    };

    This is the easiest way I in linux to get the NHD 5" working. Also I think this is the display TI delivers along with EVM.

    If this does not work for you You can try entering your display in drivers/video/da8xx-fb.c -> "known_lcd_panels". (I am again explaning from linux perspective.Hope da8xx-fb driver is also same for android)

    static struct da8xx_panel known_lcd_panels[] = {
    	/* Sharp LCD035Q3DG01 */
    	[0] = {
    		.name = "Sharp_LCD035Q3DG01",
    		.width = 320,
    		.height = 240,
    		.hfp = 8,
    		.hbp = 6,
    		.hsw = 0,
    		.vfp = 2,
    		.vbp = 2,
    		.vsw = 0,
    		.pxl_clk = 4608000,
    		.invert_pxl_clk = 1,
    	},
    	/* Sharp LK043T1DG01 */
    	[1] = {
    		.name = "Sharp_LK043T1DG01",
    		.width = 480,
    		.height = 272,
    		.hfp = 2,
    		.hbp = 2,
    		.hsw = 41,
    		.vfp = 3,
    		.vbp = 3,
    		.vsw = 10,
    		.pxl_clk = 7833600,
    		.invert_pxl_clk = 0,
    	},
    	/* ThreeFive S9700RTWV35TR */
    	[2] = {
    		.name = "TFC_S9700RTWV35TR_01B",
    		.width = 800,
    		.height = 480,
    		.hfp = 39,
    		.hbp = 39,
    		.hsw = 47,
    		.vfp = 13,
    		.vbp = 29,
    		.vsw = 2,
    		.pxl_clk = 30000000,
    		.invert_pxl_clk = 0,
    	},
    	/* Newhaven Display */
    	[3] = {
    		.name = "NHD-4.3-ATXI#-T-1",
    		.width = 480,
    		.height = 272,
    		.hfp = 8,
    		.hbp = 43,
    		.hsw = 4,
    		.vfp = 4,
    		.vbp = 12,
    		.vsw = 10,
    		.pxl_clk = 9000000,
    		.invert_pxl_clk = 0,
    	},
    };

    The individual parameters can be inferred from the display datasheet.

    Hope this helps.

    Ram

  • Thanks! I got it to work by modifying da8xx-fb.c file. Since the timing is the same, all I needed is to change the resolution.

    I'm am currently to get the driver to work with the android system.

    If I get it to work, I'll post my solution.

  • Hi Ram,

    I have similar problem that you had with tslib not being able to recognize the events.  I get message:

    tslib: Selected device is not a touchscreen (must support ABS and KEY event types).

    However, if I do cat dev/event2 and touch the screen I got gibberish data at the console so I am pretty sure that touchscreen is working

    I used your version of touchscreen driver and still getting the same problem.  Do I need to get the latest version of tslib and recompile it to get it working?

    Any help is greatly appreciated,

    Ljubisa



  • Hi Ljubisa,

    cat /dev/event2 | hexdump should give you sensible values.

    Yes you have to recompile latest tslib and copy the drivers. Also please make sure that all tslib environment variables are setup correctly. look at tslib source code for raw input, you can understand more about whats going on.

    With sdk 6.00 my driver worked without compiling tslib anew.

    Ram

  • Ram,

    What do you mean by the driver needs ft_app.i to compile? I built the android image without any error. However, I don't know if it works because I have yet to test it.

    What is tslib? Do you think I need to build for the android build?

    I also plan to get the captouch_wake pin to work with the driver. I don't want to build the pull-up resistor. : ) Any idea on how to go about it? Should I look in the ft5x06_ts.h? Or should I look in the board-am335xevm.c file?

    Also what is pinmux file? Is this another name for the board-am335xevm.c file?

    Thanks!

  • Hi Minh,

    ft_app.i is a dummy firmware file that the driver requires. It will not compile without this, since this is referenced in the driver file. Make sure that the ft5x06_ts object file is being generated and you have modified config file to build this driver while compiling kernel.

    TSLIB: I am not sure if you even require tslib for android. I am sorry, I have no idea about touchscreen driver integration in android.

    WAKE:Look at my driver code posted in this thread. In probe method I set the wake pin to high using standard GPIO access method. The controller needs to have high level on wake up to be in normal mode. Atleast this is what I noticed in my NHD display. So the we set a high level on this wake up pin using any GPIO.

    PINMUX:pinmux is done in board-am335xevm file. look at any one of the setup pin mux functions in board-am335x.c  and you will get the idea how to initialize a pin in a particular muxmode. For wake pin you need to initalise the pin in GPIO mode. The board config file I posted in this thread also has this.

    Ram

  • Thanks!

    FT_APP.I: I get that I need the ft_app.i. I placed it in the same /driver/input/ folder as the ft5x06_ts.c. 

    However, I do not know how to go about putting it in the config file to build the driver. I used the TI's command:

    make ARCH=arm CROSS-COMPILE=arm-arago-linux-gnueabi- xconfig

    But that only gives me what they have available to compile. How do I go about added the FTx06 Touchscreen driver to the config so it will build it with the kernel?

    PINMUX: I just copied where you added the pinmux to my board-am335xevm.c file:

    static struct pinmux_config gpio_captouch_mux[] = {
    {"gpmc_clk.gpio2_1", OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},
    {"gpmc_csn3.gpio2_0", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT},
    {NULL, 0},
    };

    Is this right?

    WAKE: I am still a bit confused about it, but worst case, I will make it a pull-up.

    Thanks for all the help!

  • Hi Minh,

    You have to add the following to KConfig in drivers/input//touchscreen folder. You will also notice here entries for other touchscreen drivers. Adding here will present your driver in config tool.

    config TOUCHSCREEN_FT5X06
    	tristate "FocalTech ft5x06 touchscreen"
    	depends on I2C
    	help
    	  Say Y here if you want to support FT5X06 in NewHaven 5inch display
    	  touchscreen controller.
    
    	  If unsure, say N.
    
    	  To compile this driver as a module, choose M here: the
    	  module will be called ft5x06_ts.
    
    endif

    Now you have to add the following in Makefile under the same folder. Again you will notice here the entries for other touchscreen drivers too.

    obj-$(CONFIG_TOUCHSCREEN_FT5X06)	+= ft5x06_ts.o

    Now have a look at kernel config in TI kernel compilation wiki for am335x and you will know how to check your new driver to be compiled along with kernel. Do not build it as a module.

    MUX: Yes this is the pinmux structure for captouch. One pin is interrupt (from ft5x06) and another is wakeup (to ft5x06).  Check out the following code and insert it into ft5x0x_ts_probe function after creating the workqueue (before reading from the chip).

    err = gpio_request(GPIO_TO_PIN(2,1),FT5X0X_NAME);
    	gpio_direction_output(GPIO_TO_PIN(2,1),1);
    	gpio_export(GPIO_TO_PIN(2,1),true);
    
    	err = gpio_request(GPIO_TO_PIN(2,0),FT5X0X_NAME);
    	gpio_direction_input(GPIO_TO_PIN(2,0));
    	gpio_export(GPIO_TO_PIN(2,0),true);
    	ct_irq_num = gpio_to_irq(GPIO_TO_PIN(2,0));

    This should take care of the Wake pin and interrupt pin for you.

    If you compile successfully, try with dmesg | grep ft in serial console to see messages from driver. You should not see any read errors / timeouts.

    Before compiling the kernel with the driver, see from with i2cdump, whether you are able to read from the chip to elimiate any hardware issues. The wake pin can set to high from console using gpio device class filesystem access. Again refer to TI wiki on GPIO. It is really elaborate and explains in detail.

    Hope this helps.

    Ram