diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 00eecb45fe..2998726fa7 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -499,6 +499,29 @@ config INPUT_CYPRESS_MBR3108_NPOLLWAITERS endif # INPUT_CYPRESS_MBR3108 +config INPUT_GT9XX + bool "Goodix GT9xx Driver" + default n + select INPUT_TOUCHSCREEN + ---help--- + Enable support for Goodix GT9xx touch panel. + +if INPUT_GT9XX + +config INPUT_GT9XX_NPOLLWAITERS + int "Number of waiters to poll" + default 1 + ---help--- + Maximum number of threads that can be waiting on poll() + +config INPUT_GT9XX_I2C_FREQUENCY + int "I2C frequency (Hz)" + default 400000 + ---help--- + I2C frequency in Hz + +endif # INPUT_GT9XX + config INPUT_BUTTONS bool "Button Inputs" default n diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs index c6e58d8b19..3f8795041b 100644 --- a/drivers/input/Make.defs +++ b/drivers/input/Make.defs @@ -72,6 +72,10 @@ ifeq ($(CONFIG_INPUT_CYPRESS_MBR3108),y) CSRCS += cypress_mbr3108.c endif +ifeq ($(CONFIG_INPUT_GT9XX),y) + CSRCS += gt9xx.c +endif + ifeq ($(CONFIG_INPUT_BUTTONS),y) CSRCS += button_upper.c ifeq ($(CONFIG_INPUT_BUTTONS_LOWER),y) diff --git a/drivers/input/gt9xx.c b/drivers/input/gt9xx.c new file mode 100644 index 0000000000..695aee7b2e --- /dev/null +++ b/drivers/input/gt9xx.c @@ -0,0 +1,956 @@ +/**************************************************************************** + * drivers/input/gt9xx.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reference: + * "NuttX RTOS for PinePhone: Touch Panel" + * https://lupyuen.github.io/articles/touch2 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/* Default I2C Frequency is 400 kHz */ + +#ifndef CONFIG_INPUT_GT9XX_I2C_FREQUENCY +# define CONFIG_INPUT_GT9XX_I2C_FREQUENCY 400000 +#endif + +/* Default Number of Poll Waiters is 1 */ + +#ifndef CONFIG_INPUT_GT9XX_NPOLLWAITERS +# define CONFIG_INPUT_GT9XX_NPOLLWAITERS 1 +#endif + +/* I2C Registers for Goodix GT9XX Touch Panel */ + +#define GTP_REG_VERSION 0x8140 /* Product ID */ +#define GTP_READ_COOR_ADDR 0x814e /* Touch Panel Status */ +#define GTP_POINT1 0x8150 /* Touch Point 1 */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Touch Panel Device */ + +struct gt9xx_dev_s +{ + /* I2C bus and address for device */ + + struct i2c_master_s *i2c; + uint8_t addr; + + /* Callback for Board-Specific Operations */ + + const struct gt9xx_board_s *board; + + /* Device State */ + + mutex_t devlock; /* Mutex to prevent concurrent reads */ + uint8_t cref; /* Reference Counter for device */ + bool int_pending; /* True if a Touch Interrupt is pending processing */ + uint16_t x; /* X Coordinate of Last Touch Point */ + uint16_t y; /* Y Coordinate of Last Touch Point */ + uint8_t flags; /* Touch Up or Touch Down for Last Touch Point */ + + /* Poll Waiters for device */ + + struct pollfd *fds[CONFIG_INPUT_GT9XX_NPOLLWAITERS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int gt9xx_open(FAR struct file *filep); +static int gt9xx_close(FAR struct file *filep); +static ssize_t gt9xx_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int gt9xx_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* File Operations for Touch Panel */ + +static const struct file_operations g_gt9xx_fileops = +{ + gt9xx_open, /* open */ + gt9xx_close, /* close */ + gt9xx_read, /* read */ + NULL, /* write */ + NULL, /* seek */ + NULL, /* ioctl */ + NULL, /* truncate */ + NULL, /* mmap */ + gt9xx_poll /* poll */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gt9xx_i2c_read + * + * Description: + * Read a Touch Panel Register over I2C. + * + * Input Parameters: + * dev - Touch Panel Device + * reg - I2C Register to be read + * buf - Receive Buffer + * buflen - Number of bytes to be read + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_i2c_read(FAR struct gt9xx_dev_s *dev, + uint16_t reg, + uint8_t *buf, + size_t buflen) +{ + int ret; + + /* Send the Register Address, MSB first */ + + uint8_t regbuf[2] = + { + reg >> 8, /* First Byte: MSB */ + reg & 0xff /* Second Byte: LSB */ + }; + + /* Compose the I2C Messages */ + + struct i2c_msg_s msgv[2] = + { + { + /* Send the I2C Register Address */ + + .frequency = CONFIG_INPUT_GT9XX_I2C_FREQUENCY, + .addr = dev->addr, + .flags = 0, + .buffer = regbuf, + .length = sizeof(regbuf) + }, + { + /* Receive the I2C Register Values */ + + .frequency = CONFIG_INPUT_GT9XX_I2C_FREQUENCY, + .addr = dev->addr, + .flags = I2C_M_READ, + .buffer = buf, + .length = buflen + } + }; + + const int msgv_len = sizeof(msgv) / sizeof(msgv[0]); + + iinfo("reg=0x%x, buflen=%ld\n", reg, buflen); + DEBUGASSERT(dev && dev->i2c && buf); + + /* Execute the I2C Transfer */ + + ret = I2C_TRANSFER(dev->i2c, msgv, msgv_len); + if (ret < 0) + { + ierr("I2C Read failed: %d\n", ret); + return ret; + } + +#ifdef CONFIG_DEBUG_INPUT_INFO + iinfodumpbuffer("gt9xx_i2c_read", buf, buflen); +#endif /* CONFIG_DEBUG_INPUT_INFO */ + + return OK; +} + +/**************************************************************************** + * Name: gt9xx_i2c_write + * + * Description: + * Write to a Touch Panel Register over I2C. + * + * Input Parameters: + * dev - Touch Panel Device + * reg - I2C Register to be written + * val - Value to be written + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_i2c_write(FAR struct gt9xx_dev_s *dev, + uint16_t reg, + uint8_t val) +{ + int ret; + + /* Send the Register Address, MSB first */ + + uint8_t regbuf[2] = + { + reg >> 8, /* First Byte: MSB */ + reg & 0xff /* Second Byte: LSB */ + }; + + /* Send the Register Value */ + + uint8_t buf[1] = + { + val /* Value to be written */ + }; + + /* Compose the I2C Messages */ + + struct i2c_msg_s msgv[2] = + { + { + /* Send the I2C Register Address */ + + .frequency = CONFIG_INPUT_GT9XX_I2C_FREQUENCY, + .addr = dev->addr, + .flags = 0, + .buffer = regbuf, + .length = sizeof(regbuf) + }, + { + /* Send the I2C Register Value */ + + .frequency = CONFIG_INPUT_GT9XX_I2C_FREQUENCY, + .addr = dev->addr, + .flags = I2C_M_NOSTART, + .buffer = buf, + .length = sizeof(buf) + } + }; + + const int msgv_len = sizeof(msgv) / sizeof(msgv[0]); + + iinfo("reg=0x%x, val=%d\n", reg, val); + DEBUGASSERT(dev && dev->i2c); + + /* Execute the I2C Transfer */ + + ret = I2C_TRANSFER(dev->i2c, msgv, msgv_len); + if (ret < 0) + { + ierr("I2C Write failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: gt9xx_probe_device + * + * Description: + * Read the Product ID from the Touch Panel over I2C. + * + * Input Parameters: + * dev - Touch Panel Device + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_probe_device(FAR struct gt9xx_dev_s *dev) +{ + int ret; + uint8_t id[4]; + + /* Read the Product ID */ + + ret = gt9xx_i2c_read(dev, GTP_REG_VERSION, id, sizeof(id)); + if (ret < 0) + { + ierr("I2C Probe failed: %d\n", ret); + return ret; + } + + /* For GT917S: Product ID will be 39 31 37 53, i.e. "917S" */ + +#ifdef CONFIG_DEBUG_INPUT_INFO + iinfodumpbuffer("gt9xx_probe_device", id, sizeof(id)); +#endif /* CONFIG_DEBUG_INPUT_INFO */ + + return OK; +} + +/**************************************************************************** + * Name: gt9xx_set_status + * + * Description: + * Set the Touch Panel Status over I2C. + * + * Input Parameters: + * dev - Touch Panel Device + * status - Status value to be set + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_set_status(FAR struct gt9xx_dev_s *dev, uint8_t status) +{ + int ret; + + iinfo("status=%d\n", status); + DEBUGASSERT(dev); + + /* Write to the Status Register over I2C */ + + ret = gt9xx_i2c_write(dev, GTP_READ_COOR_ADDR, status); + if (ret < 0) + { + ierr("Set Status failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: gt9xx_read_touch_data + * + * Description: + * Read a Touch Sample from Touch Panel. Returns either 0 or 1 + * Touch Points. + * + * Input Parameters: + * dev - Touch Panel Device + * sample - Returned Touch Sample (0 or 1 Touch Points) + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_read_touch_data(FAR struct gt9xx_dev_s *dev, + FAR struct touch_sample_s *sample) +{ + uint8_t status[1]; + uint8_t status_code; + uint8_t touched_points; + uint8_t touch[6]; + uint16_t x; + uint16_t y; + uint8_t flags; + int ret; + + /* Erase the Touch Sample and Touch Point */ + + iinfo("\n"); + DEBUGASSERT(dev && sample); + memset(sample, 0, sizeof(*sample)); + + /* Read the Touch Panel Status */ + + ret = gt9xx_i2c_read(dev, GTP_READ_COOR_ADDR, status, sizeof(status)); + if (ret < 0) + { + ierr("Read Touch Panel Status failed: %d\n", ret); + return ret; + } + + /* Decode the Status Code and the Touched Points */ + + status_code = status[0] & 0x80; + touched_points = status[0] & 0x0f; + + /* If Touch Panel Status is OK and Touched Points is 1 or more */ + + if (status_code != 0 && touched_points >= 1) + { + /* Read the First Touch Point (6 bytes) */ + + ret = gt9xx_i2c_read(dev, GTP_POINT1, touch, sizeof(touch)); + if (ret < 0) + { + ierr("Read Touch Point failed: %d\n", ret); + return ret; + } + + /* Decode the Touch Coordinates */ + + x = touch[0] + (touch[1] << 8); + y = touch[2] + (touch[3] << 8); + + /* Return the Touch Coordinates as Touch Down */ + + flags = TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID; + sample->npoints = 1; + sample->point[0].id = 0; + sample->point[0].x = x; + sample->point[0].y = y; + sample->point[0].flags = flags; + iinfo("touch down x=%d, y=%d\n", x, y); + } + + /* Set the Touch Panel Status to 0 */ + + ret = gt9xx_set_status(dev, 0); + if (ret < 0) + { + ierr("Set Touch Panel Status failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: gt9xx_read + * + * Description: + * Read a Touch Sample from Touch Panel. Returns either 0 or 1 + * Touch Points. + * + * Input Parameters: + * dev - Touch Panel Device + * buffer - Returned Touch Sample (0 or 1 Touch Points) + * buflen - Size of buffer + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static ssize_t gt9xx_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode; + FAR struct gt9xx_dev_s *priv; + struct touch_sample_s sample; + const size_t outlen = sizeof(sample); + irqstate_t flags; + int ret; + + /* Returned Touch Sample will have 0 or 1 Touch Points */ + + iinfo("buflen=%ld\n", buflen); + if (buflen < outlen) + { + ierr("Buffer should be at least %ld bytes, got %ld bytes\n", + outlen, buflen); + return -EINVAL; + } + + /* Get the Touch Panel Device */ + + DEBUGASSERT(filep); + inode = filep->f_inode; + DEBUGASSERT(inode && inode->i_private); + priv = inode->i_private; + + /* Begin Mutex: Lock to prevent concurrent reads */ + + ret = nxmutex_lock(&priv->devlock); + if (ret < 0) + { + return ret; + } + + ret = -EINVAL; + + /* If waiting for Touch Up, return the Last Touch Point as Touch Up */ + + if (priv->flags & TOUCH_DOWN) + { + /* Begin Critical Section */ + + flags = enter_critical_section(); + + /* Mark the Last Touch Point as Touch Up */ + + priv->flags = TOUCH_UP | TOUCH_ID_VALID | TOUCH_POS_VALID; + + /* End Critical Section */ + + leave_critical_section(flags); + + /* Return the Last Touch Point, changed to Touch Up */ + + memset(&sample, 0, sizeof(sample)); + sample.npoints = 1; + sample.point[0].id = 0; + sample.point[0].x = priv->x; + sample.point[0].y = priv->y; + sample.point[0].flags = priv->flags; + memcpy(buffer, &sample, sizeof(sample)); + ret = OK; + iinfo("touch up x=%d, y=%d\n", priv->x, priv->y); + } + else + { + /* Otherwise read the Touch Point over I2C */ + + ret = gt9xx_read_touch_data(priv, &sample); + + /* Skip duplicates */ + + if (sample.npoints >= 1 && + priv->x == sample.point[0].x && + priv->y == sample.point[0].y) + { + memset(&sample, 0, sizeof(sample)); + sample.npoints = 0; + iinfo("skip duplicate x=%d, y=%d\n", priv->x, priv->y); + } + + /* Return the Touch Point */ + + memcpy(buffer, &sample, sizeof(sample)); + + /* Begin Critical Section */ + + flags = enter_critical_section(); + + /* Clear the Interrupt Pending Flag */ + + priv->int_pending = false; + + /* Remember the Last Touch Point */ + + if (sample.npoints >= 1) + { + priv->x = sample.point[0].x; + priv->y = sample.point[0].y; + priv->flags = sample.point[0].flags; + } + + /* End Critical Section */ + + leave_critical_section(flags); + } + + /* End Mutex: Unlock to allow next read */ + + nxmutex_unlock(&priv->devlock); + return (ret < 0) ? ret : outlen; +} + +/**************************************************************************** + * Name: gt9xx_open + * + * Description: + * Open the Touch Panel Device. If this is the first open, we power on + * the Touch Panel, probe for the Touch Panel and enable Touch Panel + * Interrupts. + * + * Input Parameters: + * filep - File Struct for Touch Panel + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct gt9xx_dev_s *priv; + unsigned int use_count; + int ret; + + /* Get the Touch Panel Device */ + + iinfo("\n"); + DEBUGASSERT(filep); + inode = filep->f_inode; + DEBUGASSERT(inode && inode->i_private); + priv = inode->i_private; + + /* Begin Mutex: Lock to prevent concurrent update to Reference Count */ + + ret = nxmutex_lock(&priv->devlock); + if (ret < 0) + { + ierr("Lock Mutex failed: %d\n", ret); + return ret; + } + + /* Get next Reference Count */ + + use_count = priv->cref + 1; + DEBUGASSERT(use_count < UINT8_MAX && use_count > priv->cref); + if (use_count == 1) + { + /* If first user, power on the Touch Panel */ + + DEBUGASSERT(priv->board->set_power != NULL); + ret = priv->board->set_power(priv->board, true); + if (ret < 0) + { + goto out_lock; + } + + /* Let Touch Panel power up before probing */ + + nxsig_usleep(100 * 1000); + + /* Check that Touch Panel exists on I2C */ + + ret = gt9xx_probe_device(priv); + if (ret < 0) + { + /* No such device, power off the Touch Panel */ + + priv->board->set_power(priv->board, false); + goto out_lock; + } + + /* Enable Touch Panel Interrupts */ + + DEBUGASSERT(priv->board->irq_enable); + priv->board->irq_enable(priv->board, true); + } + + /* Set the Reference Count */ + + priv->cref = use_count; + + /* End Mutex: Unlock to allow update to Reference Count */ + +out_lock: + nxmutex_unlock(&priv->devlock); + return ret; +} + +/**************************************************************************** + * Name: gt9xx_close + * + * Description: + * Close the Touch Panel Device. If this is the final close, we disable + * Touch Panel Interrupts and power off the Touch Panel. + * + * Input Parameters: + * filep - File Struct for Touch Panel + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct gt9xx_dev_s *priv; + int use_count; + int ret; + + /* Get the Touch Panel Device */ + + iinfo("\n"); + DEBUGASSERT(filep); + inode = filep->f_inode; + DEBUGASSERT(inode && inode->i_private); + priv = inode->i_private; + + /* Begin Mutex: Lock to prevent concurrent update to Reference Count */ + + ret = nxmutex_lock(&priv->devlock); + if (ret < 0) + { + ierr("Lock Mutex failed: %d\n", ret); + return ret; + } + + /* Decrement the Reference Count */ + + use_count = priv->cref - 1; + DEBUGASSERT(use_count >= 0); + if (use_count == 0) + { + /* If final user, disable Touch Panel Interrupts */ + + DEBUGASSERT(priv->board && priv->board->irq_enable); + priv->board->irq_enable(priv->board, false); + + /* Power off the Touch Panel */ + + DEBUGASSERT(priv->board->set_power); + priv->board->set_power(priv->board, false); + } + + /* Set the Reference Count */ + + priv->cref = use_count; + + /* End Mutex: Unlock to allow update to Reference Count */ + + nxmutex_unlock(&priv->devlock); + return OK; +} + +/**************************************************************************** + * Name: gt9xx_poll + * + * Description: + * Setup or teardown a poll for the Touch Panel Device. + * + * Input Parameters: + * filep - File Struct for Touch Panel + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * setup - true: Setup the poll; false: Teardown the poll + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct gt9xx_dev_s *priv; + FAR struct inode *inode; + bool pending; + int ret = 0; + int i; + + /* Get the Touch Panel Device */ + + iinfo("setup=%d\n", setup); + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct gt9xx_dev_s *)inode->i_private; + + /* Begin Mutex: Lock to prevent concurrent update to Poll Waiters */ + + ret = nxmutex_lock(&priv->devlock); + if (ret < 0) + { + ierr("Lock Mutex failed: %d\n", ret); + return ret; + } + + if (setup) + { + /* If Poll Setup: Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto out; + } + + /* Find an available slot for the Poll Waiter */ + + for (i = 0; i < CONFIG_INPUT_GT9XX_NPOLLWAITERS; i++) + { + /* Found an available slot */ + + if (!priv->fds[i]) + { + /* Bind the poll structure and this slot */ + + priv->fds[i] = fds; + fds->priv = &priv->fds[i]; + break; + } + } + + if (i >= CONFIG_INPUT_GT9XX_NPOLLWAITERS) + { + /* No slots available */ + + fds->priv = NULL; + ret = -EBUSY; + } + else + { + /* If Interrupt Pending is set, notify the Poll Waiters */ + + pending = priv->int_pending; + if (pending) + { + poll_notify(priv->fds, + CONFIG_INPUT_GT9XX_NPOLLWAITERS, + POLLIN); + } + } + } + else if (fds->priv) + { + /* If Poll Teardown: Remove the poll setup */ + + FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; + DEBUGASSERT(slot != NULL); + + *slot = NULL; + fds->priv = NULL; + } + + /* End Mutex: Unlock to allow update to Poll Waiters */ + +out: + nxmutex_unlock(&priv->devlock); + return ret; +} + +/**************************************************************************** + * Name: gt9xx_isr_handler + * + * Description: + * Interrupt Handler for Touch Panel. + * + * Input Parameters: + * irq - IRQ Number + * context - IRQ Context + * arg - Touch Panel Device + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int gt9xx_isr_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct gt9xx_dev_s *priv = (FAR struct gt9xx_dev_s *)arg; + irqstate_t flags; + + DEBUGASSERT(priv); + + /* Begin Critical Section */ + + flags = enter_critical_section(); + + /* Set the Interrupt Pending Flag */ + + priv->int_pending = true; + + /* End Critical Section */ + + leave_critical_section(flags); + + /* Notify the Poll Waiters */ + + poll_notify(priv->fds, CONFIG_INPUT_GT9XX_NPOLLWAITERS, POLLIN); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gt9xx_register + * + * Description: + * Register the driver for Goodix GT9XX Touch Panel. Attach the + * Interrupt Handler for the Touch Panel and disable Touch Interrupts. + * + * Input Parameters: + * devpath - Device Path (e.g. "/dev/input0") + * dev - I2C Bus + * i2c_devaddr - I2C Address of Touch Panel + * board_config - Callback for Board-Specific Operations + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int gt9xx_register(FAR const char *devpath, + FAR struct i2c_master_s *i2c_dev, + uint8_t i2c_devaddr, + const struct gt9xx_board_s *board_config) +{ + struct gt9xx_dev_s *priv; + int ret = 0; + + iinfo("devpath=%s, i2c_devaddr=%d\n", devpath, i2c_devaddr); + DEBUGASSERT(devpath != NULL && i2c_dev != NULL && board_config != NULL); + + /* Allocate the Touch Panel Device Structure */ + + priv = kmm_zalloc(sizeof(struct gt9xx_dev_s)); + if (!priv) + { + ierr("GT9XX Memory Allocation failed\n"); + return -ENOMEM; + } + + /* Setup the Touch Panel Device Structure */ + + priv->addr = i2c_devaddr; + priv->i2c = i2c_dev; + priv->board = board_config; + nxmutex_init(&priv->devlock); + + /* Register the Touch Input Driver */ + + ret = register_driver(devpath, &g_gt9xx_fileops, 0666, priv); + if (ret < 0) + { + nxmutex_destroy(&priv->devlock); + kmm_free(priv); + ierr("GT9XX Registration failed: %d\n", ret); + return ret; + } + + /* Attach the Interrupt Handler */ + + DEBUGASSERT(priv->board->irq_attach); + priv->board->irq_attach(priv->board, gt9xx_isr_handler, priv); + + /* Disable Touch Panel Interrupts */ + + DEBUGASSERT(priv->board->irq_enable); + priv->board->irq_enable(priv->board, false); + + iinfo("GT9XX Touch Panel registered\n"); + return OK; +} diff --git a/include/nuttx/input/gt9xx.h b/include/nuttx/input/gt9xx.h new file mode 100644 index 0000000000..fe105e7eb4 --- /dev/null +++ b/include/nuttx/input/gt9xx.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * include/nuttx/input/gt9xx.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_INPUT_GT9XX_H +#define __INCLUDE_NUTTX_INPUT_GT9XX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Callback for Board-Specific Operations */ + +struct gt9xx_board_s +{ + /* Attach the Interrupt Handler for Touch Panel */ + + int (*irq_attach) (const struct gt9xx_board_s *state, + xcpt_t isr, + FAR void *arg); + + /* Enable or disable Interrupts for the Touch Panel. Will be called by + * Interrupt Handler. + */ + + void (*irq_enable) (const struct gt9xx_board_s *state, bool enable); + + /* Power on or off the Touch Panel */ + + int (*set_power) (const struct gt9xx_board_s *state, bool on); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: gt9xx_register + * + * Description: + * Register the driver for Goodix GT9XX Touch Panel. Attach the + * Interrupt Handler for the Touch Panel and disable Touch Interrupts. + * + * Input Parameters: + * devpath - Device Path (e.g. "/dev/input0") + * dev - I2C Bus + * i2c_devaddr - I2C Address of Touch Panel + * board_config - Callback for Board-Specific Operations + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int gt9xx_register(FAR const char *devpath, + FAR struct i2c_master_s *dev, + uint8_t i2c_devaddr, + const struct gt9xx_board_s *board_config); + +#endif /* __INCLUDE_NUTTX_INPUT_GT9XX_H */