1 /*
2 * Copyright (c) 2021 Bestechnic (Shanghai) Co., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "touch_fts.h"
16 #include "cmsis_os2.h"
17 #include "gpio_if.h"
18 #include "i2c_if.h"
19 #include "hdf_device_desc.h"
20 #include "hdf_log.h"
21 #ifdef LOSCFG_DRIVERS_HDF_CONFIG_MACRO
22 #include "hcs_macro.h"
23 #include "hdf_config_macro.h"
24 #else
25 #include "device_resource_if.h"
26 #endif
27 static int tpd_interrupt_handler(uint16_t gpio, void *arg);
28
29 struct tpd_priv {
30 struct touch_device *dev;
31 struct i2c_client i2c_client;
32 uint16_t gpio_rst;
33 uint16_t gpio_int;
34 };
35
36 static struct tpd_priv priv = {
37 .gpio_rst = 5, // TSP_RST - GPIO05
38 .gpio_int = 23, // TSP_INT - GPIO27
39 .i2c_client = {
40 .id = 1, // TSP_SCL/SDA - I2C1 = GPIO06/GPIO07
41 .addr = 0x38,
42 }};
43
44 /*****************************************************************************
45 * i2c related
46 *****************************************************************************/
47 #define I2C_RETRY_NUMBER 3
fts_i2c_read(struct i2c_client * client,uint8_t * writebuf,int writelen,uint8_t * readbuf,int readlen)48 static int fts_i2c_read(struct i2c_client *client, uint8_t *writebuf, int writelen, uint8_t *readbuf, int readlen)
49 {
50 int ret = 0;
51 int i = 0;
52
53 if (client == NULL) {
54 HDF_LOGE("[IIC][%s]i2c_client==NULL!", __func__);
55 return -EINVAL;
56 }
57
58 if (readlen > 0) {
59 if (writelen > 0) {
60 struct I2cMsg msgs[] = {
61 {
62 .addr = client->addr,
63 .flags = 0,
64 .len = writelen,
65 .buf = writebuf,
66 },
67 {
68 .addr = client->addr,
69 .flags = I2C_FLAG_READ,
70 .len = readlen,
71 .buf = readbuf,
72 },
73 };
74 for (i = 0; i < I2C_RETRY_NUMBER; i++) {
75 ret = I2cTransfer(client->handle, msgs, 2);
76 if (ret < 0) {
77 HDF_LOGE("[IIC]: i2c_transfer(write) error, ret=%d!!", ret);
78 } else
79 break;
80 }
81 } else {
82 struct I2cMsg msgs[] = {
83 {
84 .addr = client->addr,
85 .flags = I2C_FLAG_READ,
86 .len = readlen,
87 .buf = readbuf,
88 },
89 };
90 for (i = 0; i < I2C_RETRY_NUMBER; i++) {
91 ret = I2cTransfer(client->handle, msgs, 1);
92 if (ret < 0) {
93 HDF_LOGE("[IIC]: i2c_transfer(read) error, ret=%d!!", ret);
94 } else
95 break;
96 }
97 }
98 }
99
100 return ret;
101 }
102
fts_i2c_write(struct i2c_client * client,uint8_t * writebuf,int writelen)103 static int fts_i2c_write(struct i2c_client *client, uint8_t *writebuf, int writelen)
104 {
105 int ret = 0;
106 int i = 0;
107
108 if (client == NULL) {
109 HDF_LOGE("[IIC][%s]i2c_client==NULL!", __func__);
110 return -EINVAL;
111 }
112
113 if (writelen > 0) {
114 struct I2cMsg msgs[] = {
115 {
116 .addr = client->addr,
117 .flags = 0,
118 .len = writelen,
119 .buf = writebuf,
120 },
121 };
122 for (i = 0; i < I2C_RETRY_NUMBER; i++) {
123 ret = I2cTransfer(client->handle, msgs, 1);
124 if (ret < 0) {
125 HDF_LOGE("[IIC]: i2c_transfer(write) error, ret=%d!!", ret);
126 } else
127 break;
128 }
129 }
130
131 return ret;
132 }
133
fts_i2c_write_reg(struct i2c_client * client,uint8_t regaddr,uint8_t regvalue)134 static int fts_i2c_write_reg(struct i2c_client *client, uint8_t regaddr, uint8_t regvalue)
135 {
136 uint8_t buf[2] = {0};
137
138 buf[0] = regaddr;
139 buf[1] = regvalue;
140 return fts_i2c_write(client, buf, sizeof(buf));
141 }
142
fts_i2c_read_reg(struct i2c_client * client,uint8_t regaddr,uint8_t * regvalue)143 static int fts_i2c_read_reg(struct i2c_client *client, uint8_t regaddr, uint8_t *regvalue)
144 {
145 return fts_i2c_read(client, ®addr, 1, regvalue, 1);
146 }
147
fts_i2c_hid2std(struct i2c_client * client)148 static void fts_i2c_hid2std(struct i2c_client *client)
149 {
150 int ret = 0;
151 uint8_t buf[3] = {0xeb, 0xaa, 0x09};
152
153 ret = fts_i2c_write(client, buf, 3);
154 if (ret < 0)
155 HDF_LOGE("hid2std cmd write fail");
156 else {
157 osDelay(10);
158 buf[0] = buf[1] = buf[2] = 0;
159 ret = fts_i2c_read(client, NULL, 0, buf, 3);
160 if (ret < 0)
161 HDF_LOGE("hid2std cmd read fail");
162 else if ((0xeb == buf[0]) && (0xaa == buf[1]) && (0x08 == buf[2])) {
163 HDF_LOGE("hidi2c change to stdi2c successful");
164 } else {
165 HDF_LOGE("hidi2c change to stdi2c fail");
166 }
167 }
168 }
169
170 /*****************************************************************************
171 * chip related
172 *****************************************************************************/
173 #define INTERVAL_READ_REG 100 /* interval time per read reg unit:ms */
174 #define TIMEOUT_READ_REG 1000 /* timeout of read reg unit:ms */
175 #define FTS_I2C_SLAVE_ADDR 0x38
176
177 static struct fts_ts_data *fts_data;
178
fts_get_chip_types(struct fts_ts_data * ts_data,uint8_t id_h,uint8_t id_l,bool fw_valid)179 static int fts_get_chip_types(struct fts_ts_data *ts_data, uint8_t id_h, uint8_t id_l, bool fw_valid)
180 {
181 int i = 0;
182 struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING;
183 uint32_t ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t);
184
185 if ((0x0 == id_h) || (0x0 == id_l)) {
186 HDF_LOGE("id_h/id_l is 0");
187 return -EINVAL;
188 }
189
190 HDF_LOGI("verify id:0x%02x%02x", id_h, id_l);
191 for (i = 0; i < ctype_entries; i++) {
192 if (VALID == fw_valid) {
193 if ((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl))
194 break;
195 } else {
196 if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl)) || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl)) || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl)))
197 break;
198 }
199 }
200
201 if (i >= ctype_entries) {
202 return -ENODATA;
203 }
204
205 ts_data->ic_info.ids = ctype[i];
206 return 0;
207 }
208
fts_get_ic_information(struct fts_ts_data * ts_data)209 static int fts_get_ic_information(struct fts_ts_data *ts_data)
210 {
211 int ret = 0;
212 int cnt = 0;
213 uint8_t chip_id[2] = {0};
214 uint8_t id_cmd[4] = {0};
215 uint32_t id_cmd_len = 0;
216 struct i2c_client *client = ts_data->client;
217
218 ts_data->ic_info.is_incell = FTS_CHIP_IDC;
219 ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED;
220 do {
221 ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id[0]);
222 ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID2, &chip_id[1]);
223 if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
224 HDF_LOGD("i2c read invalid, read:0x%02x%02x", chip_id[0], chip_id[1]);
225 } else {
226 ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID);
227 if (!ret)
228 break;
229 else
230 HDF_LOGD("TP not ready, read:0x%02x%02x", chip_id[0], chip_id[1]);
231 }
232
233 cnt++;
234 osDelay(INTERVAL_READ_REG);
235 } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
236
237 if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) {
238 HDF_LOGI("fw is invalid, need read boot id");
239 if (ts_data->ic_info.hid_supported) {
240 fts_i2c_hid2std(client);
241 }
242
243 id_cmd[0] = FTS_CMD_START1;
244 id_cmd[1] = FTS_CMD_START2;
245 ret = fts_i2c_write(client, id_cmd, 2);
246 if (ret < 0) {
247 HDF_LOGE("start cmd write fail");
248 return ret;
249 }
250
251 osDelay(FTS_CMD_START_DELAY);
252 id_cmd[0] = FTS_CMD_READ_ID;
253 id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00;
254 if (ts_data->ic_info.is_incell)
255 id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
256 else
257 id_cmd_len = FTS_CMD_READ_ID_LEN;
258 ret = fts_i2c_read(client, id_cmd, id_cmd_len, chip_id, 2);
259 if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
260 HDF_LOGE("read boot id fail");
261 return -EIO;
262 }
263 ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], 0);
264 if (ret < 0) {
265 HDF_LOGE("can't get ic informaton");
266 return ret;
267 }
268 }
269
270 HDF_LOGI("get ic information, chip id = 0x%02x%02x",
271 ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl);
272
273 return 0;
274 }
275
fts_reset_proc(int hdelayms)276 static int fts_reset_proc(int hdelayms)
277 {
278 GpioWrite(priv.gpio_rst, 0);
279 osDelay(5);
280 GpioWrite(priv.gpio_rst, 1);
281 if (hdelayms) {
282 osDelay(hdelayms);
283 }
284 return 0;
285 }
286
fts_read_touchdata(struct fts_ts_data * data)287 static int fts_read_touchdata(struct fts_ts_data *data)
288 {
289 int ret = 0;
290 int i = 0;
291 uint8_t pointid;
292 int base;
293 struct ts_event *events = data->events;
294 int max_touch_num = FTS_TOUCH_POINT_NUM;
295 uint8_t *buf = data->point_buf;
296
297 data->point_num = 0;
298 data->touch_point = 0;
299
300 memset(buf, 0xFF, data->pnt_buf_size);
301 buf[0] = 0x00;
302
303 ret = fts_i2c_read(data->client, buf, 1, buf, data->pnt_buf_size);
304 if (ret < 0) {
305 HDF_LOGE("read touchdata failed, ret:%d", ret);
306 return ret;
307 }
308 data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
309
310 if (data->ic_info.is_incell) {
311 if ((data->point_num == 0x0F) && (buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF) &&
312 (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) {
313 HDF_LOGI("touch buff is 0xff, need recovery state");
314 return -EIO;
315 }
316 }
317 if (data->point_num > max_touch_num) {
318 HDF_LOGI("invalid point_num(%d)", data->point_num);
319 return -EIO;
320 }
321
322 for (i = 0; i < max_touch_num; i++) {
323 base = FTS_ONE_TCH_LEN * i;
324
325 pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4;
326 if (pointid >= FTS_MAX_ID)
327 break;
328 else if (pointid >= max_touch_num) {
329 HDF_LOGE("ID(%d) beyond max_touch_number", pointid);
330 return -EINVAL;
331 }
332
333 data->touch_point++;
334
335 events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) +
336 (buf[FTS_TOUCH_X_L_POS + base] & 0xFF);
337 events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) +
338 (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF);
339 events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6;
340 events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4;
341 events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4;
342 events[i].p = buf[FTS_TOUCH_PRE_POS + base];
343
344 if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) {
345 HDF_LOGI("abnormal touch data from fw");
346 return -EIO;
347 }
348 }
349 if (data->touch_point == 0) {
350 HDF_LOGI("no touch point information");
351 return -EIO;
352 }
353
354 return 0;
355 }
356
tpd_irq_registration(struct fts_ts_data * ts_data)357 static int tpd_irq_registration(struct fts_ts_data *ts_data)
358 {
359 GpioSetDir(priv.gpio_int, GPIO_DIR_IN); // GPIO_DIR_IN_PULLUP
360 GpioSetIrq(priv.gpio_int, GPIO_IRQ_TRIGGER_FALLING, tpd_interrupt_handler, NULL);
361 GpioDisableIrq(priv.gpio_int);
362 HDF_LOGI("IRQ request succussfully, irq:%d", priv.gpio_int);
363 return 0;
364 }
365
fts_input_init(struct fts_ts_data * ts_data)366 static int fts_input_init(struct fts_ts_data *ts_data)
367 {
368 int point_num = 0;
369 int ret = 0;
370
371 point_num = FTS_TOUCH_POINT_NUM;
372 ts_data->pnt_buf_size = point_num * FTS_ONE_TCH_LEN + 3;
373 ts_data->point_buf = (uint8_t *)malloc(ts_data->pnt_buf_size);
374 if (!ts_data->point_buf) {
375 HDF_LOGE("failed to alloc memory for point buf!");
376 ret = -ENOMEM;
377 goto err_point_buf;
378 }
379 memset(ts_data->point_buf, 0, ts_data->pnt_buf_size);
380
381 ts_data->events = (struct ts_event *)malloc(point_num * sizeof(struct ts_event));
382 if (!ts_data->events) {
383 HDF_LOGE("failed to alloc memory for point events!");
384 ret = -ENOMEM;
385 goto err_event_buf;
386 }
387 memset(ts_data->events, 0, point_num * sizeof(struct ts_event));
388
389 return 0;
390
391 err_event_buf:
392 free(ts_data->point_buf);
393
394 err_point_buf:
395 return ret;
396 }
397
tpd_probe(struct i2c_client * client)398 static int tpd_probe(struct i2c_client *client)
399 {
400 int ret = 0;
401 struct fts_ts_data *ts_data;
402
403 ts_data = malloc(sizeof(*ts_data));
404 if (!ts_data) {
405 HDF_LOGE("Failed to allocate memory for fts_data");
406 return -ENOMEM;
407 }
408 memset(ts_data, 0, sizeof(*ts_data));
409 fts_data = ts_data;
410 ts_data->client = client;
411
412 if (client->addr != FTS_I2C_SLAVE_ADDR) {
413 HDF_LOGI("[TPD]Change i2c addr 0x%02x to %x", client->addr, FTS_I2C_SLAVE_ADDR);
414 client->addr = FTS_I2C_SLAVE_ADDR;
415 HDF_LOGI("[TPD]i2c addr=0x%x\n", client->addr);
416 }
417
418 ret = fts_input_init(ts_data);
419 if (ret) {
420 HDF_LOGE("fts input initialize fail");
421 goto err_input_init;
422 }
423
424 fts_reset_proc(200);
425 ret = fts_get_ic_information(ts_data);
426 if (ret) {
427 HDF_LOGE("not focal IC, unregister driver");
428 goto err_input_init;
429 }
430
431 HDF_LOGD("[TPD]Touch Panel Device Probe %s!", (ret < 0) ? "FAIL" : "PASS");
432
433 /* Configure gpio to irq and request irq */
434 ret = tpd_irq_registration(ts_data);
435 if (ret) {
436 HDF_LOGE("request irq failed");
437 goto err_irq_req;
438 }
439
440 HDF_LOGD("TPD_RES_Y:%d", (int)TPD_RES_Y);
441 return 0;
442
443 err_irq_req:
444 err_input_init:
445 free(ts_data->point_buf);
446 free(ts_data->events);
447 free(ts_data);
448
449 return ret;
450 }
451
tpd_remove()452 static int tpd_remove()
453 {
454 if (fts_data) {
455 free(fts_data->point_buf);
456 free(fts_data->events);
457 free(fts_data);
458 }
459 return 0;
460 }
461
462 /*****************************************************************************
463 * interfaces
464 *****************************************************************************/
tpd_get_point(struct touch_device * dev,struct touch_msg * msg)465 static int tpd_get_point(struct touch_device *dev, struct touch_msg *msg)
466 {
467 int ret = fts_read_touchdata(fts_data);
468 if (ret) {
469 return -1;
470 }
471
472 struct ts_event *events = fts_data->events;
473 msg->x = events[0].x;
474 msg->y = events[0].y;
475 msg->event = EVENT_DOWN(events[0].flag) ? TOUCH_EVENT_DOWN : TOUCH_EVENT_UP;
476 return 0;
477 }
478
tpd_interrupt_handler(uint16_t gpio,void * arg)479 static int tpd_interrupt_handler(uint16_t gpio, void *arg)
480 {
481 (void)arg;
482 if (gpio != priv.gpio_int)
483 return -1;
484
485 if (priv.dev && priv.dev->sem)
486 osSemaphoreRelease(priv.dev->sem);
487
488 return 0;
489 }
490
tpd_irq_enable(bool enable)491 static void tpd_irq_enable(bool enable)
492 {
493 if (enable) {
494 GpioEnableIrq(priv.gpio_int);
495 } else {
496 GpioDisableIrq(priv.gpio_int);
497 }
498 }
499
tpd_init(struct touch_device * dev)500 static int tpd_init(struct touch_device *dev)
501 {
502 priv.dev = dev;
503 priv.i2c_client.handle = I2cOpen(priv.i2c_client.id);
504 if (priv.i2c_client.handle == NULL) {
505 return -1;
506 }
507
508 if (tpd_probe(&priv.i2c_client)) {
509 return -1;
510 }
511
512 return 0;
513 }
514
tpd_deinit(void)515 static void tpd_deinit(void)
516 {
517 GpioDisableIrq(priv.gpio_int);
518 I2cClose(priv.i2c_client.handle);
519 tpd_remove(&priv.i2c_client);
520 }
521
522 struct touch_device g_touch_fts = {
523 .name = "fts_ts",
524 .init = tpd_init,
525 .deinit = tpd_deinit,
526 .read = tpd_get_point,
527 .irq_enable = tpd_irq_enable,
528 };
529 #ifdef LOSCFG_DRIVERS_HDF_CONFIG_MACRO
530 #define DISPLAY_INPUT_TOUCH HCS_NODE(HCS_NODE(HCS_NODE(HCS_ROOT, input), touch_config), touch_fts)
TouchDeviceGetResource(struct tpd_priv * priv)531 static uint32_t TouchDeviceGetResource(struct tpd_priv *priv)
532 {
533 priv->gpio_rst = HCS_PROP(DISPLAY_INPUT_TOUCH, gpio_rst);
534 priv->gpio_int = HCS_PROP(DISPLAY_INPUT_TOUCH, gpio_int);
535 priv->i2c_client.id = HCS_PROP(DISPLAY_INPUT_TOUCH, i2c_id);
536 priv->i2c_client.addr = HCS_PROP(DISPLAY_INPUT_TOUCH, i2c_addr);
537
538 HDF_LOGD("%s: gpio_rst=%d, gpio_int=%d, i2c_id=%d, i2c_addr=%d", __func__,
539 priv->gpio_rst, priv->gpio_int, priv->i2c_client.id, priv->i2c_client.addr);
540 return HDF_SUCCESS;
541 }
542 #else
TouchDeviceGetResource(struct tpd_priv * priv,const struct DeviceResourceNode * resourceNode)543 static uint32_t TouchDeviceGetResource(struct tpd_priv *priv, const struct DeviceResourceNode *resourceNode)
544 {
545 struct DeviceResourceIface *res = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
546 if (res == NULL || res->GetUint32 == NULL) {
547 HDF_LOGE("DeviceResourceIface is invalid");
548 return HDF_FAILURE;
549 }
550 if (res->GetUint16(resourceNode, "gpio_rst", &priv->gpio_rst, 0) != HDF_SUCCESS) {
551 HDF_LOGE("%s: failed to get gpio_rst", __func__);
552 return HDF_FAILURE;
553 }
554 if (res->GetUint16(resourceNode, "gpio_int", &priv->gpio_int, 0) != HDF_SUCCESS) {
555 HDF_LOGE("%s: failed to get gpio_int", __func__);
556 return HDF_FAILURE;
557 }
558 if (res->GetUint16(resourceNode, "i2c_id", &priv->i2c_client.id, 0) != HDF_SUCCESS) {
559 HDF_LOGE("%s: failed to get i2c_id", __func__);
560 return HDF_FAILURE;
561 }
562 if (res->GetUint16(resourceNode, "i2c_addr", &priv->i2c_client.addr, 0) != HDF_SUCCESS) {
563 HDF_LOGE("%s: failed to get i2c_addr", __func__);
564 return HDF_FAILURE;
565 }
566 HDF_LOGD("%s: gpio_rst=%d, gpio_int=%d, i2c_id=%d, i2c_addr=%d", __func__,
567 priv->gpio_rst, priv->gpio_int, priv->i2c_client.id, priv->i2c_client.addr);
568 return HDF_SUCCESS;
569 }
570 #endif
TouchDriverInit(struct HdfDeviceObject * object)571 static int32_t TouchDriverInit(struct HdfDeviceObject *object)
572 {
573 if (object == NULL) {
574 return HDF_FAILURE;
575 }
576 #ifdef LOSCFG_DRIVERS_HDF_CONFIG_MACRO
577 if (TouchDeviceGetResource(&priv) != HDF_SUCCESS) {
578 HDF_LOGE("%s: TouchDeviceGetResource failed", __func__);
579 return HDF_FAILURE;
580 }
581 #else
582 if (object->property) {
583 if (TouchDeviceGetResource(&priv, object->property) != HDF_SUCCESS) {
584 HDF_LOGE("%s: TouchDeviceGetResource failed", __func__);
585 return HDF_FAILURE;
586 }
587 }
588 #endif
589 if (RegisterTouchDevice(&g_touch_fts) != HDF_SUCCESS) {
590 HDF_LOGE("%s: RegisterTouchDevice failed", __func__);
591 return HDF_FAILURE;
592 }
593 return HDF_SUCCESS;
594 }
595
TouchDriverBind(struct HdfDeviceObject * device)596 static int32_t TouchDriverBind(struct HdfDeviceObject *device)
597 {
598 (void)device;
599 return HDF_SUCCESS;
600 }
601
TouchDriverRelease(struct HdfDeviceObject * device)602 static void TouchDriverRelease(struct HdfDeviceObject *device)
603 {
604 (void)device;
605 }
606
607 static struct HdfDriverEntry g_FTSTouchDriverEntry = {
608 .moduleVersion = 1,
609 .moduleName = "HDF_TOUCH_FTS",
610 .Bind = TouchDriverBind,
611 .Init = TouchDriverInit,
612 .Release = TouchDriverRelease,
613 };
614
615 HDF_INIT(g_FTSTouchDriverEntry);