diff --git a/drivers/lcd/Kconfig b/drivers/lcd/Kconfig index 48b9c03b0a6..c4eca0d5308 100644 --- a/drivers/lcd/Kconfig +++ b/drivers/lcd/Kconfig @@ -1177,6 +1177,7 @@ endif # LCD_RA8875 config LCD_FT80X bool "FTDI FT80x GUI Controller" default n + select SCHED_HPWORK depends on EXPERIMENTAL ---help--- Integrated LCD, Audio, Touchscreen controller driver for the FTDI diff --git a/drivers/lcd/ft80x.h b/drivers/lcd/ft80x.h index 2880f3454f8..2eb7d7f8a81 100644 --- a/drivers/lcd/ft80x.h +++ b/drivers/lcd/ft80x.h @@ -48,6 +48,9 @@ * Included Files *******************************************************************************************/ +#include +#include + /******************************************************************************************* * Pre-processor Definitions *******************************************************************************************/ @@ -244,6 +247,25 @@ * frame is scanned out (recommended). */ +/* Interrupts ******************************************************************************/ + +/* The interrupt output pin is enabled by REG_INT_EN. When REG_INT_EN is 0, INT_N is + * tri-state (pulled to high by external pull-up resistor). When REG_INT_EN is 1, INT_N is + * driven low when any of the interrupt flags in REG_INT_FLAGS are high, after masking with + * REG_INT_MASK. Writing a ‘1’ in any bit of REG_INT_MASK will enable the correspond + * interrupt. Each bit in REG_INT_FLAGS is set by a corresponding interrupt source. + * REG_INT_FLAGS is readable by the host at any time, and clears when read. + */ + +#define FT80X_INT_SWAP (1 << 0) /* Bit 0: Display swap occurred */ +#define FT80X_INT_TOUCH (1 << 1) /* Bit 1: Touch-screen touch detected */ +#define FT80X_INT_TAG (1 << 2) /* Bit 2: Touch-screen tag value change */ +#define FT80X_INT_SOUND (1 << 3) /* Bit 3: Sound effect ended */ +#define FT80X_INT_PLAYBACK (1 << 4) /* Bit 4: Audio playback ended */ +#define FT80X_INT_CMDEMPTY (1 << 5) /* Bit 5: Command FIFO empty */ +#define FT80X_INT_CMDFLAG (1 << 6) /* Bit 6: Command FIFO flag */ +#define FT80X_INT_CONVCOMPLETE (1 << 7) /* Bit 7: Touch-screen conversions completed */ + /******************************************************************************************* * Public Types *******************************************************************************************/ @@ -359,6 +381,7 @@ struct ft80x_dev_s FAR struct i2c_master_s *i2c; /* Cached SPI device reference */ #endif FAR const struct ft80x_config_s *lower; /* Cached lower half instance */ + struct work_s intwork; /* Support back end interrupt processing */ uint32_t frequency; /* Effective frequency */ sem_t exclsem; /* Mutual exclusion semaphore */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS diff --git a/drivers/lcd/ft80x_base.c b/drivers/lcd/ft80x_base.c index 384e1a5ecf2..bac6a91c368 100644 --- a/drivers/lcd/ft80x_base.c +++ b/drivers/lcd/ft80x_base.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,8 @@ * Private Function Prototypes ****************************************************************************/ +static void ft80x_interrupt_work(FAR void *arg); +static int ft80x_interrupt(int irq, FAR void *context, FAR void *arg); #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static void ft80x_destroy(FAR struct ft80x_dev_s *priv); #endif @@ -105,7 +108,7 @@ static ssize_t ft80x_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg); #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int ft80x_unlink(FAR struct inode *inode); +static int ft80x_unlink(FAR struct inode *inode); #endif /* Initialization */ @@ -136,6 +139,123 @@ static const struct file_operations g_ft80x_fops = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: ft80x_interrupt_work + * + * Description: + * Back end handling for FT80x interrupts + * + ****************************************************************************/ + +static void ft80x_interrupt_work(FAR void *arg) +{ + FAR struct ft80x_dev_s *priv = (FAR struct ft80x_dev_s *)arg; + uint32_t intflags; + + DEBUGASSERT(priv != NULL); + + /* Get the set of pending interrupts. Note that simply reading this + * register is sufficient to clear all pending interrupts. + */ + + intflags = ft80x_read_word(priv, FT80X_REG_INT_FLAGS); + + /* And process each pending interrupt. + * + * REVISIT: No interrupt sources are ever enabled in the current + * implementation. + */ + + if ((intflags & FT80X_INT_SWAP) != 0) + { + /* Display swap occurred */ + + lcdinfo("Display swap occurred\n"); + } + + if ((intflags & FT80X_INT_TOUCH) != 0) + { + /* Touch-screen touch detected */ + + lcdinfo("Touch-screen touch detected\n"); + } + + if ((intflags & FT80X_INT_TAG) != 0) + { + /* Touch-screen tag value change */ + + lcdinfo("Touch-screen tag value change\n"); + } + + if ((intflags & FT80X_INT_SOUND) != 0) + { + /* Sound effect ended */ + + lcdinfo(" Sound effect ended\n"); + } + + if ((intflags & FT80X_INT_PLAYBACK) != 0) + { + /* Audio playback ended */ + + lcdinfo("Audio playback ended\n"); + } + + if ((intflags & FT80X_INT_CMDEMPTY) != 0) + { + /* Command FIFO empty */ + + lcdinfo("Command FIFO empty\n"); + } + + if ((intflags & FT80X_INT_CMDFLAG) != 0) + { + /* Command FIFO flag */ + + lcdinfo("Command FIFO flag\n"); + } + + if ((intflags & FT80X_INT_CONVCOMPLETE) != 0) + { + /* Touch-screen conversions completed */ + + lcdinfo(" Touch-screen conversions completed\n"); + } + + /* Re-enable interrupts */ + + DEBUGASSERT(priv->lower != NULL && priv->lower->enable != NULL); + priv->lower->enable(priv->lower, true); +} + +/**************************************************************************** + * Name: ft80x_interrupt + * + * Description: + * FT80x interrupt handler + * + ****************************************************************************/ + +static int ft80x_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct ft80x_dev_s *priv = (FAR struct ft80x_dev_s *)arg; + + DEBUGASSERT(priv != NULL); + + /* Schedule to perform the interrupt work on the high priority work queue. */ + + work_queue(HPWORK, &priv->intwork, ft80x_interrupt_work, priv, 0); + + /* Disable further interrupts for the GPIO interrupt source. + * REVISIT: This assumes that interrupts will pend until re-enabled. + * In certain implementations, this can cause a loss of interrupts. + */ + + DEBUGASSERT(priv->lower != NULL && priv->lower->enable != NULL); + priv->lower->enable(priv->lower, false); + return OK; +} + /**************************************************************************** * Name: ft80x_destroy * @@ -419,20 +539,30 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg) (FAR struct ft80x_displaylist_s *)((uintptr_t)arg); if (dl == NULL || ((uintptr_t)&dl->cmd & 3) != 0 || - dl->dlsize == 0 || (dl->dlsize & 3) != 0 || + (dl->dlsize & 3) != 0 || dl->dlsize + filep->f_pos > FT80X_RAM_DL_SIZE) { ret = -EINVAL; } else { - /* This IOCTL command simply copies the display list - * provided into the FT80x display list memory. + /* Check if there is a display list. It might be useful for + * the application to issue FT80X_IOC_CREATEDL with no data in + * order to initialize the display list, then form all of the + * list entries with FT80X_IOC_APPENDDL. */ - ft80x_write_memory(priv, FT80X_RAM_DL + filep->f_pos, - &dl->cmd, dl->dlsize); - filep->f_pos += dl->dlsize; + if (dl->dlsize > 0) + { + /* This IOCTL command simply copies the display list + * provided into the FT80x display list memory. + */ + + ft80x_write_memory(priv, FT80X_RAM_DL + filep->f_pos, + &dl->cmd, dl->dlsize); + filep->f_pos += dl->dlsize; + } + ret = OK; } } @@ -824,16 +954,38 @@ int ft80x_register(FAR struct i2c_master_s *i2c, goto errout_with_sem; } - /* Register the FT80x character driver */ + /* Attach our interrupt handler */ - ret = register_driver(DEVNAME, &g_ft80x_fops, 0666, priv); + DEBUGASSERT(lower->attach != NULL && lower->enable != NULL); + ret = lower->attach(lower, ft80x_interrupt, priv); if (ret < 0) { goto errout_with_sem; } + /* Disable all interrupt sources, but enable interrupts both in the lower + * half driver and in the FT80x. + */ + + ft80x_write_word(priv, FT80X_REG_INT_MASK, 0); + ft80x_write_word(priv, FT80X_REG_INT_EN, 1); + lower->enable(lower, true); + + /* Register the FT80x character driver */ + + ret = register_driver(DEVNAME, &g_ft80x_fops, 0666, priv); + if (ret < 0) + { + goto errout_with_interrupts; + } + return OK; +errout_with_interrupts: + lower->enable(lower, false); + ft80x_write_word(priv, FT80X_REG_INT_EN, 0); + lower->attach(lower, NULL, NULL); + errout_with_sem: sem_destroy(&priv->exclsem); return ret; diff --git a/include/nuttx/lcd/ft80x.h b/include/nuttx/lcd/ft80x.h index 4db2a96c438..d6fbe12bdef 100644 --- a/include/nuttx/lcd/ft80x.h +++ b/include/nuttx/lcd/ft80x.h @@ -85,9 +85,9 @@ * struct ft80x_displaylist_s below. * Returns: None * - * These two IOCTL command simply copies the display list as provided into - * the FT80x display list memory. Display lists should generally be formed - * as follows: + * These two IOCTL command simply copy the display list as provided into the + * FT80x display list memory. Display lists should generally be formed as + * follows: * * struct ft80x_cmd_dlstart_s dlstart; # Mark the start of the display list * # Various display commands follow... @@ -95,14 +95,18 @@ * struct ft80x_cmd_swap_s swap; # Swap to the new display list * * NOTE: The functionality of FT80X_IOC_CREATEDL is the equivalent to that of - * the driver write() method. Either the write method or the FT80X_IOC_CREATEDL - * IOCTL command can be used to write the display list. + * the driver write() method. Either the write() method or the FT80X_IOC_CREATEDL + * IOCTL command can be used to create the display list. * * The difference between appending and create a display list using write() * is that it is necessary to lseek() to the beginning of the display list * to create a new display list. Subsequent writes will behave then append * to the end of the display list. * + * Need to know the current display list offset? You can set that using lseek() + * too. Just seek to the current position; the returned value will be the current + * display list offset. + * * Output values from display commands are not automatically written back in * either case but must be subsequently obtained using FT80X_IOC_GETRESULT32. *