drv: display: Initial FT800 display driver
This patch includes initial support for FT800 display driver. It includes basic features. It can be easily extended with more FT800 display list and co-processor features. Signed-off-by: Hubert Miś <hubert.mis@gmail.com>
This commit is contained in:
parent
0da8ec70dc
commit
7d8f78a08b
22 changed files with 2346 additions and 0 deletions
3
drivers/misc/CMakeLists.txt
Normal file
3
drivers/misc/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_FT800 ft8xx)
|
10
drivers/misc/Kconfig
Normal file
10
drivers/misc/Kconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Miscellaneous drivers not exposing Zephyr Device Driver API
|
||||
|
||||
# Copyright (c) 2021 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menu "Miscellaneous Drivers"
|
||||
|
||||
source "drivers/misc/ft8xx/Kconfig"
|
||||
|
||||
endmenu
|
10
drivers/misc/ft8xx/CMakeLists.txt
Normal file
10
drivers/misc/ft8xx/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(
|
||||
ft8xx.c
|
||||
ft8xx_common.c
|
||||
ft8xx_copro.c
|
||||
ft8xx_drv.c
|
||||
)
|
10
drivers/misc/ft8xx/Kconfig
Normal file
10
drivers/misc/ft8xx/Kconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
# FT8xx Embedded Video Engine configuration options
|
||||
|
||||
# Copyright (c) 2020-2021 Hubert Miś <hubert.mis@gmail.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config FT800
|
||||
bool "FT800 Embedded Video Engine driver"
|
||||
depends on SPI
|
||||
help
|
||||
Enable driver for FT800 controller.
|
228
drivers/misc/ft8xx/ft8xx.c
Normal file
228
drivers/misc/ft8xx/ft8xx.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hubert Miś
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ftdi_ft800
|
||||
|
||||
#include <drivers/misc/ft8xx/ft8xx.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <device.h>
|
||||
#include <kernel.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <drivers/misc/ft8xx/ft8xx_copro.h>
|
||||
#include <drivers/misc/ft8xx/ft8xx_common.h>
|
||||
#include <drivers/misc/ft8xx/ft8xx_dl.h>
|
||||
#include <drivers/misc/ft8xx/ft8xx_memory.h>
|
||||
|
||||
#include "ft8xx_drv.h"
|
||||
#include "ft8xx_host_commands.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ft8xx, CONFIG_DISPLAY_LOG_LEVEL);
|
||||
|
||||
#define FT8XX_DLSWAP_FRAME 0x02
|
||||
|
||||
#define FT8XX_EXPECTED_ID 0x7C
|
||||
|
||||
struct ft8xx_config {
|
||||
uint16_t vsize;
|
||||
uint16_t voffset;
|
||||
uint16_t vcycle;
|
||||
uint16_t vsync0;
|
||||
uint16_t vsync1;
|
||||
uint16_t hsize;
|
||||
uint16_t hoffset;
|
||||
uint16_t hcycle;
|
||||
uint16_t hsync0;
|
||||
uint16_t hsync1;
|
||||
uint8_t pclk;
|
||||
uint8_t pclk_pol :1;
|
||||
uint8_t cspread :1;
|
||||
uint8_t swizzle :4;
|
||||
};
|
||||
|
||||
struct ft8xx_data {
|
||||
const struct ft8xx_config *config;
|
||||
ft8xx_int_callback irq_callback;
|
||||
};
|
||||
|
||||
const static struct ft8xx_config ft8xx_config = {
|
||||
.pclk = DT_INST_PROP(0, pclk),
|
||||
.pclk_pol = DT_INST_PROP(0, pclk_pol),
|
||||
.cspread = DT_INST_PROP(0, cspread),
|
||||
.swizzle = DT_INST_PROP(0, swizzle),
|
||||
.vsize = DT_INST_PROP(0, vsize),
|
||||
.voffset = DT_INST_PROP(0, voffset),
|
||||
.vcycle = DT_INST_PROP(0, vcycle),
|
||||
.vsync0 = DT_INST_PROP(0, vsync0),
|
||||
.vsync1 = DT_INST_PROP(0, vsync1),
|
||||
.hsize = DT_INST_PROP(0, hsize),
|
||||
.hoffset = DT_INST_PROP(0, hoffset),
|
||||
.hcycle = DT_INST_PROP(0, hcycle),
|
||||
.hsync0 = DT_INST_PROP(0, hsync0),
|
||||
.hsync1 = DT_INST_PROP(0, hsync1),
|
||||
};
|
||||
|
||||
static struct ft8xx_data ft8xx_data = {
|
||||
.config = &ft8xx_config,
|
||||
.irq_callback = NULL,
|
||||
};
|
||||
|
||||
static void host_command(uint8_t cmd)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ft8xx_drv_command(cmd);
|
||||
__ASSERT(err == 0, "Writing FT8xx command failed");
|
||||
}
|
||||
|
||||
static void wait(void)
|
||||
{
|
||||
k_sleep(K_MSEC(20));
|
||||
}
|
||||
|
||||
static bool verify_chip(void)
|
||||
{
|
||||
uint32_t id = ft8xx_rd32(FT800_REG_ID);
|
||||
|
||||
return (id & 0xff) == FT8XX_EXPECTED_ID;
|
||||
}
|
||||
|
||||
static int ft8xx_init(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ft8xx_config *config = (struct ft8xx_config *)dev->config;
|
||||
|
||||
ret = ft8xx_drv_init();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("FT8xx driver initialization failed with %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset display controller */
|
||||
host_command(CORERST);
|
||||
host_command(ACTIVE);
|
||||
wait();
|
||||
host_command(CLKEXT);
|
||||
host_command(CLK48M);
|
||||
wait();
|
||||
|
||||
host_command(CORERST);
|
||||
host_command(ACTIVE);
|
||||
wait();
|
||||
host_command(CLKEXT);
|
||||
host_command(CLK48M);
|
||||
wait();
|
||||
|
||||
if (!verify_chip()) {
|
||||
LOG_ERR("FT8xx chip not recognized");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Disable LCD */
|
||||
ft8xx_wr8(FT800_REG_GPIO, 0);
|
||||
ft8xx_wr8(FT800_REG_PCLK, 0);
|
||||
|
||||
/* Configure LCD */
|
||||
ft8xx_wr16(FT800_REG_HSIZE, config->hsize);
|
||||
ft8xx_wr16(FT800_REG_HCYCLE, config->hcycle);
|
||||
ft8xx_wr16(FT800_REG_HOFFSET, config->hoffset);
|
||||
ft8xx_wr16(FT800_REG_HSYNC0, config->hsync0);
|
||||
ft8xx_wr16(FT800_REG_HSYNC1, config->hsync1);
|
||||
ft8xx_wr16(FT800_REG_VSIZE, config->vsize);
|
||||
ft8xx_wr16(FT800_REG_VCYCLE, config->vcycle);
|
||||
ft8xx_wr16(FT800_REG_VOFFSET, config->voffset);
|
||||
ft8xx_wr16(FT800_REG_VSYNC0, config->vsync0);
|
||||
ft8xx_wr16(FT800_REG_VSYNC1, config->vsync1);
|
||||
ft8xx_wr8(FT800_REG_SWIZZLE, config->swizzle);
|
||||
ft8xx_wr8(FT800_REG_PCLK_POL, config->pclk_pol);
|
||||
ft8xx_wr8(FT800_REG_CSPREAD, config->cspread);
|
||||
|
||||
/* Display initial screen */
|
||||
|
||||
/* Set the initial color */
|
||||
ft8xx_wr32(FT800_RAM_DL + 0, FT8XX_CLEAR_COLOR_RGB(0, 0x80, 0));
|
||||
/* Clear to the initial color */
|
||||
ft8xx_wr32(FT800_RAM_DL + 4, FT8XX_CLEAR(1, 1, 1));
|
||||
/* End the display list */
|
||||
ft8xx_wr32(FT800_RAM_DL + 8, FT8XX_DISPLAY());
|
||||
ft8xx_wr8(FT800_REG_DLSWAP, FT8XX_DLSWAP_FRAME);
|
||||
|
||||
/* Enable LCD */
|
||||
|
||||
/* Enable display bit */
|
||||
ft8xx_wr8(FT800_REG_GPIO_DIR, 0x80);
|
||||
ft8xx_wr8(FT800_REG_GPIO, 0x80);
|
||||
/* Enable backlight */
|
||||
ft8xx_wr16(FT800_REG_PWM_HZ, 0x00FA);
|
||||
ft8xx_wr8(FT800_REG_PWM_DUTY, 0x10);
|
||||
/* Enable LCD signals */
|
||||
ft8xx_wr8(FT800_REG_PCLK, config->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEVICE_DEFINE(ft8xx_spi, "ft8xx_spi", ft8xx_init, NULL,
|
||||
&ft8xx_data, &ft8xx_config,
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL);
|
||||
|
||||
int ft8xx_get_touch_tag(void)
|
||||
{
|
||||
/* Read FT800_REG_INT_FLAGS to clear IRQ */
|
||||
(void)ft8xx_rd8(FT800_REG_INT_FLAGS);
|
||||
|
||||
return (int)ft8xx_rd8(FT800_REG_TOUCH_TAG);
|
||||
}
|
||||
|
||||
void ft8xx_drv_irq_triggered(const struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
if (ft8xx_data.irq_callback != NULL) {
|
||||
ft8xx_data.irq_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void ft8xx_register_int(ft8xx_int_callback callback)
|
||||
{
|
||||
if (ft8xx_data.irq_callback != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ft8xx_data.irq_callback = callback;
|
||||
ft8xx_wr8(FT800_REG_INT_MASK, 0x04);
|
||||
ft8xx_wr8(FT800_REG_INT_EN, 0x01);
|
||||
}
|
||||
|
||||
void ft8xx_calibrate(struct ft8xx_touch_transform *data)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
|
||||
do {
|
||||
ft8xx_copro_cmd_dlstart();
|
||||
ft8xx_copro_cmd(FT8XX_CLEAR_COLOR_RGB(0x00, 0x00, 0x00));
|
||||
ft8xx_copro_cmd(FT8XX_CLEAR(1, 1, 1));
|
||||
ft8xx_copro_cmd_calibrate(&result);
|
||||
} while (result == 0);
|
||||
|
||||
data->a = ft8xx_rd32(FT800_REG_TOUCH_TRANSFORM_A);
|
||||
data->b = ft8xx_rd32(FT800_REG_TOUCH_TRANSFORM_B);
|
||||
data->c = ft8xx_rd32(FT800_REG_TOUCH_TRANSFORM_C);
|
||||
data->d = ft8xx_rd32(FT800_REG_TOUCH_TRANSFORM_D);
|
||||
data->e = ft8xx_rd32(FT800_REG_TOUCH_TRANSFORM_E);
|
||||
data->f = ft8xx_rd32(FT800_REG_TOUCH_TRANSFORM_F);
|
||||
}
|
||||
|
||||
void ft8xx_touch_transform_set(const struct ft8xx_touch_transform *data)
|
||||
{
|
||||
ft8xx_wr32(FT800_REG_TOUCH_TRANSFORM_A, data->a);
|
||||
ft8xx_wr32(FT800_REG_TOUCH_TRANSFORM_B, data->b);
|
||||
ft8xx_wr32(FT800_REG_TOUCH_TRANSFORM_C, data->c);
|
||||
ft8xx_wr32(FT800_REG_TOUCH_TRANSFORM_D, data->d);
|
||||
ft8xx_wr32(FT800_REG_TOUCH_TRANSFORM_E, data->e);
|
||||
ft8xx_wr32(FT800_REG_TOUCH_TRANSFORM_F, data->f);
|
||||
}
|
71
drivers/misc/ft8xx/ft8xx_common.c
Normal file
71
drivers/misc/ft8xx/ft8xx_common.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hubert Miś
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <drivers/misc/ft8xx/ft8xx_common.h>
|
||||
|
||||
#include <sys/byteorder.h>
|
||||
#include "ft8xx_drv.h"
|
||||
|
||||
void ft8xx_wr8(uint32_t address, uint8_t data)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ft8xx_drv_write(address, &data, sizeof(data));
|
||||
__ASSERT(err == 0, "Writing FT8xx data at 0x%x failed", address);
|
||||
}
|
||||
|
||||
void ft8xx_wr16(uint32_t address, uint16_t data)
|
||||
{
|
||||
int err;
|
||||
uint8_t buffer[2];
|
||||
|
||||
*(uint16_t *)buffer = sys_cpu_to_le16(data);
|
||||
err = ft8xx_drv_write(address, buffer, sizeof(buffer));
|
||||
__ASSERT(err == 0, "Writing FT8xx data at 0x%x failed", address);
|
||||
}
|
||||
|
||||
void ft8xx_wr32(uint32_t address, uint32_t data)
|
||||
{
|
||||
int err;
|
||||
uint8_t buffer[4];
|
||||
|
||||
*(uint32_t *)buffer = sys_cpu_to_le32(data);
|
||||
err = ft8xx_drv_write(address, buffer, sizeof(buffer));
|
||||
__ASSERT(err == 0, "Writing FT8xx data at 0x%x failed", address);
|
||||
}
|
||||
|
||||
uint8_t ft8xx_rd8(uint32_t address)
|
||||
{
|
||||
int err;
|
||||
uint8_t data = 0;
|
||||
|
||||
err = ft8xx_drv_read(address, &data, sizeof(data));
|
||||
__ASSERT(err == 0, "Reading FT8xx data from 0x%x failed", address);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
uint16_t ft8xx_rd16(uint32_t address)
|
||||
{
|
||||
int err;
|
||||
uint8_t buffer[2] = {0};
|
||||
|
||||
err = ft8xx_drv_read(address, buffer, sizeof(buffer));
|
||||
__ASSERT(err == 0, "Reading FT8xx data from 0x%x failed", address);
|
||||
|
||||
return sys_le16_to_cpu(*(const uint16_t *)buffer);
|
||||
}
|
||||
|
||||
uint32_t ft8xx_rd32(uint32_t address)
|
||||
{
|
||||
int err;
|
||||
uint8_t buffer[4] = {0};
|
||||
|
||||
err = ft8xx_drv_read(address, buffer, sizeof(buffer));
|
||||
__ASSERT(err == 0, "Reading FT8xx data from 0x%x failed", address);
|
||||
|
||||
return sys_le32_to_cpu(*(const uint32_t *)buffer);
|
||||
}
|
179
drivers/misc/ft8xx/ft8xx_copro.c
Normal file
179
drivers/misc/ft8xx/ft8xx_copro.c
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hubert Miś
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <drivers/misc/ft8xx/ft8xx_copro.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <drivers/misc/ft8xx/ft8xx_common.h>
|
||||
#include <drivers/misc/ft8xx/ft8xx_memory.h>
|
||||
#include "ft8xx_drv.h"
|
||||
|
||||
#define FT800_RAM_CMD_SIZE 4096UL
|
||||
|
||||
enum {
|
||||
CMD_DLSTART = 0xffffff00,
|
||||
CMD_SWAP = 0xffffff01,
|
||||
CMD_TEXT = 0xffffff0c,
|
||||
CMD_NUMBER = 0xffffff2e,
|
||||
CMD_CALIBRATE = 0xffffff15,
|
||||
} ft8xx_cmd;
|
||||
|
||||
static uint16_t reg_cmd_read;
|
||||
static uint16_t reg_cmd_write;
|
||||
|
||||
static uint16_t ram_cmd_fullness(void)
|
||||
{
|
||||
return (reg_cmd_write - reg_cmd_read) % 4096UL;
|
||||
}
|
||||
|
||||
static uint16_t ram_cmd_freespace(void)
|
||||
{
|
||||
return (FT800_RAM_CMD_SIZE - 4UL) - ram_cmd_fullness();
|
||||
}
|
||||
|
||||
static void refresh_reg_cmd_read(void)
|
||||
{
|
||||
reg_cmd_read = ft8xx_rd32(FT800_REG_CMD_READ);
|
||||
}
|
||||
|
||||
static void flush_reg_cmd_write(void)
|
||||
{
|
||||
ft8xx_wr32(FT800_REG_CMD_WRITE, reg_cmd_write);
|
||||
}
|
||||
|
||||
static void increase_reg_cmd_write(uint16_t value)
|
||||
{
|
||||
reg_cmd_write = (reg_cmd_write + value) % FT800_RAM_CMD_SIZE;
|
||||
}
|
||||
|
||||
void ft8xx_copro_cmd(uint32_t cmd)
|
||||
{
|
||||
while (ram_cmd_freespace() < sizeof(cmd)) {
|
||||
refresh_reg_cmd_read();
|
||||
}
|
||||
|
||||
ft8xx_wr32(FT800_RAM_CMD + reg_cmd_write, cmd);
|
||||
increase_reg_cmd_write(sizeof(cmd));
|
||||
|
||||
flush_reg_cmd_write();
|
||||
}
|
||||
|
||||
void ft8xx_copro_cmd_dlstart(void)
|
||||
{
|
||||
ft8xx_copro_cmd(CMD_DLSTART);
|
||||
}
|
||||
|
||||
void ft8xx_copro_cmd_swap(void)
|
||||
{
|
||||
ft8xx_copro_cmd(CMD_SWAP);
|
||||
}
|
||||
|
||||
void ft8xx_copro_cmd_text(int16_t x,
|
||||
int16_t y,
|
||||
int16_t font,
|
||||
uint16_t options,
|
||||
const char *s)
|
||||
{
|
||||
const uint16_t str_bytes = strlen(s) + 1;
|
||||
const uint16_t padding_bytes = (4 - (str_bytes % 4)) % 4;
|
||||
const uint16_t cmd_size = sizeof(CMD_TEXT) +
|
||||
sizeof(x) +
|
||||
sizeof(y) +
|
||||
sizeof(font) +
|
||||
sizeof(options) +
|
||||
str_bytes +
|
||||
padding_bytes;
|
||||
|
||||
while (ram_cmd_freespace() < cmd_size) {
|
||||
refresh_reg_cmd_read();
|
||||
}
|
||||
|
||||
ft8xx_wr32(FT800_RAM_CMD + reg_cmd_write, CMD_TEXT);
|
||||
increase_reg_cmd_write(sizeof(CMD_TEXT));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, x);
|
||||
increase_reg_cmd_write(sizeof(x));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, y);
|
||||
increase_reg_cmd_write(sizeof(y));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, font);
|
||||
increase_reg_cmd_write(sizeof(font));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, options);
|
||||
increase_reg_cmd_write(sizeof(options));
|
||||
|
||||
(void)ft8xx_drv_write(FT800_RAM_CMD + reg_cmd_write, s, str_bytes);
|
||||
increase_reg_cmd_write(str_bytes + padding_bytes);
|
||||
|
||||
flush_reg_cmd_write();
|
||||
}
|
||||
|
||||
void ft8xx_copro_cmd_number(int16_t x,
|
||||
int16_t y,
|
||||
int16_t font,
|
||||
uint16_t options,
|
||||
int32_t n)
|
||||
{
|
||||
const uint16_t cmd_size = sizeof(CMD_NUMBER) +
|
||||
sizeof(x) +
|
||||
sizeof(y) +
|
||||
sizeof(font) +
|
||||
sizeof(options) +
|
||||
sizeof(n);
|
||||
|
||||
while (ram_cmd_freespace() < cmd_size) {
|
||||
refresh_reg_cmd_read();
|
||||
}
|
||||
|
||||
ft8xx_wr32(FT800_RAM_CMD + reg_cmd_write, CMD_NUMBER);
|
||||
increase_reg_cmd_write(sizeof(CMD_NUMBER));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, x);
|
||||
increase_reg_cmd_write(sizeof(x));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, y);
|
||||
increase_reg_cmd_write(sizeof(y));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, font);
|
||||
increase_reg_cmd_write(sizeof(font));
|
||||
|
||||
ft8xx_wr16(FT800_RAM_CMD + reg_cmd_write, options);
|
||||
increase_reg_cmd_write(sizeof(options));
|
||||
|
||||
ft8xx_wr32(FT800_RAM_CMD + reg_cmd_write, n);
|
||||
increase_reg_cmd_write(sizeof(n));
|
||||
|
||||
flush_reg_cmd_write();
|
||||
}
|
||||
|
||||
void ft8xx_copro_cmd_calibrate(uint32_t *result)
|
||||
{
|
||||
const uint16_t cmd_size = sizeof(CMD_CALIBRATE) + sizeof(uint32_t);
|
||||
uint32_t result_address;
|
||||
|
||||
while (ram_cmd_freespace() < cmd_size) {
|
||||
refresh_reg_cmd_read();
|
||||
}
|
||||
|
||||
ft8xx_wr32(FT800_RAM_CMD + reg_cmd_write, CMD_CALIBRATE);
|
||||
increase_reg_cmd_write(sizeof(CMD_CALIBRATE));
|
||||
|
||||
result_address = FT800_RAM_CMD + reg_cmd_write;
|
||||
ft8xx_wr32(result_address, 1UL);
|
||||
increase_reg_cmd_write(sizeof(uint32_t));
|
||||
|
||||
flush_reg_cmd_write();
|
||||
|
||||
/* Wait until calibration is finished. */
|
||||
while (ram_cmd_fullness() > 0) {
|
||||
refresh_reg_cmd_read();
|
||||
}
|
||||
|
||||
*result = ft8xx_rd32(result_address);
|
||||
}
|
202
drivers/misc/ft8xx/ft8xx_drv.c
Normal file
202
drivers/misc/ft8xx/ft8xx_drv.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hubert Miś
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "ft8xx_drv.h"
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/spi.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#define LOG_MODULE_NAME ft8xx_drv
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#define DT_DRV_COMPAT ftdi_ft800
|
||||
#define NODE_ID DT_INST(0, DT_DRV_COMPAT)
|
||||
|
||||
/* SPI device */
|
||||
static const struct device *spi_ft8xx_dev;
|
||||
static struct spi_cs_control cs_ctrl;
|
||||
static const struct spi_config spi_cfg = {
|
||||
.frequency = 8000000UL,
|
||||
.operation = SPI_WORD_SET(8) | SPI_OP_MODE_MASTER,
|
||||
.cs = &cs_ctrl,
|
||||
};
|
||||
|
||||
/* GPIO int line */
|
||||
#define IRQ_PIN DT_GPIO_PIN(NODE_ID, irq_gpios)
|
||||
static const struct device *int_ft8xx_dev;
|
||||
|
||||
static struct gpio_callback irq_cb_data;
|
||||
|
||||
__weak void ft8xx_drv_irq_triggered(const struct device *dev,
|
||||
struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
/* Intentionally empty */
|
||||
}
|
||||
|
||||
/* Protocol details */
|
||||
#define ADDR_SIZE 3
|
||||
#define DUMMY_READ_SIZE 1
|
||||
#define COMMAND_SIZE 3
|
||||
#define MAX_READ_LEN (UINT16_MAX - ADDR_SIZE - DUMMY_READ_SIZE)
|
||||
#define MAX_WRITE_LEN (UINT16_MAX - ADDR_SIZE)
|
||||
|
||||
#define READ_OP 0x00
|
||||
#define WRITE_OP 0x80
|
||||
#define COMMAND_OP 0x40
|
||||
|
||||
static void insert_addr(uint32_t addr, uint8_t *buff)
|
||||
{
|
||||
buff[0] = (addr >> 16) & 0x3f;
|
||||
buff[1] = (addr >> 8) & 0xff;
|
||||
buff[2] = (addr) & 0xff;
|
||||
}
|
||||
|
||||
int ft8xx_drv_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cs_ctrl = (struct spi_cs_control){
|
||||
.gpio_dev = device_get_binding(
|
||||
DT_SPI_DEV_CS_GPIOS_LABEL(NODE_ID)),
|
||||
.gpio_pin = DT_SPI_DEV_CS_GPIOS_PIN(NODE_ID),
|
||||
.gpio_dt_flags = DT_SPI_DEV_CS_GPIOS_FLAGS(NODE_ID),
|
||||
.delay = 0,
|
||||
};
|
||||
|
||||
spi_ft8xx_dev = device_get_binding(DT_BUS_LABEL(NODE_ID));
|
||||
if (!spi_ft8xx_dev) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* TODO: Verify if such entry in DTS is present.
|
||||
* If not, use polling mode.
|
||||
*/
|
||||
int_ft8xx_dev = device_get_binding(DT_GPIO_LABEL(NODE_ID, irq_gpios));
|
||||
if (!int_ft8xx_dev) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure(int_ft8xx_dev, IRQ_PIN,
|
||||
GPIO_INPUT | DT_GPIO_FLAGS(NODE_ID, irq_gpios));
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_pin_interrupt_configure(int_ft8xx_dev, IRQ_PIN,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_init_callback(&irq_cb_data, ft8xx_drv_irq_triggered, BIT(IRQ_PIN));
|
||||
gpio_add_callback(int_ft8xx_dev, &irq_cb_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft8xx_drv_write(uint32_t address, const uint8_t *data, unsigned int length)
|
||||
{
|
||||
int ret;
|
||||
uint8_t addr_buf[ADDR_SIZE];
|
||||
|
||||
insert_addr(address, addr_buf);
|
||||
addr_buf[0] |= WRITE_OP;
|
||||
|
||||
struct spi_buf tx[] = {
|
||||
{
|
||||
.buf = addr_buf,
|
||||
.len = sizeof(addr_buf),
|
||||
},
|
||||
{
|
||||
/* Discard const, it is implicit for TX buffer */
|
||||
.buf = (uint8_t *)data,
|
||||
.len = length,
|
||||
},
|
||||
};
|
||||
|
||||
struct spi_buf_set tx_bufs = {
|
||||
.buffers = tx,
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
ret = spi_write(spi_ft8xx_dev, &spi_cfg, &tx_bufs);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("SPI write error: %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ft8xx_drv_read(uint32_t address, uint8_t *data, unsigned int length)
|
||||
{
|
||||
int ret;
|
||||
uint8_t dummy_read_buf[ADDR_SIZE + DUMMY_READ_SIZE];
|
||||
uint8_t addr_buf[ADDR_SIZE];
|
||||
|
||||
insert_addr(address, addr_buf);
|
||||
addr_buf[0] |= READ_OP;
|
||||
|
||||
struct spi_buf tx = {
|
||||
.buf = addr_buf,
|
||||
.len = sizeof(addr_buf),
|
||||
};
|
||||
|
||||
struct spi_buf_set tx_bufs = {
|
||||
.buffers = &tx,
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
struct spi_buf rx[] = {
|
||||
{
|
||||
.buf = dummy_read_buf,
|
||||
.len = sizeof(dummy_read_buf),
|
||||
},
|
||||
{
|
||||
.buf = data,
|
||||
.len = length,
|
||||
},
|
||||
};
|
||||
|
||||
struct spi_buf_set rx_bufs = {
|
||||
.buffers = rx,
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
ret = spi_transceive(spi_ft8xx_dev, &spi_cfg, &tx_bufs, &rx_bufs);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("SPI transceive error: %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ft8xx_drv_command(uint8_t command)
|
||||
{
|
||||
int ret;
|
||||
/* Most commands include COMMAND_OP bit. ACTIVE power mode command is
|
||||
* an exception with value 0x00.
|
||||
*/
|
||||
uint8_t cmd_buf[COMMAND_SIZE] = {command, 0, 0};
|
||||
|
||||
struct spi_buf tx = {
|
||||
.buf = cmd_buf,
|
||||
.len = sizeof(cmd_buf),
|
||||
};
|
||||
|
||||
struct spi_buf_set tx_bufs = {
|
||||
.buffers = &tx,
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
ret = spi_write(spi_ft8xx_dev, &spi_cfg, &tx_bufs);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("SPI command error: %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
36
drivers/misc/ft8xx/ft8xx_drv.h
Normal file
36
drivers/misc/ft8xx/ft8xx_drv.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hubert Miś
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief FT8XX serial driver API
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_DISPLAY_FT8XX_FT8XX_DRV_H_
|
||||
#define ZEPHYR_DRIVERS_DISPLAY_FT8XX_FT8XX_DRV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <drivers/gpio.h>
|
||||
#include <device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ft8xx_drv_init(void);
|
||||
int ft8xx_drv_read(uint32_t address, uint8_t *data, unsigned int length);
|
||||
int ft8xx_drv_write(uint32_t address, const uint8_t *data, unsigned int length);
|
||||
int ft8xx_drv_command(uint8_t command);
|
||||
|
||||
extern void ft8xx_drv_irq_triggered(const struct device *dev,
|
||||
struct gpio_callback *cb, uint32_t pins);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_DISPLAY_FT8XX_FT8XX_DRV_H_ */
|
36
drivers/misc/ft8xx/ft8xx_host_commands.h
Normal file
36
drivers/misc/ft8xx/ft8xx_host_commands.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hubert Miś
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief FT8XX host commands enumeration
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_DISPLAY_FT8XX_FT8XX_HOST_COMMANDS_H_
|
||||
#define ZEPHYR_DRIVERS_DISPLAY_FT8XX_FT8XX_HOST_COMMANDS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum ft800_command_t {
|
||||
ACTIVE = 0,
|
||||
STANDBY = 0x41,
|
||||
SLEEP = 0x42,
|
||||
PWRDOWN = 0x50,
|
||||
|
||||
CLKEXT = 0x44,
|
||||
CLK48M = 0x62,
|
||||
CLK36M = 0x61,
|
||||
|
||||
CORERST = 0x68
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_DISPLAY_FT8XX_FT8XX_HOST_COMMANDS_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue