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