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