1 /*
2 * Copyright (c) 2020 Hubert Miś
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ftdi_ft800
8
9 #include <zephyr/drivers/misc/ft8xx/ft8xx.h>
10
11 #include <stddef.h>
12 #include <stdint.h>
13
14 #include <zephyr/device.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17
18 #include <zephyr/drivers/misc/ft8xx/ft8xx_copro.h>
19 #include <zephyr/drivers/misc/ft8xx/ft8xx_common.h>
20 #include <zephyr/drivers/misc/ft8xx/ft8xx_dl.h>
21 #include <zephyr/drivers/misc/ft8xx/ft8xx_memory.h>
22
23 #include "ft8xx_dev_data.h"
24 #include "ft8xx_drv.h"
25 #include "ft8xx_host_commands.h"
26
27 LOG_MODULE_REGISTER(ft8xx, CONFIG_DISPLAY_LOG_LEVEL);
28
29 #define FT8XX_DLSWAP_FRAME 0x02
30
31 #define FT8XX_EXPECTED_ID 0x7C
32
33 struct ft8xx_config {
34 uint16_t vsize;
35 uint16_t voffset;
36 uint16_t vcycle;
37 uint16_t vsync0;
38 uint16_t vsync1;
39 uint16_t hsize;
40 uint16_t hoffset;
41 uint16_t hcycle;
42 uint16_t hsync0;
43 uint16_t hsync1;
44 uint8_t pclk;
45 uint8_t pclk_pol :1;
46 uint8_t cspread :1;
47 uint8_t swizzle :4;
48 };
49
host_command(const struct device * dev,uint8_t cmd)50 static void host_command(const struct device *dev, uint8_t cmd)
51 {
52 int err;
53
54 err = ft8xx_drv_command(dev, cmd);
55 __ASSERT(err == 0, "Writing FT8xx command failed");
56 }
57
wait(void)58 static void wait(void)
59 {
60 k_sleep(K_MSEC(20));
61 }
62
verify_chip(const struct device * dev)63 static bool verify_chip(const struct device *dev)
64 {
65 uint32_t id = ft8xx_rd32(dev, FT800_REG_ID);
66
67 return (id & 0xff) == FT8XX_EXPECTED_ID;
68 }
69
ft8xx_init(const struct device * dev)70 static int ft8xx_init(const struct device *dev)
71 {
72 int ret;
73 const struct ft8xx_config *config = dev->config;
74 struct ft8xx_data *data = dev->data;
75
76 data->ft8xx_dev = dev;
77
78 ret = ft8xx_drv_init(dev);
79 if (ret < 0) {
80 LOG_ERR("FT8xx driver initialization failed with %d", ret);
81 return ret;
82 }
83
84 /* Reset display controller */
85 host_command(dev, CORERST);
86 host_command(dev, ACTIVE);
87 wait();
88 host_command(dev, CLKEXT);
89 host_command(dev, CLK48M);
90 wait();
91
92 host_command(dev, CORERST);
93 host_command(dev, ACTIVE);
94 wait();
95 host_command(dev, CLKEXT);
96 host_command(dev, CLK48M);
97 wait();
98
99 if (!verify_chip(dev)) {
100 LOG_ERR("FT8xx chip not recognized");
101 return -ENODEV;
102 }
103
104 /* Disable LCD */
105 ft8xx_wr8(dev, FT800_REG_GPIO, 0);
106 ft8xx_wr8(dev, FT800_REG_PCLK, 0);
107
108 /* Configure LCD */
109 ft8xx_wr16(dev, FT800_REG_HSIZE, config->hsize);
110 ft8xx_wr16(dev, FT800_REG_HCYCLE, config->hcycle);
111 ft8xx_wr16(dev, FT800_REG_HOFFSET, config->hoffset);
112 ft8xx_wr16(dev, FT800_REG_HSYNC0, config->hsync0);
113 ft8xx_wr16(dev, FT800_REG_HSYNC1, config->hsync1);
114 ft8xx_wr16(dev, FT800_REG_VSIZE, config->vsize);
115 ft8xx_wr16(dev, FT800_REG_VCYCLE, config->vcycle);
116 ft8xx_wr16(dev, FT800_REG_VOFFSET, config->voffset);
117 ft8xx_wr16(dev, FT800_REG_VSYNC0, config->vsync0);
118 ft8xx_wr16(dev, FT800_REG_VSYNC1, config->vsync1);
119 ft8xx_wr8(dev, FT800_REG_SWIZZLE, config->swizzle);
120 ft8xx_wr8(dev, FT800_REG_PCLK_POL, config->pclk_pol);
121 ft8xx_wr8(dev, FT800_REG_CSPREAD, config->cspread);
122
123 /* Display initial screen */
124
125 /* Set the initial color */
126 ft8xx_wr32(dev, FT800_RAM_DL + 0, FT8XX_CLEAR_COLOR_RGB(0, 0x80, 0));
127 /* Clear to the initial color */
128 ft8xx_wr32(dev, FT800_RAM_DL + 4, FT8XX_CLEAR(1, 1, 1));
129 /* End the display list */
130 ft8xx_wr32(dev, FT800_RAM_DL + 8, FT8XX_DISPLAY());
131 ft8xx_wr8(dev, FT800_REG_DLSWAP, FT8XX_DLSWAP_FRAME);
132
133 /* Enable LCD */
134
135 /* Enable display bit */
136 ft8xx_wr8(dev, FT800_REG_GPIO_DIR, 0x80);
137 ft8xx_wr8(dev, FT800_REG_GPIO, 0x80);
138 /* Enable backlight */
139 ft8xx_wr16(dev, FT800_REG_PWM_HZ, 0x00FA);
140 ft8xx_wr8(dev, FT800_REG_PWM_DUTY, 0x10);
141 /* Enable LCD signals */
142 ft8xx_wr8(dev, FT800_REG_PCLK, config->pclk);
143
144 return 0;
145 }
146
ft8xx_get_touch_tag(const struct device * dev)147 int ft8xx_get_touch_tag(const struct device *dev)
148 {
149 /* Read FT800_REG_INT_FLAGS to clear IRQ */
150 (void)ft8xx_rd8(dev, FT800_REG_INT_FLAGS);
151
152 return (int)ft8xx_rd8(dev, FT800_REG_TOUCH_TAG);
153 }
154
ft8xx_drv_irq_triggered(const struct device * gpio_port,struct gpio_callback * cb,uint32_t pins)155 void ft8xx_drv_irq_triggered(const struct device *gpio_port, struct gpio_callback *cb,
156 uint32_t pins)
157 {
158 struct ft8xx_data *ft8xx_data = CONTAINER_OF(cb, struct ft8xx_data, irq_cb_data);
159 const struct device *ft8xx_dev = ft8xx_data->ft8xx_dev;
160
161 if (ft8xx_data->irq_callback != NULL) {
162 ft8xx_data->irq_callback(ft8xx_dev, ft8xx_data->irq_callback_ud);
163 }
164 }
165
ft8xx_register_int(const struct device * dev,ft8xx_int_callback callback,void * user_data)166 void ft8xx_register_int(const struct device *dev, ft8xx_int_callback callback, void *user_data)
167 {
168 struct ft8xx_data *ft8xx_data = dev->data;
169
170 if (ft8xx_data->irq_callback != NULL) {
171 return;
172 }
173
174 ft8xx_data->irq_callback = callback;
175 ft8xx_data->irq_callback_ud = user_data;
176 ft8xx_wr8(dev, FT800_REG_INT_MASK, 0x04);
177 ft8xx_wr8(dev, FT800_REG_INT_EN, 0x01);
178 }
179
ft8xx_calibrate(const struct device * dev,struct ft8xx_touch_transform * data)180 void ft8xx_calibrate(const struct device *dev, struct ft8xx_touch_transform *data)
181 {
182 uint32_t result = 0;
183
184 do {
185 ft8xx_copro_cmd_dlstart(dev);
186 ft8xx_copro_cmd(dev, FT8XX_CLEAR_COLOR_RGB(0x00, 0x00, 0x00));
187 ft8xx_copro_cmd(dev, FT8XX_CLEAR(1, 1, 1));
188 ft8xx_copro_cmd_calibrate(dev, &result);
189 } while (result == 0);
190
191 data->a = ft8xx_rd32(dev, FT800_REG_TOUCH_TRANSFORM_A);
192 data->b = ft8xx_rd32(dev, FT800_REG_TOUCH_TRANSFORM_B);
193 data->c = ft8xx_rd32(dev, FT800_REG_TOUCH_TRANSFORM_C);
194 data->d = ft8xx_rd32(dev, FT800_REG_TOUCH_TRANSFORM_D);
195 data->e = ft8xx_rd32(dev, FT800_REG_TOUCH_TRANSFORM_E);
196 data->f = ft8xx_rd32(dev, FT800_REG_TOUCH_TRANSFORM_F);
197 }
198
ft8xx_touch_transform_set(const struct device * dev,const struct ft8xx_touch_transform * data)199 void ft8xx_touch_transform_set(const struct device *dev, const struct ft8xx_touch_transform *data)
200 {
201 ft8xx_wr32(dev, FT800_REG_TOUCH_TRANSFORM_A, data->a);
202 ft8xx_wr32(dev, FT800_REG_TOUCH_TRANSFORM_B, data->b);
203 ft8xx_wr32(dev, FT800_REG_TOUCH_TRANSFORM_C, data->c);
204 ft8xx_wr32(dev, FT800_REG_TOUCH_TRANSFORM_D, data->d);
205 ft8xx_wr32(dev, FT800_REG_TOUCH_TRANSFORM_E, data->e);
206 ft8xx_wr32(dev, FT800_REG_TOUCH_TRANSFORM_F, data->f);
207 }
208
209 #define FT8XX_DEVICE(idx) \
210 const static struct ft8xx_config ft8xx_##idx##_config = { \
211 .pclk = DT_INST_PROP(idx, pclk), \
212 .pclk_pol = DT_INST_PROP(idx, pclk_pol), \
213 .cspread = DT_INST_PROP(idx, cspread), \
214 .swizzle = DT_INST_PROP(idx, swizzle), \
215 .vsize = DT_INST_PROP(idx, vsize), \
216 .voffset = DT_INST_PROP(idx, voffset), \
217 .vcycle = DT_INST_PROP(idx, vcycle), \
218 .vsync0 = DT_INST_PROP(idx, vsync0), \
219 .vsync1 = DT_INST_PROP(idx, vsync1), \
220 .hsize = DT_INST_PROP(idx, hsize), \
221 .hoffset = DT_INST_PROP(idx, hoffset), \
222 .hcycle = DT_INST_PROP(idx, hcycle), \
223 .hsync0 = DT_INST_PROP(idx, hsync0), \
224 .hsync1 = DT_INST_PROP(idx, hsync1), \
225 }; \
226 static struct ft8xx_data ft8xx_##idx##_data = { \
227 .ft8xx_dev = NULL, \
228 .irq_callback = NULL, \
229 .irq_callback_ud = NULL, \
230 \
231 .spi = SPI_DT_SPEC_INST_GET(idx, SPI_WORD_SET(8) | SPI_OP_MODE_MASTER, 0), \
232 .irq_gpio = GPIO_DT_SPEC_INST_GET(idx, irq_gpios), \
233 }; \
234 DEVICE_DT_INST_DEFINE(idx, ft8xx_init, NULL, &ft8xx_##idx##_data, &ft8xx_##idx##_config, \
235 POST_KERNEL, CONFIG_FT800_INIT_PRIORITY, NULL);
236
237 DT_INST_FOREACH_STATUS_OKAY(FT8XX_DEVICE)
238