Home | History | Annotate | Line # | Download | only in spi
ssdfb_spi.c revision 1.3.10.1
      1  1.3.10.1  thorpej /* $NetBSD: ssdfb_spi.c,v 1.3.10.1 2021/04/03 22:28:50 thorpej Exp $ */
      2       1.1      tnn 
      3       1.1      tnn /*
      4       1.1      tnn  * Copyright (c) 2019 The NetBSD Foundation, Inc.
      5       1.1      tnn  * All rights reserved.
      6       1.1      tnn  *
      7       1.1      tnn  * This code is derived from software contributed to The NetBSD Foundation
      8       1.1      tnn  * by Tobias Nygren.
      9       1.1      tnn  *
     10       1.1      tnn  * Redistribution and use in source and binary forms, with or without
     11       1.1      tnn  * modification, are permitted provided that the following conditions
     12       1.1      tnn  * are met:
     13       1.1      tnn  * 1. Redistributions of source code must retain the above copyright
     14       1.1      tnn  *    notice, this list of conditions and the following disclaimer.
     15       1.1      tnn  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1      tnn  *    notice, this list of conditions and the following disclaimer in the
     17       1.1      tnn  *    documentation and/or other materials provided with the distribution.
     18       1.1      tnn  *
     19       1.1      tnn  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20       1.1      tnn  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21       1.1      tnn  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22       1.1      tnn  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23       1.1      tnn  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24       1.1      tnn  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25       1.1      tnn  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26       1.1      tnn  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27       1.1      tnn  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28       1.1      tnn  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29       1.1      tnn  * POSSIBILITY OF SUCH DAMAGE.
     30       1.1      tnn  */
     31       1.1      tnn 
     32       1.1      tnn #include <sys/cdefs.h>
     33  1.3.10.1  thorpej __KERNEL_RCSID(0, "$NetBSD: ssdfb_spi.c,v 1.3.10.1 2021/04/03 22:28:50 thorpej Exp $");
     34       1.1      tnn 
     35       1.1      tnn #include <sys/param.h>
     36       1.1      tnn #include <sys/device.h>
     37       1.1      tnn #include <sys/kernel.h>
     38       1.1      tnn #include <dev/wscons/wsdisplayvar.h>
     39       1.1      tnn #include <dev/rasops/rasops.h>
     40       1.1      tnn #include <dev/spi/spivar.h>
     41       1.1      tnn #include <dev/ic/ssdfbvar.h>
     42       1.1      tnn 
     43       1.1      tnn struct bs_state {
     44       1.1      tnn 	uint8_t	*base;
     45       1.1      tnn 	uint8_t	*cur;
     46       1.1      tnn 	uint8_t	mask;
     47       1.1      tnn };
     48       1.1      tnn 
     49       1.1      tnn struct ssdfb_spi_softc {
     50       1.1      tnn 	struct ssdfb_softc	sc;
     51       1.1      tnn 	struct spi_handle	*sc_sh;
     52       1.1      tnn 	bool			sc_3wiremode;
     53       1.1      tnn };
     54       1.1      tnn 
     55       1.1      tnn static int	ssdfb_spi_match(device_t, cfdata_t, void *);
     56       1.1      tnn static void	ssdfb_spi_attach(device_t, device_t, void *);
     57       1.1      tnn 
     58       1.1      tnn static int	ssdfb_spi_cmd_3wire(void *, uint8_t *, size_t, bool);
     59       1.1      tnn static int	ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t,
     60       1.1      tnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
     61       1.1      tnn 
     62       1.1      tnn static int	ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool);
     63       1.1      tnn static int	ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t,
     64       1.1      tnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
     65       1.1      tnn 
     66       1.1      tnn static void	ssdfb_bitstream_init(struct bs_state *, uint8_t *);
     67       1.1      tnn static void	ssdfb_bitstream_append(struct bs_state *, uint8_t, uint8_t);
     68       1.1      tnn static void	ssdfb_bitstream_append_cmd(struct bs_state *, uint8_t);
     69       1.1      tnn static void	ssdfb_bitstream_append_data(struct bs_state *, uint8_t *,
     70       1.1      tnn 		    size_t);
     71       1.1      tnn static void	ssdfb_bitstream_final(struct bs_state *);
     72       1.1      tnn 
     73       1.1      tnn CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc),
     74       1.1      tnn     ssdfb_spi_match, ssdfb_spi_attach, NULL, NULL);
     75       1.1      tnn 
     76       1.3      tnn static const struct device_compatible_entry compat_data[] = {
     77  1.3.10.1  thorpej 	{ .compat = "solomon,ssd1322" },
     78  1.3.10.1  thorpej 	DEVICE_COMPAT_EOL
     79       1.3      tnn };
     80       1.3      tnn 
     81       1.1      tnn static int
     82       1.1      tnn ssdfb_spi_match(device_t parent, cfdata_t match, void *aux)
     83       1.1      tnn {
     84       1.1      tnn 	struct spi_attach_args *sa = aux;
     85       1.3      tnn 	int res;
     86       1.3      tnn 
     87       1.3      tnn 	res = spi_compatible_match(sa, match, compat_data);
     88       1.3      tnn 	if (!res)
     89       1.3      tnn 		return res;
     90       1.1      tnn 
     91       1.1      tnn 	/*
     92       1.1      tnn 	 * SSD1306 and SSD1322 data sheets specify 100ns cycle time.
     93       1.1      tnn 	 */
     94       1.1      tnn 	if (spi_configure(sa->sa_handle, SPI_MODE_0, 10000000))
     95       1.3      tnn 		res = 0;
     96       1.1      tnn 
     97       1.3      tnn 	return res;
     98       1.1      tnn }
     99       1.1      tnn 
    100       1.1      tnn static void
    101       1.1      tnn ssdfb_spi_attach(device_t parent, device_t self, void *aux)
    102       1.1      tnn {
    103       1.1      tnn 	struct ssdfb_spi_softc *sc = device_private(self);
    104       1.1      tnn 	struct cfdata *cf = device_cfdata(self);
    105       1.1      tnn 	struct spi_attach_args *sa = aux;
    106       1.1      tnn 	int flags = cf->cf_flags;
    107       1.1      tnn 
    108       1.1      tnn 	sc->sc.sc_dev = self;
    109       1.1      tnn 	sc->sc_sh = sa->sa_handle;
    110       1.1      tnn 	sc->sc.sc_cookie = (void *)sc;
    111       1.1      tnn 	if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN)
    112       1.1      tnn 		flags |= SSDFB_PRODUCT_SSD1322_GENERIC;
    113       1.1      tnn 	/*
    114       1.1      tnn 	 * Note on interface modes.
    115       1.1      tnn 	 *
    116       1.1      tnn 	 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains
    117       1.1      tnn 	 * the bit that determines if the lower 8 bits are command or data.
    118       1.1      tnn 	 *
    119       1.1      tnn 	 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO
    120       1.1      tnn 	 * pin for the command/data bit. But in other to allocate a GPIO pin
    121       1.1      tnn 	 * we need to use fdt, so only support 3 wire mode in this frontend,
    122       1.1      tnn 	 * at least for now.
    123       1.1      tnn 	 */
    124       1.1      tnn 	sc->sc_3wiremode = true;
    125       1.1      tnn 
    126       1.1      tnn 	switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) {
    127       1.1      tnn 	case SSDFB_PRODUCT_SSD1322_GENERIC:
    128       1.1      tnn 		if (sc->sc_3wiremode) {
    129       1.1      tnn 			sc->sc.sc_transfer_rect =
    130       1.1      tnn 			    ssdfb_spi_xfer_rect_3wire_ssd1322;
    131       1.1      tnn 		} else {
    132       1.1      tnn 			sc->sc.sc_transfer_rect =
    133       1.1      tnn 			    ssdfb_spi_xfer_rect_4wire_ssd1322;
    134       1.1      tnn 		}
    135       1.1      tnn 		break;
    136       1.1      tnn 	default:
    137       1.1      tnn 		panic("ssdfb_spi_attach: product not implemented");
    138       1.1      tnn 	}
    139       1.1      tnn 	if (sc->sc_3wiremode) {
    140       1.1      tnn 		sc->sc.sc_cmd = ssdfb_spi_cmd_3wire;
    141       1.1      tnn 	} else {
    142       1.1      tnn 		sc->sc.sc_cmd = ssdfb_spi_cmd_4wire;
    143       1.1      tnn 	}
    144       1.1      tnn 
    145       1.1      tnn 	ssdfb_attach(&sc->sc, flags);
    146       1.1      tnn 
    147       1.1      tnn 	device_printf(sc->sc.sc_dev, "%d-wire SPI interface\n",
    148       1.1      tnn 	    sc->sc_3wiremode == true ? 3 : 4);
    149       1.1      tnn }
    150       1.1      tnn 
    151       1.1      tnn static int
    152       1.1      tnn ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
    153       1.1      tnn {
    154       1.1      tnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
    155       1.1      tnn 	uint8_t bitstream[16 * 9 / 8];
    156       1.1      tnn 	struct bs_state s;
    157       1.1      tnn 
    158       1.1      tnn 	KASSERT(len > 0 && len <= 16);
    159       1.1      tnn 	ssdfb_bitstream_init(&s, bitstream);
    160       1.1      tnn 	ssdfb_bitstream_append_cmd(&s, *cmd);
    161       1.1      tnn 	cmd++;
    162       1.1      tnn 	len--;
    163       1.1      tnn 	ssdfb_bitstream_append_data(&s, cmd, len);
    164       1.1      tnn 	ssdfb_bitstream_final(&s);
    165       1.1      tnn 
    166       1.1      tnn 	return spi_send(sc->sc_sh, s.cur - s.base, bitstream);
    167       1.1      tnn }
    168       1.1      tnn 
    169       1.1      tnn static int
    170       1.1      tnn ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
    171       1.1      tnn     uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
    172       1.1      tnn {
    173       1.1      tnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
    174       1.1      tnn 	uint8_t bitstream[128 * 9 / 8];
    175       1.1      tnn 	struct bs_state s;
    176       1.1      tnn 	uint8_t row;
    177       1.1      tnn 	size_t rlen = (tocol + 1 - fromcol) * 2;
    178       1.1      tnn 	int error;
    179       1.1      tnn 
    180       1.1      tnn 	/*
    181       1.1      tnn 	 * Unlike iic(4), there is no way to force spi(4) to use polling.
    182       1.1      tnn 	 */
    183       1.2      tnn 	if (usepoll && !cold)
    184       1.1      tnn 		return 0;
    185       1.1      tnn 
    186       1.1      tnn 	ssdfb_bitstream_init(&s, bitstream);
    187       1.1      tnn 	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS);
    188       1.1      tnn 	ssdfb_bitstream_append_data(&s, &fromrow, 1);
    189       1.1      tnn 	ssdfb_bitstream_append_data(&s, &torow, 1);
    190       1.1      tnn 	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS);
    191       1.1      tnn 	ssdfb_bitstream_append_data(&s, &fromcol, 1);
    192       1.1      tnn 	ssdfb_bitstream_append_data(&s, &tocol, 1);
    193       1.1      tnn 	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM);
    194       1.1      tnn 	ssdfb_bitstream_final(&s);
    195       1.1      tnn 	error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
    196       1.1      tnn 	if (error)
    197       1.1      tnn 		return error;
    198       1.1      tnn 
    199       1.1      tnn 	KASSERT(rlen <= 128);
    200       1.1      tnn 	for (row = fromrow; row <= torow; row++) {
    201       1.1      tnn 		ssdfb_bitstream_init(&s, bitstream);
    202       1.1      tnn 		ssdfb_bitstream_append_data(&s, p, rlen);
    203       1.1      tnn 		ssdfb_bitstream_final(&s);
    204       1.1      tnn 		error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
    205       1.1      tnn 		if (error)
    206       1.1      tnn 			return error;
    207       1.1      tnn 		p += stride;
    208       1.1      tnn 	}
    209       1.1      tnn 
    210       1.1      tnn 	return 0;
    211       1.1      tnn }
    212       1.1      tnn 
    213       1.1      tnn static void
    214       1.1      tnn ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst)
    215       1.1      tnn {
    216       1.1      tnn 	s->base = s->cur = dst;
    217       1.1      tnn 	s->mask = 0x80;
    218       1.1      tnn }
    219       1.1      tnn 
    220       1.1      tnn static void
    221       1.1      tnn ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask)
    222       1.1      tnn {
    223       1.1      tnn 	while(srcmask) {
    224       1.1      tnn 		if (b & srcmask)
    225       1.1      tnn 			*s->cur |= s->mask;
    226       1.1      tnn 		else
    227       1.1      tnn 			*s->cur &= ~s->mask;
    228       1.1      tnn 		srcmask >>= 1;
    229       1.1      tnn 		s->mask >>= 1;
    230       1.1      tnn 		if (!s->mask) {
    231       1.1      tnn 			s->mask = 0x80;
    232       1.1      tnn 			s->cur++;
    233       1.1      tnn 		}
    234       1.1      tnn 	}
    235       1.1      tnn }
    236       1.1      tnn 
    237       1.1      tnn static void
    238       1.1      tnn ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd)
    239       1.1      tnn {
    240       1.1      tnn 	ssdfb_bitstream_append(s, 0, 1);
    241       1.1      tnn 	ssdfb_bitstream_append(s, cmd, 0x80);
    242       1.1      tnn }
    243       1.1      tnn 
    244       1.1      tnn static void
    245       1.1      tnn ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len)
    246       1.1      tnn {
    247       1.1      tnn 	while(len--) {
    248       1.1      tnn 		ssdfb_bitstream_append(s, 1, 1);
    249       1.1      tnn 		ssdfb_bitstream_append(s, *data++, 0x80);
    250       1.1      tnn 	}
    251       1.1      tnn }
    252       1.1      tnn 
    253       1.1      tnn static void
    254       1.1      tnn ssdfb_bitstream_final(struct bs_state *s)
    255       1.1      tnn {
    256       1.1      tnn 	uint8_t padding_cmd = SSD1322_CMD_WRITE_RAM;
    257       1.1      tnn 	/* padding_cmd = SSDFB_NOP_CMD; */
    258       1.1      tnn 
    259       1.1      tnn 	while (s->mask != 0x80) {
    260       1.1      tnn 		ssdfb_bitstream_append_cmd(s, padding_cmd);
    261       1.1      tnn 	}
    262       1.1      tnn }
    263       1.1      tnn 
    264       1.1      tnn static void
    265       1.1      tnn ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value)
    266       1.1      tnn {
    267       1.1      tnn 	/* TODO: this should toggle an auxilliary GPIO pin */
    268       1.1      tnn 	panic("ssdfb_spi_4wire_set_dc");
    269       1.1      tnn }
    270       1.1      tnn 
    271       1.1      tnn static int
    272       1.1      tnn ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
    273       1.1      tnn {
    274       1.1      tnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
    275       1.1      tnn 	int error;
    276       1.1      tnn 
    277       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 0);
    278       1.1      tnn 	error = spi_send(sc->sc_sh, 1, cmd);
    279       1.1      tnn 	if (error)
    280       1.1      tnn 		return error;
    281       1.1      tnn 	if (len > 1) {
    282       1.1      tnn 		ssdfb_spi_4wire_set_dc(sc, 1);
    283       1.1      tnn 		len--;
    284       1.1      tnn 		cmd++;
    285       1.1      tnn 		error = spi_send(sc->sc_sh, len, cmd);
    286       1.1      tnn 		if (error)
    287       1.1      tnn 			return error;
    288       1.1      tnn 	}
    289       1.1      tnn 
    290       1.1      tnn 	return 0;
    291       1.1      tnn }
    292       1.1      tnn 
    293       1.1      tnn static int
    294       1.1      tnn ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
    295       1.1      tnn     uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
    296       1.1      tnn {
    297       1.1      tnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
    298       1.1      tnn 	uint8_t row;
    299       1.1      tnn 	size_t rlen = (tocol + 1 - fromcol) * 2;
    300       1.1      tnn 	int error;
    301       1.1      tnn 	uint8_t cmd;
    302       1.1      tnn 	uint8_t data[2];
    303       1.1      tnn 
    304       1.1      tnn 	/*
    305       1.1      tnn 	 * Unlike iic(4), there is no way to force spi(4) to use polling.
    306       1.1      tnn 	 */
    307       1.2      tnn 	if (usepoll && !cold)
    308       1.1      tnn 		return 0;
    309       1.1      tnn 
    310       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 0);
    311       1.1      tnn 	cmd = SSD1322_CMD_SET_ROW_ADDRESS;
    312       1.1      tnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
    313       1.1      tnn 	if (error)
    314       1.1      tnn 		return error;
    315       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 1);
    316       1.1      tnn 	data[0] = fromrow;
    317       1.1      tnn 	data[1] = torow;
    318       1.1      tnn 	error = spi_send(sc->sc_sh, sizeof(data), data);
    319       1.1      tnn 	if (error)
    320       1.1      tnn 		return error;
    321       1.1      tnn 
    322       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 0);
    323       1.1      tnn 	cmd = SSD1322_CMD_SET_COLUMN_ADDRESS;
    324       1.1      tnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
    325       1.1      tnn 	if (error)
    326       1.1      tnn 		return error;
    327       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 1);
    328       1.1      tnn 	data[0] = fromcol;
    329       1.1      tnn 	data[1] = tocol;
    330       1.1      tnn 	error = spi_send(sc->sc_sh, sizeof(data), data);
    331       1.1      tnn 	if (error)
    332       1.1      tnn 		return error;
    333       1.1      tnn 
    334       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 0);
    335       1.1      tnn 	cmd = SSD1322_CMD_WRITE_RAM;
    336       1.1      tnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
    337       1.1      tnn 	if (error)
    338       1.1      tnn 		return error;
    339       1.1      tnn 
    340       1.1      tnn 	ssdfb_spi_4wire_set_dc(sc, 1);
    341       1.1      tnn 	for (row = fromrow; row <= torow; row++) {
    342       1.1      tnn 		error = spi_send(sc->sc_sh, rlen, p);
    343       1.1      tnn 		if (error)
    344       1.1      tnn 			return error;
    345       1.1      tnn 		p += stride;
    346       1.1      tnn 	}
    347       1.1      tnn 
    348       1.1      tnn 	return 0;
    349       1.1      tnn }
    350