1 /* $OpenBSD: ssdfb.c,v 1.14 2024/05/13 01:15:50 jsg Exp $ */
2 /*
3 * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/kernel.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/stdint.h>
24
25 #include <uvm/uvm_extern.h>
26
27 #include <dev/i2c/i2cvar.h>
28 #include <dev/spi/spivar.h>
29 #include <dev/usb/udlio.h>
30
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/ofw_gpio.h>
33 #include <dev/ofw/ofw_pinctrl.h>
34
35 #include <dev/wscons/wsconsio.h>
36 #include <dev/wscons/wsdisplayvar.h>
37 #include <dev/rasops/rasops.h>
38
39 #define SSDFB_SET_LOWER_COLUMN_START_ADDRESS 0x00
40 #define SSDFB_SET_HIGHER_COLUMN_START_ADDRESS 0x10
41 #define SSDFB_SET_MEMORY_ADDRESSING_MODE 0x20
42 #define SSDFB_SET_COLUMN_RANGE 0x21
43 #define SSDFB_SET_PAGE_RANGE 0x22
44 #define SSDFB_SET_START_LINE 0x40
45 #define SSDFB_SET_CONTRAST_CONTROL 0x81
46 #define SSDFB_CHARGE_PUMP 0x8d
47 #define SSDFB_SET_COLUMN_DIRECTION_NORMAL 0xa0
48 #define SSDFB_SET_COLUMN_DIRECTION_REVERSE 0xa1
49 #define SSDFB_SET_MULTIPLEX_RATIO 0xa8
50 #define SSDFB_SET_COM_OUTPUT_DIRECTION_NORMAL 0xc0
51 #define SSDFB_SET_COM_OUTPUT_DIRECTION_REMAP 0xc8
52 #define SSDFB_ENTIRE_DISPLAY_ON 0xa4
53 #define SSDFB_SET_DISPLAY_MODE_NORMAL 0xa6
54 #define SSDFB_SET_DISPLAY_MODE_INVERS 0xa7
55 #define SSDFB_SET_DISPLAY_OFF 0xae
56 #define SSDFB_SET_DISPLAY_ON 0xaf
57 #define SSDFB_SET_DISPLAY_OFFSET 0xd3
58 #define SSDFB_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xd5
59 #define SSDFB_SET_PRE_CHARGE_PERIOD 0xd9
60 #define SSDFB_SET_COM_PINS_HARD_CONF 0xda
61 #define SSDFB_SET_VCOM_DESELECT_LEVEL 0xdb
62 #define SSDFB_SET_PAGE_START_ADDRESS 0xb0
63
64 #define SSDFB_I2C_COMMAND 0x00
65 #define SSDFB_I2C_DATA 0x40
66
67 struct ssdfb_softc {
68 struct device sc_dev;
69 int sc_node;
70 int sc_width;
71 int sc_height;
72 int sc_pgoff;
73
74 uint8_t *sc_fb;
75 size_t sc_fbsize;
76 struct rasops_info sc_rinfo;
77 struct wsdisplay_emulops sc_riops;
78 int (*sc_ri_do_cursor)(struct rasops_info *);
79
80 uint8_t sc_brightness;
81 int sc_mode;
82
83 uint8_t sc_column_range[2];
84 uint8_t sc_page_range[2];
85
86 /* I2C */
87 i2c_tag_t sc_i2c_tag;
88 i2c_addr_t sc_i2c_addr;
89
90 /* SPI */
91 spi_tag_t sc_spi_tag;
92 struct spi_config sc_spi_conf;
93 uint32_t *sc_gpio;
94 size_t sc_gpiolen;
95 int sc_cd;
96
97 void (*sc_write_command)(struct ssdfb_softc *,
98 char *, size_t);
99 void (*sc_write_data)(struct ssdfb_softc *,
100 char *, size_t);
101
102 };
103
104 int ssdfb_i2c_match(struct device *, void *, void *);
105 void ssdfb_i2c_attach(struct device *, struct device *, void *);
106 int ssdfb_i2c_detach(struct device *, int);
107 void ssdfb_i2c_write_command(struct ssdfb_softc *, char *, size_t);
108 void ssdfb_i2c_write_data(struct ssdfb_softc *, char *, size_t);
109
110 int ssdfb_spi_match(struct device *, void *, void *);
111 void ssdfb_spi_attach(struct device *, struct device *, void *);
112 int ssdfb_spi_detach(struct device *, int);
113 void ssdfb_spi_write_command(struct ssdfb_softc *, char *, size_t);
114 void ssdfb_spi_write_data(struct ssdfb_softc *, char *, size_t);
115
116 void ssdfb_attach(struct ssdfb_softc *);
117 int ssdfb_detach(struct ssdfb_softc *, int);
118 void ssdfb_write_command(struct ssdfb_softc *, char *, size_t);
119 void ssdfb_write_data(struct ssdfb_softc *, char *, size_t);
120
121 void ssdfb_init(struct ssdfb_softc *);
122
123 void ssdfb_partial(struct ssdfb_softc *, uint32_t, uint32_t,
124 uint32_t, uint32_t);
125 void ssdfb_set_range(struct ssdfb_softc *, uint8_t, uint8_t,
126 uint8_t, uint8_t);
127
128 int ssdfb_ioctl(void *, u_long, caddr_t, int, struct proc *);
129 paddr_t ssdfb_mmap(void *, off_t, int);
130 int ssdfb_alloc_screen(void *, const struct wsscreen_descr *, void **,
131 int *, int *, uint32_t *);
132 void ssdfb_free_screen(void *, void *);
133 int ssdfb_show_screen(void *, void *, int, void (*cb) (void *, int, int),
134 void *);
135 int ssdfb_list_font(void *, struct wsdisplay_font *);
136 int ssdfb_load_font(void *, void *, struct wsdisplay_font *);
137
138 int ssdfb_putchar(void *, int, int, u_int, uint32_t);
139 int ssdfb_copycols(void *, int, int, int, int);
140 int ssdfb_erasecols(void *, int, int, int, uint32_t);
141 int ssdfb_copyrows(void *, int, int, int);
142 int ssdfb_eraserows(void *, int, int, uint32_t);
143 int ssdfb_do_cursor(struct rasops_info *);
144
145 const struct cfattach ssdfb_i2c_ca = {
146 sizeof(struct ssdfb_softc),
147 ssdfb_i2c_match,
148 ssdfb_i2c_attach,
149 ssdfb_i2c_detach,
150 };
151
152 const struct cfattach ssdfb_spi_ca = {
153 sizeof(struct ssdfb_softc),
154 ssdfb_spi_match,
155 ssdfb_spi_attach,
156 ssdfb_spi_detach,
157 };
158
159 struct cfdriver ssdfb_cd = {
160 NULL, "ssdfb", DV_DULL
161 };
162
163 struct wsscreen_descr ssdfb_std_descr = { "std" };
164
165 const struct wsscreen_descr *ssdfb_descrs[] = {
166 &ssdfb_std_descr
167 };
168
169 const struct wsscreen_list ssdfb_screen_list = {
170 nitems(ssdfb_descrs), ssdfb_descrs
171 };
172
173 struct wsdisplay_accessops ssdfb_accessops = {
174 .ioctl = ssdfb_ioctl,
175 .mmap = ssdfb_mmap,
176 .alloc_screen = ssdfb_alloc_screen,
177 .free_screen = ssdfb_free_screen,
178 .show_screen = ssdfb_show_screen,
179 .load_font = ssdfb_load_font,
180 .list_font = ssdfb_list_font
181 };
182
183 int
ssdfb_i2c_match(struct device * parent,void * match,void * aux)184 ssdfb_i2c_match(struct device *parent, void *match, void *aux)
185 {
186 struct i2c_attach_args *ia = aux;
187
188 if (strcmp(ia->ia_name, "solomon,ssd1306fb-i2c") == 0 ||
189 strcmp(ia->ia_name, "solomon,ssd1309fb-i2c") == 0)
190 return 1;
191
192 return 0;
193 }
194
195 void
ssdfb_i2c_attach(struct device * parent,struct device * self,void * aux)196 ssdfb_i2c_attach(struct device *parent, struct device *self, void *aux)
197 {
198 struct ssdfb_softc *sc = (struct ssdfb_softc *)self;
199 struct i2c_attach_args *ia = aux;
200
201 sc->sc_i2c_tag = ia->ia_tag;
202 sc->sc_i2c_addr = ia->ia_addr;
203 sc->sc_node = *(int *)ia->ia_cookie;
204
205 sc->sc_write_command = ssdfb_i2c_write_command;
206 sc->sc_write_data = ssdfb_i2c_write_data;
207
208 ssdfb_attach(sc);
209 }
210
211 int
ssdfb_i2c_detach(struct device * self,int flags)212 ssdfb_i2c_detach(struct device *self, int flags)
213 {
214 struct ssdfb_softc *sc = (struct ssdfb_softc *)self;
215 ssdfb_detach(sc, flags);
216 return 0;
217 }
218
219 int
ssdfb_spi_match(struct device * parent,void * match,void * aux)220 ssdfb_spi_match(struct device *parent, void *match, void *aux)
221 {
222 struct spi_attach_args *sa = aux;
223
224 if (strcmp(sa->sa_name, "solomon,ssd1309fb-spi") == 0)
225 return 1;
226
227 return 0;
228 }
229
230 void
ssdfb_spi_attach(struct device * parent,struct device * self,void * aux)231 ssdfb_spi_attach(struct device *parent, struct device *self, void *aux)
232 {
233 struct ssdfb_softc *sc = (struct ssdfb_softc *)self;
234 struct spi_attach_args *sa = aux;
235 ssize_t len;
236
237 sc->sc_spi_tag = sa->sa_tag;
238 sc->sc_node = *(int *)sa->sa_cookie;
239
240 sc->sc_spi_conf.sc_bpw = 8;
241 sc->sc_spi_conf.sc_freq = 1000 * 1000;
242 sc->sc_spi_conf.sc_cs = OF_getpropint(sc->sc_node, "reg", 0);
243 if (OF_getproplen(sc->sc_node, "spi-cpol") == 0)
244 sc->sc_spi_conf.sc_flags |= SPI_CONFIG_CPOL;
245 if (OF_getproplen(sc->sc_node, "spi-cpha") == 0)
246 sc->sc_spi_conf.sc_flags |= SPI_CONFIG_CPHA;
247 if (OF_getproplen(sc->sc_node, "spi-cs-high") == 0)
248 sc->sc_spi_conf.sc_flags |= SPI_CONFIG_CS_HIGH;
249
250 len = OF_getproplen(sc->sc_node, "cd-gpio");
251 if (len <= 0)
252 return;
253
254 sc->sc_gpio = malloc(len, M_DEVBUF, M_WAITOK);
255 OF_getpropintarray(sc->sc_node, "cd-gpio", sc->sc_gpio, len);
256 sc->sc_gpiolen = len;
257 gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_OUTPUT);
258 gpio_controller_set_pin(sc->sc_gpio, 0);
259
260 sc->sc_write_command = ssdfb_spi_write_command;
261 sc->sc_write_data = ssdfb_spi_write_data;
262
263 ssdfb_attach(sc);
264 }
265
266 int
ssdfb_spi_detach(struct device * self,int flags)267 ssdfb_spi_detach(struct device *self, int flags)
268 {
269 struct ssdfb_softc *sc = (struct ssdfb_softc *)self;
270 ssdfb_detach(sc, flags);
271 free(sc->sc_gpio, M_DEVBUF, sc->sc_gpiolen);
272 return 0;
273 }
274
275 void
ssdfb_attach(struct ssdfb_softc * sc)276 ssdfb_attach(struct ssdfb_softc *sc)
277 {
278 struct wsemuldisplaydev_attach_args aa;
279 struct rasops_info *ri;
280 uint32_t *gpio;
281 ssize_t len;
282
283 pinctrl_byname(sc->sc_node, "default");
284
285 len = OF_getproplen(sc->sc_node, "reset-gpios");
286 if (len > 0) {
287 gpio = malloc(len, M_DEVBUF, M_WAITOK);
288 OF_getpropintarray(sc->sc_node, "reset-gpios",
289 gpio, len);
290 gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
291 gpio_controller_set_pin(gpio, 1);
292 delay(100 * 1000);
293 gpio_controller_set_pin(gpio, 0);
294 delay(1000 * 1000);
295 free(gpio, M_DEVBUF, len);
296 }
297
298 sc->sc_width = OF_getpropint(sc->sc_node, "solomon,width", 96);
299 sc->sc_height = OF_getpropint(sc->sc_node, "solomon,height", 16);
300 sc->sc_pgoff = OF_getpropint(sc->sc_node, "solomon,page-offset", 1);
301
302 sc->sc_fbsize = round_page((sc->sc_width * sc->sc_height) / 8);
303 sc->sc_fb = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK | M_ZERO);
304
305 sc->sc_brightness = 223;
306
307 ri = &sc->sc_rinfo;
308 ri->ri_bits = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK | M_ZERO);
309 ri->ri_bs = mallocarray(sc->sc_width * sc->sc_height,
310 sizeof(struct wsdisplay_charcell), M_DEVBUF, M_WAITOK | M_ZERO);
311 ri->ri_flg = RI_CLEAR | RI_VCONS;
312 ri->ri_depth = 1;
313 ri->ri_width = sc->sc_width;
314 ri->ri_height = sc->sc_height;
315 ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
316 ri->ri_hw = sc;
317
318 rasops_init(ri, sc->sc_height, sc->sc_width);
319 ssdfb_std_descr.ncols = ri->ri_cols;
320 ssdfb_std_descr.nrows = ri->ri_rows;
321 ssdfb_std_descr.textops = &ri->ri_ops;
322 ssdfb_std_descr.fontwidth = ri->ri_font->fontwidth;
323 ssdfb_std_descr.fontheight = ri->ri_font->fontheight;
324 ssdfb_std_descr.capabilities = ri->ri_caps;
325
326 sc->sc_riops.putchar = ri->ri_putchar;
327 sc->sc_riops.copycols = ri->ri_copycols;
328 sc->sc_riops.erasecols = ri->ri_erasecols;
329 sc->sc_riops.copyrows = ri->ri_copyrows;
330 sc->sc_riops.eraserows = ri->ri_eraserows;
331 sc->sc_ri_do_cursor = ri->ri_do_cursor;
332
333 ri->ri_putchar = ssdfb_putchar;
334 ri->ri_copycols = ssdfb_copycols;
335 ri->ri_erasecols = ssdfb_erasecols;
336 ri->ri_copyrows = ssdfb_copyrows;
337 ri->ri_eraserows = ssdfb_eraserows;
338 ri->ri_do_cursor = ssdfb_do_cursor;
339
340 printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
341
342 memset(&aa, 0, sizeof(aa));
343 aa.console = 0;
344 aa.scrdata = &ssdfb_screen_list;
345 aa.accessops = &ssdfb_accessops;
346 aa.accesscookie = sc;
347 aa.defaultscreens = 0;
348
349 config_found_sm(&sc->sc_dev, &aa, wsemuldisplaydevprint,
350 wsemuldisplaydevsubmatch);
351 ssdfb_init(sc);
352 }
353
354 int
ssdfb_detach(struct ssdfb_softc * sc,int flags)355 ssdfb_detach(struct ssdfb_softc *sc, int flags)
356 {
357 struct rasops_info *ri = &sc->sc_rinfo;
358 free(ri->ri_bs, M_DEVBUF, sc->sc_width * sc->sc_height *
359 sizeof(struct wsdisplay_charcell));
360 free(ri->ri_bits, M_DEVBUF, sc->sc_fbsize);
361 free(sc->sc_fb, M_DEVBUF, sc->sc_fbsize);
362 return 0;
363 }
364
365 void
ssdfb_init(struct ssdfb_softc * sc)366 ssdfb_init(struct ssdfb_softc *sc)
367 {
368 uint8_t reg[2];
369
370 reg[0] = SSDFB_SET_DISPLAY_OFF;
371 ssdfb_write_command(sc, reg, 1);
372
373 reg[0] = SSDFB_SET_MEMORY_ADDRESSING_MODE;
374 reg[1] = 0x00; /* Horizontal Addressing Mode */
375 ssdfb_write_command(sc, reg, 2);
376 reg[0] = SSDFB_SET_PAGE_START_ADDRESS;
377 ssdfb_write_command(sc, reg, 1);
378 ssdfb_set_range(sc, 0, sc->sc_width - 1,
379 0, (sc->sc_height / 8) - 1);
380 if (OF_is_compatible(sc->sc_node, "solomon,ssd1309fb-i2c") ||
381 OF_is_compatible(sc->sc_node, "solomon,ssd1309fb-spi")) {
382 reg[0] = SSDFB_SET_DISPLAY_CLOCK_DIVIDE_RATIO;
383 reg[1] = 0xa0;
384 ssdfb_write_command(sc, reg, 2);
385 }
386 if (OF_is_compatible(sc->sc_node, "solomon,ssd1306fb-i2c")) {
387 reg[0] = SSDFB_SET_DISPLAY_CLOCK_DIVIDE_RATIO;
388 reg[1] = 0x80;
389 ssdfb_write_command(sc, reg, 2);
390 }
391 reg[0] = SSDFB_SET_MULTIPLEX_RATIO;
392 reg[1] = 0x3f;
393 ssdfb_write_command(sc, reg, 2);
394 reg[0] = SSDFB_SET_DISPLAY_OFFSET;
395 reg[1] = OF_getpropint(sc->sc_node, "solomon,com-offset", 0);
396 ssdfb_write_command(sc, reg, 2);
397 reg[0] = SSDFB_SET_START_LINE | 0x00;
398 ssdfb_write_command(sc, reg, 1);
399 reg[0] = SSDFB_SET_COLUMN_DIRECTION_NORMAL;
400 if (OF_getproplen(sc->sc_node, "solomon,com-invdir") == 0)
401 reg[0] = SSDFB_SET_COLUMN_DIRECTION_REVERSE;
402 ssdfb_write_command(sc, reg, 1);
403 reg[0] = SSDFB_SET_COM_OUTPUT_DIRECTION_REMAP;
404 if (OF_getproplen(sc->sc_node, "solomon,segment-no-remap") == 0)
405 reg[0] = SSDFB_SET_COM_OUTPUT_DIRECTION_NORMAL;
406 ssdfb_write_command(sc, reg, 1);
407 reg[0] = SSDFB_SET_COM_PINS_HARD_CONF;
408 reg[1] = 0x12;
409 if (OF_getproplen(sc->sc_node, "solomon,com-seq") == 0)
410 reg[1] &= ~(1 << 4);
411 if (OF_getproplen(sc->sc_node, "solomon,com-lrremap") == 0)
412 reg[1] |= 1 << 5;
413 ssdfb_write_command(sc, reg, 2);
414 reg[0] = SSDFB_SET_CONTRAST_CONTROL;
415 reg[1] = sc->sc_brightness;
416 ssdfb_write_command(sc, reg, 2);
417 reg[0] = SSDFB_SET_PRE_CHARGE_PERIOD;
418 reg[1] = (OF_getpropint(sc->sc_node, "solomon,prechargep1", 2) & 0xf) << 0;
419 reg[1] |= (OF_getpropint(sc->sc_node, "solomon,prechargep2", 2) & 0xf) << 4;
420 ssdfb_write_command(sc, reg, 2);
421 if (OF_is_compatible(sc->sc_node, "solomon,ssd1309fb-i2c") ||
422 OF_is_compatible(sc->sc_node, "solomon,ssd1309fb-spi")) {
423 reg[0] = SSDFB_SET_VCOM_DESELECT_LEVEL;
424 reg[1] = 0x34;
425 ssdfb_write_command(sc, reg, 2);
426 }
427 if (OF_is_compatible(sc->sc_node, "solomon,ssd1306fb-i2c")) {
428 reg[0] = SSDFB_SET_VCOM_DESELECT_LEVEL;
429 reg[1] = 0x20;
430 ssdfb_write_command(sc, reg, 2);
431 }
432 reg[0] = SSDFB_CHARGE_PUMP;
433 reg[1] = 0x10;
434 if (OF_is_compatible(sc->sc_node, "solomon,ssd1306fb-i2c"))
435 reg[1] |= 1 << 2;
436 ssdfb_write_command(sc, reg, 2);
437 reg[0] = SSDFB_ENTIRE_DISPLAY_ON;
438 ssdfb_write_command(sc, reg, 1);
439 reg[0] = SSDFB_SET_DISPLAY_MODE_NORMAL;
440 ssdfb_write_command(sc, reg, 1);
441
442 ssdfb_partial(sc, 0, sc->sc_width, 0, sc->sc_height);
443
444 reg[0] = SSDFB_SET_DISPLAY_ON;
445 ssdfb_write_command(sc, reg, 1);
446 }
447
448 void
ssdfb_set_range(struct ssdfb_softc * sc,uint8_t x1,uint8_t x2,uint8_t y1,uint8_t y2)449 ssdfb_set_range(struct ssdfb_softc *sc, uint8_t x1, uint8_t x2,
450 uint8_t y1, uint8_t y2)
451 {
452 uint8_t reg[3];
453
454 y1 += sc->sc_pgoff;
455 y2 += sc->sc_pgoff;
456
457 if (sc->sc_column_range[0] != x1 || sc->sc_column_range[1] != x2) {
458 sc->sc_column_range[0] = x1;
459 sc->sc_column_range[1] = x2;
460 reg[0] = SSDFB_SET_COLUMN_RANGE;
461 reg[1] = sc->sc_column_range[0];
462 reg[2] = sc->sc_column_range[1];
463 ssdfb_write_command(sc, reg, 3);
464 }
465 if (sc->sc_page_range[0] != y1 || sc->sc_page_range[1] != y2) {
466 sc->sc_page_range[0] = y1;
467 sc->sc_page_range[1] = y2;
468 reg[0] = SSDFB_SET_PAGE_RANGE;
469 reg[1] = sc->sc_page_range[0];
470 reg[2] = sc->sc_page_range[1];
471 ssdfb_write_command(sc, reg, 3);
472 }
473 }
474
475 void
ssdfb_partial(struct ssdfb_softc * sc,uint32_t x1,uint32_t x2,uint32_t y1,uint32_t y2)476 ssdfb_partial(struct ssdfb_softc *sc, uint32_t x1, uint32_t x2,
477 uint32_t y1, uint32_t y2)
478 {
479 struct rasops_info *ri = &sc->sc_rinfo;
480 uint32_t off, width, height;
481 uint8_t *bit, val;
482 int i, j, k;
483
484 if (x2 < x1 || y2 < y1)
485 return;
486
487 if (x2 > sc->sc_width || y2 > sc->sc_height)
488 return;
489
490 y1 = y1 & ~0x7;
491 y2 = roundup(y2, 8);
492
493 width = x2 - x1;
494 height = y2 - y1;
495
496 memset(sc->sc_fb, 0, (width * height) / 8);
497
498 for (i = 0; i < height; i += 8) {
499 for (j = 0; j < width; j++) {
500 bit = &sc->sc_fb[(i / 8) * width + j];
501 for (k = 0; k < 8; k++) {
502 off = ri->ri_stride * (y1 + i + k);
503 off += (x1 + j) / 8;
504 val = *(ri->ri_bits + off);
505 val &= (1 << ((x1 + j) % 8));
506 *bit |= !!val << k;
507 }
508 }
509 }
510
511 ssdfb_set_range(sc, x1, x2 - 1, y1 / 8, (y2 / 8) - 1);
512 ssdfb_write_data(sc, sc->sc_fb, (width * height) / 8);
513 }
514
515 void
ssdfb_write_command(struct ssdfb_softc * sc,char * buf,size_t len)516 ssdfb_write_command(struct ssdfb_softc *sc, char *buf, size_t len)
517 {
518 return sc->sc_write_command(sc, buf, len);
519 }
520
521 void
ssdfb_write_data(struct ssdfb_softc * sc,char * buf,size_t len)522 ssdfb_write_data(struct ssdfb_softc *sc, char *buf, size_t len)
523 {
524 return sc->sc_write_data(sc, buf, len);
525 }
526
527 void
ssdfb_i2c_write_command(struct ssdfb_softc * sc,char * buf,size_t len)528 ssdfb_i2c_write_command(struct ssdfb_softc *sc, char *buf, size_t len)
529 {
530 uint8_t type;
531
532 type = SSDFB_I2C_COMMAND;
533 iic_acquire_bus(sc->sc_i2c_tag, 0);
534 if (iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
535 sc->sc_i2c_addr, &type, sizeof(type), buf, len, 0)) {
536 printf("%s: cannot write\n", sc->sc_dev.dv_xname);
537 }
538 iic_release_bus(sc->sc_i2c_tag, 0);
539 }
540
541 void
ssdfb_i2c_write_data(struct ssdfb_softc * sc,char * buf,size_t len)542 ssdfb_i2c_write_data(struct ssdfb_softc *sc, char *buf, size_t len)
543 {
544 uint8_t type;
545
546 type = SSDFB_I2C_DATA;
547 iic_acquire_bus(sc->sc_i2c_tag, 0);
548 if (iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
549 sc->sc_i2c_addr, &type, sizeof(type), buf, len, 0)) {
550 printf("%s: cannot write\n", sc->sc_dev.dv_xname);
551 }
552 iic_release_bus(sc->sc_i2c_tag, 0);
553 }
554
555 void
ssdfb_spi_write_command(struct ssdfb_softc * sc,char * buf,size_t len)556 ssdfb_spi_write_command(struct ssdfb_softc *sc, char *buf, size_t len)
557 {
558 if (sc->sc_cd != 0) {
559 gpio_controller_set_pin(sc->sc_gpio, 0);
560 sc->sc_cd = 0;
561 delay(1);
562 }
563
564 spi_acquire_bus(sc->sc_spi_tag, 0);
565 spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
566 if (spi_write(sc->sc_spi_tag, buf, len))
567 printf("%s: cannot write\n", sc->sc_dev.dv_xname);
568 spi_release_bus(sc->sc_spi_tag, 0);
569 }
570
571 void
ssdfb_spi_write_data(struct ssdfb_softc * sc,char * buf,size_t len)572 ssdfb_spi_write_data(struct ssdfb_softc *sc, char *buf, size_t len)
573 {
574 if (sc->sc_cd != 1) {
575 gpio_controller_set_pin(sc->sc_gpio, 1);
576 sc->sc_cd = 1;
577 delay(1);
578 }
579
580 spi_acquire_bus(sc->sc_spi_tag, 0);
581 spi_config(sc->sc_spi_tag, &sc->sc_spi_conf);
582 if (spi_write(sc->sc_spi_tag, buf, len))
583 printf("%s: cannot write\n", sc->sc_dev.dv_xname);
584 spi_release_bus(sc->sc_spi_tag, 0);
585 }
586
587 int
ssdfb_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)588 ssdfb_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
589 {
590 struct ssdfb_softc *sc = v;
591 struct rasops_info *ri = &sc->sc_rinfo;
592 struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
593 struct wsdisplay_fbinfo *wdf;
594 struct udl_ioctl_damage *d;
595 int mode;
596 uint8_t reg[2];
597
598 switch (cmd) {
599 case WSDISPLAYIO_GETPARAM:
600 switch (dp->param) {
601 case WSDISPLAYIO_PARAM_BRIGHTNESS:
602 dp->min = 0;
603 dp->max = 255;
604 dp->curval = sc->sc_brightness;
605 break;
606 default:
607 return (-1);
608 }
609 break;
610 case WSDISPLAYIO_SETPARAM:
611 switch (dp->param) {
612 case WSDISPLAYIO_PARAM_BRIGHTNESS:
613 if (dp->curval == 0) {
614 reg[0] = SSDFB_SET_DISPLAY_OFF;
615 ssdfb_write_command(sc, reg, 1);
616 } else if (sc->sc_brightness == 0) {
617 reg[0] = SSDFB_SET_DISPLAY_ON;
618 ssdfb_write_command(sc, reg, 1);
619 }
620 reg[0] = SSDFB_SET_CONTRAST_CONTROL;
621 reg[1] = sc->sc_brightness = dp->curval;
622 ssdfb_write_command(sc, reg, 2);
623 break;
624 default:
625 return (-1);
626 }
627 break;
628 case WSDISPLAYIO_GTYPE:
629 *(u_int *)data = WSDISPLAY_TYPE_DL;
630 break;
631 case WSDISPLAYIO_GINFO:
632 wdf = (struct wsdisplay_fbinfo *)data;
633 wdf->width = ri->ri_width;
634 wdf->height = ri->ri_height;
635 wdf->depth = ri->ri_depth;
636 wdf->stride = ri->ri_stride;
637 wdf->offset = 0;
638 wdf->cmsize = 0; /* color map is unavailable */
639 break;
640 case WSDISPLAYIO_LINEBYTES:
641 *(u_int *)data = ri->ri_stride;
642 break;
643 case WSDISPLAYIO_SMODE:
644 mode = *(u_int *)data;
645 switch (mode) {
646 case WSDISPLAYIO_MODE_EMUL:
647 if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL) {
648 memset(ri->ri_bits, 0, sc->sc_fbsize);
649 ssdfb_partial(sc, 0, sc->sc_width,
650 0, sc->sc_height);
651 sc->sc_mode = mode;
652 }
653 break;
654 case WSDISPLAYIO_MODE_DUMBFB:
655 if (sc->sc_mode != WSDISPLAYIO_MODE_DUMBFB) {
656 memset(ri->ri_bits, 0, sc->sc_fbsize);
657 ssdfb_partial(sc, 0, sc->sc_width,
658 0, sc->sc_height);
659 sc->sc_mode = mode;
660 }
661 break;
662 case WSDISPLAYIO_MODE_MAPPED:
663 default:
664 return (-1);
665 }
666 break;
667 case WSDISPLAYIO_GETSUPPORTEDDEPTH:
668 *(u_int *)data = WSDISPLAYIO_DEPTH_1;
669 break;
670 case UDLIO_DAMAGE:
671 d = (struct udl_ioctl_damage *)data;
672 d->status = UDLIO_STATUS_OK;
673 ssdfb_partial(sc, d->x1, d->x2, d->y1, d->y2);
674 break;
675 default:
676 return (-1);
677 }
678
679 return (0);
680 }
681
682 paddr_t
ssdfb_mmap(void * v,off_t off,int prot)683 ssdfb_mmap(void *v, off_t off, int prot)
684 {
685 struct ssdfb_softc *sc = v;
686 struct rasops_info *ri = &sc->sc_rinfo;
687 paddr_t pa;
688
689 if (off >= sc->sc_fbsize || off < 0)
690 return (-1);
691
692 if (!pmap_extract(pmap_kernel(), (vaddr_t)ri->ri_bits, &pa))
693 return (-1);
694
695 return (pa + off);
696 }
697
698 int
ssdfb_alloc_screen(void * v,const struct wsscreen_descr * descr,void ** cookiep,int * curxp,int * curyp,uint32_t * attrp)699 ssdfb_alloc_screen(void *v, const struct wsscreen_descr *descr,
700 void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
701 {
702 struct ssdfb_softc *sc = v;
703 struct rasops_info *ri = &sc->sc_rinfo;
704
705 return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp);
706 }
707
708 void
ssdfb_free_screen(void * v,void * cookie)709 ssdfb_free_screen(void *v, void *cookie)
710 {
711 struct ssdfb_softc *sc = v;
712 struct rasops_info *ri = &sc->sc_rinfo;
713
714 rasops_free_screen(ri, cookie);
715 }
716
717 int
ssdfb_show_screen(void * v,void * cookie,int waitok,void (* cb)(void *,int,int),void * cb_arg)718 ssdfb_show_screen(void *v, void *cookie, int waitok,
719 void (*cb) (void *, int, int), void *cb_arg)
720 {
721 struct ssdfb_softc *sc = v;
722 struct rasops_info *ri = &sc->sc_rinfo;
723
724 return rasops_show_screen(ri, cookie, waitok, cb, cb_arg);
725 }
726
727 int
ssdfb_load_font(void * v,void * cookie,struct wsdisplay_font * font)728 ssdfb_load_font(void *v, void *cookie, struct wsdisplay_font *font)
729 {
730 struct ssdfb_softc *sc = v;
731 struct rasops_info *ri = &sc->sc_rinfo;
732
733 return (rasops_load_font(ri, cookie, font));
734 }
735
736 int
ssdfb_list_font(void * v,struct wsdisplay_font * font)737 ssdfb_list_font(void *v, struct wsdisplay_font *font)
738 {
739 struct ssdfb_softc *sc = v;
740 struct rasops_info *ri = &sc->sc_rinfo;
741
742 return (rasops_list_font(ri, font));
743 }
744
745 int
ssdfb_putchar(void * cookie,int row,int col,u_int uc,uint32_t attr)746 ssdfb_putchar(void *cookie, int row, int col, u_int uc, uint32_t attr)
747 {
748 struct rasops_info *ri = (struct rasops_info *)cookie;
749 struct ssdfb_softc *sc = ri->ri_hw;
750
751 sc->sc_riops.putchar(cookie, row, col, uc, attr);
752 ssdfb_partial(sc,
753 col * ri->ri_font->fontwidth,
754 (col + 1) * ri->ri_font->fontwidth,
755 row * ri->ri_font->fontheight,
756 (row + 1) * ri->ri_font->fontheight);
757 return 0;
758 }
759
760 int
ssdfb_copycols(void * cookie,int row,int src,int dst,int num)761 ssdfb_copycols(void *cookie, int row, int src, int dst, int num)
762 {
763 struct rasops_info *ri = (struct rasops_info *)cookie;
764 struct ssdfb_softc *sc = ri->ri_hw;
765
766 sc->sc_riops.copycols(cookie, row, src, dst, num);
767 ssdfb_partial(sc,
768 dst * ri->ri_font->fontwidth,
769 (dst + num) * ri->ri_font->fontwidth,
770 row * ri->ri_font->fontheight,
771 (row + 1) * ri->ri_font->fontheight);
772 return 0;
773 }
774
775 int
ssdfb_erasecols(void * cookie,int row,int col,int num,uint32_t attr)776 ssdfb_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
777 {
778 struct rasops_info *ri = (struct rasops_info *)cookie;
779 struct ssdfb_softc *sc = ri->ri_hw;
780
781 sc->sc_riops.erasecols(cookie, row, col, num, attr);
782 ssdfb_partial(sc,
783 col * ri->ri_font->fontwidth,
784 (col + num) * ri->ri_font->fontwidth,
785 row * ri->ri_font->fontheight,
786 (row + 1) * ri->ri_font->fontheight);
787 return 0;
788 }
789
790 int
ssdfb_copyrows(void * cookie,int src,int dst,int num)791 ssdfb_copyrows(void *cookie, int src, int dst, int num)
792 {
793 struct rasops_info *ri = (struct rasops_info *)cookie;
794 struct ssdfb_softc *sc = ri->ri_hw;
795
796 sc->sc_riops.copyrows(cookie, src, dst, num);
797 ssdfb_partial(sc, 0, sc->sc_width,
798 dst * ri->ri_font->fontheight,
799 (dst + num) * ri->ri_font->fontheight);
800 return 0;
801 }
802
803 int
ssdfb_eraserows(void * cookie,int row,int num,uint32_t attr)804 ssdfb_eraserows(void *cookie, int row, int num, uint32_t attr)
805 {
806 struct rasops_info *ri = (struct rasops_info *)cookie;
807 struct ssdfb_softc *sc = ri->ri_hw;
808
809 sc->sc_riops.eraserows(cookie, row, num, attr);
810 if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR) != 0)
811 ssdfb_partial(sc, 0, sc->sc_width, 0, sc->sc_height);
812 else
813 ssdfb_partial(sc, 0, sc->sc_width,
814 row * ri->ri_font->fontheight,
815 (row + num) * ri->ri_font->fontheight);
816 return 0;
817 }
818
819 int
ssdfb_do_cursor(struct rasops_info * ri)820 ssdfb_do_cursor(struct rasops_info *ri)
821 {
822 struct ssdfb_softc *sc = ri->ri_hw;
823 int orow, ocol, nrow, ncol;
824
825 orow = ri->ri_crow;
826 ocol = ri->ri_ccol;
827 sc->sc_ri_do_cursor(ri);
828 nrow = ri->ri_crow;
829 ncol = ri->ri_ccol;
830
831 ssdfb_partial(sc,
832 ocol * ri->ri_font->fontwidth,
833 (ocol + 1) * ri->ri_font->fontwidth,
834 orow * ri->ri_font->fontheight,
835 (orow + 1) * ri->ri_font->fontheight);
836 ssdfb_partial(sc,
837 ncol * ri->ri_font->fontwidth,
838 (ncol + 1) * ri->ri_font->fontwidth,
839 nrow * ri->ri_font->fontheight,
840 (nrow + 1) * ri->ri_font->fontheight);
841
842 return 0;
843 }
844