summaryrefslogtreecommitdiff
path: root/firmware/drivers/lcd.S
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/lcd.S')
-rwxr-xr-xfirmware/drivers/lcd.S307
1 files changed, 307 insertions, 0 deletions
diff --git a/firmware/drivers/lcd.S b/firmware/drivers/lcd.S
new file mode 100755
index 0000000000..3ab993c1d0
--- /dev/null
+++ b/firmware/drivers/lcd.S
@@ -0,0 +1,307 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Jens Arnold
11 * Based on the work of Alan Korr and Jörg Hohensohn
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include "config.h"
22#include "sh7034.h"
23
24#define LCDR (PBDR_ADDR+1)
25
26#ifdef HAVE_LCD_CHARCELLS
27#define LCD_DS 1 /* PB0 = 1 --- 0001 --- LCD-DS */
28#define LCD_CS 2 /* PB1 = 1 --- 0010 --- /LCD-CS */
29#define LCD_SD 4 /* PB2 = 1 --- 0100 --- LCD-SD */
30#define LCD_SC 8 /* PB3 = 1 --- 1000 --- LCD-SC */
31#else
32#define LCD_SD 1 /* PB0 = 1 --- 0001 */
33#define LCD_SC 2 /* PB1 = 1 --- 0010 */
34#define LCD_RS 4 /* PB2 = 1 --- 0100 */
35#define LCD_CS 8 /* PB3 = 1 --- 1000 */
36#define LCD_DS LCD_RS
37#endif
38
39/*
40 * About /CS,DS,SC,SD
41 * ------------------
42 *
43 * LCD on JBP and JBR uses a SPI protocol to receive orders (SDA and SCK lines)
44 *
45 * - /CS -> Chip Selection line :
46 * 0 : LCD chipset is activated.
47 * - DS -> Data Selection line, latched at the rising edge
48 * of the 8th serial clock (*) :
49 * 0 : instruction register,
50 * 1 : data register;
51 * - SC -> Serial Clock line (SDA).
52 * - SD -> Serial Data line (SCK), latched at the rising edge
53 * of each serial clock (*).
54 *
55 * _ _
56 * /CS \ /
57 * \______________________________________________________/
58 * _____ ____ ____ ____ ____ ____ ____ ____ ____ _____
59 * SD \/ D7 \/ D6 \/ D5 \/ D4 \/ D3 \/ D2 \/ D1 \/ D0 \/
60 * _____/\____/\____/\____/\____/\____/\____/\____/\____/\_____
61 *
62 * _____ _ _ _ _ _ _ _ ________
63 * SC \ * \ * \ * \ * \ * \ * \ * \ *
64 * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
65 * _ _________________________________________________________
66 * DS \/
67 * _/\_________________________________________________________
68 *
69 */
70
71 .section .icode,"ax",@progbits
72
73 .align 2
74 .global _lcd_write_command
75 .type _lcd_write_command,@function
76
77/* Write a command byte to the lcd controller
78 *
79 * Arguments:
80 * r4 - data byte (int)
81 *
82 * Register usage:
83 * r0 - scratch
84 * r1 - data byte (copied)
85 * r2 - precalculated port value (CS, DS and SC low, SD high),
86 * negated (neg)!
87 * r3 - lcd port address
88 * r5 - 1 (byte count for reuse of the loop in _lcd_write_data)
89 */
90
91_lcd_write_command:
92 mov.l .lcdr,r3 /* put lcd data port address in r3 */
93 mov r4,r1 /* copy data byte to r1 */
94 mov #1,r5 /* set byte count to 1 (!) */
95
96 /* This code will fail if an interrupt changes the contents of PBDRL.
97 * If so, we must disable the interrupt here. */
98
99 mov.b @r3,r0 /* r0 = PBDRL */
100 or #(LCD_SD),r0 /* r0 |= LCD_SD */
101 and #(~(LCD_CS|LCD_DS|LCD_SC)),r0 /* r0 &= ~(LCD_CS|LCD_DS|LCD_SC) */
102
103 bra .single_transfer /* jump into the transfer loop */
104 neg r0,r2 /* r2 = 0 - r0 */
105
106
107 .align 2
108 .global _lcd_write_data
109 .type _lcd_write_data,@function
110
111
112/* A high performance function to write data to the display,
113 * one or multiple bytes.
114 *
115 * Arguments:
116 * r4 - data address
117 * r5 - byte count
118 *
119 * Register usage:
120 * r0 - scratch
121 * r1 - current data byte
122 * r2 - precalculated port value (CS and SC low, DS and SD high),
123 * negated (neg)!
124 * r3 - lcd port address
125 */
126
127_lcd_write_data:
128 mov.l .lcdr,r3 /* put lcd data port address in r3 */
129 nop /* align here */
130
131 /* This code will fail if an interrupt changes the contents of PBDRL.
132 * If so, we must disable the interrupt here. If disabling interrupts
133 * for a long time (~9200 clks = ~830 µs for transferring 112 bytes on
134 * recorders)is undesirable, the loop has to be rewritten to
135 * disable/precalculate/transfer/enable for each iteration. However,
136 * this would significantly decrease performance. */
137
138 mov.b @r3,r0 /* r0 = PBDRL */
139 or #(LCD_DS|LCD_SD),r0 /* r0 |= LCD_DS|LCD_SD */
140 and #(~(LCD_CS|LCD_SC)),r0 /* r0 &= ~(LCD_CS|LCD_SC) */
141 neg r0,r2 /* r2 = 0 - r0 */
142
143#ifdef HAVE_LCD_CHARCELLS
144/* optimized player version, also works for recorders */
145
146 .align 2
147.multi_transfer:
148 mov.b @r4+,r1 /* load data byte from memory */
149
150.single_transfer:
151 shll16 r1 /* shift data to most significant byte */
152 shll8 r1
153
154 shll r1 /* shift the msb into carry */
155 neg r2,r0 /* copy negated precalculated port value */
156 /* uses neg here for compatibility with recorder version */
157 bt 1f /* data bit = 1? */
158 and #(~LCD_SD),r0 /* no: r0 &= ~LCD_SD */
159 1:
160 shll r1 /* next shift here for alignment */
161 mov.b r0,@r3 /* set data to port */
162 or #(LCD_SC),r0 /* rise SC (independent of SD level) */
163 mov.b r0,@r3 /* set to port */
164
165 neg r2,r0
166 bt 1f
167 and #(~LCD_SD),r0
168 1:
169 mov.b r0,@r3
170 or #(LCD_SC),r0
171 mov.b r0,@r3
172
173 shll r1
174 neg r2,r0
175 bt 1f
176 and #(~LCD_SD),r0
177 1:
178 shll r1
179 mov.b r0,@r3
180 or #(LCD_SC),r0
181 mov.b r0,@r3
182
183 neg r2,r0
184 bt 1f
185 and #(~LCD_SD),r0
186 1:
187 mov.b r0,@r3
188 or #(LCD_SC),r0
189 mov.b r0,@r3
190
191 shll r1
192 neg r2,r0
193 bt 1f
194 and #(~LCD_SD),r0
195 1:
196 shll r1
197 mov.b r0,@r3
198 or #(LCD_SC),r0
199 mov.b r0,@r3
200
201 neg r2,r0
202 bt 1f
203 and #(~LCD_SD),r0
204 1:
205 mov.b r0,@r3
206 or #(LCD_SC),r0
207 mov.b r0,@r3
208
209 shll r1
210 neg r2,r0
211 bt 1f
212 and #(~LCD_SD),r0
213 1:
214 shll r1
215 mov.b r0,@r3
216 or #(LCD_SC),r0
217 mov.b r0,@r3
218
219 neg r2,r0
220 bt 1f
221 and #(~LCD_SD),r0
222 1:
223 mov.b r0,@r3
224 or #(LCD_SC),r0
225 mov.b r0,@r3
226
227#else /* HAVE_LCD_CHARCELLS */
228/* further optimized version, exploits that SD is on bit 0 for recorders */
229
230 .align 2
231.multi_transfer:
232 mov.b @r4+,r1 /* load data byte from memory */
233 nop
234
235.single_transfer:
236 shll16 r1 /* shift data to most significant byte */
237 shll8 r1
238 not r1,r1 /* and invert for use with negc */
239
240 shll r1 /* shift the MSB into carry */
241 negc r2,r0 /* carry to SD, SC low */
242 shll r1 /* next shift here for alignment */
243 mov.b r0,@r3 /* set data to port */
244 or #(LCD_SC),r0 /* rise SC (independent of SD level) */
245 mov.b r0,@r3 /* set to port */
246
247 negc r2,r0
248 mov.b r0,@r3
249 or #(LCD_SC),r0
250 mov.b r0,@r3
251
252 shll r1
253 negc r2,r0
254 shll r1
255 mov.b r0,@r3
256 or #(LCD_SC),r0
257 mov.b r0,@r3
258
259 negc r2,r0
260 mov.b r0,@r3
261 or #(LCD_SC),r0
262 mov.b r0,@r3
263
264 shll r1
265 negc r2,r0
266 shll r1
267 mov.b r0,@r3
268 or #(LCD_SC),r0
269 mov.b r0,@r3
270
271 negc r2,r0
272 mov.b r0,@r3
273 or #(LCD_SC),r0
274 mov.b r0,@r3
275
276 shll r1
277 negc r2,r0
278 shll r1
279 mov.b r0,@r3
280 or #(LCD_SC),r0
281 mov.b r0,@r3
282
283 negc r2,r0
284 mov.b r0,@r3
285 or #(LCD_SC),r0
286 mov.b r0,@r3
287
288#endif /* HAVE_LCD_CHARCELLS */
289
290 add #-1,r5 /* decrease byte count */
291 tst r5,r5 /* r5 == 0 ? */
292 bf .multi_transfer /* no: next iteration */
293
294 or #(LCD_CS|LCD_DS|LCD_SD|LCD_SC),r0 /* restore port */
295 rts
296 mov.b r0,@r3
297
298 /* This is the place to reenable the interrupts, if we have disabled
299 * them. See above. */
300
301 .align 2
302.lcdr:
303 .long LCDR
304
305.end:
306 .size _lcd_write_command,.end-_lcd_write_command
307