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