1 /*
2  * Copyright (c) 2024 Shen Xuyang <shenxuyang@shlinyuantech.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #define DT_DRV_COMPAT istech_ist3931
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/pm/device.h>
12 #include <zephyr/drivers/display.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 
16 #define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL
17 LOG_MODULE_REGISTER(ist3931);
18 
19 #include "display_ist3931.h"
20 
21 struct ist3931_config {
22 	struct i2c_dt_spec bus;
23 	struct gpio_dt_spec reset_gpio;
24 	uint16_t height;
25 	uint16_t width;
26 	bool vc;      /* voltage_converter_circuits_enabled */
27 	bool vf;      /* voltage_follower_circuits_enabled */
28 	uint8_t bias; /* 0-7 */
29 	uint8_t ct;   /* 0-255 */
30 	uint8_t duty; /* 1-64 */
31 	uint16_t fr;  /* frame_frequency_division */
32 	bool shl;     /* 0 COM1->COMN 1 COMN->COM1 */
33 	bool adc;     /* 0 seg1->seg132 1 seg132->seg1 */
34 	bool eon;     /* 0 normal 1 Entire ON */
35 	bool rev;     /* 0 RAM1->LCD ON 1 RAM0->LCD ON */
36 	uint8_t x_offset;
37 	uint8_t y_offset;
38 };
39 
ist3931_write_bus(const struct device * dev,uint8_t * buf,bool command,uint32_t num_bytes)40 static int ist3931_write_bus(const struct device *dev, uint8_t *buf, bool command,
41 			     uint32_t num_bytes)
42 {
43 	const struct ist3931_config *config = dev->config;
44 	const uint8_t control_byte = command ? IST3931_CMD_BYTE : IST3931_DATA_BYTE;
45 	uint8_t i2c_write_buf[IST3931_RAM_WIDTH / 4];
46 
47 	for (uint32_t i = 0; i < num_bytes; i++) {
48 		i2c_write_buf[i * 2] = control_byte;
49 		i2c_write_buf[i * 2 + 1] = buf[i];
50 	};
51 	return i2c_write_dt(&config->bus, i2c_write_buf, num_bytes * 2);
52 };
53 
ist3931_bus_ready(const struct device * dev)54 static inline bool ist3931_bus_ready(const struct device *dev)
55 {
56 	const struct ist3931_config *config = dev->config;
57 
58 	return i2c_is_ready_dt(&config->bus);
59 };
60 
ist3931_set_power(const struct device * dev)61 static inline int ist3931_set_power(const struct device *dev)
62 {
63 	const struct ist3931_config *config = dev->config;
64 	uint8_t cmd_buf = IST3931_CMD_POWER_CONTROL | config->vc | config->vf << 1;
65 
66 	return ist3931_write_bus(dev, &cmd_buf, 1, 1);
67 };
68 
ist3931_set_bias(const struct device * dev)69 static inline int ist3931_set_bias(const struct device *dev)
70 {
71 	const struct ist3931_config *config = dev->config;
72 	uint8_t cmd_buf = IST3931_CMD_BIAS | config->bias;
73 
74 	return ist3931_write_bus(dev, &cmd_buf, 1, 1);
75 };
76 
ist3931_set_ct(const struct device * dev)77 static inline int ist3931_set_ct(const struct device *dev)
78 {
79 	const struct ist3931_config *config = dev->config;
80 	uint8_t cmd_buf[2] = {IST3931_CMD_CT, config->ct};
81 
82 	return ist3931_write_bus(dev, cmd_buf, 1, 2);
83 };
84 
ist3931_set_fr(const struct device * dev)85 static inline int ist3931_set_fr(const struct device *dev)
86 {
87 	const struct ist3931_config *config = dev->config;
88 	uint8_t cmd_buf[3] = {IST3931_CMD_FRAME_CONTROL, config->fr & 0x00FF, config->fr >> 8};
89 
90 	return ist3931_write_bus(dev, cmd_buf, 1, 3);
91 };
92 
ist3931_set_duty(const struct device * dev)93 static inline int ist3931_set_duty(const struct device *dev)
94 {
95 	const struct ist3931_config *config = dev->config;
96 	uint8_t cmd_buf[2] = {(IST3931_CMD_SET_DUTY_LSB | (config->duty & 0x0F)),
97 			      (IST3931_CMD_SET_DUTY_MSB | (config->duty >> 4))};
98 
99 	return ist3931_write_bus(dev, cmd_buf, 1, 2);
100 };
101 
ist3931_driver_display_control(const struct device * dev)102 static inline int ist3931_driver_display_control(const struct device *dev)
103 {
104 	const struct ist3931_config *config = dev->config;
105 	uint8_t cmd_buf = IST3931_CMD_DRIVER_DISPLAY_CONTROL | config->shl << 3 | config->adc << 2 |
106 			  config->eon << 1 | config->rev;
107 
108 	return ist3931_write_bus(dev, &cmd_buf, 1, 1);
109 };
110 
ist3931_driver_set_display_on(const struct device * dev)111 static inline int ist3931_driver_set_display_on(const struct device *dev)
112 {
113 	uint8_t cmd_buf = IST3931_CMD_DISPLAY_ON_OFF | 1;
114 
115 	return ist3931_write_bus(dev, &cmd_buf, 1, 1);
116 };
117 
ist3931_driver_sleep_on_off(const struct device * dev,const bool sleep)118 static inline int ist3931_driver_sleep_on_off(const struct device *dev, const bool sleep)
119 {
120 	uint8_t cmd_buf = IST3931_CMD_SLEEP_MODE | sleep;
121 
122 	return ist3931_write_bus(dev, &cmd_buf, 1, 1);
123 };
124 
ist3931_driver_set_com_pad_map(const struct device * dev)125 static inline int ist3931_driver_set_com_pad_map(const struct device *dev)
126 {
127 	uint8_t cmd_buf[5] = {
128 		IST3931_CMD_IST_COMMAND_ENTRY, IST3931_CMD_IST_COMMAND_ENTRY,
129 		IST3931_CMD_IST_COMMAND_ENTRY, IST3931_CMD_IST_COMMAND_ENTRY,
130 		IST3931_CMD_IST_COM_MAPPING,
131 	};
132 	uint8_t cmd_buf2 = IST3931_CMD_EXIT_ENTRY;
133 
134 	ist3931_write_bus(dev, &cmd_buf[0], 1, 5);
135 	k_sleep(K_MSEC(IST3931_CMD_DELAY));
136 	ist3931_write_bus(dev, &cmd_buf2, 1, 1);
137 	return 0;
138 };
139 
ist3931_driver_set_ay(const struct device * dev,uint16_t y)140 static inline int ist3931_driver_set_ay(const struct device *dev, uint16_t y)
141 {
142 	const struct ist3931_config *config = dev->config;
143 	uint8_t cmd_buf1 = IST3931_CMD_SET_AY_ADD_LSB | ((config->y_offset + y) & 0x0F);
144 	uint8_t cmd_buf2 = IST3931_CMD_SET_AY_ADD_MSB | ((config->y_offset + y) >> 4);
145 	uint8_t cmd_buf[2] = {cmd_buf1, cmd_buf2};
146 
147 	return ist3931_write_bus(dev, &cmd_buf[0], 1, 2);
148 };
149 
ist3931_driver_set_ax(const struct device * dev,uint8_t x)150 static inline int ist3931_driver_set_ax(const struct device *dev, uint8_t x)
151 {
152 	const struct ist3931_config *config = dev->config;
153 	uint8_t cmd_buf = IST3931_CMD_SET_AX_ADD | (config->x_offset + x);
154 
155 	return ist3931_write_bus(dev, &cmd_buf, 1, 1);
156 };
157 
ist3931_init_device(const struct device * dev)158 static int ist3931_init_device(const struct device *dev)
159 {
160 	const struct ist3931_config *config = dev->config;
161 
162 	gpio_pin_set_dt(&config->reset_gpio, 1);
163 	k_sleep(K_MSEC(IST3931_RESET_DELAY));
164 	gpio_pin_set_dt(&config->reset_gpio, 0);
165 	k_sleep(K_MSEC(IST3931_RESET_DELAY));
166 	gpio_pin_set_dt(&config->reset_gpio, 1);
167 	k_sleep(K_MSEC(IST3931_RESET_DELAY));
168 	ist3931_set_power(dev);
169 	ist3931_set_bias(dev);
170 	ist3931_set_ct(dev);
171 	ist3931_set_fr(dev);
172 	ist3931_set_duty(dev);
173 	ist3931_driver_display_control(dev);
174 	ist3931_driver_set_display_on(dev);
175 	ist3931_driver_set_com_pad_map(dev);
176 	return 0;
177 }
178 
ist3931_init(const struct device * dev)179 static int ist3931_init(const struct device *dev)
180 {
181 	const struct ist3931_config *config = dev->config;
182 	int ret;
183 
184 	if (ist3931_bus_ready(dev)) {
185 		LOG_ERR("I2C device not ready");
186 		return -ENODEV;
187 	}
188 	LOG_INF("I2C device ready");
189 
190 	if (!gpio_is_ready_dt(&config->reset_gpio)) {
191 		LOG_ERR("Reset GPIO device not ready");
192 		return -ENODEV;
193 	}
194 	ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
195 	if (ret) {
196 		LOG_ERR("Couldn't configure reset pin");
197 		return ret;
198 	}
199 	ret = ist3931_init_device(dev);
200 	if (ret) {
201 		LOG_ERR("Failed to initialize device!");
202 		return ret;
203 	}
204 
205 	return 0;
206 }
207 
ist3931_get_capabilities(const struct device * dev,struct display_capabilities * caps)208 static void ist3931_get_capabilities(const struct device *dev, struct display_capabilities *caps)
209 {
210 	const struct ist3931_config *config = dev->config;
211 
212 	memset(caps, 0, sizeof(struct display_capabilities));
213 	caps->x_resolution = config->width;
214 	caps->y_resolution = config->height;
215 	caps->supported_pixel_formats = PIXEL_FORMAT_MONO10 | PIXEL_FORMAT_MONO01;
216 	caps->current_pixel_format = PIXEL_FORMAT_MONO01;
217 	caps->current_orientation = DISPLAY_ORIENTATION_NORMAL;
218 }
219 
ist3931_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)220 static int ist3931_write(const struct device *dev, const uint16_t x, const uint16_t y,
221 			 const struct display_buffer_descriptor *desc, const void *buf)
222 {
223 	uint8_t *write_data_start = (uint8_t *)buf;
224 	int ret;
225 
226 	ret = ist3931_driver_set_ay(dev, y);
227 	if (ret < 0) {
228 		return ret;
229 	}
230 	ret = ist3931_driver_set_ax(dev, x);
231 	if (ret < 0) {
232 		return ret;
233 	}
234 
235 	__ASSERT(x <= IST3931_RAM_WIDTH, "x is out of range");
236 	__ASSERT(y <= IST3931_RAM_HEIGHT, "y is out of range");
237 	__ASSERT(x + desc->width <= IST3931_RAM_WIDTH, "x+width is out of range");
238 	__ASSERT(y + desc->height <= IST3931_RAM_HEIGHT, "y+height is out of range");
239 
240 	for (uint16_t i = 0; i < desc->height; i++) {
241 		ist3931_driver_set_ay(dev, i + y);
242 		ist3931_driver_set_ax(dev, x);
243 		ist3931_write_bus(dev, write_data_start, 0, desc->width / 8);
244 		write_data_start += desc->width / 8;
245 	}
246 	return 0;
247 }
248 
ist3931_blanking_on(const struct device * dev)249 static inline int ist3931_blanking_on(const struct device *dev)
250 {
251 	return ist3931_driver_sleep_on_off(dev, false);
252 }
253 
ist3931_blanking_off(const struct device * dev)254 static inline int ist3931_blanking_off(const struct device *dev)
255 {
256 	return ist3931_driver_sleep_on_off(dev, true);
257 }
258 
259 static DEVICE_API(display, ist3931_api) = {
260 	.write = ist3931_write,
261 	.get_capabilities = ist3931_get_capabilities,
262 	.blanking_on = ist3931_blanking_on,
263 	.blanking_off = ist3931_blanking_off,
264 };
265 
266 #define IST3931_INIT(inst)                                                                         \
267 	static const struct ist3931_config ist3931_config_##inst = {                               \
268 		.bus = I2C_DT_SPEC_INST_GET(inst),                                                 \
269 		.reset_gpio = GPIO_DT_SPEC_INST_GET(inst, reset_gpios),                            \
270 		.vc = DT_INST_PROP(inst, voltage_converter),                                       \
271 		.vf = DT_INST_PROP(inst, voltage_follower),                                        \
272 		.bias = DT_INST_PROP(inst, lcd_bias),                                              \
273 		.ct = DT_INST_PROP(inst, lcd_ct),                                                  \
274 		.duty = DT_INST_PROP(inst, duty_ratio),                                            \
275 		.fr = DT_INST_PROP(inst, frame_control),                                           \
276 		.shl = DT_INST_PROP(inst, reverse_com_output),                                     \
277 		.adc = DT_INST_PROP(inst, reverse_seg_driver),                                     \
278 		.eon = DT_INST_PROP(inst, e_force_on),                                             \
279 		.rev = DT_INST_PROP(inst, reverse_ram_lcd),                                        \
280 		.width = DT_INST_PROP(inst, width),                                                \
281 		.height = DT_INST_PROP(inst, height),                                              \
282 		.x_offset = DT_INST_PROP(inst, x_offset),                                          \
283 		.y_offset = DT_INST_PROP(inst, y_offset),                                          \
284 	};                                                                                         \
285                                                                                                    \
286 	DEVICE_DT_INST_DEFINE(inst, &ist3931_init, NULL, NULL, &ist3931_config_##inst,             \
287 			      POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, &ist3931_api);
288 
289 DT_INST_FOREACH_STATUS_OKAY(IST3931_INIT)
290