[Android Software Development] swm5126:
EDIT:

A better fix has been found for touchscreens using HTCs driver allowing us to actually use the original driver instead of porting another (although porting did seem to work mostly, this seems more ideal).

http://xdaforums.com/showthread.php?p=19047963

Guess I'll leave this all here though in case someone ever looks it up again.

Ok, since I've been getting so many requests for information about how I did this, I decided to start a thread dedicated to it.

With Ice Cream Sandwich, Google seems to have changed the way it expects the touchscreen drivers to behave. Many of the HTC phones (starting with the Evo and Inc) all the way up to current HTC phones utilize some version of HTC's driver (drivers/input/touchscreen/atmel.c) written for the Atmel maXTouch touchscreen. This driver doesn't work at all on ICS. Many people will say its only the SDK port we're working with, but I have a good feeling some adjustments will need to be made to get the HTC driver working when the source is released anyways.

The few drivers that do work are:

synaptics-i2c-rmi.c - for Synaptics TS

---> Nexus One, Desire...possibly others that utilize this driver

mxt224.c - for Atmel maXTouch

---> Samsung Nexus S and most likely other Samsung phones

elan8232-i2c.c for elan TS

---> HTC Leo/HD2

From what I can tell, the only real big different that jumps out at me between these drivers and ours is the way the driver WRITES to the i2c bus. The Samsung and elan drivers both use i2c_master_send to write to the i2c bus. The Synaptics driver does it a little differently, using i2c_smbus_write_byte_data, which appears to do something similar, but needs to be used more often to accomplish the same thing.

The HTC driver uses i2c_transfer, which according to what I've found online, is not the preferred method of writing to the bus and i2c_master_send should be used instead.

It would seem like it would be easier to just modify the existing HTC driver to incorporate the newer method, but I didn't realize that until after I had already gotten the Samsung driver working! It should be possible, but I'm not sure I have the expertise to do this as it would require tweaking a lot of code in the driver.

Anyways, on to what I actually did.

I was able to compile the Samsung driver into cuviper's CM7 kernel, but the driver kept hanging on reading the i2c bus from what I could see. I figured it'd have this problem with writing to it as well, so I changed both methods at the same time.

Here's the original read and write methods from HTC's atmel.c driver

int i2c_atmel_read(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length)
{
int retry;
uint8_t addr[2];

struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = addr,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = data,
}
};
addr[0] = address & 0xFF;
addr[1] = (address >> 8) & 0xFF;

for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) {
if (i2c_transfer(client->adapter, msg, 2) == 2)
break;
mdelay(10);
}
if (retry == ATMEL_I2C_RETRY_TIMES) {
printk(KERN_ERR "i2c_read_block retry over %d\n",
ATMEL_I2C_RETRY_TIMES);
return -EIO;
}
return 0;

}

int i2c_atmel_write(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length)
{
int retry, loop_i;
uint8_t buf[length + 2];

struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = length + 2,
.buf = buf,
}
};

buf[0] = address & 0xFF;
buf[1] = (address >> 8) & 0xFF;

for (loop_i = 0; loop_i < length; loop_i++)
buf[loop_i + 2] = data[loop_i];

for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) {
if (i2c_transfer(client->adapter, msg, 1) == 1)
break;
mdelay(10);
}

if (retry == ATMEL_I2C_RETRY_TIMES) {
printk(KERN_ERR "i2c_write_block retry over %d\n",
ATMEL_I2C_RETRY_TIMES);
return -EIO;
}
return 0;

}

And here's Samsung's methods from their mxt224.c driver

static int read_mem(struct mxt224_data *data, u16 reg, u8 len, u8 *buf)
{
int ret;
u16 le_reg = cpu_to_le16(reg);
struct i2c_msg msg[2] = {
{
.addr = data->client->addr,
.flags = 0,
.len = 2,
.buf = (u8 *)&le_reg,
},
{
.addr = data->client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = buf,
},
};

ret = i2c_transfer(data->client->adapter, msg, 2);
if (ret < 0)
return ret;

return ret == 2 ? 0 : -EIO;
}

static int write_mem(struct mxt224_data *data, u16 reg, u8 len, const u8 *buf)
{
int ret;
u8 tmp[len + 2];

put_unaligned_le16(cpu_to_le16(reg), tmp);
memcpy(tmp + 2, buf, len);

ret = i2c_master_send(data->client, tmp, sizeof(tmp));
if (ret < 0)
return ret;

return ret == sizeof(tmp) ? 0 : -EIO;
}

You can see they're very similar. Anyways, for some reason, cpu_to_le16 function either doesn't work correctly with the Snapdragon, or the build isn't linking them right, even though I made sure it had all the right files and #includes. So I took them out. And replaced them with part of the functions from the HTC driver. And after a couple failed attempts at booting, I came up with this:

static int read_mem(struct mxt224_data *data, u16 reg, u8 len, u8 *buf)
{
int ret;
uint8_t addr[2];

struct i2c_msg msg[] = {
{
.addr = data->client->addr,
.flags = 0,
.len = 2,
.buf = addr,
},
{
.addr = data->client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = buf,
},
};

addr[0] = reg & 0xFF;
addr[1] = (reg >> 8) & 0xFF;

ret = i2c_transfer(data->client->adapter, msg, 2);

if (ret < 0)
return ret;

return ret == 2 ? 0 : -EIO;
}

static int write_mem(struct mxt224_data *data, u16 reg, u8 len, const u8 *buf)
{
int ret;
uint8_t addr[len + 2];

addr[0] = reg & 0xFF;
addr[1] = (reg >> 8) & 0xFF;

memcpy(addr, buf, len);

ret = i2c_master_send(data->client, addr, sizeof(addr));

if (ret < 0)
return ret;

return ret == sizeof(addr) ? 0 : -EIO;
}

After those changes, I was able to use the touchscreen in CM7 just like I had with the HTC driver. Very responsive, no problems whatsoever. Also works fine in the ICS SDK ports. It only works in pointer mode in ICS currently, but that isn't a driver issue I don't think and can be fixed by changing a couple of the configuration files.

The only other things I had to change where in my board init file, but that was simply adding and deleting some of the touchscreen init functions and replacing them with similar ones from the Samsung driver. As well as populating the config info with HTC's values (although they also seemed to work fine with the stock ones from the Samsung build).

Anyways, if you have any questions, or more information please let me know. I could be totally wrong about the i2c bus thing, but as of right now it seems to make sense to me!

Here's my github for anyone that wishes to look at my changes.

Edit: atmel.c is the correct name of the driver source file. I got it mixed up with the way the HTC driver identifies itself in the kernel, which is atmel-touchscreen. Sorry for the confusion. Thanks to the_platypus to catching this.

Also, just wanted to reiterate this should work with just about any HTC phone using the maXTouch sensor. Just grab mxt224.c and .h and put them in the correct spot, modify the Kconfig file for touchscreens, and edit your board init file accordingly.