]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/ergodox/infinity/drivers/gdisp/IS31FL3731C/gdisp_IS31FL3731C.c
Initial structure for Ergodox as subprojects
[qmk_firmware.git] / keyboards / ergodox / infinity / drivers / gdisp / IS31FL3731C / gdisp_IS31FL3731C.c
1 /*
2 Copyright 2016 Fred Sundvik <fsundvik@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "gfx.h"
19
20 #if GFX_USE_GDISP
21
22 #define GDISP_DRIVER_VMT                        GDISPVMT_IS31FL3731C_ERGODOX
23 #include "drivers/gdisp/IS31FL3731C/gdisp_lld_config.h"
24 #include "src/gdisp/gdisp_driver.h"
25
26 #include "board_IS31FL3731C.h"
27
28 /*===========================================================================*/
29 /* Driver local definitions.                                                 */
30 /*===========================================================================*/
31
32 #ifndef GDISP_SCREEN_HEIGHT
33         #define GDISP_SCREEN_HEIGHT             9
34 #endif
35 #ifndef GDISP_SCREEN_WIDTH
36         #define GDISP_SCREEN_WIDTH              16
37 #endif
38 #ifndef GDISP_INITIAL_CONTRAST
39         #define GDISP_INITIAL_CONTRAST  0
40 #endif
41 #ifndef GDISP_INITIAL_BACKLIGHT
42         #define GDISP_INITIAL_BACKLIGHT 100
43 #endif
44
45 #define GDISP_FLG_NEEDFLUSH                     (GDISP_FLG_DRIVER<<0)
46
47 #define IS31_ADDR_DEFAULT 0x74
48
49 #define IS31_REG_CONFIG  0x00
50 // bits in reg
51 #define IS31_REG_CONFIG_PICTUREMODE 0x00
52 #define IS31_REG_CONFIG_AUTOPLAYMODE 0x08
53 #define IS31_REG_CONFIG_AUDIOPLAYMODE 0x18
54 // D2:D0 bits are starting frame for autoplay mode
55
56 #define IS31_REG_PICTDISP 0x01 // D2:D0 frame select for picture mode
57
58 #define IS31_REG_AUTOPLAYCTRL1 0x02
59 // D6:D4 number of loops (000=infty)
60 // D2:D0 number of frames to be used
61
62 #define IS31_REG_AUTOPLAYCTRL2 0x03 // D5:D0 delay time (*11ms)
63
64 #define IS31_REG_DISPLAYOPT 0x05
65 #define IS31_REG_DISPLAYOPT_INTENSITY_SAME 0x20 // same intensity for all frames
66 #define IS31_REG_DISPLAYOPT_BLINK_ENABLE 0x8
67 // D2:D0 bits blink period time (*0.27s)
68
69 #define IS31_REG_AUDIOSYNC 0x06
70 #define IS31_REG_AUDIOSYNC_ENABLE 0x1
71
72 #define IS31_REG_FRAMESTATE 0x07
73
74 #define IS31_REG_BREATHCTRL1 0x08
75 // D6:D4 fade out time (26ms*2^i)
76 // D2:D0 fade in time (26ms*2^i)
77
78 #define IS31_REG_BREATHCTRL2 0x09
79 #define IS31_REG_BREATHCTRL2_ENABLE 0x10
80 // D2:D0 extinguish time (3.5ms*2^i)
81
82 #define IS31_REG_SHUTDOWN 0x0A
83 #define IS31_REG_SHUTDOWN_OFF 0x0
84 #define IS31_REG_SHUTDOWN_ON 0x1
85
86 #define IS31_REG_AGCCTRL 0x0B
87 #define IS31_REG_ADCRATE 0x0C
88
89 #define IS31_COMMANDREGISTER 0xFD
90 #define IS31_FUNCTIONREG 0x0B    // helpfully called 'page nine'
91 #define IS31_FUNCTIONREG_SIZE 0xD
92
93 #define IS31_FRAME_SIZE 0xB4
94
95 #define IS31_PWM_REG 0x24
96 #define IS31_PWM_SIZE 0x90
97
98 #define IS31_LED_MASK_SIZE 0x12
99 #define IS31_SCREEN_WIDTH 16
100
101 #define IS31
102
103 //Generated by http://jared.geek.nz/2013/feb/linear-led-pwm
104 const unsigned char cie[256] = {
105     0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
106     1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
107     2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
108     3, 4, 4, 4, 4, 4, 4, 5, 5, 5,
109     5, 5, 6, 6, 6, 6, 6, 7, 7, 7,
110     7, 8, 8, 8, 8, 9, 9, 9, 10, 10,
111     10, 10, 11, 11, 11, 12, 12, 12, 13, 13,
112     13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
113     17, 18, 18, 19, 19, 20, 20, 21, 21, 22,
114     22, 23, 23, 24, 24, 25, 25, 26, 26, 27,
115     28, 28, 29, 29, 30, 31, 31, 32, 32, 33,
116     34, 34, 35, 36, 37, 37, 38, 39, 39, 40,
117     41, 42, 43, 43, 44, 45, 46, 47, 47, 48,
118     49, 50, 51, 52, 53, 54, 54, 55, 56, 57,
119     58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
120     68, 70, 71, 72, 73, 74, 75, 76, 77, 79,
121     80, 81, 82, 83, 85, 86, 87, 88, 90, 91,
122     92, 94, 95, 96, 98, 99, 100, 102, 103, 105,
123     106, 108, 109, 110, 112, 113, 115, 116, 118, 120,
124     121, 123, 124, 126, 128, 129, 131, 132, 134, 136,
125     138, 139, 141, 143, 145, 146, 148, 150, 152, 154,
126     155, 157, 159, 161, 163, 165, 167, 169, 171, 173,
127     175, 177, 179, 181, 183, 185, 187, 189, 191, 193,
128     196, 198, 200, 202, 204, 207, 209, 211, 214, 216,
129     218, 220, 223, 225, 228, 230, 232, 235, 237, 240,
130     242, 245, 247, 250, 252, 255,
131 };
132
133
134 /*===========================================================================*/
135 /* Driver local functions.                                                   */
136 /*===========================================================================*/
137
138 typedef struct{
139     uint8_t write_buffer_offset;
140     uint8_t write_buffer[IS31_FRAME_SIZE];
141     uint8_t frame_buffer[GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH];
142     uint8_t page;
143 }__attribute__((__packed__)) PrivData;
144
145 // Some common routines and macros
146 #define PRIV(g)                         ((PrivData*)g->priv)
147
148 /*===========================================================================*/
149 /* Driver exported functions.                                                */
150 /*===========================================================================*/
151
152 static GFXINLINE void write_page(GDisplay* g, uint8_t page) {
153     uint8_t tx[2] __attribute__((aligned(2)));
154     tx[0] = IS31_COMMANDREGISTER;
155     tx[1] = page;
156     write_data(g, tx, 2);
157 }
158
159 static GFXINLINE void write_register(GDisplay* g, uint8_t page, uint8_t reg, uint8_t data) {
160     uint8_t tx[2] __attribute__((aligned(2)));
161     tx[0] = reg;
162     tx[1] = data;
163     write_page(g, page);
164     write_data(g, tx, 2);
165 }
166
167 static GFXINLINE void write_ram(GDisplay *g, uint8_t page, uint16_t offset, uint16_t length) {
168     PRIV(g)->write_buffer_offset = offset;
169     write_page(g, page);
170     write_data(g, (uint8_t*)PRIV(g), length + 1);
171 }
172
173 LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
174         // The private area is the display surface.
175         g->priv = gfxAlloc(sizeof(PrivData));
176     __builtin_memset(PRIV(g), 0, sizeof(PrivData));
177         PRIV(g)->page = 0;
178
179         // Initialise the board interface
180         init_board(g);
181         gfxSleepMilliseconds(10);
182
183     // zero function page, all registers (assuming full_page is all zeroes)
184     write_ram(g, IS31_FUNCTIONREG, 0, IS31_FUNCTIONREG_SIZE);
185     set_hardware_shutdown(g, false);
186     gfxSleepMilliseconds(10);
187     // software shutdown
188     write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF);
189     gfxSleepMilliseconds(10);
190     // zero function page, all registers
191     write_ram(g, IS31_FUNCTIONREG, 0, IS31_FUNCTIONREG_SIZE);
192     gfxSleepMilliseconds(10);
193
194
195     // zero all LED registers on all 8 pages, and enable the mask
196     __builtin_memcpy(PRIV(g)->write_buffer, get_led_mask(g), IS31_LED_MASK_SIZE);
197     for(uint8_t i=0; i<8; i++) {
198         write_ram(g, i, 0, IS31_FRAME_SIZE);
199         gfxSleepMilliseconds(1);
200     }
201
202     // software shutdown disable (i.e. turn stuff on)
203     write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
204     gfxSleepMilliseconds(10);
205
206     // Finish Init
207     post_init_board(g);
208
209         /* Initialise the GDISP structure */
210         g->g.Width = GDISP_SCREEN_WIDTH;
211         g->g.Height = GDISP_SCREEN_HEIGHT;
212         g->g.Orientation = GDISP_ROTATE_0;
213         g->g.Powermode = powerOn;
214         g->g.Backlight = GDISP_INITIAL_BACKLIGHT;
215         g->g.Contrast = GDISP_INITIAL_CONTRAST;
216         return TRUE;
217 }
218
219 #if GDISP_HARDWARE_FLUSH
220         LLDSPEC void gdisp_lld_flush(GDisplay *g) {
221                 // Don't flush if we don't need it.
222                 if (!(g->flags & GDISP_FLG_NEEDFLUSH))
223                         return;
224
225                 PRIV(g)->page++;
226                 PRIV(g)->page %= 2;
227                 // TODO: some smarter algorithm for this
228                 // We should run only one physical page at a time
229                 // This way we don't need to send so much data, and
230                 // we could use slightly less memory
231                 uint8_t* src = PRIV(g)->frame_buffer;
232                 for (int y=0;y<GDISP_SCREEN_HEIGHT;y++) {
233                     for (int x=0;x<GDISP_SCREEN_WIDTH;x++) {
234                         PRIV(g)->write_buffer[get_led_address(g, x, y)]=cie[*src];
235                         ++src;
236                     }
237                 }
238         write_ram(g, PRIV(g)->page, IS31_PWM_REG, IS31_PWM_SIZE);
239         gfxSleepMilliseconds(1);
240         write_register(g, IS31_FUNCTIONREG, IS31_REG_PICTDISP, PRIV(g)->page);
241
242                 g->flags &= ~GDISP_FLG_NEEDFLUSH;
243         }
244 #endif
245
246 #if GDISP_HARDWARE_DRAWPIXEL
247         LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) {
248                 coord_t         x, y;
249
250                 switch(g->g.Orientation) {
251                 default:
252                 case GDISP_ROTATE_0:
253                         x = g->p.x;
254                         y = g->p.y;
255                         break;
256                 case GDISP_ROTATE_180:
257                         x = GDISP_SCREEN_WIDTH-1 - g->p.x;
258                         y = g->p.y;
259                         break;
260                 }
261                 PRIV(g)->frame_buffer[y * GDISP_SCREEN_WIDTH + x] = gdispColor2Native(g->p.color);
262                 g->flags |= GDISP_FLG_NEEDFLUSH;
263         }
264 #endif
265
266 #if GDISP_HARDWARE_PIXELREAD
267         LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g) {
268                 coord_t         x, y;
269
270                 switch(g->g.Orientation) {
271                 default:
272                 case GDISP_ROTATE_0:
273                         x = g->p.x;
274                         y = g->p.y;
275                         break;
276                 case GDISP_ROTATE_180:
277                         x = GDISP_SCREEN_WIDTH-1 - g->p.x;
278                         y = g->p.y;
279                         break;
280                 }
281                 return gdispNative2Color(PRIV(g)->frame_buffer[y * GDISP_SCREEN_WIDTH + x]);
282         }
283 #endif
284
285 #if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
286         LLDSPEC void gdisp_lld_control(GDisplay *g) {
287                 switch(g->p.x) {
288                 case GDISP_CONTROL_POWER:
289                         if (g->g.Powermode == (powermode_t)g->p.ptr)
290                                 return;
291                         switch((powermode_t)g->p.ptr) {
292                         case powerOff:
293                         case powerSleep:
294                         case powerDeepSleep:
295                 write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_OFF);
296                                 break;
297                         case powerOn:
298                 write_register(g, IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
299                                 break;
300                         default:
301                                 return;
302                         }
303                         g->g.Powermode = (powermode_t)g->p.ptr;
304                         return;
305
306                 case GDISP_CONTROL_ORIENTATION:
307                         if (g->g.Orientation == (orientation_t)g->p.ptr)
308                                 return;
309                         switch((orientation_t)g->p.ptr) {
310                         /* Rotation is handled by the drawing routines */
311                         case GDISP_ROTATE_0:
312                         case GDISP_ROTATE_180:
313                                 g->g.Height = GDISP_SCREEN_HEIGHT;
314                                 g->g.Width = GDISP_SCREEN_WIDTH;
315                                 break;
316                         case GDISP_ROTATE_90:
317                         case GDISP_ROTATE_270:
318                                 g->g.Height = GDISP_SCREEN_WIDTH;
319                                 g->g.Width = GDISP_SCREEN_HEIGHT;
320                                 break;
321                         default:
322                                 return;
323                         }
324                         g->g.Orientation = (orientation_t)g->p.ptr;
325                         return;
326
327                 case GDISP_CONTROL_CONTRAST:
328                         return;
329                 }
330         }
331 #endif // GDISP_NEED_CONTROL
332
333 #endif // GFX_USE_GDISP