Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
19 kB · 766 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>#include <linux/i2c.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/input/touchscreen.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/of.h>#include <linux/property.h>#include <linux/regulator/consumer.h>#include <linux/slab.h>
/* Register Map */
#define ZINITIX_SWRESET_CMD 0x0000#define ZINITIX_WAKEUP_CMD 0x0001
#define ZINITIX_IDLE_CMD 0x0004#define ZINITIX_SLEEP_CMD 0x0005
#define ZINITIX_CLEAR_INT_STATUS_CMD 0x0003#define ZINITIX_CALIBRATE_CMD 0x0006#define ZINITIX_SAVE_STATUS_CMD 0x0007#define ZINITIX_SAVE_CALIBRATION_CMD 0x0008#define ZINITIX_RECALL_FACTORY_CMD 0x000f
#define ZINITIX_THRESHOLD 0x0020
#define ZINITIX_LARGE_PALM_REJECT_AREA_TH 0x003F
#define ZINITIX_DEBUG_REG 0x0115 /* 0~7 */
#define ZINITIX_TOUCH_MODE 0x0010
#define ZINITIX_CHIP_REVISION 0x0011#define ZINITIX_CHIP_BTX0X_MASK 0xF0F0#define ZINITIX_CHIP_BT4X2 0x4020#define ZINITIX_CHIP_BT4X3 0x4030#define ZINITIX_CHIP_BT4X4 0x4040
#define ZINITIX_FIRMWARE_VERSION 0x0012
#define ZINITIX_USB_DETECT 0x116
#define ZINITIX_MINOR_FW_VERSION 0x0121
#define ZINITIX_VENDOR_ID 0x001C#define ZINITIX_HW_ID 0x0014
#define ZINITIX_DATA_VERSION_REG 0x0013#define ZINITIX_SUPPORTED_FINGER_NUM 0x0015#define ZINITIX_EEPROM_INFO 0x0018#define ZINITIX_INITIAL_TOUCH_MODE 0x0019
#define ZINITIX_TOTAL_NUMBER_OF_X 0x0060#define ZINITIX_TOTAL_NUMBER_OF_Y 0x0061
#define ZINITIX_DELAY_RAW_FOR_HOST 0x007f
#define ZINITIX_BUTTON_SUPPORTED_NUM 0x00B0#define ZINITIX_BUTTON_SENSITIVITY 0x00B2#define ZINITIX_DUMMY_BUTTON_SENSITIVITY 0X00C8
#define ZINITIX_X_RESOLUTION 0x00C0#define ZINITIX_Y_RESOLUTION 0x00C1
#define ZINITIX_POINT_STATUS_REG 0x0080
#define ZINITIX_BT4X2_ICON_STATUS_REG 0x009A#define ZINITIX_BT4X3_ICON_STATUS_REG 0x00A0#define ZINITIX_BT4X4_ICON_STATUS_REG 0x00A0#define ZINITIX_BT5XX_ICON_STATUS_REG 0x00AA
#define ZINITIX_POINT_COORD_REG (ZINITIX_POINT_STATUS_REG + 2)
#define ZINITIX_AFE_FREQUENCY 0x0100#define ZINITIX_DND_N_COUNT 0x0122#define ZINITIX_DND_U_COUNT 0x0135
#define ZINITIX_RAWDATA_REG 0x0200
#define ZINITIX_EEPROM_INFO_REG 0x0018
#define ZINITIX_INT_ENABLE_FLAG 0x00f0#define ZINITIX_PERIODICAL_INTERRUPT_INTERVAL 0x00f1
#define ZINITIX_BTN_WIDTH 0x016d
#define ZINITIX_CHECKSUM_RESULT 0x012c
#define ZINITIX_INIT_FLASH 0x01d0#define ZINITIX_WRITE_FLASH 0x01d1#define ZINITIX_READ_FLASH 0x01d2
#define ZINITIX_INTERNAL_FLAG_02 0x011e#define ZINITIX_INTERNAL_FLAG_03 0x011f
#define ZINITIX_I2C_CHECKSUM_WCNT 0x016a#define ZINITIX_I2C_CHECKSUM_RESULT 0x016c
/* Interrupt & status register flags */
#define BIT_PT_CNT_CHANGE BIT(0)#define BIT_DOWN BIT(1)#define BIT_MOVE BIT(2)#define BIT_UP BIT(3)#define BIT_PALM BIT(4)#define BIT_PALM_REJECT BIT(5)#define BIT_RESERVED_0 BIT(6)#define BIT_RESERVED_1 BIT(7)#define BIT_WEIGHT_CHANGE BIT(8)#define BIT_PT_NO_CHANGE BIT(9)#define BIT_REJECT BIT(10)#define BIT_PT_EXIST BIT(11)#define BIT_RESERVED_2 BIT(12)#define BIT_ERROR BIT(13)#define BIT_DEBUG BIT(14)#define BIT_ICON_EVENT BIT(15)
#define SUB_BIT_EXIST BIT(0)#define SUB_BIT_DOWN BIT(1)#define SUB_BIT_MOVE BIT(2)#define SUB_BIT_UP BIT(3)#define SUB_BIT_UPDATE BIT(4)#define SUB_BIT_WAIT BIT(5)
#define DEFAULT_TOUCH_POINT_MODE 2#define MAX_SUPPORTED_FINGER_NUM 5#define MAX_SUPPORTED_BUTTON_NUM 8
#define CHIP_ON_DELAY 15 // ms#define FIRMWARE_ON_DELAY 40 // ms
struct point_coord { __le16 x; __le16 y; u8 width; u8 sub_status; // currently unused, but needed as padding: u8 minor_width; u8 angle;};
struct touch_event { __le16 status; u8 finger_mask; u8 time_stamp; struct point_coord point_coord[MAX_SUPPORTED_FINGER_NUM];};
struct bt541_ts_data { struct i2c_client *client; struct input_dev *input_dev; struct touchscreen_properties prop; struct regulator_bulk_data supplies[2]; u32 zinitix_mode; u32 keycodes[MAX_SUPPORTED_BUTTON_NUM]; int num_keycodes; bool have_versioninfo; u16 chip_revision; u16 firmware_version; u16 regdata_version; u16 icon_status_reg;};
static int zinitix_read_data(struct i2c_client *client, u16 reg, void *values, size_t length){ __le16 reg_le = cpu_to_le16(reg); int ret;
/* A single i2c_transfer() transaction does not work here. */ ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le)); if (ret != sizeof(reg_le)) return ret < 0 ? ret : -EIO;
ret = i2c_master_recv(client, (u8 *)values, length); if (ret != length) return ret < 0 ? ret : -EIO;
return 0;}
static int zinitix_write_u16(struct i2c_client *client, u16 reg, u16 value){ __le16 packet[2] = {cpu_to_le16(reg), cpu_to_le16(value)}; int ret;
ret = i2c_master_send(client, (u8 *)packet, sizeof(packet)); if (ret != sizeof(packet)) return ret < 0 ? ret : -EIO;
return 0;}
static int zinitix_write_cmd(struct i2c_client *client, u16 reg){ __le16 reg_le = cpu_to_le16(reg); int ret;
ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le)); if (ret != sizeof(reg_le)) return ret < 0 ? ret : -EIO;
return 0;}
static u16 zinitix_get_u16_reg(struct bt541_ts_data *bt541, u16 vreg){ struct i2c_client *client = bt541->client; int error; __le16 val;
error = zinitix_read_data(client, vreg, (void *)&val, 2); if (error) return U8_MAX;
return le16_to_cpu(val);}
static int zinitix_init_touch(struct bt541_ts_data *bt541){ struct i2c_client *client = bt541->client; int i; int error; u16 int_flags;
error = zinitix_write_cmd(client, ZINITIX_SWRESET_CMD); if (error) { dev_err(&client->dev, "Failed to write reset command\n"); return error; }
/* * Read and cache the chip revision and firmware version the first time * we get here. */ if (!bt541->have_versioninfo) { bt541->chip_revision = zinitix_get_u16_reg(bt541, ZINITIX_CHIP_REVISION); bt541->firmware_version = zinitix_get_u16_reg(bt541, ZINITIX_FIRMWARE_VERSION); bt541->regdata_version = zinitix_get_u16_reg(bt541, ZINITIX_DATA_VERSION_REG); bt541->have_versioninfo = true;
dev_dbg(&client->dev, "chip revision %04x firmware version %04x regdata version %04x\n", bt541->chip_revision, bt541->firmware_version, bt541->regdata_version);
/* * Determine the "icon" status register which varies by the * chip. */ switch (bt541->chip_revision & ZINITIX_CHIP_BTX0X_MASK) { case ZINITIX_CHIP_BT4X2: bt541->icon_status_reg = ZINITIX_BT4X2_ICON_STATUS_REG; break;
case ZINITIX_CHIP_BT4X3: bt541->icon_status_reg = ZINITIX_BT4X3_ICON_STATUS_REG; break;
case ZINITIX_CHIP_BT4X4: bt541->icon_status_reg = ZINITIX_BT4X4_ICON_STATUS_REG; break;
default: bt541->icon_status_reg = ZINITIX_BT5XX_ICON_STATUS_REG; break; } }
error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, 0x0); if (error) { dev_err(&client->dev, "Failed to reset interrupt enable flag\n"); return error; }
/* initialize */ error = zinitix_write_u16(client, ZINITIX_X_RESOLUTION, bt541->prop.max_x); if (error) return error;
error = zinitix_write_u16(client, ZINITIX_Y_RESOLUTION, bt541->prop.max_y); if (error) return error;
error = zinitix_write_u16(client, ZINITIX_SUPPORTED_FINGER_NUM, MAX_SUPPORTED_FINGER_NUM); if (error) return error;
error = zinitix_write_u16(client, ZINITIX_BUTTON_SUPPORTED_NUM, bt541->num_keycodes); if (error) return error;
error = zinitix_write_u16(client, ZINITIX_INITIAL_TOUCH_MODE, bt541->zinitix_mode); if (error) return error;
error = zinitix_write_u16(client, ZINITIX_TOUCH_MODE, bt541->zinitix_mode); if (error) return error;
int_flags = BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE | BIT_UP; if (bt541->num_keycodes) int_flags |= BIT_ICON_EVENT;
error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, int_flags); if (error) return error;
/* clear queue */ for (i = 0; i < 10; i++) { zinitix_write_cmd(client, ZINITIX_CLEAR_INT_STATUS_CMD); udelay(10); }
return 0;}
static int zinitix_init_regulators(struct bt541_ts_data *bt541){ struct device *dev = &bt541->client->dev; int error;
/* * Some older device trees have erroneous names for the regulators, * so check if "vddo" is present and in that case use these names. * Else use the proper supply names on the component. */ if (of_property_present(dev->of_node, "vddo-supply")) { bt541->supplies[0].supply = "vdd"; bt541->supplies[1].supply = "vddo"; } else { /* Else use the proper supply names */ bt541->supplies[0].supply = "vcca"; bt541->supplies[1].supply = "vdd"; } error = devm_regulator_bulk_get(dev, ARRAY_SIZE(bt541->supplies), bt541->supplies); if (error < 0) { dev_err(dev, "Failed to get regulators: %d\n", error); return error; }
return 0;}
static int zinitix_send_power_on_sequence(struct bt541_ts_data *bt541){ int error; struct i2c_client *client = bt541->client;
error = zinitix_write_u16(client, 0xc000, 0x0001); if (error) { dev_err(&client->dev, "Failed to send power sequence(vendor cmd enable)\n"); return error; } udelay(10);
error = zinitix_write_cmd(client, 0xc004); if (error) { dev_err(&client->dev, "Failed to send power sequence (intn clear)\n"); return error; } udelay(10);
error = zinitix_write_u16(client, 0xc002, 0x0001); if (error) { dev_err(&client->dev, "Failed to send power sequence (nvm init)\n"); return error; } mdelay(2);
error = zinitix_write_u16(client, 0xc001, 0x0001); if (error) { dev_err(&client->dev, "Failed to send power sequence (program start)\n"); return error; } msleep(FIRMWARE_ON_DELAY);
return 0;}
static void zinitix_report_finger(struct bt541_ts_data *bt541, int slot, const struct point_coord *p){ u16 x, y;
if (unlikely(!(p->sub_status & (SUB_BIT_UP | SUB_BIT_DOWN | SUB_BIT_MOVE)))) { dev_dbg(&bt541->client->dev, "unknown finger event %#02x\n", p->sub_status); return; }
x = le16_to_cpu(p->x); y = le16_to_cpu(p->y);
input_mt_slot(bt541->input_dev, slot); if (input_mt_report_slot_state(bt541->input_dev, MT_TOOL_FINGER, !(p->sub_status & SUB_BIT_UP))) { touchscreen_report_pos(bt541->input_dev, &bt541->prop, x, y, true); input_report_abs(bt541->input_dev, ABS_MT_TOUCH_MAJOR, p->width); dev_dbg(&bt541->client->dev, "finger %d %s (%u, %u)\n", slot, p->sub_status & SUB_BIT_DOWN ? "down" : "move", x, y); } else { dev_dbg(&bt541->client->dev, "finger %d up (%u, %u)\n", slot, x, y); }}
static void zinitix_report_keys(struct bt541_ts_data *bt541, u16 icon_events){ int i;
for (i = 0; i < bt541->num_keycodes; i++) input_report_key(bt541->input_dev, bt541->keycodes[i], icon_events & BIT(i));}
static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler){ struct bt541_ts_data *bt541 = bt541_handler; struct i2c_client *client = bt541->client; struct touch_event touch_event; unsigned long finger_mask; __le16 icon_events; int error; int i;
memset(&touch_event, 0, sizeof(struct touch_event));
error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG, &touch_event, sizeof(struct touch_event)); if (error) { dev_err(&client->dev, "Failed to read in touchpoint struct\n"); goto out; }
if (le16_to_cpu(touch_event.status) & BIT_ICON_EVENT) { error = zinitix_read_data(bt541->client, bt541->icon_status_reg, &icon_events, sizeof(icon_events)); if (error) { dev_err(&client->dev, "Failed to read icon events\n"); goto out; }
zinitix_report_keys(bt541, le16_to_cpu(icon_events)); }
finger_mask = touch_event.finger_mask; for_each_set_bit(i, &finger_mask, MAX_SUPPORTED_FINGER_NUM) { const struct point_coord *p = &touch_event.point_coord[i];
/* Only process contacts that are actually reported */ if (p->sub_status & SUB_BIT_EXIST) zinitix_report_finger(bt541, i, p); }
input_mt_sync_frame(bt541->input_dev); input_sync(bt541->input_dev);
out: zinitix_write_cmd(bt541->client, ZINITIX_CLEAR_INT_STATUS_CMD); return IRQ_HANDLED;}
static int zinitix_start(struct bt541_ts_data *bt541){ int error;
error = regulator_bulk_enable(ARRAY_SIZE(bt541->supplies), bt541->supplies); if (error) { dev_err(&bt541->client->dev, "Failed to enable regulators: %d\n", error); return error; }
msleep(CHIP_ON_DELAY);
error = zinitix_send_power_on_sequence(bt541); if (error) { dev_err(&bt541->client->dev, "Error while sending power-on sequence: %d\n", error); return error; }
error = zinitix_init_touch(bt541); if (error) { dev_err(&bt541->client->dev, "Error while configuring touch IC\n"); return error; }
enable_irq(bt541->client->irq);
return 0;}
static int zinitix_stop(struct bt541_ts_data *bt541){ int error;
disable_irq(bt541->client->irq);
error = regulator_bulk_disable(ARRAY_SIZE(bt541->supplies), bt541->supplies); if (error) { dev_err(&bt541->client->dev, "Failed to disable regulators: %d\n", error); return error; }
return 0;}
static int zinitix_input_open(struct input_dev *dev){ struct bt541_ts_data *bt541 = input_get_drvdata(dev);
return zinitix_start(bt541);}
static void zinitix_input_close(struct input_dev *dev){ struct bt541_ts_data *bt541 = input_get_drvdata(dev);
zinitix_stop(bt541);}
static int zinitix_init_input_dev(struct bt541_ts_data *bt541){ struct input_dev *input_dev; int error; int i;
input_dev = devm_input_allocate_device(&bt541->client->dev); if (!input_dev) { dev_err(&bt541->client->dev, "Failed to allocate input device."); return -ENOMEM; }
input_set_drvdata(input_dev, bt541); bt541->input_dev = input_dev;
input_dev->name = "Zinitix Capacitive TouchScreen"; input_dev->phys = "input/ts"; input_dev->id.bustype = BUS_I2C; input_dev->open = zinitix_input_open; input_dev->close = zinitix_input_close;
if (bt541->num_keycodes) { input_dev->keycode = bt541->keycodes; input_dev->keycodemax = bt541->num_keycodes; input_dev->keycodesize = sizeof(bt541->keycodes[0]); for (i = 0; i < bt541->num_keycodes; i++) input_set_capability(input_dev, EV_KEY, bt541->keycodes[i]); }
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
touchscreen_parse_properties(input_dev, true, &bt541->prop); if (!bt541->prop.max_x || !bt541->prop.max_y) { dev_err(&bt541->client->dev, "Touchscreen-size-x and/or touchscreen-size-y not set in dts\n"); return -EINVAL; }
error = input_mt_init_slots(input_dev, MAX_SUPPORTED_FINGER_NUM, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); if (error) { dev_err(&bt541->client->dev, "Failed to initialize MT slots: %d", error); return error; }
error = input_register_device(input_dev); if (error) { dev_err(&bt541->client->dev, "Failed to register input device: %d", error); return error; }
return 0;}
static int zinitix_ts_probe(struct i2c_client *client){ struct bt541_ts_data *bt541; int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "Failed to assert adapter's support for plain I2C.\n"); return -ENXIO; }
bt541 = devm_kzalloc(&client->dev, sizeof(*bt541), GFP_KERNEL); if (!bt541) return -ENOMEM;
bt541->client = client; i2c_set_clientdata(client, bt541);
error = zinitix_init_regulators(bt541); if (error) { dev_err(&client->dev, "Failed to initialize regulators: %d\n", error); return error; }
error = devm_request_threaded_irq(&client->dev, client->irq, NULL, zinitix_ts_irq_handler, IRQF_ONESHOT | IRQF_NO_AUTOEN, client->name, bt541); if (error) { dev_err(&client->dev, "Failed to request IRQ: %d\n", error); return error; }
if (device_property_present(&client->dev, "linux,keycodes")) { bt541->num_keycodes = device_property_count_u32(&client->dev, "linux,keycodes"); if (bt541->num_keycodes < 0) { dev_err(&client->dev, "Failed to count keys (%d)\n", bt541->num_keycodes); return bt541->num_keycodes; } else if (bt541->num_keycodes > ARRAY_SIZE(bt541->keycodes)) { dev_err(&client->dev, "Too many keys defined (%d)\n", bt541->num_keycodes); return -EINVAL; }
error = device_property_read_u32_array(&client->dev, "linux,keycodes", bt541->keycodes, bt541->num_keycodes); if (error) { dev_err(&client->dev, "Unable to parse \"linux,keycodes\" property: %d\n", error); return error; } }
error = zinitix_init_input_dev(bt541); if (error) { dev_err(&client->dev, "Failed to initialize input device: %d\n", error); return error; }
error = device_property_read_u32(&client->dev, "zinitix,mode", &bt541->zinitix_mode); if (error < 0) { /* fall back to mode 2 */ bt541->zinitix_mode = DEFAULT_TOUCH_POINT_MODE; }
if (bt541->zinitix_mode != 2) { /* * If there are devices that don't support mode 2, support * for other modes (0, 1) will be needed. */ dev_err(&client->dev, "Malformed zinitix,mode property, must be 2 (supplied: %d)\n", bt541->zinitix_mode); return -EINVAL; }
return 0;}
static int zinitix_suspend(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); struct bt541_ts_data *bt541 = i2c_get_clientdata(client);
guard(mutex)(&bt541->input_dev->mutex);
if (input_device_enabled(bt541->input_dev)) zinitix_stop(bt541);
return 0;}
static int zinitix_resume(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); struct bt541_ts_data *bt541 = i2c_get_clientdata(client); int error;
guard(mutex)(&bt541->input_dev->mutex);
if (input_device_enabled(bt541->input_dev)) { error = zinitix_start(bt541); if (error) return error; }
return 0;}
static DEFINE_SIMPLE_DEV_PM_OPS(zinitix_pm_ops, zinitix_suspend, zinitix_resume);
#ifdef CONFIG_OFstatic const struct of_device_id zinitix_of_match[] = { { .compatible = "zinitix,bt402" }, { .compatible = "zinitix,bt403" }, { .compatible = "zinitix,bt404" }, { .compatible = "zinitix,bt412" }, { .compatible = "zinitix,bt413" }, { .compatible = "zinitix,bt431" }, { .compatible = "zinitix,bt432" }, { .compatible = "zinitix,bt531" }, { .compatible = "zinitix,bt532" }, { .compatible = "zinitix,bt538" }, { .compatible = "zinitix,bt541" }, { .compatible = "zinitix,bt548" }, { .compatible = "zinitix,bt554" }, { .compatible = "zinitix,at100" }, { }};MODULE_DEVICE_TABLE(of, zinitix_of_match);#endif
static struct i2c_driver zinitix_ts_driver = { .probe = zinitix_ts_probe, .driver = { .name = "Zinitix-TS", .pm = pm_sleep_ptr(&zinitix_pm_ops), .of_match_table = of_match_ptr(zinitix_of_match), },};module_i2c_driver(zinitix_ts_driver);
MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");MODULE_DESCRIPTION("Zinitix touchscreen driver");MODULE_LICENSE("GPL v2");