1 /* $NetBSD: ssdfb_spi.c,v 1.11 2021/08/19 17:50:18 tnn 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.11 2021/08/19 17:50:18 tnn 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 struct spi_handle *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 res; 106 107 res = spi_compatible_match(sa, match, compat_data); 108 if (!res) 109 return res; 110 111 /* 112 * SSD1306 and SSD1322 data sheets specify 100ns cycle time. 113 */ 114 if (spi_configure(sa->sa_handle, SPI_MODE_0, 10000000)) 115 res = 0; 116 117 return res; 118 } 119 120 static void 121 ssdfb_spi_attach(device_t parent, device_t self, void *aux) 122 { 123 struct ssdfb_spi_softc *sc = device_private(self); 124 struct cfdata *cf = device_cfdata(self); 125 struct spi_attach_args *sa = aux; 126 int flags = cf->cf_flags; 127 128 sc->sc.sc_dev = self; 129 sc->sc_sh = sa->sa_handle; 130 sc->sc.sc_cookie = (void *)sc; 131 if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) { 132 const struct device_compatible_entry *dce = 133 device_compatible_lookup(sa->sa_compat, sa->sa_ncompat, compat_data); 134 if (dce) 135 flags |= (int)dce->value; 136 else 137 flags |= SSDFB_PRODUCT_SSD1322_GENERIC; 138 } 139 /* 140 * Note on interface modes. 141 * 142 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains 143 * the bit that determines if the lower 8 bits are command or data. 144 * 145 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO 146 * pin for the command/data bit. 147 */ 148 #ifdef FDT 149 const int phandle = sa->sa_cookie; 150 sc->sc_gpio_dc = 151 fdtbus_gpio_acquire(phandle, "dc-gpio", GPIO_PIN_OUTPUT); 152 if (!sc->sc_gpio_dc) 153 sc->sc_gpio_dc = 154 fdtbus_gpio_acquire(phandle, "cd-gpio", GPIO_PIN_OUTPUT); 155 sc->sc_3wiremode = (sc->sc_gpio_dc == NULL); 156 sc->sc_gpio_res = 157 fdtbus_gpio_acquire(phandle, "res-gpio", GPIO_PIN_OUTPUT); 158 if (sc->sc_gpio_res) { 159 fdtbus_gpio_write_raw(sc->sc_gpio_res, 0); 160 DELAY(100); 161 fdtbus_gpio_write_raw(sc->sc_gpio_res, 1); 162 DELAY(100); 163 } 164 #else 165 sc->sc_3wiremode = true; 166 #endif 167 168 sc->sc.sc_cmd = sc->sc_3wiremode 169 ? ssdfb_spi_cmd_3wire 170 : ssdfb_spi_cmd_4wire; 171 172 switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) { 173 case SSDFB_PRODUCT_SH1106_GENERIC: 174 sc->sc.sc_transfer_rect = sc->sc_3wiremode 175 ? NULL 176 : ssdfb_spi_xfer_rect_4wire_sh1106; 177 sc->sc_padding_cmd = SSDFB_CMD_NOP; 178 sc->sc_late_dc_deassert = true; 179 break; 180 case SSDFB_PRODUCT_SSD1306_GENERIC: 181 sc->sc.sc_transfer_rect = sc->sc_3wiremode 182 ? NULL 183 : ssdfb_spi_xfer_rect_4wire_ssd1306; 184 sc->sc_padding_cmd = SSDFB_CMD_NOP; 185 sc->sc_late_dc_deassert = true; 186 break; 187 case SSDFB_PRODUCT_SSD1322_GENERIC: 188 sc->sc.sc_transfer_rect = sc->sc_3wiremode 189 ? ssdfb_spi_xfer_rect_3wire_ssd1322 190 : ssdfb_spi_xfer_rect_4wire_ssd1322; 191 sc->sc_padding_cmd = SSD1322_CMD_WRITE_RAM; 192 break; 193 case SSDFB_PRODUCT_SSD1353_GENERIC: 194 case SSDFB_PRODUCT_DEP_160128A_RGB: 195 sc->sc.sc_transfer_rect = sc->sc_3wiremode 196 ? NULL /* not supported here */ 197 : ssdfb_spi_xfer_rect_4wire_ssd1353; 198 break; 199 } 200 201 if (!sc->sc.sc_transfer_rect) { 202 aprint_error(": sc_transfer_rect not implemented\n"); 203 return; 204 } 205 206 ssdfb_attach(&sc->sc, flags); 207 208 aprint_normal_dev(self, "%d-wire SPI interface\n", 209 sc->sc_3wiremode == true ? 3 : 4); 210 } 211 212 static int 213 ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll) 214 { 215 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 216 uint8_t bitstream[16 * 9 / 8]; 217 struct bs_state s; 218 219 KASSERT(len > 0 && len <= 16); 220 ssdfb_bitstream_init(&s, bitstream); 221 ssdfb_bitstream_append_cmd(&s, *cmd); 222 cmd++; 223 len--; 224 ssdfb_bitstream_append_data(&s, cmd, len); 225 ssdfb_bitstream_final(&s, sc->sc_padding_cmd); 226 227 return spi_send(sc->sc_sh, s.cur - s.base, bitstream); 228 } 229 230 static int 231 ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol, 232 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 233 { 234 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 235 uint8_t bitstream[128 * 9 / 8]; 236 struct bs_state s; 237 size_t rlen = (tocol + 1 - fromcol) * 2; 238 int error; 239 240 /* 241 * Unlike iic(4), there is no way to force spi(4) to use polling. 242 */ 243 if (usepoll && !cold) 244 return 0; 245 246 ssdfb_bitstream_init(&s, bitstream); 247 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS); 248 ssdfb_bitstream_append_data(&s, &fromrow, 1); 249 ssdfb_bitstream_append_data(&s, &torow, 1); 250 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS); 251 ssdfb_bitstream_append_data(&s, &fromcol, 1); 252 ssdfb_bitstream_append_data(&s, &tocol, 1); 253 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM); 254 ssdfb_bitstream_final(&s, sc->sc_padding_cmd); 255 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream); 256 if (error) 257 return error; 258 259 KASSERT(rlen <= 128); 260 while (fromrow <= torow) { 261 ssdfb_bitstream_init(&s, bitstream); 262 ssdfb_bitstream_append_data(&s, p, rlen); 263 ssdfb_bitstream_final(&s, sc->sc_padding_cmd); 264 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream); 265 if (error) 266 return error; 267 fromrow++; 268 p += stride; 269 } 270 271 return 0; 272 } 273 274 static void 275 ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst) 276 { 277 s->base = s->cur = dst; 278 s->mask = 0x80; 279 } 280 281 static void 282 ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask) 283 { 284 while(srcmask) { 285 if (b & srcmask) 286 *s->cur |= s->mask; 287 else 288 *s->cur &= ~s->mask; 289 srcmask >>= 1; 290 s->mask >>= 1; 291 if (!s->mask) { 292 s->mask = 0x80; 293 s->cur++; 294 } 295 } 296 } 297 298 static void 299 ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd) 300 { 301 ssdfb_bitstream_append(s, 0, 1); 302 ssdfb_bitstream_append(s, cmd, 0x80); 303 } 304 305 static void 306 ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len) 307 { 308 while(len--) { 309 ssdfb_bitstream_append(s, 1, 1); 310 ssdfb_bitstream_append(s, *data++, 0x80); 311 } 312 } 313 314 static void 315 ssdfb_bitstream_final(struct bs_state *s, uint8_t padding_cmd) 316 { 317 while (s->mask != 0x80) { 318 ssdfb_bitstream_append_cmd(s, padding_cmd); 319 } 320 } 321 322 static void 323 ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value) 324 { 325 #ifdef FDT 326 fdtbus_gpio_write_raw(sc->sc_gpio_dc, value); 327 #else 328 panic("ssdfb_spi_4wire_set_dc"); 329 #endif 330 } 331 332 static int 333 ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll) 334 { 335 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 336 int error; 337 338 ssdfb_spi_4wire_set_dc(sc, 0); 339 error = spi_send(sc->sc_sh, 1, cmd); 340 if (error) 341 return error; 342 if (len > 1) { 343 if (!sc->sc_late_dc_deassert) 344 ssdfb_spi_4wire_set_dc(sc, 1); 345 len--; 346 cmd++; 347 error = spi_send(sc->sc_sh, len, cmd); 348 if (error) 349 return error; 350 } 351 352 return 0; 353 } 354 355 static int 356 ssdfb_spi_xfer_rect_4wire_sh1106(void *cookie, uint8_t fromcol, uint8_t tocol, 357 uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll) 358 { 359 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 360 size_t rlen = tocol + 1 - fromcol; 361 int error; 362 uint8_t cmd[] = { 363 SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage, 364 SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4), 365 SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf) 366 }; 367 368 if (usepoll && !cold) 369 return 0; 370 371 while (frompage <= topage) { 372 cmd[0] = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage; 373 ssdfb_spi_4wire_set_dc(sc, 0); 374 error = spi_send(sc->sc_sh, sizeof(cmd), cmd); 375 if (error) 376 return error; 377 ssdfb_spi_4wire_set_dc(sc, 1); 378 error = spi_send(sc->sc_sh, rlen, p); 379 if (error) 380 return error; 381 frompage++; 382 p += stride; 383 } 384 385 return 0; 386 } 387 388 static int 389 ssdfb_spi_xfer_rect_4wire_ssd1306(void *cookie, uint8_t fromcol, uint8_t tocol, 390 uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll) 391 { 392 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 393 size_t rlen = tocol + 1 - fromcol; 394 int error; 395 uint8_t cmd[] = { 396 SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE, 397 SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL, 398 SSD1306_CMD_SET_COLUMN_ADDRESS, 399 fromcol, 400 tocol, 401 SSD1306_CMD_SET_PAGE_ADDRESS, 402 frompage, 403 topage 404 }; 405 406 if (usepoll && !cold) 407 return 0; 408 409 ssdfb_spi_4wire_set_dc(sc, 0); 410 error = spi_send(sc->sc_sh, sizeof(cmd), cmd); 411 if (error) 412 return error; 413 ssdfb_spi_4wire_set_dc(sc, 1); 414 415 while (frompage <= topage) { 416 error = spi_send(sc->sc_sh, rlen, p); 417 if (error) 418 return error; 419 frompage++; 420 p += stride; 421 } 422 423 return 0; 424 } 425 426 static int 427 ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol, 428 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 429 { 430 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 431 size_t rlen = (tocol + 1 - fromcol) * 2; 432 int error; 433 uint8_t cmd; 434 uint8_t data[2]; 435 436 if (usepoll && !cold) 437 return 0; 438 439 ssdfb_spi_4wire_set_dc(sc, 0); 440 cmd = SSD1322_CMD_SET_ROW_ADDRESS; 441 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 442 if (error) 443 return error; 444 ssdfb_spi_4wire_set_dc(sc, 1); 445 data[0] = fromrow; 446 data[1] = torow; 447 error = spi_send(sc->sc_sh, sizeof(data), data); 448 if (error) 449 return error; 450 451 ssdfb_spi_4wire_set_dc(sc, 0); 452 cmd = SSD1322_CMD_SET_COLUMN_ADDRESS; 453 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 454 if (error) 455 return error; 456 ssdfb_spi_4wire_set_dc(sc, 1); 457 data[0] = fromcol; 458 data[1] = tocol; 459 error = spi_send(sc->sc_sh, sizeof(data), data); 460 if (error) 461 return error; 462 463 ssdfb_spi_4wire_set_dc(sc, 0); 464 cmd = SSD1322_CMD_WRITE_RAM; 465 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 466 if (error) 467 return error; 468 469 ssdfb_spi_4wire_set_dc(sc, 1); 470 while (fromrow <= torow) { 471 error = spi_send(sc->sc_sh, rlen, p); 472 if (error) 473 return error; 474 fromrow++; 475 p += stride; 476 } 477 478 return 0; 479 } 480 481 static int 482 ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol, 483 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 484 { 485 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 486 size_t rlen = (tocol + 1 - fromcol) * 3; 487 uint8_t bitstream[160 * 3]; 488 uint8_t *dstp, *srcp, *endp; 489 int error; 490 uint8_t cmd; 491 uint8_t data[2]; 492 493 if (usepoll && !cold) 494 return 0; 495 496 ssdfb_spi_4wire_set_dc(sc, 0); 497 cmd = SSD1353_CMD_SET_ROW_ADDRESS; 498 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 499 if (error) 500 return error; 501 ssdfb_spi_4wire_set_dc(sc, 1); 502 data[0] = fromrow; 503 data[1] = torow; 504 if (sc->sc.sc_upsidedown) { 505 /* fix picture outside frame on 160x128 panel */ 506 data[0] += 132 - sc->sc.sc_p->p_height; 507 data[1] += 132 - sc->sc.sc_p->p_height; 508 } 509 error = spi_send(sc->sc_sh, sizeof(data), data); 510 if (error) 511 return error; 512 513 ssdfb_spi_4wire_set_dc(sc, 0); 514 cmd = SSD1353_CMD_SET_COLUMN_ADDRESS; 515 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 516 if (error) 517 return error; 518 ssdfb_spi_4wire_set_dc(sc, 1); 519 data[0] = fromcol; 520 data[1] = tocol; 521 error = spi_send(sc->sc_sh, sizeof(data), data); 522 if (error) 523 return error; 524 525 ssdfb_spi_4wire_set_dc(sc, 0); 526 cmd = SSD1353_CMD_WRITE_RAM; 527 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 528 if (error) 529 return error; 530 531 ssdfb_spi_4wire_set_dc(sc, 1); 532 KASSERT(rlen <= sizeof(bitstream)); 533 while (fromrow <= torow) { 534 /* downconvert each row from 32bpp rgba to 18bpp panel format */ 535 dstp = bitstream; 536 endp = dstp + rlen; 537 srcp = p; 538 while (dstp < endp) { 539 *dstp++ = (*srcp++) >> 2; 540 *dstp++ = (*srcp++) >> 2; 541 *dstp++ = (*srcp++) >> 2; 542 srcp++; 543 } 544 error = spi_send(sc->sc_sh, rlen, bitstream); 545 if (error) 546 return error; 547 fromrow++; 548 p += stride; 549 } 550 551 return 0; 552 } 553