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