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