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