1/* 2 * lws abstract display implementation for ili9341 on spi 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/ili9341.h> 27 28 29static uint8_t ili9341_320x240_init[] = { 30 /* 31 * This provides 70Hz 320x240 at RGB565, we assume im[3:0] is 1110 32 * which is 4-bit SPI 33 */ 34 35 3, ILI9341_FACPWCTRB, 0x00, 0x83, 0x30, 36 4, ILI9341_FACDRTIMCTRA, 0x64, 0x03, 0x12, 0x81, 37 3, ILI9341_FACPWCTRA, 0x85, 0x01, 0x79, 38 5, ILI9341_FACPUMPRAT, 0x39, 0x2c, 0x00, 0x34, 0x02, 39 1, ILI9341_FACDRTIMCTR, 0x20, 40 2, ILI9341_FACPWCTR1, 0x00, 0x00, 41 42 1, ILI9341_PWCTR1, 0x26, 43 1, ILI9341_PWCTR2, 0x11, 44 2, ILI9341_VMCTR1, 0x35, 0x3e, 45 1, ILI9341_VMCTR2, 0xbe, 46 1, ILI9341_MADCTL, 0x28, 47 1, ILI9341_VSCRSADD, 0x00, 48 1, ILI9341_PIXFMT, 0x55, 49 2, ILI9341_FRMCTR1, 0x00, 0x1b, 50 1, ILI9341_FACSETGAMMACRV, 0x00, 51 1, ILI9341_GAMMASET, 0x01, 52 15, ILI9341_GMCTRP1, 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 53 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 54 0x00, 55 15, ILI9341_GMCTRN1, 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 56 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 57 0x0f, 58 4, ILI9341_DFUNCTR, 0x0a, 0x82, 0x27, 0x00, 59}; 60 61int 62lws_display_ili9341_spi_init(const struct lws_display *disp) 63{ 64 const lws_display_ili9341_t *ili = (const lws_display_ili9341_t *)disp; 65 lws_spi_desc_t desc; 66 size_t pos = 0; 67 uint8_t u[8]; 68 69 lwsl_user("%s\n", __func__); 70 71 /* hardware nRESET */ 72 73 if (ili->gpio) { 74 ili->gpio->mode(ili->reset_gpio, LWSGGPIO_FL_WRITE | 75 LWSGGPIO_FL_PULLUP); 76 ili->gpio->set(ili->reset_gpio, 0); 77 78 lws_msleep(1); 79 ili->gpio->set(ili->reset_gpio, 1); 80 lws_msleep(1); 81 } 82 83 /* 84 * We cut the init table up into transactions... atm we just go with 85 * the fact that bb spi is synchronous, using async / dma we can't use 86 * a single desc on the stack like this 87 */ 88 89 memset(&desc, 0, sizeof(desc)); 90 desc.count_cmd = 1; 91 92 while (pos < LWS_ARRAY_SIZE(ili9341_320x240_init)) { 93 desc.count_write = ili9341_320x240_init[pos++]; 94 desc.src = &ili9341_320x240_init[pos++]; 95 desc.data = &ili9341_320x240_init[pos]; 96 pos += desc.count_write; 97 98 ili->spi->queue(ili->spi, &desc); 99 } 100 101 u[0] = ILI9341_SLPOUT; 102 desc.src = &u[0]; 103 desc.count_write = 0; 104 ili->spi->queue(ili->spi, &desc); 105 106 lws_msleep(5); 107 108 u[0] = ILI9341_DISPON; 109 ili->spi->queue(ili->spi, &desc); 110 111 return 0; 112} 113 114/* backlight handled by PWM */ 115 116int 117lws_display_ili9341_spi_brightness(const struct lws_display *disp, uint8_t b) 118{ 119 return 0; 120} 121 122int 123lws_display_ili9341_spi_blit(const struct lws_display *disp, const uint8_t *src, 124 lws_display_scalar x, lws_display_scalar y, 125 lws_display_scalar w, lws_display_scalar h) 126{ 127 const lws_display_ili9341_t *ili = (const lws_display_ili9341_t *)disp; 128 lws_spi_desc_t desc; 129 uint8_t u[5]; 130 131 memset(&desc, 0, sizeof(desc)); 132 desc.count_cmd = 1; 133 desc.src = &u[0]; 134 desc.count_write = 0; 135 136 /* 137 * Blit a line at a time 138 */ 139 140 while (h--) { 141 142 u[0] = ILI9341_CASET; 143 desc.data = &u[1]; 144 u[1] = x; 145 u[2] = x; 146 u[3] = w >> 8; 147 u[4] = w & 0xff; 148 desc.count_write = 4; 149 ili->spi->queue(ili->spi, &desc); 150 151 u[0] = ILI9341_PASET; 152 u[1] = y >> 8; 153 u[2] = y & 0xff; 154 u[3] = (y + 1) >> 8; 155 u[4] = (y + 1) & 0xff; 156 desc.count_write = 4; 157 ili->spi->queue(ili->spi, &desc); 158 159 u[0] = ILI9341_RAMWR; 160 desc.data = src; 161 desc.count_write = w * 2; 162 ili->spi->queue(ili->spi, &desc); 163 src += w * 2; 164 y++; 165 } 166 167 return 0; 168} 169 170int 171lws_display_ili9341_spi_power(const struct lws_display *disp, int state) 172{ 173 174 const lws_display_ili9341_t *ili = (const lws_display_ili9341_t *)disp; 175 lws_spi_desc_t desc; 176 uint8_t u[1]; 177 178 memset(&desc, 0, sizeof(desc)); 179 desc.count_cmd = 1; 180 desc.data = desc.src = &u[0]; 181 u[0] = state ? ILI9341_SLPOUT : ILI9341_SLPIN; 182 ili->spi->queue(ili->spi, &desc); 183 184 /* we're not going to do anything useful for 5ms after this */ 185 186 return 0; 187} 188