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