summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2017-12-14 06:22:04 +0100
committerWilliam Wilgus <me.theuser@yahoo.com>2018-07-26 04:23:28 +0200
commitdcd71e66bd5f0b521c9f98ebe83ff1f7abb62918 (patch)
tree418633d02677aa6f81e6fafa524a181554c823d4
parent056d4b0ec044aedd14bd657f4b6a6f8e96c127e3 (diff)
downloadrockbox-dcd71e66bd5f0b521c9f98ebe83ff1f7abb62918.tar.gz
rockbox-dcd71e66bd5f0b521c9f98ebe83ff1f7abb62918.zip
Optimize lcd-ssd1303 driver (clip series)
Saves 100+ bytes (50 of it in iram), saves a bit of power Internal LCD clock decreased but with added efficiency of drawing routines loses only around 2 Hz on the scanrate (~75Hz) while fps is slightly increased Column offsets are now calculated outside the loops saving a few instructions Passing a LCD_NOP command after lcd_update turns off Data/Cmd# gpio saving a bit more power Added a function lcd_write_cmd_triple() that allows 3 commands to be sent at once when enabled with LCD_USE_FIFO_FOR_COMMANDS it sends them back to back without checking FIFO status in between or sending to thhe D/C# Gpio. Makes an assumption about the FIFO being large enough to accept 3 commands after being emptied which should be the case on the clipv1, clipv2, clipplus. I have only enabled it for the clip plus as thats the only device I have to test it on. On clip+ the SSP clock is now turned off when screen is off Change-Id: Ib5fd24697bfe4ac8b8ee017361e789e4a7910d21
-rw-r--r--firmware/target/arm/as3525/lcd-clip.h10
-rw-r--r--firmware/target/arm/as3525/lcd-ssd1303.c211
-rw-r--r--firmware/target/arm/as3525/sansa-clip/lcd-clip.c28
-rw-r--r--firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c32
-rw-r--r--firmware/target/arm/as3525/sansa-clipv2/lcd-clipv2.c29
5 files changed, 228 insertions, 82 deletions
diff --git a/firmware/target/arm/as3525/lcd-clip.h b/firmware/target/arm/as3525/lcd-clip.h
index eb76401bc0..3bde92430c 100644
--- a/firmware/target/arm/as3525/lcd-clip.h
+++ b/firmware/target/arm/as3525/lcd-clip.h
@@ -18,12 +18,22 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#ifndef __LCDCLIP_H__
22#define __LCDCLIP_H__
21 23
22#include "config.h" 24#include "config.h"
23 25
26#if !defined(BOOTLOADER) && defined(SANSA_CLIPPLUS) /* only tested for clipplus */
27/* Ensure empty FIFO for lcd commands is at least 3 deep */
28#define LCD_USE_FIFO_FOR_COMMANDS
29#endif
30
31void lcd_write_cmd_triplet(int cmd1, int cmd2, int cmd3);
32
24/* return variant number: 0 = clipv1, clipv2, old clip+, 1 = newer clip+ */ 33/* return variant number: 0 = clipv1, clipv2, old clip+, 1 = newer clip+ */
25int lcd_hw_init(void) INIT_ATTR; 34int lcd_hw_init(void) INIT_ATTR;
26 35
27/* target-specific power enable */ 36/* target-specific power enable */
28void lcd_enable_power(bool onoff); 37void lcd_enable_power(bool onoff);
29 38
39#endif /*__LCDCLIP_H__*/
diff --git a/firmware/target/arm/as3525/lcd-ssd1303.c b/firmware/target/arm/as3525/lcd-ssd1303.c
index a00398a998..2aa0b844c5 100644
--- a/firmware/target/arm/as3525/lcd-ssd1303.c
+++ b/firmware/target/arm/as3525/lcd-ssd1303.c
@@ -10,6 +10,7 @@
10 * Copyright (C) 2002 by Alan Korr 10 * Copyright (C) 2002 by Alan Korr
11 * Copyright (C) 2008 François Dinel 11 * Copyright (C) 2008 François Dinel
12 * Copyright (C) 2008-2009 Rafaël Carré 12 * Copyright (C) 2008-2009 Rafaël Carré
13 * Copyright (C) 2017 William Wilgus
13 * 14 *
14 * This program is free software; you can redistribute it and/or 15 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License 16 * modify it under the terms of the GNU General Public License
@@ -118,10 +119,10 @@ void lcd_set_flip(bool yesno)
118#ifdef HAVE_LCD_ENABLE 119#ifdef HAVE_LCD_ENABLE
119void lcd_enable(bool enable) 120void lcd_enable(bool enable)
120{ 121{
121 if(display_on == enable) 122 if (display_on == enable)
122 return; 123 return;
123 124
124 if( (display_on = enable) ) /* simple '=' is not a typo ! */ 125 if ( (display_on = enable) ) /* simple '=' is not a typo ! */
125 { 126 {
126 lcd_enable_power(enable); 127 lcd_enable_power(enable);
127 lcd_write_command(LCD_SET_DISPLAY_ON); 128 lcd_write_command(LCD_SET_DISPLAY_ON);
@@ -149,50 +150,65 @@ void lcd_init_device(void)
149 variant = lcd_hw_init(); 150 variant = lcd_hw_init();
150 offset = (variant == 0) ? 2 : 0; 151 offset = (variant == 0) ? 2 : 0;
151 152
152 /* Set display clock (divide ratio = 1) and oscillator frequency (1) */ 153 /* power on display to accept commands */
153 lcd_write_command(LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ); 154 lcd_enable_power(true);
154 lcd_write_command(0x10); 155
155 156 lcd_write_cmd_triplet
156 /* Set VCOM deselect level to 0.76V */ 157 (
157 lcd_write_command(LCD_SET_VCOM_DESELECT_LEVEL); 158 /* Set display clock */
158 lcd_write_command(0x34); 159 (LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ),
159 160 /* Set display clock (divide ratio = 1) and oscillator frequency (1) */
160 /* Set pre-charge period (p1period is 2 dclk and p2period is 5 dclk) */ 161 (0x10),
161 lcd_write_command(LCD_SET_PRECHARGE_PERIOD); 162 /* Set VCOM deselect level */
162 lcd_write_command(0x25); 163 (LCD_SET_VCOM_DESELECT_LEVEL)
164 );
165
166 lcd_write_cmd_triplet
167 (
168 /* Set VCOM deselect level to 0.76V */
169 (0x34),
170 /* Set pre-charge period */
171 (LCD_SET_PRECHARGE_PERIOD),
172 /* Set pre-charge period (p1period is 2 dclk and p2period is 5 dclk) */
173 (0x25)
174 );
163 175
164 /* Set contrast register to 12% */ 176 /* Set contrast register to 12% */
165 lcd_set_contrast(lcd_default_contrast()); 177 lcd_set_contrast(lcd_default_contrast());
166 178
167 /* Configure DC-DC */ 179 lcd_write_cmd_triplet
168 lcd_write_command(LCD_SET_DC_DC); 180 (
169 lcd_write_command((variant == 0) ? 0x8A : 0x10); 181 /* Configure DC-DC */
170 182 (LCD_SET_DC_DC),
171 /* Set starting line as 0 */ 183 /* Configure DC-DC */
172 lcd_write_command(LCD_SET_DISPLAY_START_LINE /*|(0 & 0x3f)*/); 184 ((variant == 0) ? 0x8A : 0x10),
173 185 /* Set starting line as 0 */
174 /* Column 131 is remapped to SEG0 */ 186 (LCD_SET_DISPLAY_START_LINE /*|(0 & 0x3f)*/)
175 lcd_write_command(LCD_SET_SEGMENT_REMAP_INV); 187 );
176 188
177 /* Invert COM scan direction (N-1 to 0) */ 189 lcd_write_cmd_triplet
178 lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV); 190 (
179 191 /* Column 131 is remapped to SEG0 */
180 /* Set normal display mode (not every pixel ON) */ 192 (LCD_SET_SEGMENT_REMAP_INV),
181 lcd_write_command(LCD_SET_ENTIRE_DISPLAY_OFF); 193 /* Invert COM scan direction (N-1 to 0) */
182 194 (LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV),
183 /* Set normal display mode (not inverted) */ 195 /* Set normal display mode (not every pixel ON) */
184 lcd_write_command(LCD_SET_NORMAL_DISPLAY); 196 (LCD_SET_ENTIRE_DISPLAY_OFF)
185 197 );
186 /* Clear whole framebuffer, including "overscan" 198
187 * We don't need to handle that out of screen columns in lcd_clear_display() 199 lcd_write_cmd_triplet
188 * since we will never write into it anymore 200 (
189 */ 201 /* Set normal display mode (not inverted) */
190 lcd_write_command (LCD_SET_HIGHER_COLUMN_ADDRESS /*| 0*/); 202 (LCD_SET_NORMAL_DISPLAY),
191 lcd_write_command (LCD_SET_LOWER_COLUMN_ADDRESS /*| 0*/); 203 /* set upper 4 bits of 8-bit column address */
204 (LCD_SET_HIGHER_COLUMN_ADDRESS /*| 0*/),
205 /* set lower 4 bits of 8-bit column address */
206 (LCD_SET_LOWER_COLUMN_ADDRESS /*| 0*/)
207 );
192 208
193 fb_data p_bytes[LCD_WIDTH + 2 * offset]; 209 fb_data p_bytes[LCD_WIDTH + 2 * offset];
194 memset(p_bytes, 0, sizeof(p_bytes)); /* fills with 0 : pixel off */ 210 memset(p_bytes, 0, sizeof(p_bytes)); /* fills with 0 : pixel off */
195 for(i = 0; i < 8; i++) 211 for (i = 0; i < 8; i++)
196 { 212 {
197 lcd_write_command (LCD_SET_PAGE_ADDRESS | (i /*& 0xf*/)); 213 lcd_write_command (LCD_SET_PAGE_ADDRESS | (i /*& 0xf*/));
198 lcd_write_data(p_bytes, LCD_WIDTH + 2 * offset); 214 lcd_write_data(p_bytes, LCD_WIDTH + 2 * offset);
@@ -205,27 +221,48 @@ void lcd_init_device(void)
205 221
206/*** Update functions ***/ 222/*** Update functions ***/
207 223
224/* returns LCD_CNTL_HIGHCOL or'd with higher 4 bits of
225 the 8-bit column address for the display data RAM.
226*/
227static inline int get_column_high_byte(const int x)
228{
229 return (LCD_CNTL_HIGHCOL | (((x+offset) >> 4) & 0xf));
230}
231
232/* returns LCD_CNTL_LOWCOL or'd with lower 4 bits of
233 the 8-bit column address for the display data RAM.
234*/
235static inline int get_column_low_byte(const int x)
236{
237 return (LCD_CNTL_LOWCOL | ((x+offset) & 0xf));
238}
239
208/* Performance function that works with an external buffer 240/* Performance function that works with an external buffer
209 note that by and bheight are in 8-pixel units! */ 241 note that by and bheight are in 8-pixel units! */
210void lcd_blit_mono(const unsigned char *data, int x, int by, int width, 242void lcd_blit_mono(const unsigned char *data, int x, int by, int width,
211 int bheight, int stride) 243 int bheight, int stride)
212{ 244{
213 if(!display_on) 245 if (!display_on)
214 return; 246 return;
215 247
248 const int column_high = get_column_high_byte(x);
249 const int column_low = get_column_low_byte(x);
250
216 /* Copy display bitmap to hardware */ 251 /* Copy display bitmap to hardware */
217 while (bheight--) 252 while (bheight--)
218 { 253 {
219 lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); 254 lcd_write_cmd_triplet
220 lcd_write_command (LCD_CNTL_HIGHCOL | (((x+offset)>>4) & 0xf)); 255 (
221 lcd_write_command (LCD_CNTL_LOWCOL | ((x+offset) & 0xf)); 256 (LCD_CNTL_PAGE | (by++ & 0xf)),
257 (column_high),
258 (column_low)
259 );
222 260
223 lcd_write_data(data, width); 261 lcd_write_data(data, width);
224 data += stride; 262 data += stride;
225 } 263 }
226} 264}
227 265
228
229#ifndef BOOTLOADER 266#ifndef BOOTLOADER
230/* Helper function for lcd_grey_phase_blit(). */ 267/* Helper function for lcd_grey_phase_blit(). */
231void lcd_grey_data(unsigned char *values, unsigned char *phases, int count); 268void lcd_grey_data(unsigned char *values, unsigned char *phases, int count);
@@ -235,16 +272,22 @@ void lcd_grey_data(unsigned char *values, unsigned char *phases, int count);
235void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, 272void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases,
236 int x, int by, int width, int bheight, int stride) 273 int x, int by, int width, int bheight, int stride)
237{ 274{
238 if(!display_on) 275 if (!display_on)
239 return; 276 return;
240 277
278 const int column_high = get_column_high_byte(x);
279 const int column_low = get_column_low_byte(x);
280
241 stride <<= 3; /* 8 pixels per block */ 281 stride <<= 3; /* 8 pixels per block */
242 /* Copy display bitmap to hardware */ 282 /* Copy display bitmap to hardware */
243 while (bheight--) 283 while (bheight--)
244 { 284 {
245 lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); 285 lcd_write_cmd_triplet
246 lcd_write_command (LCD_CNTL_HIGHCOL | (((x+offset)>>4) & 0xf)); 286 (
247 lcd_write_command (LCD_CNTL_LOWCOL | ((x+offset) & 0xf)); 287 (LCD_CNTL_PAGE | (by++ & 0xf)),
288 (column_high),
289 (column_low)
290 );
248 291
249 lcd_grey_data(values, phases, width); 292 lcd_grey_data(values, phases, width);
250 293
@@ -255,26 +298,41 @@ void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases,
255 298
256#endif 299#endif
257 300
301/* Shared internal function for lcd_update and lcd_update_rect
302 WARNING does NOT check bounds
303*/
304static void internal_update_rect(int, int, int, int) ICODE_ATTR;
305static void internal_update_rect(int x, int y, int width, int height)
306{
307 if (!display_on)
308 return;
309
310 const int column_high = get_column_high_byte(x);
311 const int column_low = get_column_low_byte(x);
312
313 /* Copy specified rectange bitmap to hardware */
314 for (; y <= height; y++)
315 {
316 lcd_write_cmd_triplet
317 (
318 (LCD_CNTL_PAGE | (y & 0xf)),
319 (column_high),
320 (column_low)
321 );
322
323 lcd_write_data (FBADDR(x,y), width);
324 }
325 lcd_write_command (LCD_NOP); /* return to command mode */
326
327}
258 328
259/* Update the display. 329/* Update the display.
260 This must be called after all other LCD functions that change the display. */ 330 This must be called after all other LCD functions that change the display. */
261void lcd_update(void) ICODE_ATTR; 331void lcd_update(void) ICODE_ATTR;
262void lcd_update(void) 332void lcd_update(void)
263{ 333{
264 int y;
265
266 if(!display_on)
267 return;
268
269 /* Copy display bitmap to hardware */ 334 /* Copy display bitmap to hardware */
270 for (y = 0; y < LCD_FBHEIGHT; y++) 335 internal_update_rect(0, 0, LCD_WIDTH, LCD_FBHEIGHT - 1);
271 {
272 lcd_write_command (LCD_CNTL_PAGE | (y & 0xf));
273 lcd_write_command (LCD_CNTL_HIGHCOL | ((offset >> 4) & 0xf));
274 lcd_write_command (LCD_CNTL_LOWCOL | (offset & 0xf));
275
276 lcd_write_data (FBADDR(0, y), LCD_WIDTH);
277 }
278} 336}
279 337
280/* Update a fraction of the display. */ 338/* Update a fraction of the display. */
@@ -282,45 +340,34 @@ void lcd_update_rect(int, int, int, int) ICODE_ATTR;
282void lcd_update_rect(int x, int y, int width, int height) 340void lcd_update_rect(int x, int y, int width, int height)
283{ 341{
284 int ymax; 342 int ymax;
285 343 if (!display_on)
286 if(!display_on)
287 return; 344 return;
288 345
289 /* The Y coordinates have to work on even 8 pixel rows */ 346 /* make sure the rectangle is bounded in the screen */
290 if (x < 0) 347 if (width > LCD_WIDTH - x)/* Clip right */
348 width = LCD_WIDTH - x;
349 if (x < 0)/* Clip left */
291 { 350 {
292 width += x; 351 width += x;
293 x = 0; 352 x = 0;
294 } 353 }
295
296 if (x + width > LCD_WIDTH)
297 width = LCD_WIDTH - x;
298
299 if (width <= 0) 354 if (width <= 0)
300 return; /* nothing left to do, 0 is harmful to lcd_write_data() */ 355 return; /* nothing left to do, 0 is harmful to lcd_write_data() */
301 356
302 if (y < 0) 357 if (height > LCD_HEIGHT - y) /* Clip bottom */
358 height = LCD_HEIGHT - y;
359 if (y < 0) /* Clip top */
303 { 360 {
304 height += y; 361 height += y;
305 y = 0; 362 y = 0;
306 } 363 }
307
308 if (y + height > LCD_HEIGHT)
309 height = LCD_HEIGHT - y;
310
311 if (height <= 0) 364 if (height <= 0)
312 return; /* nothing left to do */ 365 return; /* nothing left to do */
313 366
367 /* The Y coordinates have to work on even 8 pixel rows */
314 ymax = (y + height-1) >> 3; 368 ymax = (y + height-1) >> 3;
315 y >>= 3; 369 y >>= 3;
316 370
317 /* Copy specified rectange bitmap to hardware */ 371 /* Copy specified rectange bitmap to hardware */
318 for (; y <= ymax; y++) 372 internal_update_rect(x, y, width, ymax);
319 {
320 lcd_write_command (LCD_CNTL_PAGE | (y & 0xf));
321 lcd_write_command (LCD_CNTL_HIGHCOL | (((x+offset) >> 4) & 0xf));
322 lcd_write_command (LCD_CNTL_LOWCOL | ((x+offset) & 0xf));
323
324 lcd_write_data (FBADDR(x,y), width);
325 }
326} 373}
diff --git a/firmware/target/arm/as3525/sansa-clip/lcd-clip.c b/firmware/target/arm/as3525/sansa-clip/lcd-clip.c
index 21d8902739..8331b9e895 100644
--- a/firmware/target/arm/as3525/sansa-clip/lcd-clip.c
+++ b/firmware/target/arm/as3525/sansa-clip/lcd-clip.c
@@ -69,6 +69,34 @@ void lcd_write_command(int byte)
69 ; 69 ;
70} 70}
71 71
72void lcd_write_cmd_triplet(int cmd1, int cmd2, int cmd3)
73{
74#ifndef LCD_USE_FIFO_FOR_COMMANDS
75 lcd_write_command(cmd1);
76 lcd_write_command(cmd2);
77 lcd_write_command(cmd3);
78#else
79 /* combine writes to data register */
80
81 while ((DBOP_STAT & (1<<10)) == 0) /* While push fifo is not empty */
82 ;
83 /* FIFO is empty at this point */
84
85 /* unset D/C# (data or command) */
86 GPIOA_PIN(5) = 0;
87
88 /* Write command */
89 /* !!makes assumption FIFO is at least (3) levels deep! */
90 /* Only bits 15:12 and 3:0 of DBOP_DOUT are meaningful */
91 DBOP_DOUT = (cmd1 << 8) | cmd1;
92 DBOP_DOUT = (cmd2 << 8) | cmd2;
93 DBOP_DOUT = (cmd3 << 8) | cmd3;
94 /* While push fifo is not empty */
95 while ((DBOP_STAT & (1<<10)) == 0)
96 ;
97#endif
98}
99
72void lcd_write_data(const fb_data* p_bytes, int count) 100void lcd_write_data(const fb_data* p_bytes, int count)
73{ 101{
74 volatile int i = 0; 102 volatile int i = 0;
diff --git a/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c b/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
index c0b128a486..a50a9e5c80 100644
--- a/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
+++ b/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
@@ -64,6 +64,31 @@ void lcd_write_command(int byte)
64 ; 64 ;
65} 65}
66 66
67void lcd_write_cmd_triplet(int cmd1, int cmd2, int cmd3)
68{
69#ifndef LCD_USE_FIFO_FOR_COMMANDS
70 lcd_write_command(cmd1);
71 lcd_write_command(cmd2);
72 lcd_write_command(cmd3);
73#else
74 /* combine writes to data register */
75 while(SSP_SR & (1<<4)) /* BSY flag */
76 ;
77 /* FIFO is empty at this point */
78
79 /* LCD command mode */
80 GPIOB_PIN(2) = 0;
81
82 /* !!makes assumption FIFO is at least (3) levels deep!! */
83 SSP_DATA = cmd1;
84 SSP_DATA = cmd2;
85 SSP_DATA = cmd3;
86
87 while(SSP_SR & (1<<4)) /* BSY flag */
88 ;
89#endif
90}
91
67void lcd_write_data(const fb_data* p_bytes, int count) 92void lcd_write_data(const fb_data* p_bytes, int count)
68{ 93{
69 /* LCD data mode */ 94 /* LCD data mode */
@@ -80,6 +105,13 @@ void lcd_write_data(const fb_data* p_bytes, int count)
80 105
81void lcd_enable_power(bool onoff) 106void lcd_enable_power(bool onoff)
82{ 107{
108#ifndef BOOTLOADER
109 if (onoff)
110 bitset32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE);
111 else
112 bitclr32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE);
113#else
83 (void) onoff; 114 (void) onoff;
115#endif
84} 116}
85 117
diff --git a/firmware/target/arm/as3525/sansa-clipv2/lcd-clipv2.c b/firmware/target/arm/as3525/sansa-clipv2/lcd-clipv2.c
index d7266f72a0..0b3317255a 100644
--- a/firmware/target/arm/as3525/sansa-clipv2/lcd-clipv2.c
+++ b/firmware/target/arm/as3525/sansa-clipv2/lcd-clipv2.c
@@ -63,6 +63,35 @@ void lcd_write_command(int byte)
63 DBOP_TIMPOL_23 = 0xE037E037; 63 DBOP_TIMPOL_23 = 0xE037E037;
64} 64}
65 65
66void lcd_write_cmd_triplet(int cmd1, int cmd2, int cmd3)
67{
68#ifndef LCD_USE_FIFO_FOR_COMMANDS
69 lcd_write_command(cmd1);
70 lcd_write_command(cmd2);
71 lcd_write_command(cmd3);
72#else
73 /* combine writes to data register */
74
75 while ((DBOP_STAT & (1<<10)) == 0) /* While push fifo is not empty */
76 ;
77 /* FIFO is empty at this point */
78 /* unset D/C# (data or command) */
79 GPIOB_PIN(2) = 0;
80 DBOP_TIMPOL_23 = 0xE0370036;
81
82 /* Write command */
83 /* !!makes assumption FIFO is at least (3) levels deep! */
84 DBOP_DOUT8 = cmd1;
85 DBOP_DOUT8 = cmd2;
86 DBOP_DOUT8 = cmd3;
87 /* While push fifo is not empty */
88 while ((DBOP_STAT & (1<<10)) == 0)
89 ;
90
91 DBOP_TIMPOL_23 = 0xE037E037;
92#endif
93}
94
66void lcd_write_data(const fb_data* p_bytes, int count) 95void lcd_write_data(const fb_data* p_bytes, int count)
67{ 96{
68 volatile int i = 0; 97 volatile int i = 0;