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