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