1*fc683fa3Stnn /* $NetBSD: ssdfb.c,v 1.22 2021/08/21 17:50:02 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*fc683fa3Stnn __KERNEL_RCSID(0, "$NetBSD: ssdfb.c,v 1.22 2021/08/21 17:50:02 tnn Exp $");
34e690818eStnn
35e690818eStnn #include "opt_ddb.h"
36e690818eStnn
37e690818eStnn #include <sys/param.h>
38e690818eStnn #include <sys/kernel.h>
3933ea73e5Stnn #include <sys/conf.h>
40e690818eStnn #include <sys/condvar.h>
41e690818eStnn #include <sys/kmem.h>
42e690818eStnn #include <sys/kthread.h>
43ea5c7e85Sriastradh
44ea5c7e85Sriastradh #include <uvm/uvm_device.h>
45ea5c7e85Sriastradh #include <uvm/uvm_extern.h>
46f379a12fStnn #ifdef pmap_is_modified
47f379a12fStnn #include <uvm/uvm_page.h>
48f379a12fStnn #endif
49ea5c7e85Sriastradh
50e690818eStnn #include <dev/wscons/wsdisplayvar.h>
51e690818eStnn #include <dev/rasops/rasops.h>
52e690818eStnn #include <dev/ic/ssdfbvar.h>
53e690818eStnn
54e690818eStnn #if defined(DDB)
55e690818eStnn #include <machine/db_machdep.h>
56e690818eStnn #include <ddb/db_extern.h>
57e690818eStnn #endif
58e690818eStnn
59e690818eStnn /* userland interface */
60e690818eStnn static int ssdfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
61e690818eStnn static paddr_t ssdfb_mmap(void *, void *, off_t, int);
62e690818eStnn
63e690818eStnn /* wscons screen management */
64e690818eStnn static int ssdfb_alloc_screen(void *, const struct wsscreen_descr *,
65e690818eStnn void **, int *, int *, long *);
66e690818eStnn static void ssdfb_free_screen(void *, void *);
67e690818eStnn static int ssdfb_show_screen(void *, void *, int,
68e690818eStnn void (*cb) (void *, int, int), void *);
69e690818eStnn
70e690818eStnn /* rasops hooks */
71e690818eStnn static void ssdfb_putchar(void *, int, int, u_int, long);
72e690818eStnn static void ssdfb_copycols(void *, int, int, int, int);
73e690818eStnn static void ssdfb_erasecols(void *, int, int, int, long);
74e690818eStnn static void ssdfb_copyrows(void *, int, int, int);
75e690818eStnn static void ssdfb_eraserows(void *, int, int, long);
76e690818eStnn static void ssdfb_cursor(void *, int, int, int);
77e690818eStnn
78e690818eStnn /* hardware interface */
798e57c9bfStnn static int ssdfb_init_ssd1306(struct ssdfb_softc *);
80ef712effStnn static int ssdfb_init_ssd1322(struct ssdfb_softc *);
8123cac501Stnn static int ssdfb_init_ssd1353(struct ssdfb_softc *);
82e690818eStnn static int ssdfb_set_contrast(struct ssdfb_softc *, uint8_t, bool);
83e690818eStnn static int ssdfb_set_display_on(struct ssdfb_softc *, bool, bool);
84e690818eStnn static int ssdfb_set_mode(struct ssdfb_softc *, u_int);
85e690818eStnn
86e690818eStnn /* frame buffer damage tracking and synchronization */
8733ea73e5Stnn static void ssdfb_udv_attach(struct ssdfb_softc *sc);
88e690818eStnn static bool ssdfb_is_modified(struct ssdfb_softc *sc);
89e690818eStnn static bool ssdfb_clear_modify(struct ssdfb_softc *sc);
90e690818eStnn static void ssdfb_damage(struct ssdfb_softc *);
91e690818eStnn static void ssdfb_thread(void *);
92e690818eStnn static void ssdfb_set_usepoll(struct ssdfb_softc *, bool);
93e690818eStnn static int ssdfb_sync(struct ssdfb_softc *, bool);
94ef712effStnn static int ssdfb_sync_ssd1306(struct ssdfb_softc *, bool);
95ef712effStnn static int ssdfb_sync_ssd1322(struct ssdfb_softc *, bool);
9623cac501Stnn static int ssdfb_sync_ssd1353(struct ssdfb_softc *, bool);
97ef712effStnn static uint64_t ssdfb_transpose_block(uint8_t *, size_t);
98e690818eStnn
99e690818eStnn /* misc helpers */
100e690818eStnn static const struct ssdfb_product *
101e690818eStnn ssdfb_lookup_product(ssdfb_product_id_t);
102e690818eStnn static int ssdfb_pick_font(int *, struct wsdisplay_font **);
103e690818eStnn static void ssdfb_clear_screen(struct ssdfb_softc *);
104e690818eStnn #if defined(DDB)
105e690818eStnn static void ssdfb_ddb_trap_callback(int);
106e690818eStnn #endif
107e690818eStnn
108e690818eStnn static const char *ssdfb_controller_names[] = {
109e690818eStnn [SSDFB_CONTROLLER_UNKNOWN] = "unknown",
110e690818eStnn [SSDFB_CONTROLLER_SSD1306] = "Solomon Systech SSD1306",
1118e57c9bfStnn [SSDFB_CONTROLLER_SH1106] = "Sino Wealth SH1106",
11223cac501Stnn [SSDFB_CONTROLLER_SSD1322] = "Solomon Systech SSD1322",
11323cac501Stnn [SSDFB_CONTROLLER_SSD1353] = "Solomon Systech SSD1353"
114e690818eStnn };
115e690818eStnn
116e690818eStnn /*
117e690818eStnn * Display module assemblies supported by this driver.
118e690818eStnn */
119e690818eStnn static const struct ssdfb_product ssdfb_products[] = {
120e690818eStnn {
121e690818eStnn .p_product_id = SSDFB_PRODUCT_SSD1306_GENERIC,
122e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SSD1306,
123e690818eStnn .p_name = "generic",
124e690818eStnn .p_width = 128,
125e690818eStnn .p_height = 64,
126ef712effStnn .p_bits_per_pixel = 1,
127e690818eStnn .p_panel_shift = 0,
128e690818eStnn .p_fosc = 0x8,
129e690818eStnn .p_fosc_div = 0,
130e690818eStnn .p_precharge = 0x1,
131e690818eStnn .p_discharge = 0xf,
132e690818eStnn .p_compin_cfg = SSDFB_COM_PINS_A1_MASK
133e690818eStnn | SSDFB_COM_PINS_ALTERNATIVE_MASK,
134e690818eStnn .p_vcomh_deselect_level = SSD1306_VCOMH_DESELECT_LEVEL_0_77_VCC,
135e690818eStnn .p_default_contrast = 0x7f,
136e690818eStnn .p_multiplex_ratio = 0x3f,
137ef712effStnn .p_init = ssdfb_init_ssd1306,
138ef712effStnn .p_sync = ssdfb_sync_ssd1306
139e690818eStnn },
140e690818eStnn {
141e690818eStnn .p_product_id = SSDFB_PRODUCT_SH1106_GENERIC,
142e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SH1106,
143e690818eStnn .p_name = "generic",
144e690818eStnn .p_width = 128,
145e690818eStnn .p_height = 64,
146ef712effStnn .p_bits_per_pixel = 1,
147e690818eStnn .p_panel_shift = 2,
148e690818eStnn .p_fosc = 0x5,
149e690818eStnn .p_fosc_div = 0,
150e690818eStnn .p_precharge = 0x2,
151e690818eStnn .p_discharge = 0x2,
152e690818eStnn .p_compin_cfg = SSDFB_COM_PINS_A1_MASK
153e690818eStnn | SSDFB_COM_PINS_ALTERNATIVE_MASK,
154e690818eStnn .p_vcomh_deselect_level = SH1106_VCOMH_DESELECT_LEVEL_DEFAULT,
155e690818eStnn .p_default_contrast = 0x80,
156e690818eStnn .p_multiplex_ratio = 0x3f,
157ef712effStnn .p_init = ssdfb_init_ssd1306,
158ef712effStnn .p_sync = ssdfb_sync_ssd1306
159e690818eStnn },
160e690818eStnn {
161e690818eStnn .p_product_id = SSDFB_PRODUCT_ADAFRUIT_938,
162e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SSD1306,
163e690818eStnn .p_name = "Adafruit Industries, LLC product 938",
164e690818eStnn .p_width = 128,
165e690818eStnn .p_height = 64,
166ef712effStnn .p_bits_per_pixel = 1,
167e690818eStnn .p_panel_shift = 0,
168e690818eStnn .p_fosc = 0x8,
169e690818eStnn .p_fosc_div = 0,
170e690818eStnn .p_precharge = 0x1,
171e690818eStnn .p_discharge = 0xf,
172e690818eStnn .p_compin_cfg = 0x12,
173e690818eStnn .p_vcomh_deselect_level = 0x40,
174e690818eStnn .p_default_contrast = 0x8f,
175e690818eStnn .p_multiplex_ratio = 0x3f,
176ef712effStnn .p_init = ssdfb_init_ssd1306,
177ef712effStnn .p_sync = ssdfb_sync_ssd1306
178e690818eStnn },
179e690818eStnn {
180e690818eStnn .p_product_id = SSDFB_PRODUCT_ADAFRUIT_931,
181e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SSD1306,
182e690818eStnn .p_name = "Adafruit Industries, LLC product 931",
183e690818eStnn .p_width = 128,
184e690818eStnn .p_height = 32,
185ef712effStnn .p_bits_per_pixel = 1,
186e690818eStnn .p_panel_shift = 0,
187e690818eStnn .p_fosc = 0x8,
188e690818eStnn .p_fosc_div = 0,
189e690818eStnn .p_precharge = 0x1,
190e690818eStnn .p_discharge = 0xf,
191e690818eStnn .p_compin_cfg = 0x2,
192e690818eStnn .p_vcomh_deselect_level = 0x40,
193e690818eStnn .p_default_contrast = 0x8f,
194e690818eStnn .p_multiplex_ratio = 0x1f,
195ef712effStnn .p_init = ssdfb_init_ssd1306,
196ef712effStnn .p_sync = ssdfb_sync_ssd1306
197ef712effStnn },
198ef712effStnn {
199ef712effStnn .p_product_id = SSDFB_PRODUCT_SSD1322_GENERIC,
200ef712effStnn .p_controller_id = SSDFB_CONTROLLER_SSD1322,
201ef712effStnn .p_name = "generic",
202ef712effStnn .p_width = 256,
203ef712effStnn .p_height = 64,
204ef712effStnn .p_bits_per_pixel = 4,
205ef712effStnn .p_panel_shift = 28,
206ef712effStnn .p_vcomh_deselect_level = SSD1322_DEFAULT_VCOMH,
207ef712effStnn .p_fosc = SSD1322_DEFAULT_FREQUENCY,
208ef712effStnn .p_fosc_div = SSD1322_DEFAULT_DIVIDER,
209ef712effStnn .p_default_contrast = SSD1322_DEFAULT_CONTRAST_CURRENT,
210ef712effStnn .p_multiplex_ratio = 0x3f,
211ef712effStnn .p_init = ssdfb_init_ssd1322,
212ef712effStnn .p_sync = ssdfb_sync_ssd1322
21323cac501Stnn },
21423cac501Stnn {
21523cac501Stnn .p_product_id = SSDFB_PRODUCT_SSD1353_GENERIC,
21623cac501Stnn .p_controller_id = SSDFB_CONTROLLER_SSD1353,
21723cac501Stnn .p_name = "generic",
21823cac501Stnn .p_width = 160,
21923cac501Stnn .p_height = 132,
22023cac501Stnn .p_bits_per_pixel = 32,
22123cac501Stnn .p_rgb = true,
22223cac501Stnn .p_panel_shift = 0,
22323cac501Stnn .p_compin_cfg = SSD1353_REMAP_RGB | SSD1353_REMAP_SPLIT_ODD_EVEN
22423cac501Stnn | __SHIFTIN(2, SSD1353_REMAP_PIXEL_FORMAT_MASK),
22523cac501Stnn .p_vcomh_deselect_level = SSD1353_DEFAULT_VCOMH,
22623cac501Stnn .p_fosc = SSD1353_DEFAULT_FREQUENCY,
22723cac501Stnn .p_fosc_div = SSD1353_DEFAULT_DIVIDER,
22823cac501Stnn .p_default_contrast = SSD1353_DEFAULT_CONTRAST_CONTROL,
22923cac501Stnn .p_multiplex_ratio = 0x83,
23023cac501Stnn .p_init = ssdfb_init_ssd1353,
23123cac501Stnn .p_sync = ssdfb_sync_ssd1353
23223cac501Stnn },
23323cac501Stnn {
23423cac501Stnn .p_product_id = SSDFB_PRODUCT_DEP_160128A_RGB,
23523cac501Stnn .p_controller_id = SSDFB_CONTROLLER_SSD1353,
23623cac501Stnn .p_name = "Display Elektronik GmbH DEP 160128A(1)-RGB",
23723cac501Stnn .p_width = 160,
23823cac501Stnn .p_height = 128,
23923cac501Stnn .p_bits_per_pixel = 32,
24023cac501Stnn .p_rgb = true,
24123cac501Stnn .p_panel_shift = 0,
24223cac501Stnn .p_compin_cfg = SSD1353_REMAP_RGB | SSD1353_REMAP_SPLIT_ODD_EVEN
24323cac501Stnn | __SHIFTIN(2, SSD1353_REMAP_PIXEL_FORMAT_MASK),
24423cac501Stnn .p_vcomh_deselect_level = SSD1353_DEFAULT_VCOMH,
24523cac501Stnn .p_fosc = SSD1353_DEFAULT_FREQUENCY,
24623cac501Stnn .p_fosc_div = SSD1353_DEFAULT_DIVIDER,
24723cac501Stnn .p_default_contrast = SSD1353_DEFAULT_CONTRAST_CONTROL,
24823cac501Stnn .p_multiplex_ratio = 0x83,
24923cac501Stnn .p_init = ssdfb_init_ssd1353,
25023cac501Stnn .p_sync = ssdfb_sync_ssd1353
251e690818eStnn }
252e690818eStnn };
253e690818eStnn
254e690818eStnn static const struct wsdisplay_accessops ssdfb_accessops = {
255e690818eStnn .ioctl = ssdfb_ioctl,
256e690818eStnn .mmap = ssdfb_mmap,
257e690818eStnn .alloc_screen = ssdfb_alloc_screen,
258e690818eStnn .free_screen = ssdfb_free_screen,
259e690818eStnn .show_screen = ssdfb_show_screen
260e690818eStnn };
261e690818eStnn
262e690818eStnn #define SSDFB_CMD1(c) do { cmd[0] = (c); error = sc->sc_cmd(sc->sc_cookie, cmd, 1, usepoll); } while(0)
263e690818eStnn #define SSDFB_CMD2(c, a) do { cmd[0] = (c); cmd[1] = (a); error = sc->sc_cmd(sc->sc_cookie, cmd, 2, usepoll); } while(0)
264ef712effStnn #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)
265e690818eStnn
266e690818eStnn void
ssdfb_attach(struct ssdfb_softc * sc,int flags)267e690818eStnn ssdfb_attach(struct ssdfb_softc *sc, int flags)
268e690818eStnn {
269e690818eStnn struct wsemuldisplaydev_attach_args aa;
270e690818eStnn struct rasops_info *ri = &sc->sc_ri;
271e690818eStnn int error = 0;
272e690818eStnn long defattr;
273e690818eStnn const struct ssdfb_product *p;
2747660f3d3Stnn int kt_flags;
275e690818eStnn
276e690818eStnn p = ssdfb_lookup_product(flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK);
277e690818eStnn if (p == NULL) {
278e690818eStnn aprint_error(": unknown display assembly\n");
279e690818eStnn return;
280e690818eStnn }
281e690818eStnn sc->sc_p = p;
282e690818eStnn
283e690818eStnn aprint_naive("\n");
284e690818eStnn aprint_normal(": %s (%s)\n",
285e690818eStnn ssdfb_controller_names[p->p_controller_id],
286e690818eStnn p->p_name);
287e690818eStnn
288e690818eStnn sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
289e690818eStnn sc->sc_is_console = flags & SSDFB_ATTACH_FLAG_CONSOLE ? true : false;
290e690818eStnn sc->sc_inverse = flags & SSDFB_ATTACH_FLAG_INVERSE ? true : false;
291e690818eStnn sc->sc_upsidedown = flags & SSDFB_ATTACH_FLAG_UPSIDEDOWN ? true : false;
292e690818eStnn sc->sc_backoff = 1;
293e690818eStnn sc->sc_contrast = sc->sc_p->p_default_contrast;
294ef712effStnn sc->sc_gddram_len = sc->sc_p->p_width * sc->sc_p->p_height
295ef712effStnn * sc->sc_p->p_bits_per_pixel / 8;
296e690818eStnn sc->sc_gddram = kmem_alloc(sc->sc_gddram_len, KM_SLEEP);
297e690818eStnn if (sc->sc_gddram == NULL)
298e690818eStnn goto out;
299e690818eStnn
300e690818eStnn aprint_normal_dev(sc->sc_dev, "%dx%d%s\n", sc->sc_p->p_width,
301e690818eStnn sc->sc_p->p_height, sc->sc_is_console ? ", console" : "");
302e690818eStnn
303e690818eStnn /*
304e690818eStnn * Initialize rasops. The native depth is 1-bit monochrome and we
305e690818eStnn * support this in text emul mode via rasops1. But modern Xorg
306e690818eStnn * userland has many rendering glitches when running with 1-bit depth
307e690818eStnn * so to better support this use case we instead declare ourselves as
308e690818eStnn * an 8-bit display with a two entry constant color map.
309e690818eStnn */
310e690818eStnn error = ssdfb_pick_font(&sc->sc_fontcookie, &sc->sc_font);
311e690818eStnn if (error) {
312e690818eStnn aprint_error_dev(sc->sc_dev, "no font\n");
313e690818eStnn goto out;
314e690818eStnn }
315ef712effStnn #ifdef SSDFB_USE_NATIVE_DEPTH
316ef712effStnn ri->ri_depth = sc->sc_p->p_bits_per_pixel;
317ef712effStnn #else
318d6ec530eStnn if (sc->sc_p->p_rgb && sc->sc_p->p_bits_per_pixel == 32) {
319d6ec530eStnn ri->ri_depth = sc->sc_p->p_bits_per_pixel;
320d6ec530eStnn ri->ri_rnum = 8;
321d6ec530eStnn ri->ri_gnum = 8;
322d6ec530eStnn ri->ri_bnum = 8;
323d6ec530eStnn #if _BYTE_ORDER == _LITTLE_ENDIAN
324d6ec530eStnn ri->ri_rpos = 0;
325d6ec530eStnn ri->ri_gpos = 8;
326d6ec530eStnn ri->ri_bpos = 16;
327d6ec530eStnn #else
328d6ec530eStnn ri->ri_rpos = 24;
329d6ec530eStnn ri->ri_gpos = 16;
330d6ec530eStnn ri->ri_bpos = 8;
331d6ec530eStnn #endif
332d6ec530eStnn } else {
333e690818eStnn ri->ri_depth = 8;
334d6ec530eStnn }
335ef712effStnn #endif
336e690818eStnn ri->ri_font = sc->sc_font;
337e690818eStnn ri->ri_width = sc->sc_p->p_width;
338e690818eStnn ri->ri_height = sc->sc_p->p_height;
339e690818eStnn ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
340e690818eStnn ri->ri_hw = sc;
341d6ec530eStnn ri->ri_flg = RI_FULLCLEAR;
342d6ec530eStnn if (!sc->sc_p->p_rgb) {
343d6ec530eStnn ri->ri_flg |= RI_FORCEMONO;
344d6ec530eStnn }
345e690818eStnn sc->sc_ri_bits_len = round_page(ri->ri_stride * ri->ri_height);
346e690818eStnn ri->ri_bits = (u_char *)uvm_km_alloc(kernel_map, sc->sc_ri_bits_len,
347e690818eStnn 0, UVM_KMF_WIRED);
348e690818eStnn if (ri->ri_bits == NULL)
349e690818eStnn goto out;
350e690818eStnn
351e690818eStnn error = rasops_init(ri,
352e690818eStnn sc->sc_p->p_height / sc->sc_font->fontheight,
353e690818eStnn sc->sc_p->p_width / sc->sc_font->fontwidth);
354e690818eStnn if (error)
355e690818eStnn goto out;
356e690818eStnn
357d6ec530eStnn if (!sc->sc_p->p_rgb) {
358e690818eStnn ri->ri_caps &= ~WSSCREEN_WSCOLORS;
359d6ec530eStnn }
360e690818eStnn
361e690818eStnn /*
362e690818eStnn * Save original emul ops & insert our damage notification hooks.
363e690818eStnn */
364e690818eStnn sc->sc_orig_riops = ri->ri_ops;
365e690818eStnn ri->ri_ops.putchar = ssdfb_putchar;
366e690818eStnn ri->ri_ops.copycols = ssdfb_copycols;
367e690818eStnn ri->ri_ops.erasecols = ssdfb_erasecols;
368e690818eStnn ri->ri_ops.copyrows = ssdfb_copyrows;
369e690818eStnn ri->ri_ops.eraserows = ssdfb_eraserows;
370e690818eStnn ri->ri_ops.cursor = ssdfb_cursor;
371e690818eStnn
372e690818eStnn /*
373e690818eStnn * Set up the screen.
374e690818eStnn */
375e690818eStnn sc->sc_screen_descr = (struct wsscreen_descr){
376e690818eStnn .name = "default",
377e690818eStnn .ncols = ri->ri_cols,
378e690818eStnn .nrows = ri->ri_rows,
379e690818eStnn .textops = &ri->ri_ops,
380e690818eStnn .fontwidth = ri->ri_font->fontwidth,
381e690818eStnn .fontheight = ri->ri_font->fontheight,
382e690818eStnn .capabilities = ri->ri_caps
383e690818eStnn };
384e690818eStnn sc->sc_screens[0] = &sc->sc_screen_descr;
385e690818eStnn sc->sc_screenlist = (struct wsscreen_list){
386e690818eStnn .nscreens = 1,
387e690818eStnn .screens = sc->sc_screens
388e690818eStnn };
389e690818eStnn
390e690818eStnn /*
391e690818eStnn * Initialize hardware.
392e690818eStnn */
3938e57c9bfStnn error = p->p_init(sc);
394e690818eStnn if (error)
395e690818eStnn goto out;
396e690818eStnn
397e690818eStnn if (sc->sc_is_console)
398e690818eStnn ssdfb_set_usepoll(sc, true);
399e690818eStnn
4007660f3d3Stnn mutex_init(&sc->sc_cond_mtx, MUTEX_DEFAULT,
4017660f3d3Stnn ISSET(flags, SSDFB_ATTACH_FLAG_MPSAFE) ? IPL_SCHED : IPL_BIO);
402e690818eStnn cv_init(&sc->sc_cond, "ssdfb");
4037660f3d3Stnn kt_flags = KTHREAD_MUSTJOIN;
4047660f3d3Stnn /* XXX spi(4) is not MPSAFE yet. */
4057660f3d3Stnn if (ISSET(flags, SSDFB_ATTACH_FLAG_MPSAFE))
4067660f3d3Stnn kt_flags |= KTHREAD_MPSAFE;
4077660f3d3Stnn error = kthread_create(PRI_SOFTCLOCK, kt_flags, NULL, ssdfb_thread, sc,
4087660f3d3Stnn &sc->sc_thread, "%s", device_xname(sc->sc_dev));
409e690818eStnn if (error) {
410e690818eStnn cv_destroy(&sc->sc_cond);
411e690818eStnn mutex_destroy(&sc->sc_cond_mtx);
412e690818eStnn goto out;
413e690818eStnn }
414e690818eStnn
415e690818eStnn /*
416e690818eStnn * Attach wsdisplay.
417e690818eStnn */
418e690818eStnn if (sc->sc_is_console) {
419e690818eStnn (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
420e690818eStnn wsdisplay_cnattach(&sc->sc_screen_descr, ri, 0, 0, defattr);
421e690818eStnn #if defined(DDB)
422e690818eStnn db_trap_callback = ssdfb_ddb_trap_callback;
423e690818eStnn #endif
424e690818eStnn }
425e690818eStnn aa = (struct wsemuldisplaydev_attach_args){
426e690818eStnn .console = sc->sc_is_console,
427e690818eStnn .scrdata = &sc->sc_screenlist,
428e690818eStnn .accessops = &ssdfb_accessops,
429e690818eStnn .accesscookie = sc
430e690818eStnn };
431e690818eStnn sc->sc_wsdisplay =
432beecddb6Sthorpej config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, CFARGS_NONE);
433e690818eStnn
434e690818eStnn return;
435e690818eStnn out:
436e690818eStnn aprint_error_dev(sc->sc_dev, "attach failed: %d\n", error);
437e690818eStnn if (sc->sc_gddram != NULL)
438e690818eStnn kmem_free(sc->sc_gddram, sc->sc_gddram_len);
439e690818eStnn if (ri->ri_bits != NULL)
440e690818eStnn uvm_km_free(kernel_map, (vaddr_t)ri->ri_bits, sc->sc_ri_bits_len,
441e690818eStnn UVM_KMF_WIRED);
442e690818eStnn if (sc->sc_fontcookie > 0)
443e690818eStnn (void) wsfont_unlock(sc->sc_fontcookie);
444e690818eStnn }
445e690818eStnn
446e690818eStnn int
ssdfb_detach(struct ssdfb_softc * sc)447e690818eStnn ssdfb_detach(struct ssdfb_softc *sc)
448e690818eStnn {
449e690818eStnn mutex_enter(&sc->sc_cond_mtx);
450e690818eStnn sc->sc_detaching = true;
451e690818eStnn cv_broadcast(&sc->sc_cond);
452e690818eStnn mutex_exit(&sc->sc_cond_mtx);
453e690818eStnn kthread_join(sc->sc_thread);
454e690818eStnn
45533ea73e5Stnn if (sc->sc_uobj != NULL) {
456b4dac182Sad rw_enter(sc->sc_uobj->vmobjlock, RW_WRITER);
45733ea73e5Stnn sc->sc_uobj->uo_refs--;
458b4dac182Sad rw_exit(sc->sc_uobj->vmobjlock);
45933ea73e5Stnn }
460e690818eStnn config_detach(sc->sc_wsdisplay, DETACH_FORCE);
461e690818eStnn
462e690818eStnn cv_destroy(&sc->sc_cond);
463e690818eStnn mutex_destroy(&sc->sc_cond_mtx);
464e690818eStnn uvm_km_free(kernel_map, (vaddr_t)sc->sc_ri.ri_bits, sc->sc_ri_bits_len,
465e690818eStnn UVM_KMF_WIRED);
466e690818eStnn kmem_free(sc->sc_gddram, sc->sc_gddram_len);
467e690818eStnn (void) wsfont_unlock(sc->sc_fontcookie);
468e690818eStnn return 0;
469e690818eStnn }
470e690818eStnn
471e690818eStnn static int
ssdfb_ioctl(void * v,void * vs,u_long cmd,void * data,int flag,struct lwp * l)472e690818eStnn ssdfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
473e690818eStnn {
474e690818eStnn struct ssdfb_softc *sc = v;
475e690818eStnn struct wsdisplay_param *wdp;
476e690818eStnn struct wsdisplay_cmap *wc;
477ef712effStnn u_char cmap[16];
478ef712effStnn int cmaplen = 1 << sc->sc_p->p_bits_per_pixel;
479ef712effStnn int i;
480ef712effStnn struct wsdisplayio_fbinfo *fbi;
481e690818eStnn int error;
482e690818eStnn
483e690818eStnn switch (cmd) {
484e690818eStnn case WSDISPLAYIO_GTYPE:
485e690818eStnn *(u_int *)data = WSDISPLAY_TYPE_SSDFB;
486e690818eStnn return 0;
487e690818eStnn case WSDISPLAYIO_GINFO:
488e690818eStnn *(struct wsdisplay_fbinfo *)data = (struct wsdisplay_fbinfo){
489e690818eStnn .width = sc->sc_ri.ri_width,
490e690818eStnn .height = sc->sc_ri.ri_height,
491e690818eStnn .depth = sc->sc_ri.ri_depth,
492ef712effStnn .cmsize = cmaplen
493e690818eStnn };
494e690818eStnn return 0;
495e690818eStnn case WSDISPLAYIO_GET_FBINFO:
496ef712effStnn fbi = (struct wsdisplayio_fbinfo *)data;
497ef712effStnn error = wsdisplayio_get_fbinfo(&sc->sc_ri, fbi);
498d6ec530eStnn if (!sc->sc_p->p_rgb) {
499ef712effStnn fbi->fbi_subtype.fbi_cmapinfo.cmap_entries = cmaplen;
500ef712effStnn /* fbi->fbi_pixeltype = WSFB_GREYSCALE */;
501d6ec530eStnn }
502ef712effStnn return error;
503e690818eStnn case WSDISPLAYIO_LINEBYTES:
504e690818eStnn *(u_int *)data = sc->sc_ri.ri_stride;
505e690818eStnn return 0;
506e690818eStnn case WSDISPLAYIO_GETPARAM:
507e690818eStnn wdp = (struct wsdisplay_param *)data;
508e690818eStnn if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST)
509e690818eStnn return EINVAL;
510e690818eStnn wdp->min = 0;
511e690818eStnn wdp->max = 0xff;
512e690818eStnn wdp->curval = sc->sc_contrast;
513e690818eStnn return 0;
514e690818eStnn case WSDISPLAYIO_SETPARAM:
515e690818eStnn wdp = (struct wsdisplay_param *)data;
516e690818eStnn if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST)
517e690818eStnn return EINVAL;
518e690818eStnn if (wdp->curval < 0 || wdp->curval > 0xff)
519e690818eStnn return EINVAL;
520e690818eStnn return ssdfb_set_contrast(sc, wdp->curval, sc->sc_usepoll);
521e690818eStnn case WSDISPLAYIO_GMODE:
522e690818eStnn *(u_int *)data = sc->sc_mode;
523e690818eStnn return 0;
524e690818eStnn case WSDISPLAYIO_SMODE:
525e690818eStnn return ssdfb_set_mode(sc, *(u_int *)data);
526e690818eStnn case WSDISPLAYIO_GVIDEO:
527e690818eStnn *(u_int *)data = sc->sc_display_on
528e690818eStnn ? WSDISPLAYIO_VIDEO_ON
529e690818eStnn : WSDISPLAYIO_VIDEO_OFF;
530e690818eStnn return 0;
531e690818eStnn case WSDISPLAYIO_SVIDEO:
532e690818eStnn switch (*(u_int *)data) {
533e690818eStnn case WSDISPLAYIO_VIDEO_ON:
534e690818eStnn case WSDISPLAYIO_VIDEO_OFF:
535e690818eStnn break;
536e690818eStnn default:
537e690818eStnn return EINVAL;
538e690818eStnn }
539e690818eStnn return ssdfb_set_display_on(sc,
540e690818eStnn *(u_int *)data == WSDISPLAYIO_VIDEO_ON ? true : false,
541e690818eStnn sc->sc_usepoll);
542e690818eStnn #if 0 /* don't let userland mess with polling yet */
543e690818eStnn case WSDISPLAYIO_SET_POLLING:
544e690818eStnn switch (*(u_int *)data) {
545e690818eStnn case 0:
546e690818eStnn case 1:
547e690818eStnn break;
548e690818eStnn default:
549e690818eStnn return EINVAL;
550e690818eStnn }
551e690818eStnn mutex_enter(&sc->sc_cond_mtx);
552e690818eStnn ssdfb_set_usepoll(sc, *(u_int *)data ? true : false);
553e690818eStnn cv_broadcast(&sc->sc_cond);
554e690818eStnn mutex_exit(&sc->sc_cond_mtx);
555e690818eStnn return 0;
556e690818eStnn #endif
557e690818eStnn case WSDISPLAYIO_GETCMAP:
558d6ec530eStnn if (sc->sc_p->p_rgb)
559d6ec530eStnn return ENOTSUP;
560e690818eStnn wc = (struct wsdisplay_cmap *)data;
561ef712effStnn if (wc->index >= cmaplen ||
562ef712effStnn wc->count > cmaplen - wc->index)
563e690818eStnn return EINVAL;
564ef712effStnn for(i = 0; i < cmaplen; i++) {
565ef712effStnn cmap[i] = 255 * i / (cmaplen - 1);
566ef712effStnn }
567e690818eStnn error = copyout(&cmap[wc->index], wc->red, wc->count);
568e690818eStnn if (error)
569e690818eStnn return error;
570e690818eStnn error = copyout(&cmap[wc->index], wc->green, wc->count);
571e690818eStnn if (error)
572e690818eStnn return error;
573e690818eStnn error = copyout(&cmap[wc->index], wc->blue, wc->count);
574e690818eStnn return error;
575e690818eStnn case WSDISPLAYIO_PUTCMAP:
576e690818eStnn return ENODEV;
577e690818eStnn }
578e690818eStnn
579e690818eStnn return EPASSTHROUGH;
580e690818eStnn }
581e690818eStnn
582e690818eStnn static paddr_t
ssdfb_mmap(void * v,void * vs,off_t off,int prot)583e690818eStnn ssdfb_mmap(void *v, void *vs, off_t off, int prot)
584e690818eStnn {
585e690818eStnn struct ssdfb_softc *sc = (struct ssdfb_softc *)v;
586e690818eStnn struct rasops_info *ri = &sc->sc_ri;
587e690818eStnn vaddr_t va_base = (vaddr_t)ri->ri_bits;
588e690818eStnn paddr_t pa;
589e690818eStnn
590e690818eStnn if (off < 0 || off >= sc->sc_ri_bits_len || (off & PAGE_MASK) != 0)
591e690818eStnn return -1;
592e690818eStnn
593e690818eStnn if (!pmap_extract(pmap_kernel(), va_base + off, &pa))
594e690818eStnn return -1;
595e690818eStnn
596e690818eStnn return atop(pa);
597e690818eStnn }
598e690818eStnn
599e690818eStnn static int
ssdfb_alloc_screen(void * v,const struct wsscreen_descr * descr,void ** cookiep,int * curxp,int * curyp,long * attrp)600e690818eStnn ssdfb_alloc_screen(void *v, const struct wsscreen_descr *descr, void **cookiep,
601e690818eStnn int *curxp, int *curyp, long *attrp)
602e690818eStnn {
603e690818eStnn struct ssdfb_softc *sc = v;
604e690818eStnn struct rasops_info *ri = &sc->sc_ri;
605e690818eStnn
606e690818eStnn if (sc->sc_nscreens > 0)
607e690818eStnn return ENOMEM;
608e690818eStnn
609e690818eStnn ri->ri_ops.allocattr(ri, 0, 0, 0, attrp);
610e690818eStnn *cookiep = &sc->sc_ri;
611e690818eStnn *curxp = 0;
612e690818eStnn *curyp = 0;
613e690818eStnn sc->sc_nscreens++;
614e690818eStnn
615e690818eStnn return 0;
616e690818eStnn }
617e690818eStnn
618e690818eStnn static void
ssdfb_free_screen(void * v,void * cookie)619e690818eStnn ssdfb_free_screen(void *v, void *cookie)
620e690818eStnn {
621e690818eStnn struct ssdfb_softc *sc = v;
622e690818eStnn
623e690818eStnn if (sc->sc_is_console)
624e690818eStnn panic("ssdfb_free_screen: is console");
625e690818eStnn
626e690818eStnn sc->sc_nscreens--;
627e690818eStnn }
628e690818eStnn
629e690818eStnn static int
ssdfb_show_screen(void * v,void * cookie,int waitok,void (* cb)(void *,int,int),void * cb_arg)630e690818eStnn ssdfb_show_screen(void *v, void *cookie, int waitok,
631e690818eStnn void (*cb) (void *, int, int), void *cb_arg)
632e690818eStnn {
633e690818eStnn return 0;
634e690818eStnn }
635e690818eStnn
636e690818eStnn static void
ssdfb_putchar(void * cookie,int row,int col,u_int c,long attr)637e690818eStnn ssdfb_putchar(void *cookie, int row, int col, u_int c, long attr)
638e690818eStnn {
639e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie;
640e690818eStnn struct ssdfb_softc *sc = ri->ri_hw;
641e690818eStnn
642e690818eStnn sc->sc_orig_riops.putchar(cookie, row, col, c, attr);
643e690818eStnn ssdfb_damage(sc);
644e690818eStnn }
645e690818eStnn
646e690818eStnn static void
ssdfb_copycols(void * cookie,int row,int srccol,int dstcol,int ncols)647e690818eStnn ssdfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
648e690818eStnn {
649e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie;
650e690818eStnn struct ssdfb_softc *sc = ri->ri_hw;
651e690818eStnn
652e690818eStnn sc->sc_orig_riops.copycols(cookie, row, srccol, dstcol, ncols);
653e690818eStnn ssdfb_damage(sc);
654e690818eStnn }
655e690818eStnn
656e690818eStnn static void
ssdfb_erasecols(void * cookie,int row,int startcol,int ncols,long fillattr)657e690818eStnn ssdfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
658e690818eStnn {
659e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie;
660e690818eStnn struct ssdfb_softc *sc = ri->ri_hw;
661e690818eStnn
662e690818eStnn sc->sc_orig_riops.erasecols(cookie, row, startcol, ncols, fillattr);
663e690818eStnn ssdfb_damage(sc);
664e690818eStnn }
665e690818eStnn
666e690818eStnn static void
ssdfb_copyrows(void * cookie,int srcrow,int dstrow,int nrows)667e690818eStnn ssdfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
668e690818eStnn {
669e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie;
670e690818eStnn struct ssdfb_softc *sc = ri->ri_hw;
671e690818eStnn
672e690818eStnn sc->sc_orig_riops.copyrows(cookie, srcrow, dstrow, nrows);
673e690818eStnn ssdfb_damage(sc);
674e690818eStnn }
675e690818eStnn
676e690818eStnn static void
ssdfb_eraserows(void * cookie,int row,int nrows,long fillattr)677e690818eStnn ssdfb_eraserows(void *cookie, int row, int nrows, long fillattr)
678e690818eStnn {
679e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie;
680e690818eStnn struct ssdfb_softc *sc = ri->ri_hw;
681e690818eStnn
682e690818eStnn sc->sc_orig_riops.eraserows(cookie, row, nrows, fillattr);
683e690818eStnn ssdfb_damage(sc);
684e690818eStnn }
685e690818eStnn
686e690818eStnn static void
ssdfb_cursor(void * cookie,int on,int row,int col)687e690818eStnn ssdfb_cursor(void *cookie, int on, int row, int col)
688e690818eStnn {
689e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie;
690e690818eStnn struct ssdfb_softc *sc = ri->ri_hw;
691e690818eStnn
692e690818eStnn sc->sc_orig_riops.cursor(cookie, on, row, col);
693e690818eStnn ssdfb_damage(sc);
694e690818eStnn }
695e690818eStnn
696e690818eStnn static int
ssdfb_init_ssd1306(struct ssdfb_softc * sc)6978e57c9bfStnn ssdfb_init_ssd1306(struct ssdfb_softc *sc)
698e690818eStnn {
699e690818eStnn int error;
70033ea73e5Stnn uint8_t cmd[2];
701e690818eStnn bool usepoll = true;
702e690818eStnn
703e690818eStnn /*
704e690818eStnn * Enter sleep.
705e690818eStnn */
706e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_OFF);
707e690818eStnn if (error)
708e690818eStnn return error;
709e690818eStnn SSDFB_CMD1(SSDFB_CMD_DEACTIVATE_SCROLL);
710e690818eStnn if (error)
711e690818eStnn return error;
712e690818eStnn SSDFB_CMD1(SSDFB_CMD_ENTIRE_DISPLAY_OFF);
713e690818eStnn if (error)
714e690818eStnn return error;
715e690818eStnn
716e690818eStnn /*
717e690818eStnn * Configure physical display panel layout.
718e690818eStnn */
719e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio);
720e690818eStnn if (error)
721e690818eStnn return error;
722e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_OFFSET, 0);
723e690818eStnn if (error)
724e690818eStnn return error;
725e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_START_LINE_BASE + 0x00);
726e690818eStnn if (error)
727e690818eStnn return error;
728e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_COM_PINS_HARDWARE_CFG, sc->sc_p->p_compin_cfg);
729e690818eStnn if (error)
730e690818eStnn return error;
731e690818eStnn if (sc->sc_upsidedown) {
732e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_REVERSE);
733e690818eStnn if (error)
734e690818eStnn return error;
735e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_REMAP);
736e690818eStnn if (error)
737e690818eStnn return error;
738e690818eStnn } else {
739e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_NORMAL);
740e690818eStnn if (error)
741e690818eStnn return error;
742e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_NORMAL);
743e690818eStnn if (error)
744e690818eStnn return error;
745e690818eStnn }
746e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse);
747e690818eStnn if (error)
748e690818eStnn return error;
749e690818eStnn
750e690818eStnn /*
751e690818eStnn * Configure timing characteristics.
752e690818eStnn */
753e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_CLOCK_RATIO,
75409fa88a1Stnn __SHIFTIN(sc->sc_p->p_fosc, SSDFB_DISPLAY_CLOCK_OSCILLATOR_MASK) |
75509fa88a1Stnn __SHIFTIN(sc->sc_p->p_fosc_div, SSDFB_DISPLAY_CLOCK_DIVIDER_MASK));
756e690818eStnn if (error)
757e690818eStnn return error;
758e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_CONTRAST_CONTROL, sc->sc_contrast);
759e690818eStnn if (error)
760e690818eStnn return error;
761e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_PRECHARGE_PERIOD,
76209fa88a1Stnn __SHIFTIN(sc->sc_p->p_precharge, SSDFB_PRECHARGE_MASK) |
76309fa88a1Stnn __SHIFTIN(sc->sc_p->p_discharge, SSDFB_DISCHARGE_MASK));
764e690818eStnn if (error)
765e690818eStnn return error;
766e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_VCOMH_DESELECT_LEVEL,
767e690818eStnn sc->sc_p->p_vcomh_deselect_level);
768e690818eStnn if (error)
769e690818eStnn return error;
770e690818eStnn
771e690818eStnn /*
772ef712effStnn * Start charge pumps.
773e690818eStnn */
774ef712effStnn if (sc->sc_p->p_controller_id == SSDFB_CONTROLLER_SH1106) {
775ef712effStnn SSDFB_CMD1(SH1106_CMD_SET_CHARGE_PUMP_7V4);
776e690818eStnn if (error)
777e690818eStnn return error;
778e690818eStnn SSDFB_CMD2(SH1106_CMD_SET_DC_DC, SH1106_DC_DC_ON);
779e690818eStnn if (error)
780e690818eStnn return error;
781ef712effStnn } else {
782ef712effStnn SSDFB_CMD2(SSD1306_CMD_SET_CHARGE_PUMP,
783ef712effStnn SSD1306_CHARGE_PUMP_ENABLE);
784ef712effStnn if (error)
785ef712effStnn return error;
786e690818eStnn }
787e690818eStnn
788e690818eStnn ssdfb_clear_screen(sc);
789ef712effStnn error = sc->sc_p->p_sync(sc, usepoll);
790ef712effStnn if (error)
791ef712effStnn return error;
792ef712effStnn error = ssdfb_set_display_on(sc, true, usepoll);
793ef712effStnn
794ef712effStnn return error;
795ef712effStnn }
796ef712effStnn
797ef712effStnn static int
ssdfb_init_ssd1322(struct ssdfb_softc * sc)798ef712effStnn ssdfb_init_ssd1322(struct ssdfb_softc *sc)
799ef712effStnn {
800ef712effStnn int error;
801ef712effStnn uint8_t cmd[3];
802ef712effStnn bool usepoll = true;
803ef712effStnn uint8_t remap;
804ef712effStnn uint8_t dualcom;
805ef712effStnn
806ef712effStnn /*
807ef712effStnn * Enter sleep.
808ef712effStnn */
809ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_COMMAND_LOCK, SSD1322_COMMAND_UNLOCK_MAGIC);
810ef712effStnn if (error)
811ef712effStnn return error;
812ef712effStnn SSDFB_CMD1(SSD1322_CMD_SET_SLEEP_MODE_ON);
813ef712effStnn if (error)
814ef712effStnn return error;
815ef712effStnn
816ef712effStnn /*
817ef712effStnn * Start charge pumps.
818ef712effStnn */
819ef712effStnn SSDFB_CMD2(SSD1322_CMD_FUNCTION_SELECTION,
820ef712effStnn SSD1322_FUNCTION_SELECTION_INTERNAL_VDD);
821ef712effStnn if (error)
822ef712effStnn return error;
823ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_VCOMH, sc->sc_p->p_vcomh_deselect_level);
824ef712effStnn if (error)
825ef712effStnn return error;
826ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL,
827ef712effStnn SSD1322_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL);
828ef712effStnn if (error)
829ef712effStnn return error;
830ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_GPIO,
831ef712effStnn SSD1322_GPIO0_DISABLED | SSD1322_GPIO1_DISABLED);
832ef712effStnn if (error)
833ef712effStnn return error;
834ef712effStnn
835ef712effStnn /*
836ef712effStnn * Configure timing characteristics.
837ef712effStnn */
838ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_FRONT_CLOCK_DIVIDER,
839ef712effStnn __SHIFTIN(sc->sc_p->p_fosc, SSD1322_FREQUENCY_MASK) |
840ef712effStnn __SHIFTIN(sc->sc_p->p_fosc_div, SSD1322_DIVIDER_MASK));
841ef712effStnn if (error)
842ef712effStnn return error;
843ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_PHASE_LENGTH,
844ef712effStnn __SHIFTIN(SSD1322_DEFAULT_PHASE_2,
845ef712effStnn SSD1322_PHASE_LENGTH_PHASE_2_MASK) |
846ef712effStnn __SHIFTIN(SSD1322_DEFAULT_PHASE_1,
847ef712effStnn SSD1322_PHASE_LENGTH_PHASE_1_MASK));
848ef712effStnn if (error)
849ef712effStnn return error;
850ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_SECOND_PRECHARGE_PERIOD,
851fd450c25Stnn SSD1322_DEFAULT_SECOND_PRECHARGE_PERIOD);
852ef712effStnn if (error)
853ef712effStnn return error;
854ef712effStnn
855ef712effStnn /*
856ef712effStnn * Configure physical display panel layout.
857ef712effStnn */
858fd450c25Stnn SSDFB_CMD2(SSD1322_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio);
859ef712effStnn if (error)
860ef712effStnn return error;
861ef712effStnn if (sc->sc_upsidedown)
862ef712effStnn remap = 0x10;
863ef712effStnn else
864ef712effStnn remap = 0x2;
865ef712effStnn dualcom = 0x1;
866ef712effStnn if (sc->sc_p->p_multiplex_ratio <= 63)
867ef712effStnn dualcom |= 0x10;
868ef712effStnn SSDFB_CMD3(SSD1322_CMD_SET_REMAP_AND_DUAL_COM_LINE_MODE, remap, dualcom);
869ef712effStnn if (error)
870ef712effStnn return error;
871ef712effStnn
872ef712effStnn /*
873ef712effStnn * Contrast settings.
874ef712effStnn */
875ef712effStnn SSDFB_CMD1(SSD1322_CMD_SET_DEFAULT_GRAY_SCALE_TABLE);
876ef712effStnn if (error)
877ef712effStnn return error;
878ef712effStnn SSDFB_CMD3(SSD1322_CMD_DISPLAY_ENHANCEMENT_A,
879ef712effStnn SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC1,
880ef712effStnn SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC2);
881ef712effStnn if (error)
882ef712effStnn return error;
883ef712effStnn SSDFB_CMD3(SSD1322_CMD_DISPLAY_ENHANCEMENT_B,
884ef712effStnn SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC1,
885ef712effStnn SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC2);
886ef712effStnn if (error)
887ef712effStnn return error;
888ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_CONTRAST_CURRENT,
889ef712effStnn sc->sc_contrast);
890ef712effStnn if (error)
891ef712effStnn return error;
892ef712effStnn SSDFB_CMD2(SSD1322_CMD_MASTER_CONTRAST_CURRENT_CONTROL,
893ef712effStnn SSD1322_DEFAULT_MASTER_CONTRAST_CURRENT_CONTROL);
894ef712effStnn if (error)
895ef712effStnn return error;
896ef712effStnn
897ef712effStnn /*
898ef712effStnn * Reset display engine state.
899ef712effStnn */
900ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_DISPLAY_OFFSET, 0x00);
901ef712effStnn if (error)
902ef712effStnn return error;
903ef712effStnn SSDFB_CMD2(SSD1322_CMD_SET_DISPLAY_START_LINE, 0x00);
904ef712effStnn if (error)
905ef712effStnn return error;
906ef712effStnn SSDFB_CMD1(SSD1322_CMD_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse);
907ef712effStnn if (error)
908ef712effStnn return error;
909ef712effStnn SSDFB_CMD1(SSD1322_CMD_EXIT_PARTIAL_DISPLAY);
910ef712effStnn if (error)
911ef712effStnn return error;
912ef712effStnn
913ef712effStnn ssdfb_clear_screen(sc);
914e690818eStnn error = ssdfb_sync(sc, usepoll);
915e690818eStnn if (error)
916e690818eStnn return error;
917ef712effStnn
918e690818eStnn error = ssdfb_set_display_on(sc, true, usepoll);
919e690818eStnn
920e690818eStnn return error;
921e690818eStnn }
922e690818eStnn
923e690818eStnn static int
ssdfb_init_ssd1353(struct ssdfb_softc * sc)92423cac501Stnn ssdfb_init_ssd1353(struct ssdfb_softc *sc)
92523cac501Stnn {
92623cac501Stnn int error;
92723cac501Stnn uint8_t cmd[3];
92823cac501Stnn bool usepoll = true;
92923cac501Stnn uint8_t remap;
93023cac501Stnn
93123cac501Stnn /*
93223cac501Stnn * Enter sleep.
93323cac501Stnn */
93423cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_COMMAND_LOCK, SSD1353_COMMAND_UNLOCK_MAGIC);
93523cac501Stnn if (error)
93623cac501Stnn return error;
93723cac501Stnn SSDFB_CMD1(SSD1353_CMD_RESET);
93823cac501Stnn if (error)
93923cac501Stnn return error;
94023cac501Stnn SSDFB_CMD1(SSD1353_CMD_DEACTIVATE_SCROLL);
94123cac501Stnn if (error)
94223cac501Stnn return error;
94323cac501Stnn SSDFB_CMD1(SSD1353_CMD_SET_DISPLAY_OFF);
94423cac501Stnn if (error)
94523cac501Stnn return error;
94623cac501Stnn
94723cac501Stnn /*
94823cac501Stnn * Start charge pumps.
94923cac501Stnn */
95023cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_VCOMH, sc->sc_p->p_vcomh_deselect_level);
95123cac501Stnn if (error)
95223cac501Stnn return error;
95323cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL,
95423cac501Stnn SSD1353_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL);
95523cac501Stnn if (error)
95623cac501Stnn return error;
95723cac501Stnn
95823cac501Stnn /*
95923cac501Stnn * Configure timing characteristics.
96023cac501Stnn */
96123cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_FRONT_CLOCK_DIVIDER,
96223cac501Stnn __SHIFTIN(sc->sc_p->p_fosc, SSD1322_FREQUENCY_MASK) |
96323cac501Stnn __SHIFTIN(sc->sc_p->p_fosc_div, SSD1322_DIVIDER_MASK));
96423cac501Stnn if (error)
96523cac501Stnn return error;
96623cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_PHASE_LENGTH,
96723cac501Stnn __SHIFTIN(SSD1353_DEFAULT_PHASE_2,
96823cac501Stnn SSD1322_PHASE_LENGTH_PHASE_2_MASK) |
96923cac501Stnn __SHIFTIN(SSD1353_DEFAULT_PHASE_1,
97023cac501Stnn SSD1322_PHASE_LENGTH_PHASE_1_MASK));
97123cac501Stnn if (error)
97223cac501Stnn return error;
97323cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_SECOND_PRECHARGE_PERIOD,
97423cac501Stnn SSD1353_DEFAULT_SECOND_PRECHARGE_PERIOD);
97523cac501Stnn if (error)
97623cac501Stnn return error;
97723cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_SECOND_PRECHARGE_SPEED,
97823cac501Stnn SSD1353_DEFAULT_SECOND_PRECHARGE_SPEED);
97923cac501Stnn if (error)
98023cac501Stnn return error;
98123cac501Stnn
98223cac501Stnn /*
98323cac501Stnn * Configure physical display panel layout.
98423cac501Stnn */
98523cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio);
98623cac501Stnn if (error)
98723cac501Stnn return error;
98823cac501Stnn remap = sc->sc_p->p_compin_cfg;
98923cac501Stnn if (sc->sc_upsidedown)
99023cac501Stnn remap ^= SSD1353_REMAP_COM_DIRECTION;
99123cac501Stnn else
99223cac501Stnn remap ^= SSD1353_REMAP_SEG_DIRECTION;
99323cac501Stnn SSDFB_CMD2(SSD1353_CMD_REMAP_COLOR_DEPTH, remap);
99423cac501Stnn if (error)
99523cac501Stnn return error;
99623cac501Stnn
99723cac501Stnn /*
99823cac501Stnn * Contrast settings.
99923cac501Stnn */
100023cac501Stnn SSDFB_CMD1(SSD1353_CMD_SET_DEFAULT_GRAY_SCALE_TABLE);
100123cac501Stnn if (error)
100223cac501Stnn return error;
100323cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_CONTRAST_CONTROL_A, sc->sc_contrast);
100423cac501Stnn if (error)
100523cac501Stnn return error;
100623cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_CONTRAST_CONTROL_B, sc->sc_contrast);
100723cac501Stnn if (error)
100823cac501Stnn return error;
100923cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_CONTRAST_CONTROL_C, sc->sc_contrast);
101023cac501Stnn if (error)
101123cac501Stnn return error;
101223cac501Stnn SSDFB_CMD2(SSD1353_CMD_MASTER_CURRENT_CONTROL,
101323cac501Stnn SSD1353_DEFAULT_MASTER_CURRENT_ATTENUATION);
101423cac501Stnn if (error)
101523cac501Stnn return error;
101623cac501Stnn
101723cac501Stnn /*
101823cac501Stnn * Reset display engine state.
101923cac501Stnn */
102023cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_DISPLAY_OFFSET, 0x00);
102123cac501Stnn if (error)
102223cac501Stnn return error;
102323cac501Stnn SSDFB_CMD2(SSD1353_CMD_SET_DISPLAY_START_LINE, 0x00);
102423cac501Stnn if (error)
102523cac501Stnn return error;
102623cac501Stnn SSDFB_CMD1(sc->sc_inverse
102723cac501Stnn ? SSD1353_CMD_INVERSE_DISPLAY
102823cac501Stnn : SSD1353_CMD_NORMAL_DISPLAY);
102923cac501Stnn if (error)
103023cac501Stnn return error;
103123cac501Stnn
103223cac501Stnn ssdfb_clear_screen(sc);
103323cac501Stnn error = ssdfb_sync(sc, usepoll);
103423cac501Stnn if (error)
103523cac501Stnn return error;
103623cac501Stnn
103723cac501Stnn error = ssdfb_set_display_on(sc, true, usepoll);
103823cac501Stnn
103923cac501Stnn return error;
104023cac501Stnn }
104123cac501Stnn
104223cac501Stnn static int
ssdfb_set_contrast(struct ssdfb_softc * sc,uint8_t value,bool usepoll)1043e690818eStnn ssdfb_set_contrast(struct ssdfb_softc *sc, uint8_t value, bool usepoll)
1044e690818eStnn {
1045e690818eStnn uint8_t cmd[2];
104623cac501Stnn int error;
1047e690818eStnn
104823cac501Stnn cmd[1] = sc->sc_contrast = value;
1049ef712effStnn switch (sc->sc_p->p_controller_id) {
1050ef712effStnn case SSDFB_CONTROLLER_SSD1322:
1051ef712effStnn cmd[0] = SSD1322_CMD_SET_CONTRAST_CURRENT;
1052ef712effStnn break;
105323cac501Stnn case SSDFB_CONTROLLER_SSD1353:
105423cac501Stnn cmd[0] = SSD1353_CMD_SET_CONTRAST_CONTROL_A;
105523cac501Stnn error = sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll);
105623cac501Stnn if (error)
105723cac501Stnn return error;
105823cac501Stnn cmd[0] = SSD1353_CMD_SET_CONTRAST_CONTROL_B;
105923cac501Stnn error = sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll);
106023cac501Stnn if (error)
106123cac501Stnn return error;
106223cac501Stnn cmd[0] = SSD1353_CMD_SET_CONTRAST_CONTROL_C;
1063*fc683fa3Stnn break;
1064ef712effStnn default:
1065ef712effStnn cmd[0] = SSDFB_CMD_SET_CONTRAST_CONTROL;
1066*fc683fa3Stnn break;
1067ef712effStnn }
1068e690818eStnn
1069ef712effStnn return sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll);
1070e690818eStnn }
1071e690818eStnn
1072e690818eStnn static int
ssdfb_set_display_on(struct ssdfb_softc * sc,bool value,bool usepoll)1073e690818eStnn ssdfb_set_display_on(struct ssdfb_softc *sc, bool value, bool usepoll)
1074e690818eStnn {
1075e690818eStnn uint8_t cmd[1];
1076e690818eStnn int error;
1077e690818eStnn sc->sc_display_on = value;
1078e690818eStnn
1079e690818eStnn SSDFB_CMD1(value ? SSDFB_CMD_SET_DISPLAY_ON : SSDFB_CMD_SET_DISPLAY_OFF);
1080e690818eStnn
1081e690818eStnn return error;
1082e690818eStnn }
1083e690818eStnn
1084e690818eStnn static int
ssdfb_set_mode(struct ssdfb_softc * sc,u_int mode)1085e690818eStnn ssdfb_set_mode(struct ssdfb_softc *sc, u_int mode)
1086e690818eStnn {
1087e690818eStnn switch (mode) {
1088e690818eStnn case WSDISPLAYIO_MODE_EMUL:
1089e690818eStnn case WSDISPLAYIO_MODE_DUMBFB:
1090e690818eStnn break;
1091e690818eStnn default:
1092e690818eStnn return EINVAL;
1093e690818eStnn }
1094e690818eStnn if (mode == sc->sc_mode)
1095e690818eStnn return 0;
1096e690818eStnn mutex_enter(&sc->sc_cond_mtx);
1097e690818eStnn sc->sc_mode = mode;
1098e690818eStnn cv_broadcast(&sc->sc_cond);
1099e690818eStnn mutex_exit(&sc->sc_cond_mtx);
1100e690818eStnn ssdfb_clear_screen(sc);
1101e690818eStnn ssdfb_damage(sc);
1102e690818eStnn
1103e690818eStnn return 0;
1104e690818eStnn }
1105e690818eStnn
1106e690818eStnn static void
ssdfb_damage(struct ssdfb_softc * sc)1107e690818eStnn ssdfb_damage(struct ssdfb_softc *sc)
1108e690818eStnn {
1109e690818eStnn int s;
1110e690818eStnn
1111e690818eStnn if (sc->sc_usepoll) {
1112e690818eStnn (void) ssdfb_sync(sc, true);
1113e690818eStnn } else {
1114e690818eStnn /*
1115e690818eStnn * kernel code isn't permitted to call us via kprintf at
1116e690818eStnn * splhigh. In case misbehaving code calls us anyway we can't
1117e690818eStnn * safely take the mutex so we skip the damage notification.
1118e690818eStnn */
1119e690818eStnn if (sc->sc_is_console) {
1120e690818eStnn s = splhigh();
1121e690818eStnn splx(s);
1122e690818eStnn if (s == IPL_HIGH)
1123e690818eStnn return;
1124e690818eStnn }
1125e690818eStnn mutex_enter(&sc->sc_cond_mtx);
1126e690818eStnn sc->sc_modified = true;
1127e690818eStnn cv_broadcast(&sc->sc_cond);
1128e690818eStnn mutex_exit(&sc->sc_cond_mtx);
1129e690818eStnn }
1130e690818eStnn }
1131e690818eStnn
113233ea73e5Stnn static void
ssdfb_udv_attach(struct ssdfb_softc * sc)113333ea73e5Stnn ssdfb_udv_attach(struct ssdfb_softc *sc)
113433ea73e5Stnn {
113533ea73e5Stnn extern const struct cdevsw wsdisplay_cdevsw;
113633ea73e5Stnn dev_t dev;
113733ea73e5Stnn #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen))
113833ea73e5Stnn dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw),
113933ea73e5Stnn WSDISPLAYMINOR(device_unit(sc->sc_wsdisplay), 0));
114033ea73e5Stnn sc->sc_uobj = udv_attach(dev, VM_PROT_READ|VM_PROT_WRITE, 0,
114133ea73e5Stnn sc->sc_ri_bits_len);
114233ea73e5Stnn }
114333ea73e5Stnn
1144e690818eStnn static bool
ssdfb_is_modified(struct ssdfb_softc * sc)1145e690818eStnn ssdfb_is_modified(struct ssdfb_softc *sc)
1146e690818eStnn {
1147e690818eStnn vaddr_t va, va_end;
1148e690818eStnn
1149e690818eStnn if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)
1150e690818eStnn return sc->sc_modified;
1151e690818eStnn
115233ea73e5Stnn if (sc->sc_uobj == NULL)
115333ea73e5Stnn return false;
115433ea73e5Stnn
1155e690818eStnn va = (vaddr_t)sc->sc_ri.ri_bits;
1156e690818eStnn va_end = va + sc->sc_ri_bits_len;
1157e690818eStnn while (va < va_end) {
1158e690818eStnn if (pmap_is_modified(uvm_pageratop(va)))
1159e690818eStnn return true;
1160e690818eStnn va += PAGE_SIZE;
1161e690818eStnn }
1162e690818eStnn
1163e690818eStnn return false;
1164e690818eStnn }
1165e690818eStnn
1166e690818eStnn static bool
ssdfb_clear_modify(struct ssdfb_softc * sc)1167e690818eStnn ssdfb_clear_modify(struct ssdfb_softc *sc)
1168e690818eStnn {
1169e690818eStnn vaddr_t va, va_end;
1170e690818eStnn bool ret;
1171e690818eStnn
1172e690818eStnn if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
1173ef712effStnn mutex_enter(&sc->sc_cond_mtx);
1174e690818eStnn ret = sc->sc_modified;
1175e690818eStnn sc->sc_modified = false;
1176ef712effStnn mutex_exit(&sc->sc_cond_mtx);
1177e690818eStnn return ret;
1178e690818eStnn }
1179e690818eStnn
118033ea73e5Stnn if (sc->sc_uobj == NULL)
118133ea73e5Stnn return false;
118233ea73e5Stnn
1183e690818eStnn va = (vaddr_t)sc->sc_ri.ri_bits;
1184e690818eStnn va_end = va + sc->sc_ri_bits_len;
1185e690818eStnn ret = false;
1186e690818eStnn while (va < va_end) {
1187e690818eStnn if (pmap_clear_modify(uvm_pageratop(va)))
1188e690818eStnn ret = true;
1189e690818eStnn va += PAGE_SIZE;
1190e690818eStnn }
1191e690818eStnn
1192e690818eStnn return ret;
1193e690818eStnn }
1194e690818eStnn
1195e690818eStnn static void
ssdfb_thread(void * arg)11962f60d4f5Stnn ssdfb_thread(void *arg)
11972f60d4f5Stnn {
1198e690818eStnn struct ssdfb_softc *sc = (struct ssdfb_softc *)arg;
1199e690818eStnn int error;
1200e690818eStnn
1201e690818eStnn mutex_enter(&sc->sc_cond_mtx);
1202e690818eStnn
1203e690818eStnn if (sc->sc_usepoll)
1204e690818eStnn ssdfb_set_usepoll(sc, false);
1205e690818eStnn
1206e690818eStnn while(!sc->sc_detaching) {
120733ea73e5Stnn if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB &&
120833ea73e5Stnn sc->sc_uobj == NULL) {
120933ea73e5Stnn mutex_exit(&sc->sc_cond_mtx);
121033ea73e5Stnn ssdfb_udv_attach(sc);
121133ea73e5Stnn mutex_enter(&sc->sc_cond_mtx);
121233ea73e5Stnn }
1213e690818eStnn if (!ssdfb_is_modified(sc)) {
1214e690818eStnn if (cv_timedwait(&sc->sc_cond, &sc->sc_cond_mtx,
1215e690818eStnn sc->sc_mode == WSDISPLAYIO_MODE_EMUL
1216e690818eStnn ? 0 : sc->sc_backoff) == EWOULDBLOCK
1217e690818eStnn && sc->sc_backoff < mstohz(200)) {
1218e690818eStnn sc->sc_backoff <<= 1;
1219e690818eStnn }
1220e690818eStnn continue;
1221e690818eStnn }
1222e690818eStnn sc->sc_backoff = 1;
1223e690818eStnn mutex_exit(&sc->sc_cond_mtx);
1224ef712effStnn (void) ssdfb_clear_modify(sc);
1225ef712effStnn if (!sc->sc_usepoll) {
1226e690818eStnn error = ssdfb_sync(sc, false);
1227e690818eStnn if (error)
1228ef712effStnn device_printf(sc->sc_dev,
1229ef712effStnn "ssdfb_sync: error %d\n",
1230e690818eStnn error);
1231ef712effStnn }
1232e690818eStnn mutex_enter(&sc->sc_cond_mtx);
1233e690818eStnn }
1234e690818eStnn
1235e690818eStnn mutex_exit(&sc->sc_cond_mtx);
123650090521Stnn kthread_exit(0);
1237e690818eStnn }
1238e690818eStnn
1239e690818eStnn static void
ssdfb_set_usepoll(struct ssdfb_softc * sc,bool enable)12402f60d4f5Stnn ssdfb_set_usepoll(struct ssdfb_softc *sc, bool enable)
12412f60d4f5Stnn {
1242e690818eStnn sc->sc_usepoll = enable;
1243e690818eStnn }
1244e690818eStnn
1245e690818eStnn static int
ssdfb_sync(struct ssdfb_softc * sc,bool usepoll)12462f60d4f5Stnn ssdfb_sync(struct ssdfb_softc *sc, bool usepoll)
12472f60d4f5Stnn {
1248ef712effStnn return sc->sc_p->p_sync(sc, usepoll);
1249ef712effStnn }
1250ef712effStnn
1251ef712effStnn static int
ssdfb_sync_ssd1306(struct ssdfb_softc * sc,bool usepoll)1252ef712effStnn ssdfb_sync_ssd1306(struct ssdfb_softc *sc, bool usepoll)
1253ef712effStnn {
1254e690818eStnn struct rasops_info *ri = &sc->sc_ri;
1255e690818eStnn int block_size = 8;
1256e690818eStnn int ri_block_stride = ri->ri_stride * block_size;
1257e690818eStnn int height_in_blocks = sc->sc_p->p_height / block_size;
1258e690818eStnn int width_in_blocks = sc->sc_p->p_width / block_size;
1259e690818eStnn int ri_block_step = block_size * ri->ri_depth / 8;
1260e690818eStnn int x, y;
1261e690818eStnn union ssdfb_block *blockp;
1262e690818eStnn uint64_t raw_block;
1263e690818eStnn uint8_t *src;
1264e690818eStnn int x1, x2, y1, y2;
1265e690818eStnn
1266e690818eStnn /*
1267e690818eStnn * Transfer rasops bitmap into gddram shadow buffer while keeping track
1268e690818eStnn * of the bounding box of the dirty region we scribbled over.
1269e690818eStnn */
1270e690818eStnn x1 = width_in_blocks;
1271e690818eStnn x2 = -1;
1272e690818eStnn y1 = height_in_blocks;
1273e690818eStnn y2 = -1;
1274e690818eStnn for (y = 0; y < height_in_blocks; y++) {
1275e690818eStnn src = &ri->ri_bits[y * ri_block_stride];
1276e690818eStnn blockp = &sc->sc_gddram[y * width_in_blocks];
1277e690818eStnn for (x = 0; x < width_in_blocks; x++) {
1278ef712effStnn raw_block = ssdfb_transpose_block(src, ri->ri_stride);
1279e690818eStnn if (raw_block != blockp->raw) {
1280e690818eStnn blockp->raw = raw_block;
1281e690818eStnn if (x1 > x)
1282e690818eStnn x1 = x;
1283e690818eStnn if (x2 < x)
1284e690818eStnn x2 = x;
1285e690818eStnn if (y1 > y)
1286e690818eStnn y1 = y;
1287e690818eStnn if (y2 < y)
1288e690818eStnn y2 = y;
1289e690818eStnn }
1290e690818eStnn src += ri_block_step;
1291e690818eStnn blockp++;
1292e690818eStnn }
1293e690818eStnn }
1294e690818eStnn if (x2 != -1)
1295e690818eStnn return sc->sc_transfer_rect(sc->sc_cookie,
1296e690818eStnn x1 * block_size + sc->sc_p->p_panel_shift,
1297e690818eStnn (x2 + 1) * block_size - 1 + sc->sc_p->p_panel_shift,
1298e690818eStnn y1,
1299e690818eStnn y2,
1300e690818eStnn &sc->sc_gddram[y1 * width_in_blocks + x1].col[0],
1301e690818eStnn sc->sc_p->p_width,
1302e690818eStnn usepoll);
1303e690818eStnn
1304e690818eStnn return 0;
1305e690818eStnn }
1306e690818eStnn
1307ef712effStnn static int
ssdfb_sync_ssd1322(struct ssdfb_softc * sc,bool usepoll)1308ef712effStnn ssdfb_sync_ssd1322(struct ssdfb_softc *sc, bool usepoll)
1309ef712effStnn {
1310ef712effStnn struct rasops_info *ri = &sc->sc_ri;
1311ef712effStnn int block_size_w = 4;
1312ef712effStnn int width = sc->sc_p->p_width;
1313ef712effStnn int height = sc->sc_p->p_height;
1314ef712effStnn int width_in_blocks = width / block_size_w;
1315ef712effStnn int x, y;
1316ef712effStnn uint16_t *blockp;
1317ef712effStnn uint16_t raw_block;
1318ef712effStnn uint16_t *src;
1319ef712effStnn uint32_t *src32;
1320ef712effStnn int x1, x2, y1, y2;
1321ef712effStnn
1322ef712effStnn /*
1323ef712effStnn * Transfer rasops bitmap into gddram shadow buffer while keeping track
1324ef712effStnn * of the bounding box of the dirty region we scribbled over.
1325ef712effStnn */
1326d6ec530eStnn x1 = width;
1327ef712effStnn x2 = -1;
1328d6ec530eStnn y1 = height;
1329ef712effStnn y2 = -1;
1330ef712effStnn blockp = (uint16_t*)sc->sc_gddram;
1331ef712effStnn for (y = 0; y < height; y++) {
1332ef712effStnn src = (uint16_t*)&ri->ri_bits[y * ri->ri_stride];
1333ef712effStnn src32 = (uint32_t*)src;
1334ef712effStnn for (x = 0; x < width_in_blocks; x++) {
1335ef712effStnn #ifdef SSDFB_USE_NATIVE_DEPTH
1336ef712effStnn raw_block =
1337ef712effStnn ((*src << 12) & 0xf000) |
1338ef712effStnn ((*src << 4) & 0x0f00) |
1339ef712effStnn ((*src >> 4) & 0x00f0) |
1340ef712effStnn ((*src >> 12) & 0x000f);
1341ef712effStnn src++;
1342ef712effStnn #else
1343ef712effStnn raw_block =
13448ed75fc0Stnn # if _BYTE_ORDER == _LITTLE_ENDIAN
1345ef712effStnn ((*src32 << 8) & 0x0f00) |
1346ef712effStnn ((*src32 << 4) & 0xf000) |
1347ef712effStnn ((*src32 >> 16) & 0x000f) |
1348ef712effStnn ((*src32 >> 20) & 0x00f0);
13498ed75fc0Stnn # else
13508ed75fc0Stnn ((*src32 >> 24) & 0x000f) |
13518ed75fc0Stnn ((*src32 >> 12) & 0x00f0) |
13528ed75fc0Stnn ((*src32 ) & 0x0f00) |
13538ed75fc0Stnn ((*src32 << 12) & 0xf000);
1354ef712effStnn # endif
1355ef712effStnn src32++;
1356ef712effStnn #endif
1357ef712effStnn if (raw_block != *blockp) {
1358ef712effStnn *blockp = raw_block;
1359ef712effStnn if (x1 > x)
1360ef712effStnn x1 = x;
1361ef712effStnn if (x2 < x)
1362ef712effStnn x2 = x;
1363ef712effStnn if (y1 > y)
1364ef712effStnn y1 = y;
1365ef712effStnn if (y2 < y)
1366ef712effStnn y2 = y;
1367ef712effStnn }
1368ef712effStnn blockp++;
1369ef712effStnn }
1370ef712effStnn }
1371ef712effStnn blockp = (uint16_t*)sc->sc_gddram;
1372ef712effStnn if (x2 != -1)
1373ef712effStnn return sc->sc_transfer_rect(sc->sc_cookie,
1374ef712effStnn x1 + sc->sc_p->p_panel_shift,
1375ef712effStnn x2 + sc->sc_p->p_panel_shift,
1376ef712effStnn y1,
1377ef712effStnn y2,
1378ef712effStnn (uint8_t*)&blockp[y1 * width_in_blocks + x1],
1379ef712effStnn width * sc->sc_p->p_bits_per_pixel / 8,
1380ef712effStnn usepoll);
1381ef712effStnn return 0;
1382ef712effStnn }
1383ef712effStnn
138423cac501Stnn static int
ssdfb_sync_ssd1353(struct ssdfb_softc * sc,bool usepoll)138523cac501Stnn ssdfb_sync_ssd1353(struct ssdfb_softc *sc, bool usepoll)
138623cac501Stnn {
138723cac501Stnn int width = sc->sc_p->p_width;
138823cac501Stnn int height = sc->sc_p->p_height;
138923cac501Stnn struct rasops_info *ri = &sc->sc_ri;
139023cac501Stnn int x, y;
139123cac501Stnn uint32_t *src, *blockp;
139223cac501Stnn int x1, x2, y1, y2;
139323cac501Stnn
139423cac501Stnn /*
139523cac501Stnn * Transfer rasops bitmap into gddram shadow buffer while keeping track
139623cac501Stnn * of the bounding box of the dirty region we scribbled over.
139723cac501Stnn */
139823cac501Stnn x1 = width;
139923cac501Stnn x2 = -1;
140023cac501Stnn y1 = height;
140123cac501Stnn y2 = -1;
140223cac501Stnn blockp = (uint32_t*)sc->sc_gddram;
140323cac501Stnn for (y = 0; y < height; y++) {
140423cac501Stnn src = (uint32_t*)&ri->ri_bits[y * ri->ri_stride];
140523cac501Stnn for (x = 0; x < width; x++) {
140623cac501Stnn if (*blockp != *src) {
140723cac501Stnn *blockp = *src;
140823cac501Stnn if (x1 > x)
140923cac501Stnn x1 = x;
141023cac501Stnn if (x2 < x)
141123cac501Stnn x2 = x;
141223cac501Stnn if (y1 > y)
141323cac501Stnn y1 = y;
141423cac501Stnn if (y2 < y)
141523cac501Stnn y2 = y;
141623cac501Stnn }
141723cac501Stnn blockp++;
141823cac501Stnn src++;
141923cac501Stnn }
142023cac501Stnn }
142123cac501Stnn
142223cac501Stnn blockp = (uint32_t*)sc->sc_gddram;
142323cac501Stnn if (x2 != -1)
142423cac501Stnn return sc->sc_transfer_rect(sc->sc_cookie,
142523cac501Stnn x1 + sc->sc_p->p_panel_shift,
142623cac501Stnn x2 + sc->sc_p->p_panel_shift,
142723cac501Stnn y1,
142823cac501Stnn y2,
142923cac501Stnn (uint8_t*)&blockp[y1 * width + x1],
143023cac501Stnn width * sc->sc_p->p_bits_per_pixel / 8,
143123cac501Stnn usepoll);
143223cac501Stnn return 0;
143323cac501Stnn }
143423cac501Stnn
1435e690818eStnn static uint64_t
ssdfb_transpose_block(uint8_t * src,size_t src_stride)1436ef712effStnn ssdfb_transpose_block(uint8_t *src, size_t src_stride)
1437e690818eStnn {
1438e690818eStnn uint64_t x = 0;
1439ef712effStnn #ifdef SSDFB_USE_NATIVE_DEPTH
1440e690818eStnn uint64_t t;
1441e690818eStnn int i;
1442e690818eStnn
1443e690818eStnn /*
1444e690818eStnn * collect the 8x8 block.
1445e690818eStnn */
1446e690818eStnn for (i = 0; i < 8; i++) {
1447e690818eStnn x >>= 8;
1448e690818eStnn x |= (uint64_t)src[i * src_stride] << 56;
1449e690818eStnn }
1450e690818eStnn
1451e690818eStnn /*
1452e690818eStnn * Transpose it into gddram layout.
1453e690818eStnn * Post-transpose bswap is the same as pre-transpose bit order reversal.
1454e690818eStnn * We do this to match rasops1 bit order.
1455e690818eStnn */
1456e690818eStnn t = (x ^ (x >> 28)) & 0x00000000F0F0F0F0ULL;
1457e690818eStnn x = x ^ t ^ (t << 28);
1458e690818eStnn t = (x ^ (x >> 14)) & 0x0000CCCC0000CCCCULL;
1459e690818eStnn x = x ^ t ^ (t << 14);
1460e690818eStnn t = (x ^ (x >> 7)) & 0x00AA00AA00AA00AAULL;
1461e690818eStnn x = x ^ t ^ (t << 7);
1462e690818eStnn x = bswap64(x);
1463ef712effStnn #else
1464e690818eStnn int m, n;
1465e690818eStnn
1466e690818eStnn for (m = 0; m < 8; m++) {
1467e690818eStnn for (n = 0; n < 8; n++) {
1468e690818eStnn x >>= 1;
1469e690818eStnn x |= src[n * src_stride + m] ? (1ULL << 63) : 0;
1470e690818eStnn }
1471e690818eStnn }
1472ef712effStnn #endif
1473e690818eStnn return htole64(x);
1474e690818eStnn }
1475e690818eStnn
1476e690818eStnn static const struct ssdfb_product *
ssdfb_lookup_product(ssdfb_product_id_t id)14772f60d4f5Stnn ssdfb_lookup_product(ssdfb_product_id_t id)
14782f60d4f5Stnn {
1479e690818eStnn int i;
1480e690818eStnn
1481e690818eStnn for (i = 0; i < __arraycount(ssdfb_products); i++) {
1482e690818eStnn if (ssdfb_products[i].p_product_id == id)
1483e690818eStnn return &ssdfb_products[i];
1484e690818eStnn }
1485e690818eStnn
1486e690818eStnn return NULL;
1487e690818eStnn }
1488e690818eStnn
1489e690818eStnn static int
ssdfb_pick_font(int * cookiep,struct wsdisplay_font ** fontp)14902f60d4f5Stnn ssdfb_pick_font(int *cookiep, struct wsdisplay_font **fontp)
14912f60d4f5Stnn {
1492e690818eStnn int error;
1493e690818eStnn int c;
1494e690818eStnn struct wsdisplay_font *f;
1495e690818eStnn int i;
1496e690818eStnn uint8_t d[4][2] = {{5, 8}, {8, 8}, {8, 10} ,{8, 16}};
1497e690818eStnn
1498e690818eStnn /*
14996f6ddff5Stnn * Try to find fonts in order of increasing size.
1500e690818eStnn */
1501e690818eStnn wsfont_init();
1502e690818eStnn for(i = 0; i < __arraycount(d); i++) {
1503e690818eStnn c = wsfont_find(NULL, d[i][0], d[i][1], 0,
1504e690818eStnn WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R,
1505e690818eStnn WSFONT_FIND_BITMAP);
1506e690818eStnn if (c > 0)
1507e690818eStnn break;
1508e690818eStnn }
1509e690818eStnn if (c <= 0)
1510e690818eStnn return ENOENT;
1511e690818eStnn error = wsfont_lock(c, &f);
1512e690818eStnn if (error)
1513e690818eStnn return error;
1514e690818eStnn *cookiep = c;
1515e690818eStnn *fontp = f;
1516e690818eStnn
1517e690818eStnn return 0;
1518e690818eStnn }
1519e690818eStnn
1520e690818eStnn static void
ssdfb_clear_screen(struct ssdfb_softc * sc)1521e690818eStnn ssdfb_clear_screen(struct ssdfb_softc *sc)
1522e690818eStnn {
1523e690818eStnn struct rasops_info *ri = &sc->sc_ri;
1524e690818eStnn
1525e690818eStnn memset(sc->sc_gddram, 0xff, sc->sc_gddram_len);
1526e690818eStnn memset(ri->ri_bits, 0, sc->sc_ri_bits_len);
1527e690818eStnn }
1528e690818eStnn
1529e690818eStnn #if defined(DDB)
1530e690818eStnn static void
ssdfb_ddb_trap_callback(int enable)1531e690818eStnn ssdfb_ddb_trap_callback(int enable)
1532e690818eStnn {
1533e690818eStnn extern struct cfdriver ssdfb_cd;
1534e690818eStnn struct ssdfb_softc *sc;
1535e690818eStnn int i;
1536e690818eStnn
1537e690818eStnn for (i = 0; i < ssdfb_cd.cd_ndevs; i++) {
1538e690818eStnn sc = device_lookup_private(&ssdfb_cd, i);
1539e690818eStnn if (sc != NULL && sc->sc_is_console) {
1540e690818eStnn ssdfb_set_usepoll(sc, (bool)enable);
1541e690818eStnn }
1542e690818eStnn }
1543e690818eStnn }
1544e690818eStnn #endif
1545