ssdfb.c revision 1.18 1 /* $NetBSD: ssdfb.c,v 1.18 2021/08/05 19:07:09 tnn Exp $ */
2
3 /*
4 * Copyright (c) 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tobias Nygren.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: ssdfb.c,v 1.18 2021/08/05 19:07:09 tnn Exp $");
34
35 #include "opt_ddb.h"
36
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/conf.h>
40 #include <sys/condvar.h>
41 #include <sys/kmem.h>
42 #include <sys/kthread.h>
43
44 #include <uvm/uvm_device.h>
45 #include <uvm/uvm_extern.h>
46
47 #include <dev/wscons/wsdisplayvar.h>
48 #include <dev/rasops/rasops.h>
49 #include <dev/ic/ssdfbvar.h>
50
51 #if defined(DDB)
52 #include <machine/db_machdep.h>
53 #include <ddb/db_extern.h>
54 #endif
55
56 /* userland interface */
57 static int ssdfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
58 static paddr_t ssdfb_mmap(void *, void *, off_t, int);
59
60 /* wscons screen management */
61 static int ssdfb_alloc_screen(void *, const struct wsscreen_descr *,
62 void **, int *, int *, long *);
63 static void ssdfb_free_screen(void *, void *);
64 static int ssdfb_show_screen(void *, void *, int,
65 void (*cb) (void *, int, int), void *);
66
67 /* rasops hooks */
68 static void ssdfb_putchar(void *, int, int, u_int, long);
69 static void ssdfb_copycols(void *, int, int, int, int);
70 static void ssdfb_erasecols(void *, int, int, int, long);
71 static void ssdfb_copyrows(void *, int, int, int);
72 static void ssdfb_eraserows(void *, int, int, long);
73 static void ssdfb_cursor(void *, int, int, int);
74
75 /* hardware interface */
76 static int ssdfb_init_ssd1306(struct ssdfb_softc *);
77 static int ssdfb_init_ssd1322(struct ssdfb_softc *);
78 static int ssdfb_init_ssd1353(struct ssdfb_softc *);
79 static int ssdfb_set_contrast(struct ssdfb_softc *, uint8_t, bool);
80 static int ssdfb_set_display_on(struct ssdfb_softc *, bool, bool);
81 static int ssdfb_set_mode(struct ssdfb_softc *, u_int);
82
83 /* frame buffer damage tracking and synchronization */
84 static void ssdfb_udv_attach(struct ssdfb_softc *sc);
85 static bool ssdfb_is_modified(struct ssdfb_softc *sc);
86 static bool ssdfb_clear_modify(struct ssdfb_softc *sc);
87 static void ssdfb_damage(struct ssdfb_softc *);
88 static void ssdfb_thread(void *);
89 static void ssdfb_set_usepoll(struct ssdfb_softc *, bool);
90 static int ssdfb_sync(struct ssdfb_softc *, bool);
91 static int ssdfb_sync_ssd1306(struct ssdfb_softc *, bool);
92 static int ssdfb_sync_ssd1322(struct ssdfb_softc *, bool);
93 static int ssdfb_sync_ssd1353(struct ssdfb_softc *, bool);
94 static uint64_t ssdfb_transpose_block(uint8_t *, size_t);
95
96 /* misc helpers */
97 static const struct ssdfb_product *
98 ssdfb_lookup_product(ssdfb_product_id_t);
99 static int ssdfb_pick_font(int *, struct wsdisplay_font **);
100 static void ssdfb_clear_screen(struct ssdfb_softc *);
101 #if defined(DDB)
102 static void ssdfb_ddb_trap_callback(int);
103 #endif
104
105 static const char *ssdfb_controller_names[] = {
106 [SSDFB_CONTROLLER_UNKNOWN] = "unknown",
107 [SSDFB_CONTROLLER_SSD1306] = "Solomon Systech SSD1306",
108 [SSDFB_CONTROLLER_SH1106] = "Sino Wealth SH1106",
109 [SSDFB_CONTROLLER_SSD1322] = "Solomon Systech SSD1322",
110 [SSDFB_CONTROLLER_SSD1353] = "Solomon Systech SSD1353"
111 };
112
113 /*
114 * Display module assemblies supported by this driver.
115 */
116 static const struct ssdfb_product ssdfb_products[] = {
117 {
118 .p_product_id = SSDFB_PRODUCT_SSD1306_GENERIC,
119 .p_controller_id = SSDFB_CONTROLLER_SSD1306,
120 .p_name = "generic",
121 .p_width = 128,
122 .p_height = 64,
123 .p_bits_per_pixel = 1,
124 .p_panel_shift = 0,
125 .p_fosc = 0x8,
126 .p_fosc_div = 0,
127 .p_precharge = 0x1,
128 .p_discharge = 0xf,
129 .p_compin_cfg = SSDFB_COM_PINS_A1_MASK
130 | SSDFB_COM_PINS_ALTERNATIVE_MASK,
131 .p_vcomh_deselect_level = SSD1306_VCOMH_DESELECT_LEVEL_0_77_VCC,
132 .p_default_contrast = 0x7f,
133 .p_multiplex_ratio = 0x3f,
134 .p_init = ssdfb_init_ssd1306,
135 .p_sync = ssdfb_sync_ssd1306
136 },
137 {
138 .p_product_id = SSDFB_PRODUCT_SH1106_GENERIC,
139 .p_controller_id = SSDFB_CONTROLLER_SH1106,
140 .p_name = "generic",
141 .p_width = 128,
142 .p_height = 64,
143 .p_bits_per_pixel = 1,
144 .p_panel_shift = 2,
145 .p_fosc = 0x5,
146 .p_fosc_div = 0,
147 .p_precharge = 0x2,
148 .p_discharge = 0x2,
149 .p_compin_cfg = SSDFB_COM_PINS_A1_MASK
150 | SSDFB_COM_PINS_ALTERNATIVE_MASK,
151 .p_vcomh_deselect_level = SH1106_VCOMH_DESELECT_LEVEL_DEFAULT,
152 .p_default_contrast = 0x80,
153 .p_multiplex_ratio = 0x3f,
154 .p_init = ssdfb_init_ssd1306,
155 .p_sync = ssdfb_sync_ssd1306
156 },
157 {
158 .p_product_id = SSDFB_PRODUCT_ADAFRUIT_938,
159 .p_controller_id = SSDFB_CONTROLLER_SSD1306,
160 .p_name = "Adafruit Industries, LLC product 938",
161 .p_width = 128,
162 .p_height = 64,
163 .p_bits_per_pixel = 1,
164 .p_panel_shift = 0,
165 .p_fosc = 0x8,
166 .p_fosc_div = 0,
167 .p_precharge = 0x1,
168 .p_discharge = 0xf,
169 .p_compin_cfg = 0x12,
170 .p_vcomh_deselect_level = 0x40,
171 .p_default_contrast = 0x8f,
172 .p_multiplex_ratio = 0x3f,
173 .p_init = ssdfb_init_ssd1306,
174 .p_sync = ssdfb_sync_ssd1306
175 },
176 {
177 .p_product_id = SSDFB_PRODUCT_ADAFRUIT_931,
178 .p_controller_id = SSDFB_CONTROLLER_SSD1306,
179 .p_name = "Adafruit Industries, LLC product 931",
180 .p_width = 128,
181 .p_height = 32,
182 .p_bits_per_pixel = 1,
183 .p_panel_shift = 0,
184 .p_fosc = 0x8,
185 .p_fosc_div = 0,
186 .p_precharge = 0x1,
187 .p_discharge = 0xf,
188 .p_compin_cfg = 0x2,
189 .p_vcomh_deselect_level = 0x40,
190 .p_default_contrast = 0x8f,
191 .p_multiplex_ratio = 0x1f,
192 .p_init = ssdfb_init_ssd1306,
193 .p_sync = ssdfb_sync_ssd1306
194 },
195 {
196 .p_product_id = SSDFB_PRODUCT_SSD1322_GENERIC,
197 .p_controller_id = SSDFB_CONTROLLER_SSD1322,
198 .p_name = "generic",
199 .p_width = 256,
200 .p_height = 64,
201 .p_bits_per_pixel = 4,
202 .p_panel_shift = 28,
203 .p_vcomh_deselect_level = SSD1322_DEFAULT_VCOMH,
204 .p_fosc = SSD1322_DEFAULT_FREQUENCY,
205 .p_fosc_div = SSD1322_DEFAULT_DIVIDER,
206 .p_default_contrast = SSD1322_DEFAULT_CONTRAST_CURRENT,
207 .p_multiplex_ratio = 0x3f,
208 .p_init = ssdfb_init_ssd1322,
209 .p_sync = ssdfb_sync_ssd1322
210 },
211 {
212 .p_product_id = SSDFB_PRODUCT_SSD1353_GENERIC,
213 .p_controller_id = SSDFB_CONTROLLER_SSD1353,
214 .p_name = "generic",
215 .p_width = 160,
216 .p_height = 132,
217 .p_bits_per_pixel = 32,
218 .p_rgb = true,
219 .p_panel_shift = 0,
220 .p_compin_cfg = SSD1353_REMAP_RGB | SSD1353_REMAP_SPLIT_ODD_EVEN
221 | __SHIFTIN(2, SSD1353_REMAP_PIXEL_FORMAT_MASK),
222 .p_vcomh_deselect_level = SSD1353_DEFAULT_VCOMH,
223 .p_fosc = SSD1353_DEFAULT_FREQUENCY,
224 .p_fosc_div = SSD1353_DEFAULT_DIVIDER,
225 .p_default_contrast = SSD1353_DEFAULT_CONTRAST_CONTROL,
226 .p_multiplex_ratio = 0x83,
227 .p_init = ssdfb_init_ssd1353,
228 .p_sync = ssdfb_sync_ssd1353
229 },
230 {
231 .p_product_id = SSDFB_PRODUCT_DEP_160128A_RGB,
232 .p_controller_id = SSDFB_CONTROLLER_SSD1353,
233 .p_name = "Display Elektronik GmbH DEP 160128A(1)-RGB",
234 .p_width = 160,
235 .p_height = 128,
236 .p_bits_per_pixel = 32,
237 .p_rgb = true,
238 .p_panel_shift = 0,
239 .p_compin_cfg = SSD1353_REMAP_RGB | SSD1353_REMAP_SPLIT_ODD_EVEN
240 | __SHIFTIN(2, SSD1353_REMAP_PIXEL_FORMAT_MASK),
241 .p_vcomh_deselect_level = SSD1353_DEFAULT_VCOMH,
242 .p_fosc = SSD1353_DEFAULT_FREQUENCY,
243 .p_fosc_div = SSD1353_DEFAULT_DIVIDER,
244 .p_default_contrast = SSD1353_DEFAULT_CONTRAST_CONTROL,
245 .p_multiplex_ratio = 0x83,
246 .p_init = ssdfb_init_ssd1353,
247 .p_sync = ssdfb_sync_ssd1353
248 }
249 };
250
251 static const struct wsdisplay_accessops ssdfb_accessops = {
252 .ioctl = ssdfb_ioctl,
253 .mmap = ssdfb_mmap,
254 .alloc_screen = ssdfb_alloc_screen,
255 .free_screen = ssdfb_free_screen,
256 .show_screen = ssdfb_show_screen
257 };
258
259 #define SSDFB_CMD1(c) do { cmd[0] = (c); error = sc->sc_cmd(sc->sc_cookie, cmd, 1, usepoll); } while(0)
260 #define SSDFB_CMD2(c, a) do { cmd[0] = (c); cmd[1] = (a); error = sc->sc_cmd(sc->sc_cookie, cmd, 2, usepoll); } while(0)
261 #define SSDFB_CMD3(c, a, b) do { cmd[0] = (c); cmd[1] = (a); cmd[2] = (b); error = sc->sc_cmd(sc->sc_cookie, cmd, 3, usepoll); } while(0)
262
263 void
264 ssdfb_attach(struct ssdfb_softc *sc, int flags)
265 {
266 struct wsemuldisplaydev_attach_args aa;
267 struct rasops_info *ri = &sc->sc_ri;
268 int error = 0;
269 long defattr;
270 const struct ssdfb_product *p;
271
272 p = ssdfb_lookup_product(flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK);
273 if (p == NULL) {
274 aprint_error(": unknown display assembly\n");
275 return;
276 }
277 sc->sc_p = p;
278
279 aprint_naive("\n");
280 aprint_normal(": %s (%s)\n",
281 ssdfb_controller_names[p->p_controller_id],
282 p->p_name);
283
284 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
285 sc->sc_is_console = flags & SSDFB_ATTACH_FLAG_CONSOLE ? true : false;
286 sc->sc_inverse = flags & SSDFB_ATTACH_FLAG_INVERSE ? true : false;
287 sc->sc_upsidedown = flags & SSDFB_ATTACH_FLAG_UPSIDEDOWN ? true : false;
288 sc->sc_backoff = 1;
289 sc->sc_contrast = sc->sc_p->p_default_contrast;
290 sc->sc_gddram_len = sc->sc_p->p_width * sc->sc_p->p_height
291 * sc->sc_p->p_bits_per_pixel / 8;
292 sc->sc_gddram = kmem_alloc(sc->sc_gddram_len, KM_SLEEP);
293 if (sc->sc_gddram == NULL)
294 goto out;
295
296 aprint_normal_dev(sc->sc_dev, "%dx%d%s\n", sc->sc_p->p_width,
297 sc->sc_p->p_height, sc->sc_is_console ? ", console" : "");
298
299 /*
300 * Initialize rasops. The native depth is 1-bit monochrome and we
301 * support this in text emul mode via rasops1. But modern Xorg
302 * userland has many rendering glitches when running with 1-bit depth
303 * so to better support this use case we instead declare ourselves as
304 * an 8-bit display with a two entry constant color map.
305 */
306 error = ssdfb_pick_font(&sc->sc_fontcookie, &sc->sc_font);
307 if (error) {
308 aprint_error_dev(sc->sc_dev, "no font\n");
309 goto out;
310 }
311 #ifdef SSDFB_USE_NATIVE_DEPTH
312 ri->ri_depth = sc->sc_p->p_bits_per_pixel;
313 #else
314 if (sc->sc_p->p_rgb && sc->sc_p->p_bits_per_pixel == 32) {
315 ri->ri_depth = sc->sc_p->p_bits_per_pixel;
316 ri->ri_rnum = 8;
317 ri->ri_gnum = 8;
318 ri->ri_bnum = 8;
319 #if _BYTE_ORDER == _LITTLE_ENDIAN
320 ri->ri_rpos = 0;
321 ri->ri_gpos = 8;
322 ri->ri_bpos = 16;
323 #else
324 ri->ri_rpos = 24;
325 ri->ri_gpos = 16;
326 ri->ri_bpos = 8;
327 #endif
328 } else {
329 ri->ri_depth = 8;
330 }
331 #endif
332 ri->ri_font = sc->sc_font;
333 ri->ri_width = sc->sc_p->p_width;
334 ri->ri_height = sc->sc_p->p_height;
335 ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
336 ri->ri_hw = sc;
337 ri->ri_flg = RI_FULLCLEAR;
338 if (!sc->sc_p->p_rgb) {
339 ri->ri_flg |= RI_FORCEMONO;
340 }
341 sc->sc_ri_bits_len = round_page(ri->ri_stride * ri->ri_height);
342 ri->ri_bits = (u_char *)uvm_km_alloc(kernel_map, sc->sc_ri_bits_len,
343 0, UVM_KMF_WIRED);
344 if (ri->ri_bits == NULL)
345 goto out;
346
347 error = rasops_init(ri,
348 sc->sc_p->p_height / sc->sc_font->fontheight,
349 sc->sc_p->p_width / sc->sc_font->fontwidth);
350 if (error)
351 goto out;
352
353 if (!sc->sc_p->p_rgb) {
354 ri->ri_caps &= ~WSSCREEN_WSCOLORS;
355 }
356
357 /*
358 * Save original emul ops & insert our damage notification hooks.
359 */
360 sc->sc_orig_riops = ri->ri_ops;
361 ri->ri_ops.putchar = ssdfb_putchar;
362 ri->ri_ops.copycols = ssdfb_copycols;
363 ri->ri_ops.erasecols = ssdfb_erasecols;
364 ri->ri_ops.copyrows = ssdfb_copyrows;
365 ri->ri_ops.eraserows = ssdfb_eraserows;
366 ri->ri_ops.cursor = ssdfb_cursor;
367
368 /*
369 * Set up the screen.
370 */
371 sc->sc_screen_descr = (struct wsscreen_descr){
372 .name = "default",
373 .ncols = ri->ri_cols,
374 .nrows = ri->ri_rows,
375 .textops = &ri->ri_ops,
376 .fontwidth = ri->ri_font->fontwidth,
377 .fontheight = ri->ri_font->fontheight,
378 .capabilities = ri->ri_caps
379 };
380 sc->sc_screens[0] = &sc->sc_screen_descr;
381 sc->sc_screenlist = (struct wsscreen_list){
382 .nscreens = 1,
383 .screens = sc->sc_screens
384 };
385
386 /*
387 * Initialize hardware.
388 */
389 error = p->p_init(sc);
390 if (error)
391 goto out;
392
393 if (sc->sc_is_console)
394 ssdfb_set_usepoll(sc, true);
395
396 mutex_init(&sc->sc_cond_mtx, MUTEX_DEFAULT, IPL_SCHED);
397 cv_init(&sc->sc_cond, "ssdfb");
398 error = kthread_create(PRI_SOFTCLOCK, KTHREAD_MUSTJOIN | KTHREAD_MPSAFE,
399 NULL, ssdfb_thread, sc, &sc->sc_thread, "%s",
400 device_xname(sc->sc_dev));
401 if (error) {
402 cv_destroy(&sc->sc_cond);
403 mutex_destroy(&sc->sc_cond_mtx);
404 goto out;
405 }
406
407 /*
408 * Attach wsdisplay.
409 */
410 if (sc->sc_is_console) {
411 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
412 wsdisplay_cnattach(&sc->sc_screen_descr, ri, 0, 0, defattr);
413 #if defined(DDB)
414 db_trap_callback = ssdfb_ddb_trap_callback;
415 #endif
416 }
417 aa = (struct wsemuldisplaydev_attach_args){
418 .console = sc->sc_is_console,
419 .scrdata = &sc->sc_screenlist,
420 .accessops = &ssdfb_accessops,
421 .accesscookie = sc
422 };
423 sc->sc_wsdisplay =
424 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, CFARG_EOL);
425
426 return;
427 out:
428 aprint_error_dev(sc->sc_dev, "attach failed: %d\n", error);
429 if (sc->sc_gddram != NULL)
430 kmem_free(sc->sc_gddram, sc->sc_gddram_len);
431 if (ri->ri_bits != NULL)
432 uvm_km_free(kernel_map, (vaddr_t)ri->ri_bits, sc->sc_ri_bits_len,
433 UVM_KMF_WIRED);
434 if (sc->sc_fontcookie > 0)
435 (void) wsfont_unlock(sc->sc_fontcookie);
436 }
437
438 int
439 ssdfb_detach(struct ssdfb_softc *sc)
440 {
441 mutex_enter(&sc->sc_cond_mtx);
442 sc->sc_detaching = true;
443 cv_broadcast(&sc->sc_cond);
444 mutex_exit(&sc->sc_cond_mtx);
445 kthread_join(sc->sc_thread);
446
447 if (sc->sc_uobj != NULL) {
448 rw_enter(sc->sc_uobj->vmobjlock, RW_WRITER);
449 sc->sc_uobj->uo_refs--;
450 rw_exit(sc->sc_uobj->vmobjlock);
451 }
452 config_detach(sc->sc_wsdisplay, DETACH_FORCE);
453
454 cv_destroy(&sc->sc_cond);
455 mutex_destroy(&sc->sc_cond_mtx);
456 uvm_km_free(kernel_map, (vaddr_t)sc->sc_ri.ri_bits, sc->sc_ri_bits_len,
457 UVM_KMF_WIRED);
458 kmem_free(sc->sc_gddram, sc->sc_gddram_len);
459 (void) wsfont_unlock(sc->sc_fontcookie);
460 return 0;
461 }
462
463 static int
464 ssdfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
465 {
466 struct ssdfb_softc *sc = v;
467 struct wsdisplay_param *wdp;
468 struct wsdisplay_cmap *wc;
469 u_char cmap[16];
470 int cmaplen = 1 << sc->sc_p->p_bits_per_pixel;
471 int i;
472 struct wsdisplayio_fbinfo *fbi;
473 int error;
474
475 switch (cmd) {
476 case WSDISPLAYIO_GTYPE:
477 *(u_int *)data = WSDISPLAY_TYPE_SSDFB;
478 return 0;
479 case WSDISPLAYIO_GINFO:
480 *(struct wsdisplay_fbinfo *)data = (struct wsdisplay_fbinfo){
481 .width = sc->sc_ri.ri_width,
482 .height = sc->sc_ri.ri_height,
483 .depth = sc->sc_ri.ri_depth,
484 .cmsize = cmaplen
485 };
486 return 0;
487 case WSDISPLAYIO_GET_FBINFO:
488 fbi = (struct wsdisplayio_fbinfo *)data;
489 error = wsdisplayio_get_fbinfo(&sc->sc_ri, fbi);
490 if (!sc->sc_p->p_rgb) {
491 fbi->fbi_subtype.fbi_cmapinfo.cmap_entries = cmaplen;
492 /* fbi->fbi_pixeltype = WSFB_GREYSCALE */;
493 }
494 return error;
495 case WSDISPLAYIO_LINEBYTES:
496 *(u_int *)data = sc->sc_ri.ri_stride;
497 return 0;
498 case WSDISPLAYIO_GETPARAM:
499 wdp = (struct wsdisplay_param *)data;
500 if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST)
501 return EINVAL;
502 wdp->min = 0;
503 wdp->max = 0xff;
504 wdp->curval = sc->sc_contrast;
505 return 0;
506 case WSDISPLAYIO_SETPARAM:
507 wdp = (struct wsdisplay_param *)data;
508 if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST)
509 return EINVAL;
510 if (wdp->curval < 0 || wdp->curval > 0xff)
511 return EINVAL;
512 return ssdfb_set_contrast(sc, wdp->curval, sc->sc_usepoll);
513 case WSDISPLAYIO_GMODE:
514 *(u_int *)data = sc->sc_mode;
515 return 0;
516 case WSDISPLAYIO_SMODE:
517 return ssdfb_set_mode(sc, *(u_int *)data);
518 case WSDISPLAYIO_GVIDEO:
519 *(u_int *)data = sc->sc_display_on
520 ? WSDISPLAYIO_VIDEO_ON
521 : WSDISPLAYIO_VIDEO_OFF;
522 return 0;
523 case WSDISPLAYIO_SVIDEO:
524 switch (*(u_int *)data) {
525 case WSDISPLAYIO_VIDEO_ON:
526 case WSDISPLAYIO_VIDEO_OFF:
527 break;
528 default:
529 return EINVAL;
530 }
531 return ssdfb_set_display_on(sc,
532 *(u_int *)data == WSDISPLAYIO_VIDEO_ON ? true : false,
533 sc->sc_usepoll);
534 #if 0 /* don't let userland mess with polling yet */
535 case WSDISPLAYIO_SET_POLLING:
536 switch (*(u_int *)data) {
537 case 0:
538 case 1:
539 break;
540 default:
541 return EINVAL;
542 }
543 mutex_enter(&sc->sc_cond_mtx);
544 ssdfb_set_usepoll(sc, *(u_int *)data ? true : false);
545 cv_broadcast(&sc->sc_cond);
546 mutex_exit(&sc->sc_cond_mtx);
547 return 0;
548 #endif
549 case WSDISPLAYIO_GETCMAP:
550 if (sc->sc_p->p_rgb)
551 return ENOTSUP;
552 wc = (struct wsdisplay_cmap *)data;
553 if (wc->index >= cmaplen ||
554 wc->count > cmaplen - wc->index)
555 return EINVAL;
556 for(i = 0; i < cmaplen; i++) {
557 cmap[i] = 255 * i / (cmaplen - 1);
558 }
559 error = copyout(&cmap[wc->index], wc->red, wc->count);
560 if (error)
561 return error;
562 error = copyout(&cmap[wc->index], wc->green, wc->count);
563 if (error)
564 return error;
565 error = copyout(&cmap[wc->index], wc->blue, wc->count);
566 return error;
567 case WSDISPLAYIO_PUTCMAP:
568 return ENODEV;
569 }
570
571 return EPASSTHROUGH;
572 }
573
574 static paddr_t
575 ssdfb_mmap(void *v, void *vs, off_t off, int prot)
576 {
577 struct ssdfb_softc *sc = (struct ssdfb_softc *)v;
578 struct rasops_info *ri = &sc->sc_ri;
579 vaddr_t va_base = (vaddr_t)ri->ri_bits;
580 paddr_t pa;
581
582 if (off < 0 || off >= sc->sc_ri_bits_len || (off & PAGE_MASK) != 0)
583 return -1;
584
585 if (!pmap_extract(pmap_kernel(), va_base + off, &pa))
586 return -1;
587
588 return atop(pa);
589 }
590
591 static int
592 ssdfb_alloc_screen(void *v, const struct wsscreen_descr *descr, void **cookiep,
593 int *curxp, int *curyp, long *attrp)
594 {
595 struct ssdfb_softc *sc = v;
596 struct rasops_info *ri = &sc->sc_ri;
597
598 if (sc->sc_nscreens > 0)
599 return ENOMEM;
600
601 ri->ri_ops.allocattr(ri, 0, 0, 0, attrp);
602 *cookiep = &sc->sc_ri;
603 *curxp = 0;
604 *curyp = 0;
605 sc->sc_nscreens++;
606
607 return 0;
608 }
609
610 static void
611 ssdfb_free_screen(void *v, void *cookie)
612 {
613 struct ssdfb_softc *sc = v;
614
615 if (sc->sc_is_console)
616 panic("ssdfb_free_screen: is console");
617
618 sc->sc_nscreens--;
619 }
620
621 static int
622 ssdfb_show_screen(void *v, void *cookie, int waitok,
623 void (*cb) (void *, int, int), void *cb_arg)
624 {
625 return 0;
626 }
627
628 static void
629 ssdfb_putchar(void *cookie, int row, int col, u_int c, long attr)
630 {
631 struct rasops_info *ri = (struct rasops_info *)cookie;
632 struct ssdfb_softc *sc = ri->ri_hw;
633
634 sc->sc_orig_riops.putchar(cookie, row, col, c, attr);
635 ssdfb_damage(sc);
636 }
637
638 static void
639 ssdfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
640 {
641 struct rasops_info *ri = (struct rasops_info *)cookie;
642 struct ssdfb_softc *sc = ri->ri_hw;
643
644 sc->sc_orig_riops.copycols(cookie, row, srccol, dstcol, ncols);
645 ssdfb_damage(sc);
646 }
647
648 static void
649 ssdfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
650 {
651 struct rasops_info *ri = (struct rasops_info *)cookie;
652 struct ssdfb_softc *sc = ri->ri_hw;
653
654 sc->sc_orig_riops.erasecols(cookie, row, startcol, ncols, fillattr);
655 ssdfb_damage(sc);
656 }
657
658 static void
659 ssdfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
660 {
661 struct rasops_info *ri = (struct rasops_info *)cookie;
662 struct ssdfb_softc *sc = ri->ri_hw;
663
664 sc->sc_orig_riops.copyrows(cookie, srcrow, dstrow, nrows);
665 ssdfb_damage(sc);
666 }
667
668 static void
669 ssdfb_eraserows(void *cookie, int row, int nrows, long fillattr)
670 {
671 struct rasops_info *ri = (struct rasops_info *)cookie;
672 struct ssdfb_softc *sc = ri->ri_hw;
673
674 sc->sc_orig_riops.eraserows(cookie, row, nrows, fillattr);
675 ssdfb_damage(sc);
676 }
677
678 static void
679 ssdfb_cursor(void *cookie, int on, int row, int col)
680 {
681 struct rasops_info *ri = (struct rasops_info *)cookie;
682 struct ssdfb_softc *sc = ri->ri_hw;
683
684 sc->sc_orig_riops.cursor(cookie, on, row, col);
685 ssdfb_damage(sc);
686 }
687
688 static int
689 ssdfb_init_ssd1306(struct ssdfb_softc *sc)
690 {
691 int error;
692 uint8_t cmd[2];
693 bool usepoll = true;
694
695 /*
696 * Enter sleep.
697 */
698 SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_OFF);
699 if (error)
700 return error;
701 SSDFB_CMD1(SSDFB_CMD_DEACTIVATE_SCROLL);
702 if (error)
703 return error;
704 SSDFB_CMD1(SSDFB_CMD_ENTIRE_DISPLAY_OFF);
705 if (error)
706 return error;
707
708 /*
709 * Configure physical display panel layout.
710 */
711 SSDFB_CMD2(SSDFB_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio);
712 if (error)
713 return error;
714 SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_OFFSET, 0);
715 if (error)
716 return error;
717 SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_START_LINE_BASE + 0x00);
718 if (error)
719 return error;
720 SSDFB_CMD2(SSDFB_CMD_SET_COM_PINS_HARDWARE_CFG, sc->sc_p->p_compin_cfg);
721 if (error)
722 return error;
723 if (sc->sc_upsidedown) {
724 SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_REVERSE);
725 if (error)
726 return error;
727 SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_REMAP);
728 if (error)
729 return error;
730 } else {
731 SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_NORMAL);
732 if (error)
733 return error;
734 SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_NORMAL);
735 if (error)
736 return error;
737 }
738 SSDFB_CMD1(SSDFB_CMD_SET_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse);
739 if (error)
740 return error;
741
742 /*
743 * Configure timing characteristics.
744 */
745 SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_CLOCK_RATIO,
746 __SHIFTIN(sc->sc_p->p_fosc, SSDFB_DISPLAY_CLOCK_OSCILLATOR_MASK) |
747 __SHIFTIN(sc->sc_p->p_fosc_div, SSDFB_DISPLAY_CLOCK_DIVIDER_MASK));
748 if (error)
749 return error;
750 SSDFB_CMD2(SSDFB_CMD_SET_CONTRAST_CONTROL, sc->sc_contrast);
751 if (error)
752 return error;
753 SSDFB_CMD2(SSDFB_CMD_SET_PRECHARGE_PERIOD,
754 __SHIFTIN(sc->sc_p->p_precharge, SSDFB_PRECHARGE_MASK) |
755 __SHIFTIN(sc->sc_p->p_discharge, SSDFB_DISCHARGE_MASK));
756 if (error)
757 return error;
758 SSDFB_CMD2(SSDFB_CMD_SET_VCOMH_DESELECT_LEVEL,
759 sc->sc_p->p_vcomh_deselect_level);
760 if (error)
761 return error;
762
763 /*
764 * Start charge pumps.
765 */
766 if (sc->sc_p->p_controller_id == SSDFB_CONTROLLER_SH1106) {
767 SSDFB_CMD1(SH1106_CMD_SET_CHARGE_PUMP_7V4);
768 if (error)
769 return error;
770 SSDFB_CMD2(SH1106_CMD_SET_DC_DC, SH1106_DC_DC_ON);
771 if (error)
772 return error;
773 } else {
774 SSDFB_CMD2(SSD1306_CMD_SET_CHARGE_PUMP,
775 SSD1306_CHARGE_PUMP_ENABLE);
776 if (error)
777 return error;
778 }
779
780 ssdfb_clear_screen(sc);
781 error = sc->sc_p->p_sync(sc, usepoll);
782 if (error)
783 return error;
784 error = ssdfb_set_display_on(sc, true, usepoll);
785
786 return error;
787 }
788
789 static int
790 ssdfb_init_ssd1322(struct ssdfb_softc *sc)
791 {
792 int error;
793 uint8_t cmd[3];
794 bool usepoll = true;
795 uint8_t remap;
796 uint8_t dualcom;
797
798 /*
799 * Enter sleep.
800 */
801 SSDFB_CMD2(SSD1322_CMD_SET_COMMAND_LOCK, SSD1322_COMMAND_UNLOCK_MAGIC);
802 if (error)
803 return error;
804 SSDFB_CMD1(SSD1322_CMD_SET_SLEEP_MODE_ON);
805 if (error)
806 return error;
807
808 /*
809 * Start charge pumps.
810 */
811 SSDFB_CMD2(SSD1322_CMD_FUNCTION_SELECTION,
812 SSD1322_FUNCTION_SELECTION_INTERNAL_VDD);
813 if (error)
814 return error;
815 SSDFB_CMD2(SSD1322_CMD_SET_VCOMH, sc->sc_p->p_vcomh_deselect_level);
816 if (error)
817 return error;
818 SSDFB_CMD2(SSD1322_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL,
819 SSD1322_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL);
820 if (error)
821 return error;
822 SSDFB_CMD2(SSD1322_CMD_SET_GPIO,
823 SSD1322_GPIO0_DISABLED | SSD1322_GPIO1_DISABLED);
824 if (error)
825 return error;
826
827 /*
828 * Configure timing characteristics.
829 */
830 SSDFB_CMD2(SSD1322_CMD_SET_FRONT_CLOCK_DIVIDER,
831 __SHIFTIN(sc->sc_p->p_fosc, SSD1322_FREQUENCY_MASK) |
832 __SHIFTIN(sc->sc_p->p_fosc_div, SSD1322_DIVIDER_MASK));
833 if (error)
834 return error;
835 SSDFB_CMD2(SSD1322_CMD_SET_PHASE_LENGTH,
836 __SHIFTIN(SSD1322_DEFAULT_PHASE_2,
837 SSD1322_PHASE_LENGTH_PHASE_2_MASK) |
838 __SHIFTIN(SSD1322_DEFAULT_PHASE_1,
839 SSD1322_PHASE_LENGTH_PHASE_1_MASK));
840 if (error)
841 return error;
842 SSDFB_CMD2(SSD1322_CMD_SET_SECOND_PRECHARGE_PERIOD,
843 SSD1322_DEFAULT_SECOND_PRECHARGE_PERIOD);
844 if (error)
845 return error;
846
847 /*
848 * Configure physical display panel layout.
849 */
850 SSDFB_CMD2(SSD1322_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio);
851 if (error)
852 return error;
853 if (sc->sc_upsidedown)
854 remap = 0x10;
855 else
856 remap = 0x2;
857 dualcom = 0x1;
858 if (sc->sc_p->p_multiplex_ratio <= 63)
859 dualcom |= 0x10;
860 SSDFB_CMD3(SSD1322_CMD_SET_REMAP_AND_DUAL_COM_LINE_MODE, remap, dualcom);
861 if (error)
862 return error;
863
864 /*
865 * Contrast settings.
866 */
867 SSDFB_CMD1(SSD1322_CMD_SET_DEFAULT_GRAY_SCALE_TABLE);
868 if (error)
869 return error;
870 SSDFB_CMD3(SSD1322_CMD_DISPLAY_ENHANCEMENT_A,
871 SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC1,
872 SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC2);
873 if (error)
874 return error;
875 SSDFB_CMD3(SSD1322_CMD_DISPLAY_ENHANCEMENT_B,
876 SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC1,
877 SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC2);
878 if (error)
879 return error;
880 SSDFB_CMD2(SSD1322_CMD_SET_CONTRAST_CURRENT,
881 sc->sc_contrast);
882 if (error)
883 return error;
884 SSDFB_CMD2(SSD1322_CMD_MASTER_CONTRAST_CURRENT_CONTROL,
885 SSD1322_DEFAULT_MASTER_CONTRAST_CURRENT_CONTROL);
886 if (error)
887 return error;
888
889 /*
890 * Reset display engine state.
891 */
892 SSDFB_CMD2(SSD1322_CMD_SET_DISPLAY_OFFSET, 0x00);
893 if (error)
894 return error;
895 SSDFB_CMD2(SSD1322_CMD_SET_DISPLAY_START_LINE, 0x00);
896 if (error)
897 return error;
898 SSDFB_CMD1(SSD1322_CMD_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse);
899 if (error)
900 return error;
901 SSDFB_CMD1(SSD1322_CMD_EXIT_PARTIAL_DISPLAY);
902 if (error)
903 return error;
904
905 ssdfb_clear_screen(sc);
906 error = ssdfb_sync(sc, usepoll);
907 if (error)
908 return error;
909
910 error = ssdfb_set_display_on(sc, true, usepoll);
911
912 return error;
913 }
914
915 static int
916 ssdfb_init_ssd1353(struct ssdfb_softc *sc)
917 {
918 int error;
919 uint8_t cmd[3];
920 bool usepoll = true;
921 uint8_t remap;
922
923 /*
924 * Enter sleep.
925 */
926 SSDFB_CMD2(SSD1353_CMD_SET_COMMAND_LOCK, SSD1353_COMMAND_UNLOCK_MAGIC);
927 if (error)
928 return error;
929 SSDFB_CMD1(SSD1353_CMD_RESET);
930 if (error)
931 return error;
932 SSDFB_CMD1(SSD1353_CMD_DEACTIVATE_SCROLL);
933 if (error)
934 return error;
935 SSDFB_CMD1(SSD1353_CMD_SET_DISPLAY_OFF);
936 if (error)
937 return error;
938
939 /*
940 * Start charge pumps.
941 */
942 SSDFB_CMD2(SSD1353_CMD_SET_VCOMH, sc->sc_p->p_vcomh_deselect_level);
943 if (error)
944 return error;
945 SSDFB_CMD2(SSD1353_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL,
946 SSD1353_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL);
947 if (error)
948 return error;
949
950 /*
951 * Configure timing characteristics.
952 */
953 SSDFB_CMD2(SSD1353_CMD_SET_FRONT_CLOCK_DIVIDER,
954 __SHIFTIN(sc->sc_p->p_fosc, SSD1322_FREQUENCY_MASK) |
955 __SHIFTIN(sc->sc_p->p_fosc_div, SSD1322_DIVIDER_MASK));
956 if (error)
957 return error;
958 SSDFB_CMD2(SSD1353_CMD_SET_PHASE_LENGTH,
959 __SHIFTIN(SSD1353_DEFAULT_PHASE_2,
960 SSD1322_PHASE_LENGTH_PHASE_2_MASK) |
961 __SHIFTIN(SSD1353_DEFAULT_PHASE_1,
962 SSD1322_PHASE_LENGTH_PHASE_1_MASK));
963 if (error)
964 return error;
965 SSDFB_CMD2(SSD1353_CMD_SET_SECOND_PRECHARGE_PERIOD,
966 SSD1353_DEFAULT_SECOND_PRECHARGE_PERIOD);
967 if (error)
968 return error;
969 SSDFB_CMD2(SSD1353_CMD_SET_SECOND_PRECHARGE_SPEED,
970 SSD1353_DEFAULT_SECOND_PRECHARGE_SPEED);
971 if (error)
972 return error;
973
974 /*
975 * Configure physical display panel layout.
976 */
977 SSDFB_CMD2(SSD1353_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio);
978 if (error)
979 return error;
980 remap = sc->sc_p->p_compin_cfg;
981 if (sc->sc_upsidedown)
982 remap ^= SSD1353_REMAP_COM_DIRECTION;
983 else
984 remap ^= SSD1353_REMAP_SEG_DIRECTION;
985 SSDFB_CMD2(SSD1353_CMD_REMAP_COLOR_DEPTH, remap);
986 if (error)
987 return error;
988
989 /*
990 * Contrast settings.
991 */
992 SSDFB_CMD1(SSD1353_CMD_SET_DEFAULT_GRAY_SCALE_TABLE);
993 if (error)
994 return error;
995 SSDFB_CMD2(SSD1353_CMD_SET_CONTRAST_CONTROL_A, sc->sc_contrast);
996 if (error)
997 return error;
998 SSDFB_CMD2(SSD1353_CMD_SET_CONTRAST_CONTROL_B, sc->sc_contrast);
999 if (error)
1000 return error;
1001 SSDFB_CMD2(SSD1353_CMD_SET_CONTRAST_CONTROL_C, sc->sc_contrast);
1002 if (error)
1003 return error;
1004 SSDFB_CMD2(SSD1353_CMD_MASTER_CURRENT_CONTROL,
1005 SSD1353_DEFAULT_MASTER_CURRENT_ATTENUATION);
1006 if (error)
1007 return error;
1008
1009 /*
1010 * Reset display engine state.
1011 */
1012 SSDFB_CMD2(SSD1353_CMD_SET_DISPLAY_OFFSET, 0x00);
1013 if (error)
1014 return error;
1015 SSDFB_CMD2(SSD1353_CMD_SET_DISPLAY_START_LINE, 0x00);
1016 if (error)
1017 return error;
1018 SSDFB_CMD1(sc->sc_inverse
1019 ? SSD1353_CMD_INVERSE_DISPLAY
1020 : SSD1353_CMD_NORMAL_DISPLAY);
1021 if (error)
1022 return error;
1023
1024 ssdfb_clear_screen(sc);
1025 error = ssdfb_sync(sc, usepoll);
1026 if (error)
1027 return error;
1028
1029 error = ssdfb_set_display_on(sc, true, usepoll);
1030
1031 return error;
1032 }
1033
1034 static int
1035 ssdfb_set_contrast(struct ssdfb_softc *sc, uint8_t value, bool usepoll)
1036 {
1037 uint8_t cmd[2];
1038 int error;
1039
1040 cmd[1] = sc->sc_contrast = value;
1041 switch (sc->sc_p->p_controller_id) {
1042 case SSDFB_CONTROLLER_SSD1322:
1043 cmd[0] = SSD1322_CMD_SET_CONTRAST_CURRENT;
1044 break;
1045 case SSDFB_CONTROLLER_SSD1353:
1046 cmd[0] = SSD1353_CMD_SET_CONTRAST_CONTROL_A;
1047 error = sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll);
1048 if (error)
1049 return error;
1050 cmd[0] = SSD1353_CMD_SET_CONTRAST_CONTROL_B;
1051 error = sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll);
1052 if (error)
1053 return error;
1054 cmd[0] = SSD1353_CMD_SET_CONTRAST_CONTROL_C;
1055 default:
1056 cmd[0] = SSDFB_CMD_SET_CONTRAST_CONTROL;
1057 }
1058
1059 return sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll);
1060 }
1061
1062 static int
1063 ssdfb_set_display_on(struct ssdfb_softc *sc, bool value, bool usepoll)
1064 {
1065 uint8_t cmd[1];
1066 int error;
1067 sc->sc_display_on = value;
1068
1069 SSDFB_CMD1(value ? SSDFB_CMD_SET_DISPLAY_ON : SSDFB_CMD_SET_DISPLAY_OFF);
1070
1071 return error;
1072 }
1073
1074 static int
1075 ssdfb_set_mode(struct ssdfb_softc *sc, u_int mode)
1076 {
1077 switch (mode) {
1078 case WSDISPLAYIO_MODE_EMUL:
1079 case WSDISPLAYIO_MODE_DUMBFB:
1080 break;
1081 default:
1082 return EINVAL;
1083 }
1084 if (mode == sc->sc_mode)
1085 return 0;
1086 mutex_enter(&sc->sc_cond_mtx);
1087 sc->sc_mode = mode;
1088 cv_broadcast(&sc->sc_cond);
1089 mutex_exit(&sc->sc_cond_mtx);
1090 ssdfb_clear_screen(sc);
1091 ssdfb_damage(sc);
1092
1093 return 0;
1094 }
1095
1096 static void
1097 ssdfb_damage(struct ssdfb_softc *sc)
1098 {
1099 int s;
1100
1101 if (sc->sc_usepoll) {
1102 (void) ssdfb_sync(sc, true);
1103 } else {
1104 /*
1105 * kernel code isn't permitted to call us via kprintf at
1106 * splhigh. In case misbehaving code calls us anyway we can't
1107 * safely take the mutex so we skip the damage notification.
1108 */
1109 if (sc->sc_is_console) {
1110 s = splhigh();
1111 splx(s);
1112 if (s == IPL_HIGH)
1113 return;
1114 }
1115 mutex_enter(&sc->sc_cond_mtx);
1116 sc->sc_modified = true;
1117 cv_broadcast(&sc->sc_cond);
1118 mutex_exit(&sc->sc_cond_mtx);
1119 }
1120 }
1121
1122 static void
1123 ssdfb_udv_attach(struct ssdfb_softc *sc)
1124 {
1125 extern const struct cdevsw wsdisplay_cdevsw;
1126 dev_t dev;
1127 #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen))
1128 dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw),
1129 WSDISPLAYMINOR(device_unit(sc->sc_wsdisplay), 0));
1130 sc->sc_uobj = udv_attach(dev, VM_PROT_READ|VM_PROT_WRITE, 0,
1131 sc->sc_ri_bits_len);
1132 }
1133
1134 static bool
1135 ssdfb_is_modified(struct ssdfb_softc *sc)
1136 {
1137 vaddr_t va, va_end;
1138
1139 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)
1140 return sc->sc_modified;
1141
1142 if (sc->sc_uobj == NULL)
1143 return false;
1144
1145 va = (vaddr_t)sc->sc_ri.ri_bits;
1146 va_end = va + sc->sc_ri_bits_len;
1147 while (va < va_end) {
1148 if (pmap_is_modified(uvm_pageratop(va)))
1149 return true;
1150 va += PAGE_SIZE;
1151 }
1152
1153 return false;
1154 }
1155
1156 static bool
1157 ssdfb_clear_modify(struct ssdfb_softc *sc)
1158 {
1159 vaddr_t va, va_end;
1160 bool ret;
1161
1162 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
1163 mutex_enter(&sc->sc_cond_mtx);
1164 ret = sc->sc_modified;
1165 sc->sc_modified = false;
1166 mutex_exit(&sc->sc_cond_mtx);
1167 return ret;
1168 }
1169
1170 if (sc->sc_uobj == NULL)
1171 return false;
1172
1173 va = (vaddr_t)sc->sc_ri.ri_bits;
1174 va_end = va + sc->sc_ri_bits_len;
1175 ret = false;
1176 while (va < va_end) {
1177 if (pmap_clear_modify(uvm_pageratop(va)))
1178 ret = true;
1179 va += PAGE_SIZE;
1180 }
1181
1182 return ret;
1183 }
1184
1185 static void
1186 ssdfb_thread(void *arg)
1187 {
1188 struct ssdfb_softc *sc = (struct ssdfb_softc *)arg;
1189 int error;
1190
1191 mutex_enter(&sc->sc_cond_mtx);
1192
1193 if (sc->sc_usepoll)
1194 ssdfb_set_usepoll(sc, false);
1195
1196 while(!sc->sc_detaching) {
1197 if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB &&
1198 sc->sc_uobj == NULL) {
1199 mutex_exit(&sc->sc_cond_mtx);
1200 ssdfb_udv_attach(sc);
1201 mutex_enter(&sc->sc_cond_mtx);
1202 }
1203 if (!ssdfb_is_modified(sc)) {
1204 if (cv_timedwait(&sc->sc_cond, &sc->sc_cond_mtx,
1205 sc->sc_mode == WSDISPLAYIO_MODE_EMUL
1206 ? 0 : sc->sc_backoff) == EWOULDBLOCK
1207 && sc->sc_backoff < mstohz(200)) {
1208 sc->sc_backoff <<= 1;
1209 }
1210 continue;
1211 }
1212 sc->sc_backoff = 1;
1213 mutex_exit(&sc->sc_cond_mtx);
1214 (void) ssdfb_clear_modify(sc);
1215 if (!sc->sc_usepoll) {
1216 error = ssdfb_sync(sc, false);
1217 if (error)
1218 device_printf(sc->sc_dev,
1219 "ssdfb_sync: error %d\n",
1220 error);
1221 }
1222 mutex_enter(&sc->sc_cond_mtx);
1223 }
1224
1225 mutex_exit(&sc->sc_cond_mtx);
1226 kthread_exit(0);
1227 }
1228
1229 static void
1230 ssdfb_set_usepoll(struct ssdfb_softc *sc, bool enable)
1231 {
1232 sc->sc_usepoll = enable;
1233 }
1234
1235 static int
1236 ssdfb_sync(struct ssdfb_softc *sc, bool usepoll)
1237 {
1238 return sc->sc_p->p_sync(sc, usepoll);
1239 }
1240
1241 static int
1242 ssdfb_sync_ssd1306(struct ssdfb_softc *sc, bool usepoll)
1243 {
1244 struct rasops_info *ri = &sc->sc_ri;
1245 int block_size = 8;
1246 int ri_block_stride = ri->ri_stride * block_size;
1247 int height_in_blocks = sc->sc_p->p_height / block_size;
1248 int width_in_blocks = sc->sc_p->p_width / block_size;
1249 int ri_block_step = block_size * ri->ri_depth / 8;
1250 int x, y;
1251 union ssdfb_block *blockp;
1252 uint64_t raw_block;
1253 uint8_t *src;
1254 int x1, x2, y1, y2;
1255
1256 /*
1257 * Transfer rasops bitmap into gddram shadow buffer while keeping track
1258 * of the bounding box of the dirty region we scribbled over.
1259 */
1260 x1 = width_in_blocks;
1261 x2 = -1;
1262 y1 = height_in_blocks;
1263 y2 = -1;
1264 for (y = 0; y < height_in_blocks; y++) {
1265 src = &ri->ri_bits[y * ri_block_stride];
1266 blockp = &sc->sc_gddram[y * width_in_blocks];
1267 for (x = 0; x < width_in_blocks; x++) {
1268 raw_block = ssdfb_transpose_block(src, ri->ri_stride);
1269 if (raw_block != blockp->raw) {
1270 blockp->raw = raw_block;
1271 if (x1 > x)
1272 x1 = x;
1273 if (x2 < x)
1274 x2 = x;
1275 if (y1 > y)
1276 y1 = y;
1277 if (y2 < y)
1278 y2 = y;
1279 }
1280 src += ri_block_step;
1281 blockp++;
1282 }
1283 }
1284 if (x2 != -1)
1285 return sc->sc_transfer_rect(sc->sc_cookie,
1286 x1 * block_size + sc->sc_p->p_panel_shift,
1287 (x2 + 1) * block_size - 1 + sc->sc_p->p_panel_shift,
1288 y1,
1289 y2,
1290 &sc->sc_gddram[y1 * width_in_blocks + x1].col[0],
1291 sc->sc_p->p_width,
1292 usepoll);
1293
1294 return 0;
1295 }
1296
1297 static int
1298 ssdfb_sync_ssd1322(struct ssdfb_softc *sc, bool usepoll)
1299 {
1300 struct rasops_info *ri = &sc->sc_ri;
1301 int block_size_w = 4;
1302 int width = sc->sc_p->p_width;
1303 int height = sc->sc_p->p_height;
1304 int width_in_blocks = width / block_size_w;
1305 int x, y;
1306 uint16_t *blockp;
1307 uint16_t raw_block;
1308 uint16_t *src;
1309 uint32_t *src32;
1310 int x1, x2, y1, y2;
1311
1312 /*
1313 * Transfer rasops bitmap into gddram shadow buffer while keeping track
1314 * of the bounding box of the dirty region we scribbled over.
1315 */
1316 x1 = width;
1317 x2 = -1;
1318 y1 = height;
1319 y2 = -1;
1320 blockp = (uint16_t*)sc->sc_gddram;
1321 for (y = 0; y < height; y++) {
1322 src = (uint16_t*)&ri->ri_bits[y * ri->ri_stride];
1323 src32 = (uint32_t*)src;
1324 for (x = 0; x < width_in_blocks; x++) {
1325 #ifdef SSDFB_USE_NATIVE_DEPTH
1326 raw_block =
1327 ((*src << 12) & 0xf000) |
1328 ((*src << 4) & 0x0f00) |
1329 ((*src >> 4) & 0x00f0) |
1330 ((*src >> 12) & 0x000f);
1331 src++;
1332 #else
1333 raw_block =
1334 # if _BYTE_ORDER == _LITTLE_ENDIAN
1335 ((*src32 << 8) & 0x0f00) |
1336 ((*src32 << 4) & 0xf000) |
1337 ((*src32 >> 16) & 0x000f) |
1338 ((*src32 >> 20) & 0x00f0);
1339 # else
1340 ((*src32 >> 24) & 0x000f) |
1341 ((*src32 >> 12) & 0x00f0) |
1342 ((*src32 ) & 0x0f00) |
1343 ((*src32 << 12) & 0xf000);
1344 # endif
1345 src32++;
1346 #endif
1347 if (raw_block != *blockp) {
1348 *blockp = raw_block;
1349 if (x1 > x)
1350 x1 = x;
1351 if (x2 < x)
1352 x2 = x;
1353 if (y1 > y)
1354 y1 = y;
1355 if (y2 < y)
1356 y2 = y;
1357 }
1358 blockp++;
1359 }
1360 }
1361 blockp = (uint16_t*)sc->sc_gddram;
1362 if (x2 != -1)
1363 return sc->sc_transfer_rect(sc->sc_cookie,
1364 x1 + sc->sc_p->p_panel_shift,
1365 x2 + sc->sc_p->p_panel_shift,
1366 y1,
1367 y2,
1368 (uint8_t*)&blockp[y1 * width_in_blocks + x1],
1369 width * sc->sc_p->p_bits_per_pixel / 8,
1370 usepoll);
1371 return 0;
1372 }
1373
1374 static int
1375 ssdfb_sync_ssd1353(struct ssdfb_softc *sc, bool usepoll)
1376 {
1377 int width = sc->sc_p->p_width;
1378 int height = sc->sc_p->p_height;
1379 struct rasops_info *ri = &sc->sc_ri;
1380 int x, y;
1381 uint32_t *src, *blockp;
1382 int x1, x2, y1, y2;
1383
1384 /*
1385 * Transfer rasops bitmap into gddram shadow buffer while keeping track
1386 * of the bounding box of the dirty region we scribbled over.
1387 */
1388 x1 = width;
1389 x2 = -1;
1390 y1 = height;
1391 y2 = -1;
1392 blockp = (uint32_t*)sc->sc_gddram;
1393 for (y = 0; y < height; y++) {
1394 src = (uint32_t*)&ri->ri_bits[y * ri->ri_stride];
1395 for (x = 0; x < width; x++) {
1396 if (*blockp != *src) {
1397 *blockp = *src;
1398 if (x1 > x)
1399 x1 = x;
1400 if (x2 < x)
1401 x2 = x;
1402 if (y1 > y)
1403 y1 = y;
1404 if (y2 < y)
1405 y2 = y;
1406 }
1407 blockp++;
1408 src++;
1409 }
1410 }
1411
1412 blockp = (uint32_t*)sc->sc_gddram;
1413 if (x2 != -1)
1414 return sc->sc_transfer_rect(sc->sc_cookie,
1415 x1 + sc->sc_p->p_panel_shift,
1416 x2 + sc->sc_p->p_panel_shift,
1417 y1,
1418 y2,
1419 (uint8_t*)&blockp[y1 * width + x1],
1420 width * sc->sc_p->p_bits_per_pixel / 8,
1421 usepoll);
1422 return 0;
1423 }
1424
1425 static uint64_t
1426 ssdfb_transpose_block(uint8_t *src, size_t src_stride)
1427 {
1428 uint64_t x = 0;
1429 #ifdef SSDFB_USE_NATIVE_DEPTH
1430 uint64_t t;
1431 int i;
1432
1433 /*
1434 * collect the 8x8 block.
1435 */
1436 for (i = 0; i < 8; i++) {
1437 x >>= 8;
1438 x |= (uint64_t)src[i * src_stride] << 56;
1439 }
1440
1441 /*
1442 * Transpose it into gddram layout.
1443 * Post-transpose bswap is the same as pre-transpose bit order reversal.
1444 * We do this to match rasops1 bit order.
1445 */
1446 t = (x ^ (x >> 28)) & 0x00000000F0F0F0F0ULL;
1447 x = x ^ t ^ (t << 28);
1448 t = (x ^ (x >> 14)) & 0x0000CCCC0000CCCCULL;
1449 x = x ^ t ^ (t << 14);
1450 t = (x ^ (x >> 7)) & 0x00AA00AA00AA00AAULL;
1451 x = x ^ t ^ (t << 7);
1452 x = bswap64(x);
1453 #else
1454 int m, n;
1455
1456 for (m = 0; m < 8; m++) {
1457 for (n = 0; n < 8; n++) {
1458 x >>= 1;
1459 x |= src[n * src_stride + m] ? (1ULL << 63) : 0;
1460 }
1461 }
1462 #endif
1463 return htole64(x);
1464 }
1465
1466 static const struct ssdfb_product *
1467 ssdfb_lookup_product(ssdfb_product_id_t id)
1468 {
1469 int i;
1470
1471 for (i = 0; i < __arraycount(ssdfb_products); i++) {
1472 if (ssdfb_products[i].p_product_id == id)
1473 return &ssdfb_products[i];
1474 }
1475
1476 return NULL;
1477 }
1478
1479 static int
1480 ssdfb_pick_font(int *cookiep, struct wsdisplay_font **fontp)
1481 {
1482 int error;
1483 int c;
1484 struct wsdisplay_font *f;
1485 int i;
1486 uint8_t d[4][2] = {{5, 8}, {8, 8}, {8, 10} ,{8, 16}};
1487
1488 /*
1489 * Try to find fonts in order of increasing size.
1490 */
1491 wsfont_init();
1492 for(i = 0; i < __arraycount(d); i++) {
1493 c = wsfont_find(NULL, d[i][0], d[i][1], 0,
1494 WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R,
1495 WSFONT_FIND_BITMAP);
1496 if (c > 0)
1497 break;
1498 }
1499 if (c <= 0)
1500 return ENOENT;
1501 error = wsfont_lock(c, &f);
1502 if (error)
1503 return error;
1504 *cookiep = c;
1505 *fontp = f;
1506
1507 return 0;
1508 }
1509
1510 static void
1511 ssdfb_clear_screen(struct ssdfb_softc *sc)
1512 {
1513 struct rasops_info *ri = &sc->sc_ri;
1514
1515 memset(sc->sc_gddram, 0xff, sc->sc_gddram_len);
1516 memset(ri->ri_bits, 0, sc->sc_ri_bits_len);
1517 }
1518
1519 #if defined(DDB)
1520 static void
1521 ssdfb_ddb_trap_callback(int enable)
1522 {
1523 extern struct cfdriver ssdfb_cd;
1524 struct ssdfb_softc *sc;
1525 int i;
1526
1527 for (i = 0; i < ssdfb_cd.cd_ndevs; i++) {
1528 sc = device_lookup_private(&ssdfb_cd, i);
1529 if (sc != NULL && sc->sc_is_console) {
1530 ssdfb_set_usepoll(sc, (bool)enable);
1531 }
1532 }
1533 }
1534 #endif
1535