Home | History | Annotate | Line # | Download | only in i2c
ssdfb_i2c.c revision 1.11
      1 /* $NetBSD: ssdfb_i2c.c,v 1.11 2021/08/05 22:31:20 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_i2c.c,v 1.11 2021/08/05 22:31:20 tnn Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/device.h>
     37 #include <dev/wscons/wsdisplayvar.h>
     38 #include <dev/rasops/rasops.h>
     39 #include <dev/i2c/i2cvar.h>
     40 #include <dev/ic/ssdfbvar.h>
     41 
     42 struct ssdfb_i2c_softc {
     43 	struct		ssdfb_softc sc;
     44 	i2c_tag_t	sc_i2c_tag;
     45 	i2c_addr_t	sc_i2c_addr;
     46 	size_t		sc_transfer_size;
     47 };
     48 
     49 static int	ssdfb_i2c_match(device_t, cfdata_t, void *);
     50 static void	ssdfb_i2c_attach(device_t, device_t, void *);
     51 static int	ssdfb_i2c_detach(device_t, int);
     52 
     53 static int	ssdfb_i2c_probe_transfer_size(struct ssdfb_i2c_softc *, bool);
     54 static int	ssdfb_i2c_transfer(struct ssdfb_i2c_softc *, uint8_t, uint8_t *,
     55 				size_t, int);
     56 static int	ssdfb_i2c_cmd(void *, uint8_t *, size_t, bool);
     57 static int	ssdfb_i2c_transfer_rect(void *, uint8_t, uint8_t, uint8_t,
     58 				uint8_t, uint8_t *, size_t, bool);
     59 static int	ssdfb_i2c_transfer_rect_ssd1306(void *, uint8_t, uint8_t,
     60 				uint8_t, uint8_t, uint8_t *, size_t, bool);
     61 static int	ssdfb_i2c_transfer_rect_sh1106(void *, uint8_t, uint8_t,
     62 				uint8_t, uint8_t, uint8_t *, size_t, bool);
     63 static int	ssdfb_smbus_transfer_rect(void *, uint8_t, uint8_t, uint8_t,
     64 				uint8_t, uint8_t *, size_t, bool);
     65 
     66 CFATTACH_DECL_NEW(ssdfb_iic, sizeof(struct ssdfb_i2c_softc),
     67     ssdfb_i2c_match, ssdfb_i2c_attach, ssdfb_i2c_detach, NULL);
     68 
     69 static const struct device_compatible_entry compat_data[] = {
     70 	{ .compat = "solomon,ssd1306fb-i2c",
     71 	  .value = SSDFB_PRODUCT_SSD1306_GENERIC },
     72 
     73 	{ .compat = "sino,sh1106fb-i2c",
     74 	  .value = SSDFB_PRODUCT_SH1106_GENERIC },
     75 
     76 	DEVICE_COMPAT_EOL
     77 };
     78 
     79 static int
     80 ssdfb_i2c_match(device_t parent, cfdata_t match, void *aux)
     81 {
     82 	struct i2c_attach_args *ia = aux;
     83 	int match_result;
     84 
     85 	if (iic_use_direct_match(ia, match, compat_data, &match_result))
     86 		return match_result;
     87 
     88 	switch (ia->ia_addr) {
     89 	case SSDFB_I2C_DEFAULT_ADDR:
     90 	case SSDFB_I2C_ALTERNATIVE_ADDR:
     91 		return I2C_MATCH_ADDRESS_ONLY;
     92 	}
     93 
     94 	return 0;
     95 }
     96 
     97 static void
     98 ssdfb_i2c_attach(device_t parent, device_t self, void *aux)
     99 {
    100 	struct ssdfb_i2c_softc *sc = device_private(self);
    101 	struct cfdata *cf = device_cfdata(self);
    102 	struct i2c_attach_args *ia = aux;
    103 	int flags = cf->cf_flags;
    104 
    105 	if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) {
    106 		const struct device_compatible_entry *dce =
    107 		    iic_compatible_lookup(ia, compat_data);
    108 		if (dce != NULL) {
    109 			flags |= (int)dce->value;
    110 		}
    111 	}
    112 	if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN)
    113 		flags |= SSDFB_PRODUCT_SSD1306_GENERIC;
    114 
    115 	flags |= SSDFB_ATTACH_FLAG_MPSAFE;
    116 	sc->sc.sc_dev = self;
    117 	sc->sc_i2c_tag = ia->ia_tag;
    118 	sc->sc_i2c_addr = ia->ia_addr;
    119 	sc->sc.sc_cookie = (void *)sc;
    120 	sc->sc.sc_cmd = ssdfb_i2c_cmd;
    121 	sc->sc.sc_transfer_rect = ssdfb_i2c_transfer_rect;
    122 
    123 	ssdfb_attach(&sc->sc, flags);
    124 }
    125 
    126 static int
    127 ssdfb_i2c_detach(device_t self, int flags)
    128 {
    129 	struct ssdfb_i2c_softc *sc = device_private(self);
    130 
    131 	return ssdfb_detach(&sc->sc);
    132 }
    133 
    134 static int
    135 ssdfb_i2c_probe_transfer_size(struct ssdfb_i2c_softc *sc, bool usepoll)
    136 {
    137 	int flags = usepoll ? I2C_F_POLL : 0;
    138 	uint8_t cb = SSDFB_I2C_CTRL_BYTE_DATA_MASK;
    139 	int error;
    140 	uint8_t buf[128];
    141 	size_t len;
    142 
    143 	error = iic_acquire_bus(sc->sc_i2c_tag, flags);
    144 	if (error)
    145 		return error;
    146 	len = sizeof(buf);
    147 	memset(buf, 0, len);
    148 	while (len > 0) {
    149 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    150 		    sc->sc_i2c_addr, &cb, sizeof(cb), buf, len, flags);
    151 		if (!error) {
    152 			break;
    153 		}
    154 		len >>= 1;
    155 	}
    156 	if (!error && len < 2) {
    157 		error = E2BIG;
    158 	} else {
    159 		sc->sc_transfer_size = len;
    160 	}
    161 	(void) iic_release_bus(sc->sc_i2c_tag, flags);
    162 
    163 	return error;
    164 }
    165 
    166 static int
    167 ssdfb_i2c_transfer(struct ssdfb_i2c_softc *sc, uint8_t cb, uint8_t *data,
    168 		   size_t len, int flags)
    169 {
    170 	int error;
    171 	size_t xfer_size = sc->sc_transfer_size;
    172 
    173 	while (len >= xfer_size) {
    174 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    175 		    sc->sc_i2c_addr, &cb, sizeof(cb), data, xfer_size, flags);
    176 		if (error)
    177 			return error;
    178 		len -= xfer_size;
    179 		data += xfer_size;
    180 	}
    181 	if (len > 0) {
    182 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    183 		    sc->sc_i2c_addr, &cb, sizeof(cb), data, len, flags);
    184 	}
    185 
    186 	return error;
    187 }
    188 
    189 static int
    190 ssdfb_i2c_cmd(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
    191 {
    192 	struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
    193 	int flags = usepoll ? I2C_F_POLL : 0;
    194 	uint8_t cb = 0;
    195 	int error;
    196 
    197 	error = iic_acquire_bus(sc->sc_i2c_tag, flags);
    198 	if (error)
    199 		return error;
    200 	error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    201 	    sc->sc_i2c_addr, &cb, sizeof(cb), cmd, len, flags);
    202 	(void) iic_release_bus(sc->sc_i2c_tag, flags);
    203 
    204 	return error;
    205 }
    206 
    207 static int
    208 ssdfb_i2c_transfer_rect(void *cookie, uint8_t fromcol, uint8_t tocol,
    209     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
    210 {
    211 	struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
    212 	uint8_t cmd[2];
    213 	int error;
    214 
    215 	/*
    216 	 * Test if large transfers are supported by the parent i2c bus and
    217 	 * pick the fastest transfer routine for subsequent invocations.
    218 	 */
    219 	switch (sc->sc.sc_p->p_controller_id) {
    220 	case SSDFB_CONTROLLER_SSD1306:
    221 		sc->sc.sc_transfer_rect = ssdfb_i2c_transfer_rect_ssd1306;
    222 		break;
    223 	case SSDFB_CONTROLLER_SH1106:
    224 		sc->sc.sc_transfer_rect = ssdfb_i2c_transfer_rect_sh1106;
    225 		break;
    226 	default:
    227 		sc->sc.sc_transfer_rect = ssdfb_smbus_transfer_rect;
    228 		break;
    229 	}
    230 
    231 	if (sc->sc.sc_transfer_rect != ssdfb_smbus_transfer_rect) {
    232 		error = ssdfb_i2c_probe_transfer_size(sc, usepoll);
    233 		if (error)
    234 			return error;
    235 		aprint_verbose_dev(sc->sc.sc_dev, "%zd-byte transfers\n",
    236 		    sc->sc_transfer_size);
    237 		if (sc->sc_transfer_size == 2) {
    238 			sc->sc.sc_transfer_rect = ssdfb_smbus_transfer_rect;
    239 		}
    240 	}
    241 
    242 	/*
    243 	 * Set addressing mode for SSD1306.
    244 	 */
    245 	if (sc->sc.sc_p->p_controller_id == SSDFB_CONTROLLER_SSD1306) {
    246 		cmd[0] = SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE;
    247 		cmd[1] = sc->sc.sc_transfer_rect
    248 		    == ssdfb_i2c_transfer_rect_ssd1306
    249 		    ? SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL
    250 		    : SSD1306_MEMORY_ADDRESSING_MODE_PAGE;
    251 		error = ssdfb_i2c_cmd(cookie, cmd, sizeof(cmd), usepoll);
    252 		if (error)
    253 			return error;
    254 	}
    255 
    256 	return sc->sc.sc_transfer_rect(cookie, fromcol, tocol, frompage, topage,
    257 	    p, stride, usepoll);
    258 }
    259 
    260 
    261 static int
    262 ssdfb_i2c_transfer_rect_ssd1306(void *cookie, uint8_t fromcol, uint8_t tocol,
    263     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
    264 {
    265 	struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
    266 	int flags = usepoll ? I2C_F_POLL : 0;
    267 	uint8_t cc = 0;
    268 	uint8_t cb = SSDFB_I2C_CTRL_BYTE_DATA_MASK;
    269 	size_t len = tocol + 1 - fromcol;
    270 	int error;
    271 	/*
    272 	 * SSD1306 does not implement the Continuation bit correctly.
    273 	 * The SH1106 protocol defines that a control byte WITH Co
    274 	 * set must be inserted between each command. But SSD1306
    275 	 * fails to parse the commands if we do that.
    276 	 */
    277 	uint8_t cmds[] = {
    278 		SSD1306_CMD_SET_COLUMN_ADDRESS,
    279 		fromcol, tocol,
    280 		SSD1306_CMD_SET_PAGE_ADDRESS,
    281 		frompage, topage
    282 	};
    283 
    284 	error = iic_acquire_bus(sc->sc_i2c_tag, flags);
    285 	if (error)
    286 		return error;
    287 	error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    288 	    sc->sc_i2c_addr, &cc, sizeof(cc), cmds, sizeof(cmds), flags);
    289 	if (error)
    290 		goto out;
    291 	while (frompage <= topage) {
    292 		error = ssdfb_i2c_transfer(sc, cb, p, len, flags);
    293 		if (error)
    294 			goto out;
    295 		frompage++;
    296 		p += stride;
    297 	}
    298 out:
    299 	(void) iic_release_bus(sc->sc_i2c_tag, flags);
    300 
    301 	return error;
    302 }
    303 
    304 static int
    305 ssdfb_i2c_transfer_rect_sh1106(void *cookie, uint8_t fromcol, uint8_t tocol,
    306     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
    307 {
    308 	struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
    309 	int flags = usepoll ? I2C_F_POLL : 0;
    310 	uint8_t cb = SSDFB_I2C_CTRL_BYTE_DATA_MASK;
    311 	uint8_t cc = SSDFB_I2C_CTRL_BYTE_CONTINUATION_MASK;
    312 	size_t len = tocol + 1 - fromcol;
    313 	int error;
    314 	uint8_t cmds[] = {
    315 		SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage,
    316 		SSDFB_I2C_CTRL_BYTE_CONTINUATION_MASK,
    317 		SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4),
    318 		SSDFB_I2C_CTRL_BYTE_CONTINUATION_MASK,
    319 		SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf)
    320 	};
    321 
    322 	error = iic_acquire_bus(sc->sc_i2c_tag, flags);
    323 	if (error)
    324 		return error;
    325 	while (frompage <= topage) {
    326 		cmds[0] = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage;
    327 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    328 		    sc->sc_i2c_addr, &cc, sizeof(cc), cmds, sizeof(cmds), flags);
    329 		if (error)
    330 			goto out;
    331 		error = ssdfb_i2c_transfer(sc, cb, p, len, flags);
    332 		if (error)
    333 			goto out;
    334 		frompage++;
    335 		p += stride;
    336 	}
    337 out:
    338 	(void) iic_release_bus(sc->sc_i2c_tag, flags);
    339 
    340 	return error;
    341 }
    342 
    343 /*
    344  * If the parent is an SMBus, then we can only send 2 bytes
    345  * of payload per txn. The SSD1306 triple byte commands are
    346  * not available so we have to use PAGE addressing mode
    347  * and split data into multiple txns.
    348  * This is ugly and slow but it's the best we can do.
    349  */
    350 static int
    351 ssdfb_smbus_transfer_rect(void *cookie, uint8_t fromcol, uint8_t tocol,
    352     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
    353 {
    354 	struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
    355 	int flags = usepoll ? I2C_F_POLL : 0;
    356 	uint8_t cb = SSDFB_I2C_CTRL_BYTE_DATA_MASK;
    357 	uint8_t cc = 0;
    358 	size_t len = tocol + 1 - fromcol;
    359 	uint8_t cmd_higher_col =
    360 	    SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4);
    361 	uint8_t cmd_lower_col =
    362 	    SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf);
    363 	uint8_t cmd_page;
    364 	uint8_t data[2];
    365 	uint8_t *colp;
    366 	uint8_t *endp;
    367 	int error;
    368 
    369 	error = iic_acquire_bus(sc->sc_i2c_tag, flags);
    370 	if (error)
    371 		return error;
    372 	while (frompage <= topage) {
    373 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    374 		    sc->sc_i2c_addr, &cc, sizeof(cc),
    375 		    &cmd_higher_col, sizeof(cmd_higher_col), flags);
    376 		if (error)
    377 			goto out;
    378 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    379 		    sc->sc_i2c_addr, &cc, sizeof(cc),
    380 		    &cmd_lower_col, sizeof(cmd_lower_col), flags);
    381 		if (error)
    382 			goto out;
    383 		cmd_page = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage;
    384 		error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    385 		    sc->sc_i2c_addr, &cc, sizeof(cc),
    386 		    &cmd_page, sizeof(cmd_page), flags);
    387 		if (error)
    388 			goto out;
    389 		colp = p;
    390 		endp = colp + len;
    391 		if (len & 1) {
    392 			data[0] = *colp++;
    393 			error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    394 			    sc->sc_i2c_addr, &cb, sizeof(cb), data, 1, flags);
    395 			if (error)
    396 				goto out;
    397 		}
    398 		while (colp < endp) {
    399 			/*
    400 			 * Send two bytes at a time. We can't use colp directly
    401 			 * because i2c controllers sometimes have data alignment
    402 			 * requirements.
    403 			 */
    404 			data[0] = *colp++;
    405 			data[1] = *colp++;
    406 			error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
    407 			    sc->sc_i2c_addr, &cb, sizeof(cb), data, 2, flags);
    408 			if (error)
    409 				goto out;
    410 		}
    411 		frompage++;
    412 		p += stride;
    413 	}
    414 out:
    415 	(void) iic_release_bus(sc->sc_i2c_tag, flags);
    416 
    417 	return error;
    418 }
    419