/* * 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 #ifdef CONFIG_GOOG_TOUCH_INTERFACE #include #endif #include "goodix_ts_core.h" #define TS_DRIVER_NAME "gtx8_spi" #define SPI_TRANS_PREFIX_LEN 1 #define REGISTER_WIDTH 4 #define SPI_READ_DUMMY_LEN 4 #define SPI_READ_PREFIX_LEN \ (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN) #define SPI_WRITE_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH) #define SPI_PREALLOC_RX_BUF_SIZE 4096 + SPI_READ_PREFIX_LEN #define SPI_PREALLOC_TX_BUF_SIZE 4096 + SPI_WRITE_PREFIX_LEN #define SPI_WRITE_FLAG 0xF0 #define SPI_READ_FLAG 0xF1 static struct platform_device *goodix_pdev; struct goodix_bus_interface goodix_spi_bus; /** * goodix_spi_read_bra- read device register through spi bus * @dev: pointer to device data * @addr: register address * @data: read buffer * @len: bytes to read * return: 0 - read ok, < 0 - spi transter error */ static int goodix_spi_read_bra(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len) { struct spi_device *spi = to_spi_device(dev); u8 *rx_buf = NULL; u8 *tx_buf = NULL; struct spi_transfer xfers; struct spi_message spi_msg; int ret = 0; int buf_len = SPI_READ_PREFIX_LEN + len; mutex_lock(&goodix_spi_bus.mutex); if (buf_len <= SPI_PREALLOC_RX_BUF_SIZE && buf_len <= SPI_PREALLOC_TX_BUF_SIZE) { rx_buf = goodix_spi_bus.rx_buf; tx_buf = goodix_spi_bus.tx_buf; memset(tx_buf, 0, buf_len); } else { rx_buf = kzalloc(buf_len, GFP_KERNEL); if (!rx_buf) { ts_err("alloc rx_buf failed, size:%d", buf_len); ret = -ENOMEM; goto err_alloc_rx_buf; } tx_buf = kzalloc(buf_len, GFP_KERNEL); if (!tx_buf) { ts_err("alloc tx_buf failed, size:%d", buf_len); ret = -ENOMEM; goto err_alloc_tx_buf; } } spi_message_init(&spi_msg); memset(&xfers, 0, sizeof(xfers)); /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ tx_buf[0] = SPI_READ_FLAG; tx_buf[1] = (addr >> 24) & 0xFF; tx_buf[2] = (addr >> 16) & 0xFF; tx_buf[3] = (addr >> 8) & 0xFF; tx_buf[4] = addr & 0xFF; tx_buf[5] = 0xFF; tx_buf[6] = 0xFF; tx_buf[7] = 0xFF; tx_buf[8] = 0xFF; xfers.tx_buf = tx_buf; xfers.rx_buf = rx_buf; xfers.len = buf_len; xfers.cs_change = 0; spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) { ts_err("spi transfer error:%d", ret); goto err_spi_transfer; } memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN], len); err_spi_transfer: if (tx_buf != goodix_spi_bus.tx_buf) kfree(tx_buf); err_alloc_tx_buf: if (rx_buf != goodix_spi_bus.rx_buf) kfree(rx_buf); err_alloc_rx_buf: mutex_unlock(&goodix_spi_bus.mutex); return ret; } static int goodix_spi_read(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len) { struct spi_device *spi = to_spi_device(dev); u8 *rx_buf = NULL; u8 *tx_buf = NULL; struct spi_transfer xfers; struct spi_message spi_msg; int ret = 0; int buf_len = SPI_READ_PREFIX_LEN - 1 + len; if (goodix_spi_bus.dma_mode_enabled && buf_len >= 64) { buf_len = ALIGN(buf_len, 4); } mutex_lock(&goodix_spi_bus.mutex); if (buf_len <= SPI_PREALLOC_RX_BUF_SIZE && buf_len <= SPI_PREALLOC_TX_BUF_SIZE) { rx_buf = goodix_spi_bus.rx_buf; tx_buf = goodix_spi_bus.tx_buf; memset(tx_buf, 0, buf_len); } else { rx_buf = kzalloc(buf_len, GFP_KERNEL); if (!rx_buf) { ts_err("alloc rx_buf failed, size:%d", buf_len); ret = -ENOMEM; goto err_alloc_rx_buf; } tx_buf = kzalloc(buf_len, GFP_KERNEL); if (!tx_buf) { ts_err("alloc tx_buf failed, size:%d", buf_len); ret = -ENOMEM; goto err_alloc_tx_buf; } } spi_message_init(&spi_msg); memset(&xfers, 0, sizeof(xfers)); /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ tx_buf[0] = SPI_READ_FLAG; tx_buf[1] = (addr >> 24) & 0xFF; tx_buf[2] = (addr >> 16) & 0xFF; tx_buf[3] = (addr >> 8) & 0xFF; tx_buf[4] = addr & 0xFF; tx_buf[5] = 0xFF; tx_buf[6] = 0xFF; tx_buf[7] = 0xFF; xfers.tx_buf = tx_buf; xfers.rx_buf = rx_buf; xfers.len = buf_len; xfers.cs_change = 0; if (goodix_spi_bus.dma_mode_enabled) xfers.bits_per_word = buf_len >= 64 ? 32 : 8; spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) { ts_err("spi transfer error:%d", ret); goto err_spi_transfer; } memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN - 1], len); err_spi_transfer: if (tx_buf != goodix_spi_bus.tx_buf) kfree(tx_buf); err_alloc_tx_buf: if (rx_buf != goodix_spi_bus.rx_buf) kfree(rx_buf); err_alloc_rx_buf: mutex_unlock(&goodix_spi_bus.mutex); return ret; } static int goodix_spi_read_fast(struct device *dev, unsigned int addr, struct goodix_rx_package *package, unsigned int len) { struct spi_device *spi = to_spi_device(dev); u8 *tx_buf = NULL; struct spi_transfer xfers; struct spi_message spi_msg; int ret = 0; int buf_len = SPI_READ_PREFIX_LEN - 1 + len; if (goodix_spi_bus.dma_mode_enabled && buf_len >= 64) { buf_len = ALIGN(buf_len, 4); } if (buf_len <= SPI_PREALLOC_TX_BUF_SIZE) { tx_buf = goodix_spi_bus.tx_buf; } else { tx_buf = kzalloc(buf_len, GFP_KERNEL); if (!tx_buf) { ts_err("alloc tx_buf failed, size:%d", buf_len); return -ENOMEM; } } spi_message_init(&spi_msg); memset(&xfers, 0, sizeof(xfers)); mutex_lock(&goodix_spi_bus.mutex); /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ tx_buf[0] = SPI_READ_FLAG; tx_buf[1] = (addr >> 24) & 0xFF; tx_buf[2] = (addr >> 16) & 0xFF; tx_buf[3] = (addr >> 8) & 0xFF; tx_buf[4] = addr & 0xFF; tx_buf[5] = 0xFF; tx_buf[6] = 0xFF; tx_buf[7] = 0xFF; xfers.tx_buf = tx_buf; xfers.rx_buf = package->header; xfers.len = buf_len; xfers.cs_change = 0; if (goodix_spi_bus.dma_mode_enabled) xfers.bits_per_word = buf_len >= 64 ? 32 : 8; spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); mutex_unlock(&goodix_spi_bus.mutex); if (ret < 0) { ts_err("spi transfer error:%d", ret); goto err_spi_transfer; } err_spi_transfer: if (tx_buf != goodix_spi_bus.tx_buf) kfree(tx_buf); return ret; } /** * goodix_spi_write- write device register through spi bus * @dev: pointer to device data * @addr: register address * @data: write buffer * @len: bytes to write * return: 0 - write ok; < 0 - spi transter error. */ static int goodix_spi_write(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len) { struct spi_device *spi = to_spi_device(dev); u8 *tx_buf = NULL; struct spi_transfer xfers; struct spi_message spi_msg; int ret = 0; int buf_len = SPI_WRITE_PREFIX_LEN + len; if (goodix_spi_bus.dma_mode_enabled && buf_len >= 64) { buf_len = ALIGN(buf_len, 4); } if (buf_len <= SPI_PREALLOC_TX_BUF_SIZE) { tx_buf = goodix_spi_bus.tx_buf; } else { tx_buf = kzalloc(buf_len, GFP_KERNEL); if (!tx_buf) { ts_err("alloc tx_buf failed, size:%d", buf_len); return -ENOMEM; } } spi_message_init(&spi_msg); memset(&xfers, 0, sizeof(xfers)); mutex_lock(&goodix_spi_bus.mutex); tx_buf[0] = SPI_WRITE_FLAG; tx_buf[1] = (addr >> 24) & 0xFF; tx_buf[2] = (addr >> 16) & 0xFF; tx_buf[3] = (addr >> 8) & 0xFF; tx_buf[4] = addr & 0xFF; memcpy(&tx_buf[SPI_WRITE_PREFIX_LEN], data, len); xfers.tx_buf = tx_buf; xfers.len = buf_len; xfers.cs_change = 0; if (goodix_spi_bus.dma_mode_enabled) xfers.bits_per_word = buf_len >= 64 ? 32 : 8; spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); mutex_unlock(&goodix_spi_bus.mutex); if (ret < 0) ts_err("spi transfer error:%d", ret); if (tx_buf != goodix_spi_bus.tx_buf) kfree(tx_buf); return ret; } static void goodix_pdev_release(struct device *dev) { ts_info("goodix pdev released"); kfree(goodix_pdev); } static int goodix_spi_probe(struct spi_device *spi) { int ret = 0; ts_info("goodix spi probe in"); /* init spi_device */ spi->mode = SPI_MODE_0; spi->bits_per_word = 8; spi->rt = true; ret = spi_setup(spi); if (ret) { ts_err("failed set spi mode, %d", ret); return ret; } /* get ic type */ ret = goodix_get_ic_type(spi->dev.of_node, &goodix_spi_bus); if (ret < 0) return ret; goodix_spi_bus.bus_type = GOODIX_BUS_TYPE_SPI; goodix_spi_bus.dev = &spi->dev; if (goodix_spi_bus.ic_type == IC_TYPE_BERLIN_A) { goodix_spi_bus.read = goodix_spi_read_bra; } else { goodix_spi_bus.read = goodix_spi_read; goodix_spi_bus.read_fast = goodix_spi_read_fast; } goodix_spi_bus.write = goodix_spi_write; goodix_spi_bus.rx_buf = kzalloc(SPI_PREALLOC_RX_BUF_SIZE, GFP_KERNEL); if (!goodix_spi_bus.rx_buf) { return -ENOMEM; } goodix_spi_bus.tx_buf = kzalloc(SPI_PREALLOC_TX_BUF_SIZE, GFP_KERNEL); if (!goodix_spi_bus.tx_buf) { ret = -ENOMEM; goto err_alloc_tx_buf; } mutex_init(&goodix_spi_bus.mutex); goodix_spi_bus.dma_mode_enabled = false; #ifdef CONFIG_GOOG_TOUCH_INTERFACE goodix_spi_bus.dma_mode_enabled = goog_check_spi_dma_enabled(spi); ts_info("dma_mode: %s\n", goodix_spi_bus.dma_mode_enabled ? "enabled" : "disabled"); #endif /* ts core device */ goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); if (!goodix_pdev) { ret = -ENOMEM; goto err_alloc_pdev; } 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_spi_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_register_platform_device; } ts_info("spi probe out"); return 0; err_register_platform_device: kfree(goodix_pdev); err_alloc_pdev: kfree(goodix_spi_bus.tx_buf); err_alloc_tx_buf: kfree(goodix_spi_bus.rx_buf); ts_info("spi probe out, %d", ret); return ret; } static int goodix_spi_remove(struct spi_device *spi) { platform_device_unregister(goodix_pdev); return 0; } #ifdef CONFIG_OF static const struct of_device_id spi_matches[] = { { .compatible = "goodix,brl-a", }, { .compatible = "goodix,brl-b", }, { .compatible = "goodix,brl-d", }, { .compatible = "goodix,nottingham", }, {}, }; #endif static const struct spi_device_id spi_id_table[] = { { TS_DRIVER_NAME, 0 }, {}, }; static struct spi_driver goodix_spi_driver = { .driver = { .name = TS_DRIVER_NAME, //.owner = THIS_MODULE, .of_match_table = spi_matches, }, .id_table = spi_id_table, .probe = goodix_spi_probe, .remove = goodix_spi_remove, }; int goodix_spi_bus_init(void) { ts_info("Goodix spi driver init"); return spi_register_driver(&goodix_spi_driver); } void goodix_spi_bus_exit(void) { ts_info("Goodix spi driver exit"); spi_unregister_driver(&goodix_spi_driver); }