ths
authored
18 years ago
1
/*
2
3
* Arm PrimeCell PL110 Color LCD Controller
*
4
* Copyright ( c ) 2005 - 2009 CodeSourcery .
5
6
7
8
9
* Written by Paul Brook
*
* This code is licenced under the GNU LGPL
*/
10
# include "sysbus.h"
11
# include "console.h"
12
# include "framebuffer.h"
13
14
# define PL110_CR_EN 0x001
15
# define PL110_CR_BGR 0x100
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# define PL110_CR_BEBO 0x200
# define PL110_CR_BEPO 0x400
# define PL110_CR_PWR 0x800
enum pl110_bppmode
{
BPP_1 ,
BPP_2 ,
BPP_4 ,
BPP_8 ,
BPP_16 ,
BPP_32
};
typedef struct {
31
SysBusDevice busdev ;
32
DisplayState * ds ;
33
34
35
/* The Versatile/PB uses a slightly modified PL110 controller. */
int versatile ;
36
37
38
39
40
41
42
43
44
45
46
47
uint32_t timing [ 4 ];
uint32_t cr ;
uint32_t upbase ;
uint32_t lpbase ;
uint32_t int_status ;
uint32_t int_mask ;
int cols ;
int rows ;
enum pl110_bppmode bpp ;
int invalidate ;
uint32_t pallette [ 256 ];
uint32_t raw_pallette [ 128 ];
48
qemu_irq irq ;
49
50
51
52
53
} pl110_state ;
static const unsigned char pl110_id [] =
{ 0x10 , 0x11 , 0x04 , 0x00 , 0x0d , 0xf0 , 0x05 , 0xb1 };
54
55
56
57
58
59
60
61
62
/* The Arm documentation ( DDI0224C ) says the CLDC on the Versatile board
has a different ID . However Linux only looks for the normal ID . */
# if 0
static const unsigned char pl110_versatile_id [] =
{ 0x93 , 0x10 , 0x04 , 0x00 , 0x0d , 0xf0 , 0x05 , 0xb1 };
# else
# define pl110_versatile_id pl110_id
# endif
63
# include "pixel_ops.h"
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# define BITS 8
# include "pl110_template.h"
# define BITS 15
# include "pl110_template.h"
# define BITS 16
# include "pl110_template.h"
# define BITS 24
# include "pl110_template.h"
# define BITS 32
# include "pl110_template.h"
static int pl110_enabled ( pl110_state * s )
{
return ( s -> cr & PL110_CR_EN ) && ( s -> cr & PL110_CR_PWR );
}
81
static void pl110_update_display ( void * opaque )
82
83
84
85
86
87
{
pl110_state * s = ( pl110_state * ) opaque ;
drawfn * fntable ;
drawfn fn ;
int dest_width ;
int src_width ;
88
int bpp_offset ;
89
90
int first ;
int last ;
91
92
93
if ( ! pl110_enabled ( s ))
return ;
ths
authored
18 years ago
94
95
switch ( ds_get_bits_per_pixel ( s -> ds )) {
96
97
case 0 :
return ;
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
case 8 :
fntable = pl110_draw_fn_8 ;
dest_width = 1 ;
break ;
case 15 :
fntable = pl110_draw_fn_15 ;
dest_width = 2 ;
break ;
case 16 :
fntable = pl110_draw_fn_16 ;
dest_width = 2 ;
break ;
case 24 :
fntable = pl110_draw_fn_24 ;
dest_width = 3 ;
break ;
case 32 :
fntable = pl110_draw_fn_32 ;
dest_width = 4 ;
break ;
default :
119
fprintf ( stderr , "pl110: Bad color depth \n " );
120
121
exit ( 1 );
}
122
123
124
125
126
if ( s -> cr & PL110_CR_BGR )
bpp_offset = 0 ;
else
bpp_offset = 18 ;
127
if ( s -> cr & PL110_CR_BEBO )
128
fn = fntable [ s -> bpp + 6 + bpp_offset ];
129
else if ( s -> cr & PL110_CR_BEPO )
130
fn = fntable [ s -> bpp + 12 + bpp_offset ];
131
else
132
fn = fntable [ s -> bpp + bpp_offset ];
ths
authored
18 years ago
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
src_width = s -> cols ;
switch ( s -> bpp ) {
case BPP_1 :
src_width >>= 3 ;
break ;
case BPP_2 :
src_width >>= 2 ;
break ;
case BPP_4 :
src_width >>= 1 ;
break ;
case BPP_8 :
break ;
case BPP_16 :
src_width <<= 1 ;
break ;
case BPP_32 :
src_width <<= 2 ;
break ;
}
dest_width *= s -> cols ;
155
156
157
158
159
160
161
162
163
first = 0 ;
framebuffer_update_display ( s -> ds ,
s -> upbase , s -> cols , s -> rows ,
src_width , dest_width , 0 ,
s -> invalidate ,
fn , s -> pallette ,
& first , & last );
if ( first >= 0 ) {
dpy_update ( s -> ds , 0 , first , s -> cols , last - first + 1 );
164
165
166
167
}
s -> invalidate = 0 ;
}
168
static void pl110_invalidate_display ( void * opaque )
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
{
pl110_state * s = ( pl110_state * ) opaque ;
s -> invalidate = 1 ;
}
static void pl110_update_pallette ( pl110_state * s , int n )
{
int i ;
uint32_t raw ;
unsigned int r , g , b ;
raw = s -> raw_pallette [ n ];
n <<= 1 ;
for ( i = 0 ; i < 2 ; i ++ ) {
r = ( raw & 0x1f ) << 3 ;
raw >>= 5 ;
g = ( raw & 0x1f ) << 3 ;
raw >>= 5 ;
b = ( raw & 0x1f ) << 3 ;
/* The I bit is ignored. */
raw >>= 6 ;
190
switch ( ds_get_bits_per_pixel ( s -> ds )) {
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
case 8 :
s -> pallette [ n ] = rgb_to_pixel8 ( r , g , b );
break ;
case 15 :
s -> pallette [ n ] = rgb_to_pixel15 ( r , g , b );
break ;
case 16 :
s -> pallette [ n ] = rgb_to_pixel16 ( r , g , b );
break ;
case 24 :
case 32 :
s -> pallette [ n ] = rgb_to_pixel32 ( r , g , b );
break ;
}
n ++ ;
}
}
static void pl110_resize ( pl110_state * s , int width , int height )
{
if ( width != s -> cols || height != s -> rows ) {
if ( pl110_enabled ( s )) {
213
qemu_console_resize ( s -> ds , width , height );
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
}
}
s -> cols = width ;
s -> rows = height ;
}
/* Update interrupts. */
static void pl110_update ( pl110_state * s )
{
/* TODO: Implement interrupts. */
}
static uint32_t pl110_read ( void * opaque , target_phys_addr_t offset )
{
pl110_state * s = ( pl110_state * ) opaque ;
if ( offset >= 0xfe0 && offset < 0x1000 ) {
231
232
233
234
if ( s -> versatile )
return pl110_versatile_id [( offset - 0xfe0 ) >> 2 ];
else
return pl110_id [( offset - 0xfe0 ) >> 2 ];
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
}
if ( offset >= 0x200 && offset < 0x400 ) {
return s -> raw_pallette [( offset - 0x200 ) >> 2 ];
}
switch ( offset >> 2 ) {
case 0 : /* LCDTiming0 */
return s -> timing [ 0 ];
case 1 : /* LCDTiming1 */
return s -> timing [ 1 ];
case 2 : /* LCDTiming2 */
return s -> timing [ 2 ];
case 3 : /* LCDTiming3 */
return s -> timing [ 3 ];
case 4 : /* LCDUPBASE */
return s -> upbase ;
case 5 : /* LCDLPBASE */
return s -> lpbase ;
case 6 : /* LCDIMSC */
253
254
if ( s -> versatile )
return s -> cr ;
255
256
return s -> int_mask ;
case 7 : /* LCDControl */
257
258
if ( s -> versatile )
return s -> int_mask ;
259
260
261
262
263
264
265
266
267
268
269
return s -> cr ;
case 8 : /* LCDRIS */
return s -> int_status ;
case 9 : /* LCDMIS */
return s -> int_status & s -> int_mask ;
case 11 : /* LCDUPCURR */
/* TODO: Implement vertical refresh. */
return s -> upbase ;
case 12 : /* LCDLPCURR */
return s -> lpbase ;
default :
270
hw_error ( "pl110_read: Bad offset %x \n " , ( int ) offset );
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
return 0 ;
}
}
static void pl110_write ( void * opaque , target_phys_addr_t offset ,
uint32_t val )
{
pl110_state * s = ( pl110_state * ) opaque ;
int n ;
/* For simplicity invalidate the display whenever a control register
is writen to . */
s -> invalidate = 1 ;
if ( offset >= 0x200 && offset < 0x400 ) {
/* Pallette. */
n = ( offset - 0x200 ) >> 2 ;
s -> raw_pallette [( offset - 0x200 ) >> 2 ] = val ;
pl110_update_pallette ( s , n );
289
return ;
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
}
switch ( offset >> 2 ) {
case 0 : /* LCDTiming0 */
s -> timing [ 0 ] = val ;
n = (( val & 0xfc ) + 4 ) * 4 ;
pl110_resize ( s , n , s -> rows );
break ;
case 1 : /* LCDTiming1 */
s -> timing [ 1 ] = val ;
n = ( val & 0x3ff ) + 1 ;
pl110_resize ( s , s -> cols , n );
break ;
case 2 : /* LCDTiming2 */
s -> timing [ 2 ] = val ;
break ;
case 3 : /* LCDTiming3 */
s -> timing [ 3 ] = val ;
break ;
case 4 : /* LCDUPBASE */
s -> upbase = val ;
break ;
case 5 : /* LCDLPBASE */
s -> lpbase = val ;
break ;
case 6 : /* LCDIMSC */
315
316
317
if ( s -> versatile )
goto control ;
imsc :
318
319
320
321
s -> int_mask = val ;
pl110_update ( s );
break ;
case 7 : /* LCDControl */
322
323
324
if ( s -> versatile )
goto imsc ;
control :
325
326
327
s -> cr = val ;
s -> bpp = ( val >> 1 ) & 7 ;
if ( pl110_enabled ( s )) {
328
qemu_console_resize ( s -> ds , s -> cols , s -> rows );
329
330
331
332
333
334
335
}
break ;
case 10 : /* LCDICR */
s -> int_status &= ~ val ;
pl110_update ( s );
break ;
default :
336
hw_error ( "pl110_write: Bad offset %x \n " , ( int ) offset );
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
}
}
static CPUReadMemoryFunc * pl110_readfn [] = {
pl110_read ,
pl110_read ,
pl110_read
};
static CPUWriteMemoryFunc * pl110_writefn [] = {
pl110_write ,
pl110_write ,
pl110_write
};
352
static void pl110_init ( SysBusDevice * dev )
353
{
354
pl110_state * s = FROM_SYSBUS ( pl110_state , dev );
355
356
int iomemtype ;
357
iomemtype = cpu_register_io_memory ( pl110_readfn ,
358
pl110_writefn , s );
359
360
sysbus_init_mmio ( dev , 0x1000 , iomemtype );
sysbus_init_irq ( dev , & s -> irq );
361
362
363
s -> ds = graphic_console_init ( pl110_update_display ,
pl110_invalidate_display ,
NULL , NULL , s );
364
365
/* ??? Save/restore. */
}
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
static void pl110_versatile_init ( SysBusDevice * dev )
{
pl110_state * s = FROM_SYSBUS ( pl110_state , dev );
s -> versatile = 1 ;
pl110_init ( dev );
}
static void pl110_register_devices ( void )
{
sysbus_register_dev ( "pl110" , sizeof ( pl110_state ), pl110_init );
sysbus_register_dev ( "pl110_versatile" , sizeof ( pl110_state ),
pl110_versatile_init );
}
device_init ( pl110_register_devices )