diff options
author | Wendly Li <wendlyli@google.com> | 2021-12-29 08:25:53 +0000 |
---|---|---|
committer | Wendly Li <wendlyli@google.com> | 2022-01-21 03:51:40 +0000 |
commit | f4cbd1e784f777c544763bb0e2bdb65ad5c685cf (patch) | |
tree | b46d9dba8ae66c85da61754fbdb3b9ce279c3789 /goodix_brl_i2c.c | |
parent | aa0a75aa6af4b4513db1719e841973d3a50f59c3 (diff) | |
download | goodix_touch-f4cbd1e784f777c544763bb0e2bdb65ad5c685cf.tar.gz |
Initial the driver from the original vender code
BYPASS_INCLUSIVE_LANGUAGE_REASON=master and slave are stardand of SPI
Bug: 214018056
Bug: 214118475
Change-Id: Ib1e7bbdca701fe852f665ee2986824d71d5eebe2
Signed-off-by: Wendly Li <wendlyli@google.com>
Diffstat (limited to 'goodix_brl_i2c.c')
-rw-r--r-- | goodix_brl_i2c.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/goodix_brl_i2c.c b/goodix_brl_i2c.c new file mode 100644 index 0000000..be7d638 --- /dev/null +++ b/goodix_brl_i2c.c @@ -0,0 +1,268 @@ +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. + * + */ +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "goodix_ts_core.h" + +#define TS_DRIVER_NAME "gtx8_i2c" +#define I2C_MAX_TRANSFER_SIZE 256 +#define GOODIX_BUS_RETRY_TIMES 2 +#define GOODIX_REG_ADDR_SIZE 4 + +static struct platform_device *goodix_pdev; +struct goodix_bus_interface goodix_i2c_bus; + +static int goodix_i2c_read(struct device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int transfer_length = 0; + unsigned int pos = 0, address = reg; + unsigned char get_buf[128], addr_buf[GOODIX_REG_ADDR_SIZE]; + int retry, r = 0; + struct i2c_msg msgs[] = { { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = GOODIX_REG_ADDR_SIZE, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + } }; + + if (likely(len < sizeof(get_buf))) { + /* code optimize, use stack memory */ + msgs[1].buf = &get_buf[0]; + } else { + msgs[1].buf = kzalloc(len, GFP_KERNEL); + if (msgs[1].buf == NULL) + return -ENOMEM; + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE)) + transfer_length = I2C_MAX_TRANSFER_SIZE; + else + transfer_length = len - pos; + + msgs[0].buf[0] = (address >> 24) & 0xFF; + msgs[0].buf[1] = (address >> 16) & 0xFF; + msgs[0].buf[2] = (address >> 8) & 0xFF; + msgs[0].buf[3] = address & 0xFF; + msgs[1].len = transfer_length; + + for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { + if (likely(i2c_transfer(client->adapter, msgs, 2) == + 2)) { + memcpy(&data[pos], msgs[1].buf, + transfer_length); + pos += transfer_length; + address += transfer_length; + break; + } + ts_info("I2c read retry[%d]:0x%x", retry + 1, reg); + usleep_range(2000, 2100); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EAGAIN; + goto read_exit; + } + } + +read_exit: + if (unlikely(len >= sizeof(get_buf))) + kfree(msgs[1].buf); + return r; +} + +static int goodix_i2c_write(struct device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[128]; + int retry, r = 0; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + GOODIX_REG_ADDR_SIZE < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc(len + GOODIX_REG_ADDR_SIZE, GFP_KERNEL); + if (msg.buf == NULL) + return -ENOMEM; + } + + while (pos != len) { + if (unlikely(len - pos > + I2C_MAX_TRANSFER_SIZE - GOODIX_REG_ADDR_SIZE)) + transfer_length = + I2C_MAX_TRANSFER_SIZE - GOODIX_REG_ADDR_SIZE; + else + transfer_length = len - pos; + msg.buf[0] = (address >> 24) & 0xFF; + msg.buf[1] = (address >> 16) & 0xFF; + msg.buf[2] = (address >> 8) & 0xFF; + msg.buf[3] = address & 0xFF; + + msg.len = transfer_length + GOODIX_REG_ADDR_SIZE; + memcpy(&msg.buf[GOODIX_REG_ADDR_SIZE], &data[pos], + transfer_length); + + for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { + if (likely(i2c_transfer(client->adapter, &msg, 1) == + 1)) { + pos += transfer_length; + address += transfer_length; + break; + } + ts_debug("I2c write retry[%d]", retry + 1); + msleep(20); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c write failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EAGAIN; + goto write_exit; + } + } + +write_exit: + if (likely(len + GOODIX_REG_ADDR_SIZE >= sizeof(put_buf))) + kfree(msg.buf); + return r; +} + +static void goodix_pdev_release(struct device *dev) +{ + ts_info("goodix pdev released"); + kfree(goodix_pdev); +} + +static int goodix_i2c_probe( + struct i2c_client *client, const struct i2c_device_id *dev_id) +{ + int ret = 0; + + ts_info("goodix i2c probe in"); + ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); + if (!ret) + return -EIO; + + /* get ic type */ + ret = goodix_get_ic_type(client->dev.of_node); + if (ret < 0) + return ret; + + goodix_i2c_bus.ic_type = ret; + goodix_i2c_bus.bus_type = GOODIX_BUS_TYPE_I2C; + goodix_i2c_bus.dev = &client->dev; + goodix_i2c_bus.read = goodix_i2c_read; + goodix_i2c_bus.write = goodix_i2c_write; + /* ts core device */ + goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!goodix_pdev) + return -ENOMEM; + + goodix_pdev->name = GOODIX_CORE_DRIVER_NAME; + goodix_pdev->id = 0; + goodix_pdev->num_resources = 0; + /* + * you can find this platform dev in + * /sys/devices/platform/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = &goodix_i2c_bus; + goodix_pdev->dev.release = goodix_pdev_release; + + /* register platform device, then the goodix_ts_core + * module will probe the touch device. + */ + ret = platform_device_register(goodix_pdev); + if (ret) { + ts_err("failed register goodix platform device, %d", ret); + goto err_pdev; + } + ts_info("i2c probe out"); + return ret; + +err_pdev: + kfree(goodix_pdev); + goodix_pdev = NULL; + ts_info("i2c probe out, %d", ret); + return ret; +} + +static int goodix_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(goodix_pdev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_matches[] = { + { + .compatible = "goodix,gt9897", + }, + { + .compatible = "goodix,gt9966", + }, + { + .compatible = "goodix,gt9916", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_matches); +#endif + +static const struct i2c_device_id i2c_id_table[] = { + { TS_DRIVER_NAME, 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, i2c_id_table); + +static struct i2c_driver goodix_i2c_driver = { + .driver = { + .name = TS_DRIVER_NAME, + //.owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_matches), + }, + .probe = goodix_i2c_probe, + .remove = goodix_i2c_remove, + .id_table = i2c_id_table, +}; + +int goodix_i2c_bus_init(void) +{ + ts_info("Goodix i2c driver init"); + return i2c_add_driver(&goodix_i2c_driver); +} + +void goodix_i2c_bus_exit(void) +{ + ts_info("Goodix i2c driver exit"); + i2c_del_driver(&goodix_i2c_driver); +} |