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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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