1 /* $NetBSD: ssdfb_spi.c,v 1.3 2019/11/02 22:55:57 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.3 2019/11/02 22:55:57 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 43 struct bs_state { 44 uint8_t *base; 45 uint8_t *cur; 46 uint8_t mask; 47 }; 48 49 struct ssdfb_spi_softc { 50 struct ssdfb_softc sc; 51 struct spi_handle *sc_sh; 52 bool sc_3wiremode; 53 }; 54 55 static int ssdfb_spi_match(device_t, cfdata_t, void *); 56 static void ssdfb_spi_attach(device_t, device_t, void *); 57 58 static int ssdfb_spi_cmd_3wire(void *, uint8_t *, size_t, bool); 59 static int ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t, 60 uint8_t, uint8_t, uint8_t *, size_t, bool); 61 62 static int ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool); 63 static int ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t, 64 uint8_t, uint8_t, uint8_t *, size_t, bool); 65 66 static void ssdfb_bitstream_init(struct bs_state *, uint8_t *); 67 static void ssdfb_bitstream_append(struct bs_state *, uint8_t, uint8_t); 68 static void ssdfb_bitstream_append_cmd(struct bs_state *, uint8_t); 69 static void ssdfb_bitstream_append_data(struct bs_state *, uint8_t *, 70 size_t); 71 static void ssdfb_bitstream_final(struct bs_state *); 72 73 CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc), 74 ssdfb_spi_match, ssdfb_spi_attach, NULL, NULL); 75 76 static const struct device_compatible_entry compat_data[] = { 77 { "solomon,ssd1322", 0 }, 78 { NULL, 0 } 79 }; 80 81 static int 82 ssdfb_spi_match(device_t parent, cfdata_t match, void *aux) 83 { 84 struct spi_attach_args *sa = aux; 85 int res; 86 87 res = spi_compatible_match(sa, match, compat_data); 88 if (!res) 89 return res; 90 91 /* 92 * SSD1306 and SSD1322 data sheets specify 100ns cycle time. 93 */ 94 if (spi_configure(sa->sa_handle, SPI_MODE_0, 10000000)) 95 res = 0; 96 97 return res; 98 } 99 100 static void 101 ssdfb_spi_attach(device_t parent, device_t self, void *aux) 102 { 103 struct ssdfb_spi_softc *sc = device_private(self); 104 struct cfdata *cf = device_cfdata(self); 105 struct spi_attach_args *sa = aux; 106 int flags = cf->cf_flags; 107 108 sc->sc.sc_dev = self; 109 sc->sc_sh = sa->sa_handle; 110 sc->sc.sc_cookie = (void *)sc; 111 if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) 112 flags |= SSDFB_PRODUCT_SSD1322_GENERIC; 113 /* 114 * Note on interface modes. 115 * 116 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains 117 * the bit that determines if the lower 8 bits are command or data. 118 * 119 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO 120 * pin for the command/data bit. But in other to allocate a GPIO pin 121 * we need to use fdt, so only support 3 wire mode in this frontend, 122 * at least for now. 123 */ 124 sc->sc_3wiremode = true; 125 126 switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) { 127 case SSDFB_PRODUCT_SSD1322_GENERIC: 128 if (sc->sc_3wiremode) { 129 sc->sc.sc_transfer_rect = 130 ssdfb_spi_xfer_rect_3wire_ssd1322; 131 } else { 132 sc->sc.sc_transfer_rect = 133 ssdfb_spi_xfer_rect_4wire_ssd1322; 134 } 135 break; 136 default: 137 panic("ssdfb_spi_attach: product not implemented"); 138 } 139 if (sc->sc_3wiremode) { 140 sc->sc.sc_cmd = ssdfb_spi_cmd_3wire; 141 } else { 142 sc->sc.sc_cmd = ssdfb_spi_cmd_4wire; 143 } 144 145 ssdfb_attach(&sc->sc, flags); 146 147 device_printf(sc->sc.sc_dev, "%d-wire SPI interface\n", 148 sc->sc_3wiremode == true ? 3 : 4); 149 } 150 151 static int 152 ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll) 153 { 154 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 155 uint8_t bitstream[16 * 9 / 8]; 156 struct bs_state s; 157 158 KASSERT(len > 0 && len <= 16); 159 ssdfb_bitstream_init(&s, bitstream); 160 ssdfb_bitstream_append_cmd(&s, *cmd); 161 cmd++; 162 len--; 163 ssdfb_bitstream_append_data(&s, cmd, len); 164 ssdfb_bitstream_final(&s); 165 166 return spi_send(sc->sc_sh, s.cur - s.base, bitstream); 167 } 168 169 static int 170 ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol, 171 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 172 { 173 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 174 uint8_t bitstream[128 * 9 / 8]; 175 struct bs_state s; 176 uint8_t row; 177 size_t rlen = (tocol + 1 - fromcol) * 2; 178 int error; 179 180 /* 181 * Unlike iic(4), there is no way to force spi(4) to use polling. 182 */ 183 if (usepoll && !cold) 184 return 0; 185 186 ssdfb_bitstream_init(&s, bitstream); 187 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS); 188 ssdfb_bitstream_append_data(&s, &fromrow, 1); 189 ssdfb_bitstream_append_data(&s, &torow, 1); 190 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS); 191 ssdfb_bitstream_append_data(&s, &fromcol, 1); 192 ssdfb_bitstream_append_data(&s, &tocol, 1); 193 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM); 194 ssdfb_bitstream_final(&s); 195 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream); 196 if (error) 197 return error; 198 199 KASSERT(rlen <= 128); 200 for (row = fromrow; row <= torow; row++) { 201 ssdfb_bitstream_init(&s, bitstream); 202 ssdfb_bitstream_append_data(&s, p, rlen); 203 ssdfb_bitstream_final(&s); 204 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream); 205 if (error) 206 return error; 207 p += stride; 208 } 209 210 return 0; 211 } 212 213 static void 214 ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst) 215 { 216 s->base = s->cur = dst; 217 s->mask = 0x80; 218 } 219 220 static void 221 ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask) 222 { 223 while(srcmask) { 224 if (b & srcmask) 225 *s->cur |= s->mask; 226 else 227 *s->cur &= ~s->mask; 228 srcmask >>= 1; 229 s->mask >>= 1; 230 if (!s->mask) { 231 s->mask = 0x80; 232 s->cur++; 233 } 234 } 235 } 236 237 static void 238 ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd) 239 { 240 ssdfb_bitstream_append(s, 0, 1); 241 ssdfb_bitstream_append(s, cmd, 0x80); 242 } 243 244 static void 245 ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len) 246 { 247 while(len--) { 248 ssdfb_bitstream_append(s, 1, 1); 249 ssdfb_bitstream_append(s, *data++, 0x80); 250 } 251 } 252 253 static void 254 ssdfb_bitstream_final(struct bs_state *s) 255 { 256 uint8_t padding_cmd = SSD1322_CMD_WRITE_RAM; 257 /* padding_cmd = SSDFB_NOP_CMD; */ 258 259 while (s->mask != 0x80) { 260 ssdfb_bitstream_append_cmd(s, padding_cmd); 261 } 262 } 263 264 static void 265 ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value) 266 { 267 /* TODO: this should toggle an auxilliary GPIO pin */ 268 panic("ssdfb_spi_4wire_set_dc"); 269 } 270 271 static int 272 ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll) 273 { 274 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 275 int error; 276 277 ssdfb_spi_4wire_set_dc(sc, 0); 278 error = spi_send(sc->sc_sh, 1, cmd); 279 if (error) 280 return error; 281 if (len > 1) { 282 ssdfb_spi_4wire_set_dc(sc, 1); 283 len--; 284 cmd++; 285 error = spi_send(sc->sc_sh, len, cmd); 286 if (error) 287 return error; 288 } 289 290 return 0; 291 } 292 293 static int 294 ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol, 295 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll) 296 { 297 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie; 298 uint8_t row; 299 size_t rlen = (tocol + 1 - fromcol) * 2; 300 int error; 301 uint8_t cmd; 302 uint8_t data[2]; 303 304 /* 305 * Unlike iic(4), there is no way to force spi(4) to use polling. 306 */ 307 if (usepoll && !cold) 308 return 0; 309 310 ssdfb_spi_4wire_set_dc(sc, 0); 311 cmd = SSD1322_CMD_SET_ROW_ADDRESS; 312 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 313 if (error) 314 return error; 315 ssdfb_spi_4wire_set_dc(sc, 1); 316 data[0] = fromrow; 317 data[1] = torow; 318 error = spi_send(sc->sc_sh, sizeof(data), data); 319 if (error) 320 return error; 321 322 ssdfb_spi_4wire_set_dc(sc, 0); 323 cmd = SSD1322_CMD_SET_COLUMN_ADDRESS; 324 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 325 if (error) 326 return error; 327 ssdfb_spi_4wire_set_dc(sc, 1); 328 data[0] = fromcol; 329 data[1] = tocol; 330 error = spi_send(sc->sc_sh, sizeof(data), data); 331 if (error) 332 return error; 333 334 ssdfb_spi_4wire_set_dc(sc, 0); 335 cmd = SSD1322_CMD_WRITE_RAM; 336 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd); 337 if (error) 338 return error; 339 340 ssdfb_spi_4wire_set_dc(sc, 1); 341 for (row = fromrow; row <= torow; row++) { 342 error = spi_send(sc->sc_sh, rlen, p); 343 if (error) 344 return error; 345 p += stride; 346 } 347 348 return 0; 349 } 350