1 /* $NetBSD: ssdfb_spi.c,v 1.18 2025/09/13 14:10:44 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2019 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tobias Nygren. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: ssdfb_spi.c,v 1.18 2025/09/13 14:10:44 thorpej Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/device.h> 37 #include <sys/kernel.h> 38 #include <dev/wscons/wsdisplayvar.h> 39 #include <dev/rasops/rasops.h> 40 #include <dev/spi/spivar.h> 41 #include <dev/ic/ssdfbvar.h> 42 #include "opt_fdt.h" 43 #ifdef FDT 44 #include <dev/fdt/fdtvar.h> 45 #endif 46 47 struct bs_state { 48 uint8_t *base; 49 uint8_t *cur; 50 uint8_t mask; 51 }; 52 53 struct ssdfb_spi_softc { 54 struct ssdfb_softc sc; 55 spi_handle_t sc_sh; 56 #ifdef FDT 57 struct fdtbus_gpio_pin *sc_gpio_dc; 58 struct fdtbus_gpio_pin *sc_gpio_res; 59 #endif 60 bool sc_3wiremode; 61 bool sc_late_dc_deassert; 62 uint8_t sc_padding_cmd; 63 }; 64 65 static int ssdfb_spi_match(device_t, cfdata_t, void *); 66 static void ssdfb_spi_attach(device_t, device_t, void *); 67 68 static int ssdfb_spi_cmd_3wire(void *, uint8_t *, size_t, bool); 69 static int ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t, 70 uint8_t, uint8_t, uint8_t *, size_t, bool); 71 72 static int ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool); 73 static int ssdfb_spi_xfer_rect_4wire_sh1106(void *, uint8_t, uint8_t, 74 uint8_t, uint8_t, uint8_t *, size_t, bool); 75 static int ssdfb_spi_xfer_rect_4wire_ssd1306(void *, uint8_t, uint8_t, 76 uint8_t, uint8_t, uint8_t *, size_t, bool); 77 static int ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t, 78 uint8_t, uint8_t, uint8_t *, size_t, bool); 79 static int ssdfb_spi_xfer_rect_4wire_ssd1353(void *, uint8_t, uint8_t, 80 uint8_t, uint8_t, uint8_t *, size_t, bool); 81 82 static void ssdfb_bitstream_init(struct bs_state *, uint8_t *); 83 static void ssdfb_bitstream_append(struct bs_state *, uint8_t, uint8_t); 84 static void ssdfb_bitstream_append_cmd(struct bs_state *, uint8_t); 85 static void ssdfb_bitstream_append_data(struct bs_state *, uint8_t *, 86 size_t); 87 static void ssdfb_bitstream_final(struct bs_state *, uint8_t); 88 89 CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc), 90 ssdfb_spi_match, ssdfb_spi_attach, NULL, NULL); 91 92 static const struct device_compatible_entry compat_data[] = { 93 { .compat = "solomon,ssd1306", .value = SSDFB_PRODUCT_SSD1306_GENERIC }, 94 { .compat = "sino,sh1106", .value = SSDFB_PRODUCT_SH1106_GENERIC }, 95 { .compat = "solomon,ssd1322", .value = SSDFB_PRODUCT_SSD1322_GENERIC }, 96 { .compat = "solomon,ssd1353", .value = SSDFB_PRODUCT_SSD1353_GENERIC }, 97 { .compat = "dep160128a", .value = SSDFB_PRODUCT_DEP_160128A_RGB }, 98 DEVICE_COMPAT_EOL 99 }; 100 101 static int 102 ssdfb_spi_match(device_t parent, cfdata_t match, void *aux) 103 { 104 struct spi_attach_args *sa = aux; 105 int match_result; 106 107 if (spi_use_direct_match(sa, compat_data, &match_result)) { 108 return match_result; 109 } 110 111 return SPI_MATCH_DEFAULT; 112 } 113 114 static void 115 ssdfb_spi_attach(device_t parent, device_t self, void *aux) 116 { 117 struct ssdfb_spi_softc *sc = device_private(self); 118 struct cfdata *cf = device_cfdata(self); 119 struct spi_attach_args *sa = aux; 120 int flags = cf->cf_flags; 121 int error; 122 123 sc->sc.sc_dev = self; 124 sc->sc_sh = sa->sa_handle; 125 sc->sc.sc_cookie = (void *)sc; 126 127 if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) { 128 const struct device_compatible_entry *dce = 129 spi_compatible_lookup(sa, compat_data); 130 if (dce) 131 flags |= (int)dce->value; 132 else 133 flags |= SSDFB_PRODUCT_SSD1322_GENERIC; 134 } 135 136 /* 137 * SSD1306 and SSD1322 data sheets specify 100ns cycle time. 138 */ 139 error = spi_configure(self, sa->sa_handle, SPI_MODE_0, 140 SPI_FREQ_MHz(10)); 141 if (error) { 142 return; 143 } 144 145 /* 146 * Note on interface modes. 147 * 148 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains 149 * the bit that determines if the lower 8 bits are command or data. 150 * 151 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO 152 * pin for the command/data bit. 153 */ 154 devhandle_t devhandle = device_handle(self); 155 switch (devhandle_type(devhandle)) { 156 #ifdef FDT 157 case DEVHANDLE_TYPE_OF: { 158 const int phandle = devhandle_to_of(devhandle); 159 sc->sc_gpio_dc = 160 fdtbus_gpio_acquire(phandle, "dc-gpio", GPIO_PIN_OUTPUT); 161 if (!sc->sc_gpio_dc) { 162 sc->sc_gpio_dc = fdtbus_gpio_acquire(phandle, 163 "cd-gpio", GPIO_PIN_OUTPUT); 164 } 165 sc->sc_3wiremode = (sc->sc_gpio_dc == NULL); 166 sc->sc_gpio_res = 167 fdtbus_gpio_acquire(phandle, "res-gpio", GPIO_PIN_OUTPUT); 168 if (sc->sc_gpio_res) { 169 fdtbus_gpio_write_raw(sc->sc_gpio_res, 0); 170 DELAY(100); 171 fdtbus_gpio_write_raw(sc->sc_gpio_res, 1); 172 DELAY(100); 173 } 174 break; 175 } 176 #endif /* FDT */ 177 default: 178 sc->sc_3wiremode = true; 179 break; 180 } 181 182 sc->sc.sc_cmd = sc->sc_3wiremode 183 ? ssdfb_spi_cmd_3wire 184 : ssdfb_spi_cmd_4wire; 185 186 switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) { 187 case SSDFB_PRODUCT_SH1106_GENERIC: 188 sc->sc.sc_transfer_rect = sc->sc_3wiremode 189 ? NULL 190 : ssdfb_spi_xfer_rect_4wire_sh1106; 191 sc->sc_padding_cmd = SSDFB_CMD_NOP; 192 sc->sc_late_dc_deassert = true; 193 break; 194 case SSDFB_PRODUCT_SSD1306_GENERIC: 195 sc->sc.sc_transfer_rect = sc->sc_3wiremode 196 ? NULL 197 : ssdfb_spi_xfer_rect_4wire_ssd1306; 198 sc->sc_padding_cmd = SSDFB_CMD_NOP; 199 sc->sc_late_dc_deassert = true; 200 break; 201 case SSDFB_PRODUCT_SSD1322_GENERIC: 202 sc->sc.sc_transfer_rect = sc->sc_3wiremode 203 ? ssdfb_spi_xfer_rect_3wire_ssd1322 204 : ssdfb_spi_xfer_rect_4wire_ssd1322; 205 sc->sc_padding_cmd = SSD1322_CMD_WRITE_RAM; 206 break; 207 case SSDFB_PRODUCT_SSD1353_GENERIC: 208 case SSDFB_PRODUCT_DEP_160128A_RGB: 209 sc->sc.sc_transfer_rect = sc->sc_3wiremode 210 ? NULL /* not supported here */ 211 : ssdfb_spi_xfer_rect_4wire_ssd1353; 212 break; 213 } 214 215 if (!sc->sc.sc_transfer_rect) { 216 aprint_error(": sc_transfer_rect not implemented\n"); 217 return; 218 } 219 220 ssdfb_attach(&sc->sc, flags); 221 222 aprint_normal_dev(self, "%d-wire SPI interface\n", 223 sc->sc_3wiremode == true ? 3 : 4); 224 } 225 226 static int 227 ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll) 228 { 229 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 230 uint8_t bitstream[16 * 9 / 8]; 231 struct bs_state s; 232 233 KASSERT(len > 0 && len <= 16); 234 ssdfb_bitstream_init(&s, bitstream); 235 ssdfb_bitstream_append_cmd(&s, *cmd); 236 cmd++; 237 len--; 238 ssdfb_bitstream_append_data(&s, cmd, len); 239 ssdfb_bitstream_final(&s, sc->sc_padding_cmd); 240 241 return spi_send(sc->sc_sh, s.cur - s.base, bitstream); 242 } 243 244 static int 245 ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol, 246 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 247 { 248 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 249 uint8_t bitstream[128 * 9 / 8]; 250 struct bs_state s; 251 size_t rlen = (tocol + 1 - fromcol) * 2; 252 int error; 253 254 /* 255 * Unlike iic(4), there is no way to force spi(4) to use polling. 256 */ 257 if (usepoll && !cold) 258 return 0; 259 260 ssdfb_bitstream_init(&s, bitstream); 261 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS); 262 ssdfb_bitstream_append_data(&s, &fromrow, 1); 263 ssdfb_bitstream_append_data(&s, &torow, 1); 264 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS); 265 ssdfb_bitstream_append_data(&s, &fromcol, 1); 266 ssdfb_bitstream_append_data(&s, &tocol, 1); 267 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM); 268 ssdfb_bitstream_final(&s, sc->sc_padding_cmd); 269 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream); 270 if (error) 271 return error; 272 273 KASSERT(rlen <= 128); 274 while (fromrow <= torow) { 275 ssdfb_bitstream_init(&s, bitstream); 276 ssdfb_bitstream_append_data(&s, p, rlen); 277 ssdfb_bitstream_final(&s, sc->sc_padding_cmd); 278 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream); 279 if (error) 280 return error; 281 fromrow++; 282 p += stride; 283 } 284 285 return 0; 286 } 287 288 static void 289 ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst) 290 { 291 s->base = s->cur = dst; 292 s->mask = 0x80; 293 } 294 295 static void 296 ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask) 297 { 298 while(srcmask) { 299 if (b & srcmask) 300 *s->cur |= s->mask; 301 else 302 *s->cur &= ~s->mask; 303 srcmask >>= 1; 304 s->mask >>= 1; 305 if (!s->mask) { 306 s->mask = 0x80; 307 s->cur++; 308 } 309 } 310 } 311 312 static void 313 ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd) 314 { 315 ssdfb_bitstream_append(s, 0, 1); 316 ssdfb_bitstream_append(s, cmd, 0x80); 317 } 318 319 static void 320 ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len) 321 { 322 while(len--) { 323 ssdfb_bitstream_append(s, 1, 1); 324 ssdfb_bitstream_append(s, *data++, 0x80); 325 } 326 } 327 328 static void 329 ssdfb_bitstream_final(struct bs_state *s, uint8_t padding_cmd) 330 { 331 while (s->mask != 0x80) { 332 ssdfb_bitstream_append_cmd(s, padding_cmd); 333 } 334 } 335 336 static void 337 ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value) 338 { 339 #ifdef FDT 340 fdtbus_gpio_write_raw(sc->sc_gpio_dc, value); 341 #else 342 panic("ssdfb_spi_4wire_set_dc"); 343 #endif 344 } 345 346 static int 347 ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll) 348 { 349 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 350 int error; 351 352 ssdfb_spi_4wire_set_dc(sc, 0); 353 error = spi_send(sc->sc_sh, 1, cmd); 354 if (error) 355 return error; 356 if (len > 1) { 357 if (!sc->sc_late_dc_deassert) 358 ssdfb_spi_4wire_set_dc(sc, 1); 359 len--; 360 cmd++; 361 error = spi_send(sc->sc_sh, len, cmd); 362 if (error) 363 return error; 364 } 365 366 return 0; 367 } 368 369 static int 370 ssdfb_spi_xfer_rect_4wire_sh1106(void *cookie, uint8_t fromcol, uint8_t tocol, 371 uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll) 372 { 373 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 374 size_t rlen = tocol + 1 - fromcol; 375 int error; 376 uint8_t cmd[] = { 377 SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage, 378 SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4), 379 SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf) 380 }; 381 382 if (usepoll && !cold) 383 return 0; 384 385 while (frompage <= topage) { 386 cmd[0] = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage; 387 ssdfb_spi_4wire_set_dc(sc, 0); 388 error = spi_send(sc->sc_sh, sizeof(cmd), cmd); 389 if (error) 390 return error; 391 ssdfb_spi_4wire_set_dc(sc, 1); 392 error = spi_send(sc->sc_sh, rlen, p); 393 if (error) 394 return error; 395 frompage++; 396 p += stride; 397 } 398 399 return 0; 400 } 401 402 static int 403 ssdfb_spi_xfer_rect_4wire_ssd1306(void *cookie, uint8_t fromcol, uint8_t tocol, 404 uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll) 405 { 406 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 407 size_t rlen = tocol + 1 - fromcol; 408 int error; 409 uint8_t cmd[] = { 410 SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE, 411 SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL, 412 SSD1306_CMD_SET_COLUMN_ADDRESS, 413 fromcol, 414 tocol, 415 SSD1306_CMD_SET_PAGE_ADDRESS, 416 frompage, 417 topage 418 }; 419 420 if (usepoll && !cold) 421 return 0; 422 423 ssdfb_spi_4wire_set_dc(sc, 0); 424 error = spi_send(sc->sc_sh, sizeof(cmd), cmd); 425 if (error) 426 return error; 427 ssdfb_spi_4wire_set_dc(sc, 1); 428 429 while (frompage <= topage) { 430 error = spi_send(sc->sc_sh, rlen, p); 431 if (error) 432 return error; 433 frompage++; 434 p += stride; 435 } 436 437 return 0; 438 } 439 440 static int 441 ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol, 442 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 443 { 444 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 445 size_t rlen = (tocol + 1 - fromcol) * 2; 446 int error; 447 uint8_t cmd; 448 uint8_t data[2]; 449 450 if (usepoll && !cold) 451 return 0; 452 453 ssdfb_spi_4wire_set_dc(sc, 0); 454 cmd = SSD1322_CMD_SET_ROW_ADDRESS; 455 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 456 if (error) 457 return error; 458 ssdfb_spi_4wire_set_dc(sc, 1); 459 data[0] = fromrow; 460 data[1] = torow; 461 error = spi_send(sc->sc_sh, sizeof(data), data); 462 if (error) 463 return error; 464 465 ssdfb_spi_4wire_set_dc(sc, 0); 466 cmd = SSD1322_CMD_SET_COLUMN_ADDRESS; 467 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 468 if (error) 469 return error; 470 ssdfb_spi_4wire_set_dc(sc, 1); 471 data[0] = fromcol; 472 data[1] = tocol; 473 error = spi_send(sc->sc_sh, sizeof(data), data); 474 if (error) 475 return error; 476 477 ssdfb_spi_4wire_set_dc(sc, 0); 478 cmd = SSD1322_CMD_WRITE_RAM; 479 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 480 if (error) 481 return error; 482 483 ssdfb_spi_4wire_set_dc(sc, 1); 484 while (fromrow <= torow) { 485 error = spi_send(sc->sc_sh, rlen, p); 486 if (error) 487 return error; 488 fromrow++; 489 p += stride; 490 } 491 492 return 0; 493 } 494 495 static int 496 ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol, 497 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 498 { 499 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 500 size_t rlen = (tocol + 1 - fromcol) * 3; 501 uint8_t bitstream[160 * 3]; 502 uint8_t *dstp, *srcp, *endp; 503 int error; 504 uint8_t cmd; 505 uint8_t data[2]; 506 507 if (usepoll && !cold) 508 return 0; 509 510 ssdfb_spi_4wire_set_dc(sc, 0); 511 cmd = SSD1353_CMD_SET_ROW_ADDRESS; 512 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 513 if (error) 514 return error; 515 ssdfb_spi_4wire_set_dc(sc, 1); 516 data[0] = fromrow; 517 data[1] = torow; 518 if (sc->sc.sc_upsidedown) { 519 /* fix picture outside frame on 160x128 panel */ 520 data[0] += 132 - sc->sc.sc_p->p_height; 521 data[1] += 132 - sc->sc.sc_p->p_height; 522 } 523 error = spi_send(sc->sc_sh, sizeof(data), data); 524 if (error) 525 return error; 526 527 ssdfb_spi_4wire_set_dc(sc, 0); 528 cmd = SSD1353_CMD_SET_COLUMN_ADDRESS; 529 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 530 if (error) 531 return error; 532 ssdfb_spi_4wire_set_dc(sc, 1); 533 data[0] = fromcol; 534 data[1] = tocol; 535 error = spi_send(sc->sc_sh, sizeof(data), data); 536 if (error) 537 return error; 538 539 ssdfb_spi_4wire_set_dc(sc, 0); 540 cmd = SSD1353_CMD_WRITE_RAM; 541 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 542 if (error) 543 return error; 544 545 ssdfb_spi_4wire_set_dc(sc, 1); 546 KASSERT(rlen <= sizeof(bitstream)); 547 while (fromrow <= torow) { 548 /* downconvert each row from 32bpp rgba to 18bpp panel format */ 549 dstp = bitstream; 550 endp = dstp + rlen; 551 srcp = p; 552 while (dstp < endp) { 553 *dstp++ = (*srcp++) >> 2; 554 *dstp++ = (*srcp++) >> 2; 555 *dstp++ = (*srcp++) >> 2; 556 srcp++; 557 } 558 error = spi_send(sc->sc_sh, rlen, bitstream); 559 if (error) 560 return error; 561 fromrow++; 562 p += stride; 563 } 564 565 return 0; 566 } 567