/* * 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 #include #include #if IS_ENABLED(CONFIG_SAMSUNG_TUI) #include #endif #include "goodix_ts_core.h" #define TS_DRIVER_NAME "goodix_i2c" #define I2C_MAX_TRANSFER_SIZE 256 #define GOODIX_BUS_RETRY_TIMES 3 #define BERLIN_REG_ADDR_SIZE 4 #define NORMANDY_REG_ADDR_SIZE 2 static struct platform_device *goodix_pdev; struct goodix_bus_interface goodix_i2c_bus; /* Berlin read/write ops */ 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); struct goodix_ts_core *core_data = dev_get_drvdata(dev); unsigned int transfer_length = 0; unsigned int pos = 0, address = reg; unsigned char get_buf[128], addr_buf[BERLIN_REG_ADDR_SIZE]; int retry, r = 0; struct i2c_msg msgs[] = { { .addr = client->addr, .flags = !I2C_M_RD, .buf = &addr_buf[0], .len = BERLIN_REG_ADDR_SIZE, }, { .addr = client->addr, .flags = I2C_M_RD, } }; if (!core_data) return -ENODEV; if (core_data->plat_data->power_enabled == false) { ts_err("IC power is off"); return -EIO; } #if IS_ENABLED(CONFIG_SAMSUNG_TUI) if (STUI_MODE_TOUCH_SEC & stui_get_mode()) return -EBUSY; #endif if (core_data->resume_done.done) { int ret = wait_for_completion_interruptible_timeout(&core_data->resume_done, msecs_to_jiffies(500)); if (ret <= 0) { ts_err("LPM: pm resume is not handled:%d", ret); return -EIO; } } 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); if (core_data->debug_flag & GOODIX_TS_DEBUG_PRINT_I2C_READ_CMD) { int i; pr_info("sec_input : i2c_cmd: R: lenth(%d) pos(%d) 0x%02X | ", transfer_length, pos, reg); for (i = 0; i < transfer_length; i++) pr_cont("%02X ", data[pos + i]); pr_cont("\n"); } pos += transfer_length; address += transfer_length; break; } ts_info("I2c read retry[%d]:0x%x", retry + 1, reg); msleep(20); } 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); struct goodix_ts_core *core_data = dev_get_drvdata(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 (!core_data) return -ENODEV; if (core_data->plat_data->power_enabled == false) { ts_err("IC power is off"); return -EIO; } #if IS_ENABLED(CONFIG_SAMSUNG_TUI) if (STUI_MODE_TOUCH_SEC & stui_get_mode()) return -EBUSY; #endif if (core_data->resume_done.done) { int ret = wait_for_completion_interruptible_timeout(&core_data->resume_done, msecs_to_jiffies(500)); if (ret <= 0) { ts_err("LPM: pm resume is not handled:%d", ret); return -EIO; } } if (likely(len + BERLIN_REG_ADDR_SIZE < sizeof(put_buf))) { /* code optimize,use stack memory*/ msg.buf = &put_buf[0]; } else { msg.buf = kmalloc(len + BERLIN_REG_ADDR_SIZE, GFP_KERNEL); if (msg.buf == NULL) return -ENOMEM; } while (pos != len) { if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - BERLIN_REG_ADDR_SIZE)) transfer_length = I2C_MAX_TRANSFER_SIZE - BERLIN_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 + BERLIN_REG_ADDR_SIZE; memcpy(&msg.buf[BERLIN_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; } } if (core_data->debug_flag & GOODIX_TS_DEBUG_PRINT_I2C_WRITE_CMD) { int i; pr_info("sec_input : i2c_cmd: W: 0x%02X | ", reg); for (i = 0; i < len; i++) pr_cont("%02X ", data[i]); pr_cont("\n"); } write_exit: if (likely(len + BERLIN_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_init(struct i2c_client *client) { struct goodix_ts_core *core_data = NULL; struct sec_ts_plat_data *pdata; #if 0 struct sec_tclm_data *tdata = NULL; #endif int ret = 0; if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, sizeof(struct sec_ts_plat_data), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; goto error_allocate_pdata; } client->dev.platform_data = pdata; ret = sec_input_parse_dt(&client->dev); if (ret) { input_err(true, &client->dev, "%s: Failed to parse dt\n", __func__); goto error_allocate_mem; } #if 0 tdata = devm_kzalloc(&client->dev, sizeof(struct sec_tclm_data), GFP_KERNEL); if (!tdata) { ret = -ENOMEM; goto error_allocate_tdata; } #ifdef TCLM_CONCEPT sec_tclm_parse_dt(&client->dev, tdata); #endif #endif } else { pdata = client->dev.platform_data; if (!pdata) { ret = -ENOMEM; input_err(true, &client->dev, "%s: No platform data found\n", __func__); goto error_allocate_pdata; } } core_data = devm_kzalloc(&client->dev, sizeof(struct goodix_ts_core), GFP_KERNEL); if (!core_data) { ret = -ENOMEM; input_err(true, &client->dev, "%s: Failed to allocate memory for core data\n", __func__); goto error_allocate_mem; } i2c_set_clientdata(client, core_data); pdata->pinctrl = devm_pinctrl_get(&client->dev); if (IS_ERR(pdata->pinctrl)) input_err(true, &client->dev, "%s: could not get pinctrl\n", __func__); ptsp = &client->dev; #if 0 if (!tdata) { ret = -ENOMEM; goto err_null_tdata; } #ifdef TCLM_CONCEPT sec_tclm_initialize(tdata); tdata->client = client; tdata->tclm_read = sec_tclm_data_read; tdata->tclm_write = sec_tclm_data_write; tdata->tclm_execute_force_calibration = sec_tclm_execute_force_calibration; tdata->tclm_parse_dt = sec_tclm_parse_dt; #endif #endif input_info(true, &client->dev, "%s: init resource\n", __func__); return 0; err_null_tdata: error_allocate_mem: if (!pdata->not_support_io_ldo) regulator_put(pdata->dvdd); regulator_put(pdata->avdd); error_allocate_tdata: error_allocate_pdata: input_err(true, &client->dev, "%s: failed(%d)\n", __func__, ret); input_log_fix(); return ret; } 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; ret = goodix_i2c_init(client); if (ret < 0) { ts_err("%s: fail to init resource\n", __func__); return 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) { if (!goodix_pdev) return 0; ts_info("called"); platform_device_unregister(goodix_pdev); goodix_pdev = NULL; return 0; } static void goodix_i2c_shutdown(struct i2c_client *client) { ts_info("called"); goodix_i2c_remove(client); } #ifdef CONFIG_OF static const struct of_device_id i2c_matches[] = { {.compatible = "goodix,berlin",}, {.compatible = "goodix,gt9897",}, {.compatible = "goodix,gt9966",}, {.compatible = "goodix,gt9916",}, {.compatible = "goodix,gt9885",}, {.compatible = "goodix,gt9886",}, {}, }; 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, .shutdown = goodix_i2c_shutdown, .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); }