1 /*
2  * Copyright (c) 2020 Rohit Gujarathi
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT   sharp_ls0xx
8 
9 #include <logging/log.h>
10 LOG_MODULE_REGISTER(ls0xx, CONFIG_DISPLAY_LOG_LEVEL);
11 
12 #include <string.h>
13 #include <device.h>
14 #include <drivers/display.h>
15 #include <init.h>
16 #include <drivers/gpio.h>
17 #include <drivers/spi.h>
18 #include <sys/byteorder.h>
19 
20 /* Supports LS012B7DD01, LS012B7DD06, LS013B7DH03, LS013B7DH05
21  * LS013B7DH06, LS027B7DH01A, LS032B7DD02, LS044Q7DH01
22  */
23 
24 /* Note:
25  * -> high/1 means white, low/0 means black
26  * -> Display expects LSB first
27  */
28 
29 #define LS0XX_PANEL_WIDTH   DT_INST_PROP(0, width)
30 #define LS0XX_PANEL_HEIGHT  DT_INST_PROP(0, height)
31 
32 #define LS0XX_PIXELS_PER_BYTE  8U
33 /* Adding 2 for the line number and dummy byte
34  * line_buf format for each row.
35  * +-------------------+-------------------+----------------+
36  * | line num (8 bits) | data (WIDTH bits) | dummy (8 bits) |
37  * +-------------------+-------------------+----------------+
38  */
39 #define LS0XX_BYTES_PER_LINE  ((LS0XX_PANEL_WIDTH / LS0XX_PIXELS_PER_BYTE) + 2)
40 
41 #define LS0XX_BIT_WRITECMD    0x01
42 #define LS0XX_BIT_VCOM        0x02
43 #define LS0XX_BIT_CLEAR       0x04
44 
45 struct ls0xx_data {
46 #if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
47 	const struct device *disp_dev;
48 #endif
49 #if DT_INST_NODE_HAS_PROP(0, extcomin_gpios)
50 	const struct device *extcomin_dev;
51 #endif
52 };
53 
54 struct ls0xx_config {
55 	struct spi_dt_spec bus;
56 };
57 
58 #if DT_INST_NODE_HAS_PROP(0, extcomin_gpios)
59 /* Driver will handle VCOM toggling */
ls0xx_vcom_toggle(void * a,void * b,void * c)60 static void ls0xx_vcom_toggle(void *a, void *b, void *c)
61 {
62 	struct ls0xx_data *driver = (struct ls0xx_data *)a;
63 
64 	while (1) {
65 		gpio_pin_toggle(driver->extcomin_dev,
66 				DT_INST_GPIO_PIN(0, extcomin_gpios));
67 		k_usleep(3);
68 		gpio_pin_toggle(driver->extcomin_dev,
69 				DT_INST_GPIO_PIN(0, extcomin_gpios));
70 		k_msleep(1000 / DT_INST_PROP(0, extcomin_frequency));
71 	}
72 }
73 
74 K_THREAD_STACK_DEFINE(vcom_toggle_stack, 256);
75 struct k_thread vcom_toggle_thread;
76 #endif
77 
ls0xx_blanking_off(const struct device * dev)78 static int ls0xx_blanking_off(const struct device *dev)
79 {
80 #if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
81 	struct ls0xx_data *driver = dev->data;
82 
83 	return gpio_pin_set(driver->disp_dev,
84 			    DT_INST_GPIO_PIN(0, disp_en_gpios), 1);
85 #else
86 	LOG_WRN("Unsupported");
87 	return -ENOTSUP;
88 #endif
89 }
90 
ls0xx_blanking_on(const struct device * dev)91 static int ls0xx_blanking_on(const struct device *dev)
92 {
93 #if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
94 	struct ls0xx_data *driver = dev->data;
95 
96 	return gpio_pin_set(driver->disp_dev,
97 			    DT_INST_GPIO_PIN(0, disp_en_gpios), 0);
98 #else
99 	LOG_WRN("Unsupported");
100 	return -ENOTSUP;
101 #endif
102 }
103 
ls0xx_cmd(const struct device * dev,uint8_t * buf,uint8_t len)104 static int ls0xx_cmd(const struct device *dev, uint8_t *buf, uint8_t len)
105 {
106 	const struct ls0xx_config *config = dev->config;
107 	struct spi_buf cmd_buf = { .buf = buf, .len = len };
108 	struct spi_buf_set buf_set = { .buffers = &cmd_buf, .count = 1 };
109 
110 	return spi_write_dt(&config->bus, &buf_set);
111 }
112 
ls0xx_clear(const struct device * dev)113 static int ls0xx_clear(const struct device *dev)
114 {
115 	const struct ls0xx_config *config = dev->config;
116 	uint8_t clear_cmd[2] = { LS0XX_BIT_CLEAR, 0 };
117 	int err;
118 
119 	err = ls0xx_cmd(dev, clear_cmd, sizeof(clear_cmd));
120 	spi_release_dt(&config->bus);
121 
122 	return err;
123 }
124 
ls0xx_update_display(const struct device * dev,uint16_t start_line,uint16_t num_lines,const uint8_t * data)125 static int ls0xx_update_display(const struct device *dev,
126 				uint16_t start_line,
127 				uint16_t num_lines,
128 				const uint8_t *data)
129 {
130 	const struct ls0xx_config *config = dev->config;
131 	uint8_t write_cmd[1] = { LS0XX_BIT_WRITECMD };
132 	uint8_t ln = start_line;
133 	uint8_t dummy = 27;
134 	struct spi_buf line_buf[3] = {
135 		{
136 			.len = sizeof(ln),
137 			.buf = &ln,
138 		},
139 		{
140 			.len = LS0XX_BYTES_PER_LINE - 2,
141 		},
142 		{
143 			.len = sizeof(dummy),
144 			.buf = &dummy,
145 		},
146 	};
147 	struct spi_buf_set line_set = {
148 		.buffers = line_buf,
149 		.count = ARRAY_SIZE(line_buf),
150 	};
151 	int err;
152 
153 	LOG_DBG("Lines %d to %d", start_line, start_line + num_lines - 1);
154 	err = ls0xx_cmd(dev, write_cmd, sizeof(write_cmd));
155 
156 	/* Send each line to the screen including
157 	 * the line number and dummy bits
158 	 */
159 	for (; ln <= start_line + num_lines - 1; ln++) {
160 		line_buf[1].buf = (uint8_t *)data;
161 		err |= spi_write_dt(&config->bus, &line_set);
162 		data += LS0XX_PANEL_WIDTH / LS0XX_PIXELS_PER_BYTE;
163 	}
164 
165 	/* Send another trailing 8 bits for the last line
166 	 * These can be any bits, it does not matter
167 	 * just reusing the write_cmd buffer
168 	 */
169 	err |= ls0xx_cmd(dev, write_cmd, sizeof(write_cmd));
170 
171 	spi_release_dt(&config->bus);
172 
173 	return err;
174 }
175 
176 /* Buffer width should be equal to display width */
ls0xx_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)177 static int ls0xx_write(const struct device *dev, const uint16_t x,
178 		       const uint16_t y,
179 		       const struct display_buffer_descriptor *desc,
180 		       const void *buf)
181 {
182 	LOG_DBG("X: %d, Y: %d, W: %d, H: %d", x, y, desc->width, desc->height);
183 
184 	if (buf == NULL) {
185 		LOG_WRN("Display buffer is not available");
186 		return -EINVAL;
187 	}
188 
189 	if (desc->width != LS0XX_PANEL_WIDTH) {
190 		LOG_ERR("Width not a multiple of %d", LS0XX_PANEL_WIDTH);
191 		return -EINVAL;
192 	}
193 
194 	if (desc->pitch != desc->width) {
195 		LOG_ERR("Unsupported mode");
196 		return -ENOTSUP;
197 	}
198 
199 	if ((y + desc->height) > LS0XX_PANEL_HEIGHT) {
200 		LOG_ERR("Buffer out of bounds (height)");
201 		return -EINVAL;
202 	}
203 
204 	if (x != 0) {
205 		LOG_ERR("X-coordinate has to be 0");
206 		return -EINVAL;
207 	}
208 
209 	/* Adding 1 since line numbering on the display starts with 1 */
210 	return ls0xx_update_display(dev, y + 1, desc->height, buf);
211 }
212 
ls0xx_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)213 static int ls0xx_read(const struct device *dev, const uint16_t x,
214 		      const uint16_t y,
215 		      const struct display_buffer_descriptor *desc,
216 		      void *buf)
217 {
218 	LOG_ERR("not supported");
219 	return -ENOTSUP;
220 }
221 
ls0xx_get_framebuffer(const struct device * dev)222 static void *ls0xx_get_framebuffer(const struct device *dev)
223 {
224 	LOG_ERR("not supported");
225 	return NULL;
226 }
227 
ls0xx_set_brightness(const struct device * dev,const uint8_t brightness)228 static int ls0xx_set_brightness(const struct device *dev,
229 				const uint8_t brightness)
230 {
231 	LOG_WRN("not supported");
232 	return -ENOTSUP;
233 }
234 
ls0xx_set_contrast(const struct device * dev,uint8_t contrast)235 static int ls0xx_set_contrast(const struct device *dev, uint8_t contrast)
236 {
237 	LOG_WRN("not supported");
238 	return -ENOTSUP;
239 }
240 
ls0xx_get_capabilities(const struct device * dev,struct display_capabilities * caps)241 static void ls0xx_get_capabilities(const struct device *dev,
242 				   struct display_capabilities *caps)
243 {
244 	memset(caps, 0, sizeof(struct display_capabilities));
245 	caps->x_resolution = LS0XX_PANEL_WIDTH;
246 	caps->y_resolution = LS0XX_PANEL_HEIGHT;
247 	caps->supported_pixel_formats = PIXEL_FORMAT_MONO01;
248 	caps->current_pixel_format = PIXEL_FORMAT_MONO01;
249 	caps->screen_info = SCREEN_INFO_X_ALIGNMENT_WIDTH;
250 }
251 
ls0xx_set_orientation(const struct device * dev,const enum display_orientation orientation)252 static int ls0xx_set_orientation(const struct device *dev,
253 				 const enum display_orientation orientation)
254 {
255 	LOG_ERR("Unsupported");
256 	return -ENOTSUP;
257 }
258 
ls0xx_set_pixel_format(const struct device * dev,const enum display_pixel_format pf)259 static int ls0xx_set_pixel_format(const struct device *dev,
260 				  const enum display_pixel_format pf)
261 {
262 	if (pf == PIXEL_FORMAT_MONO01) {
263 		return 0;
264 	}
265 
266 	LOG_ERR("not supported");
267 	return -ENOTSUP;
268 }
269 
ls0xx_init(const struct device * dev)270 static int ls0xx_init(const struct device *dev)
271 {
272 	const struct ls0xx_config *config = dev->config;
273 	struct ls0xx_data *driver = dev->data;
274 
275 	if (!spi_is_ready(&config->bus)) {
276 		LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
277 		return -ENODEV;
278 	}
279 
280 #if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
281 	driver->disp_dev = device_get_binding(
282 		DT_INST_GPIO_LABEL(0, disp_en_gpios));
283 	if (driver->disp_dev == NULL) {
284 		LOG_ERR("Could not get DISP pin port for LS0XX");
285 		return -EIO;
286 	}
287 	LOG_INF("Configuring DISP pin to OUTPUT_HIGH");
288 	gpio_pin_configure(driver->disp_dev,
289 			   DT_INST_GPIO_PIN(0, disp_en_gpios),
290 			   GPIO_OUTPUT_HIGH);
291 #endif
292 
293 #if DT_INST_NODE_HAS_PROP(0, extcomin_gpios)
294 	driver->extcomin_dev = device_get_binding(
295 		DT_INST_GPIO_LABEL(0, extcomin_gpios));
296 	if (driver->extcomin_dev == NULL) {
297 		LOG_ERR("Could not get EXTCOMIN pin port for LS0XX");
298 		return -EIO;
299 	}
300 	LOG_INF("Configuring EXTCOMIN pin");
301 	gpio_pin_configure(driver->extcomin_dev,
302 			   DT_INST_GPIO_PIN(0, extcomin_gpios),
303 			   GPIO_OUTPUT_LOW);
304 
305 	/* Start thread for toggling VCOM */
306 	k_tid_t vcom_toggle_tid = k_thread_create(&vcom_toggle_thread,
307 						  vcom_toggle_stack,
308 						  K_THREAD_STACK_SIZEOF(vcom_toggle_stack),
309 						  ls0xx_vcom_toggle,
310 						  driver, NULL, NULL,
311 						  3, 0, K_NO_WAIT);
312 	k_thread_name_set(vcom_toggle_tid, "ls0xx_vcom");
313 #endif  /* DT_INST_NODE_HAS_PROP(0, extcomin_gpios) */
314 
315 	/* Clear display else it shows random data */
316 	return ls0xx_clear(dev);
317 }
318 
319 static struct ls0xx_data ls0xx_driver;
320 
321 static const struct ls0xx_config ls0xx_config = {
322 	.bus = SPI_DT_SPEC_INST_GET(
323 		0, SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |
324 		SPI_TRANSFER_LSB | SPI_CS_ACTIVE_HIGH |
325 		SPI_HOLD_ON_CS | SPI_LOCK_ON, 0)
326 };
327 
328 static struct display_driver_api ls0xx_driver_api = {
329 	.blanking_on = ls0xx_blanking_on,
330 	.blanking_off = ls0xx_blanking_off,
331 	.write = ls0xx_write,
332 	.read = ls0xx_read,
333 	.get_framebuffer = ls0xx_get_framebuffer,
334 	.set_brightness = ls0xx_set_brightness,
335 	.set_contrast = ls0xx_set_contrast,
336 	.get_capabilities = ls0xx_get_capabilities,
337 	.set_pixel_format = ls0xx_set_pixel_format,
338 	.set_orientation = ls0xx_set_orientation,
339 };
340 
341 DEVICE_DT_INST_DEFINE(0, ls0xx_init, NULL,
342 		      &ls0xx_driver, &ls0xx_config,
343 		      POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY,
344 		      &ls0xx_driver_api);
345