1/* 2 * lws abstract display implementation for ssd1306 on i2c 3 * 4 * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include <private-lib-core.h> 26#include <drivers/devices/display/ssd1306.h> 27 28 29static uint8_t ssd1306_128x64_init[] = { 30 SSD1306_DISPLAYOFF, 31 SSD1306_SETDISPLAYCLOCKDIV, 0xf0, 32 SSD1306_SETMULTIPLEX, 64 - 1, 33 SSD1306_SETDISPLAYOFFSET, 0, 34 SSD1306_CHARGEPUMP, 0x14, 35 SSD1306_MEMORYMODE, 0, 36 SSD1306_SEGREMAP | (0 << 0), 37 SSD1306_COMSCANDEC, 38 SSD1306_SETCOMPINS, (1 << 4) | 0x02, 39 SSD1306_SETCONTRAST, 0, /* start at lowest */ 40 SSD1306_SETPRECHARGE, (0xf << 4) | (1 << 0), 41 SSD1306_SETVCOMDESELECT, (4 << 4), 42 SSD1306_DEACTIVATE_SCROLL, 43 SSD1306_DISPLAYALLON_RESUME, 44 SSD1306_NORMALDISPLAY, 45 SSD1306_DISPLAYON 46}; 47 48int 49lws_display_ssd1306_i2c_init(const struct lws_display *disp) 50{ 51 const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp; 52 53 si->i2c->init(si->i2c); 54 55 if (si->gpio) { 56 si->gpio->mode(si->reset_gpio, LWSGGPIO_FL_WRITE | 57 LWSGGPIO_FL_PULLUP); 58 si->gpio->set(si->reset_gpio, 0); 59 lws_msleep(1); 60 si->gpio->set(si->reset_gpio, 1); 61 lws_msleep(1); 62 } 63 64 if (lws_i2c_command_list(si->i2c, si->i2c7_address, 65 ssd1306_128x64_init, 66 LWS_ARRAY_SIZE(ssd1306_128x64_init))) { 67 lwsl_err("%s: fail\n", __func__); 68 return 1; 69 } 70 71 return 0; 72} 73 74int 75lws_display_ssd1306_i2c_contrast(const struct lws_display *disp, uint8_t b) 76{ 77 const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp; 78 uint8_t ba[2]; 79 80 ba[0] = SSD1306_SETCONTRAST; 81 ba[1] = b; 82 83 return lws_i2c_command_list(si->i2c, si->i2c7_address, 84 ba, LWS_ARRAY_SIZE(ba)); 85} 86 87int 88lws_display_ssd1306_i2c_blit(const struct lws_display *disp, const uint8_t *src, 89 lws_display_scalar x, lws_display_scalar y, 90 lws_display_scalar w, lws_display_scalar h) 91{ 92 const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp; 93 uint8_t ba[6]; 94 int n, m; 95 96 /* 97 * The display is arranged in 128x8 bands, with one byte containing 98 * the 8 vertical pixels of the band. 99 */ 100 101 if (h < 8) 102 h = 8; 103 104 ba[0] = SSD1306_COLUMNADDR; 105 ba[1] = x; 106 ba[2] = x + w - 1; 107 ba[3] = SSD1306_PAGEADDR; 108 ba[4] = y / 8; 109 ba[5] = ba[4] + (h / 8) - 1; 110 111 if (lws_i2c_command_list(si->i2c, si->i2c7_address, 112 ba, LWS_ARRAY_SIZE(ba))) { 113 lwsl_err("%s: fail\n", __func__); 114 return 1; 115 } 116 117 for (n = 0; n < (w * h) / 8;) { 118 lws_bb_i2c_start(si->i2c); 119 lws_bb_i2c_write(si->i2c, si->i2c7_address << 1); 120 lws_bb_i2c_write(si->i2c, SSD1306_SETSTARTLINE | y); 121 122 for (m = 0; m < w; m++) 123 lws_bb_i2c_write(si->i2c, src[n++]); 124 125 lws_bb_i2c_stop(si->i2c); 126 y += 8; 127 } 128 129 return 0; 130} 131 132int 133lws_display_ssd1306_i2c_power(const struct lws_display *disp, int state) 134{ 135 const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp; 136 137 if (!state) 138 return lws_i2c_command(si->i2c, si->i2c7_address, 139 SSD1306_DISPLAYOFF | !!state); 140 141 return lws_display_ssd1306_i2c_init(disp); 142} 143