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