qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

omap_lcdc.c (13586B)


      1 /*
      2  * OMAP LCD controller.
      3  *
      4  * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
      5  *
      6  * This program is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU General Public License as
      8  * published by the Free Software Foundation; either version 2 of
      9  * the License, or (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License along
     17  * with this program; if not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "qemu/osdep.h"
     21 #include "hw/irq.h"
     22 #include "ui/console.h"
     23 #include "hw/arm/omap.h"
     24 #include "framebuffer.h"
     25 #include "ui/pixel_ops.h"
     26 
     27 struct omap_lcd_panel_s {
     28     MemoryRegion *sysmem;
     29     MemoryRegion iomem;
     30     MemoryRegionSection fbsection;
     31     qemu_irq irq;
     32     QemuConsole *con;
     33 
     34     int plm;
     35     int tft;
     36     int mono;
     37     int enable;
     38     int width;
     39     int height;
     40     int interrupts;
     41     uint32_t timing[3];
     42     uint32_t subpanel;
     43     uint32_t ctrl;
     44 
     45     struct omap_dma_lcd_channel_s *dma;
     46     uint16_t palette[256];
     47     int palette_done;
     48     int frame_done;
     49     int invalidate;
     50     int sync_error;
     51 };
     52 
     53 static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
     54 {
     55     if (s->frame_done && (s->interrupts & 1)) {
     56         qemu_irq_raise(s->irq);
     57         return;
     58     }
     59 
     60     if (s->palette_done && (s->interrupts & 2)) {
     61         qemu_irq_raise(s->irq);
     62         return;
     63     }
     64 
     65     if (s->sync_error) {
     66         qemu_irq_raise(s->irq);
     67         return;
     68     }
     69 
     70     qemu_irq_lower(s->irq);
     71 }
     72 
     73 /*
     74  * 2-bit colour
     75  */
     76 static void draw_line2_32(void *opaque, uint8_t *d, const uint8_t *s,
     77                           int width, int deststep)
     78 {
     79     uint16_t *pal = opaque;
     80     uint8_t v, r, g, b;
     81 
     82     do {
     83         v = ldub_p((void *) s);
     84         r = (pal[v & 3] >> 4) & 0xf0;
     85         g = pal[v & 3] & 0xf0;
     86         b = (pal[v & 3] << 4) & 0xf0;
     87         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
     88         d += 4;
     89         v >>= 2;
     90         r = (pal[v & 3] >> 4) & 0xf0;
     91         g = pal[v & 3] & 0xf0;
     92         b = (pal[v & 3] << 4) & 0xf0;
     93         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
     94         d += 4;
     95         v >>= 2;
     96         r = (pal[v & 3] >> 4) & 0xf0;
     97         g = pal[v & 3] & 0xf0;
     98         b = (pal[v & 3] << 4) & 0xf0;
     99         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    100         d += 4;
    101         v >>= 2;
    102         r = (pal[v & 3] >> 4) & 0xf0;
    103         g = pal[v & 3] & 0xf0;
    104         b = (pal[v & 3] << 4) & 0xf0;
    105         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    106         d += 4;
    107         s++;
    108         width -= 4;
    109     } while (width > 0);
    110 }
    111 
    112 /*
    113  * 4-bit colour
    114  */
    115 static void draw_line4_32(void *opaque, uint8_t *d, const uint8_t *s,
    116                           int width, int deststep)
    117 {
    118     uint16_t *pal = opaque;
    119     uint8_t v, r, g, b;
    120 
    121     do {
    122         v = ldub_p((void *) s);
    123         r = (pal[v & 0xf] >> 4) & 0xf0;
    124         g = pal[v & 0xf] & 0xf0;
    125         b = (pal[v & 0xf] << 4) & 0xf0;
    126         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    127         d += 4;
    128         v >>= 4;
    129         r = (pal[v & 0xf] >> 4) & 0xf0;
    130         g = pal[v & 0xf] & 0xf0;
    131         b = (pal[v & 0xf] << 4) & 0xf0;
    132         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    133         d += 4;
    134         s++;
    135         width -= 2;
    136     } while (width > 0);
    137 }
    138 
    139 /*
    140  * 8-bit colour
    141  */
    142 static void draw_line8_32(void *opaque, uint8_t *d, const uint8_t *s,
    143                           int width, int deststep)
    144 {
    145     uint16_t *pal = opaque;
    146     uint8_t v, r, g, b;
    147 
    148     do {
    149         v = ldub_p((void *) s);
    150         r = (pal[v] >> 4) & 0xf0;
    151         g = pal[v] & 0xf0;
    152         b = (pal[v] << 4) & 0xf0;
    153         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    154         s++;
    155         d += 4;
    156     } while (-- width != 0);
    157 }
    158 
    159 /*
    160  * 12-bit colour
    161  */
    162 static void draw_line12_32(void *opaque, uint8_t *d, const uint8_t *s,
    163                            int width, int deststep)
    164 {
    165     uint16_t v;
    166     uint8_t r, g, b;
    167 
    168     do {
    169         v = lduw_le_p((void *) s);
    170         r = (v >> 4) & 0xf0;
    171         g = v & 0xf0;
    172         b = (v << 4) & 0xf0;
    173         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    174         s += 2;
    175         d += 4;
    176     } while (-- width != 0);
    177 }
    178 
    179 /*
    180  * 16-bit colour
    181  */
    182 static void draw_line16_32(void *opaque, uint8_t *d, const uint8_t *s,
    183                            int width, int deststep)
    184 {
    185     uint16_t v;
    186     uint8_t r, g, b;
    187 
    188     do {
    189         v = lduw_le_p((void *) s);
    190         r = (v >> 8) & 0xf8;
    191         g = (v >> 3) & 0xfc;
    192         b = (v << 3) & 0xf8;
    193         ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
    194         s += 2;
    195         d += 4;
    196     } while (-- width != 0);
    197 }
    198 
    199 static void omap_update_display(void *opaque)
    200 {
    201     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
    202     DisplaySurface *surface;
    203     drawfn draw_line;
    204     int size, height, first, last;
    205     int width, linesize, step, bpp, frame_offset;
    206     hwaddr frame_base;
    207 
    208     if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable) {
    209         return;
    210     }
    211 
    212     surface = qemu_console_surface(omap_lcd->con);
    213     if (!surface_bits_per_pixel(surface)) {
    214         return;
    215     }
    216 
    217     frame_offset = 0;
    218     if (omap_lcd->plm != 2) {
    219         cpu_physical_memory_read(
    220                 omap_lcd->dma->phys_framebuffer[omap_lcd->dma->current_frame],
    221                 omap_lcd->palette, 0x200);
    222         switch (omap_lcd->palette[0] >> 12 & 7) {
    223         case 3 ... 7:
    224             frame_offset += 0x200;
    225             break;
    226         default:
    227             frame_offset += 0x20;
    228         }
    229     }
    230 
    231     /* Colour depth */
    232     switch ((omap_lcd->palette[0] >> 12) & 7) {
    233     case 1:
    234         draw_line = draw_line2_32;
    235         bpp = 2;
    236         break;
    237 
    238     case 2:
    239         draw_line = draw_line4_32;
    240         bpp = 4;
    241         break;
    242 
    243     case 3:
    244         draw_line = draw_line8_32;
    245         bpp = 8;
    246         break;
    247 
    248     case 4 ... 7:
    249         if (!omap_lcd->tft)
    250             draw_line = draw_line12_32;
    251         else
    252             draw_line = draw_line16_32;
    253         bpp = 16;
    254         break;
    255 
    256     default:
    257         /* Unsupported at the moment.  */
    258         return;
    259     }
    260 
    261     /* Resolution */
    262     width = omap_lcd->width;
    263     if (width != surface_width(surface) ||
    264         omap_lcd->height != surface_height(surface)) {
    265         qemu_console_resize(omap_lcd->con,
    266                             omap_lcd->width, omap_lcd->height);
    267         surface = qemu_console_surface(omap_lcd->con);
    268         omap_lcd->invalidate = 1;
    269     }
    270 
    271     if (omap_lcd->dma->current_frame == 0)
    272         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
    273     else
    274         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
    275 
    276     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
    277         omap_lcd->sync_error = 1;
    278         omap_lcd_interrupts(omap_lcd);
    279         omap_lcd->enable = 0;
    280         return;
    281     }
    282 
    283     /* Content */
    284     frame_base = omap_lcd->dma->phys_framebuffer[
    285             omap_lcd->dma->current_frame] + frame_offset;
    286     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
    287     if (omap_lcd->dma->interrupts & 1)
    288         qemu_irq_raise(omap_lcd->dma->irq);
    289     if (omap_lcd->dma->dual)
    290         omap_lcd->dma->current_frame ^= 1;
    291 
    292     if (!surface_bits_per_pixel(surface)) {
    293         return;
    294     }
    295 
    296     first = 0;
    297     height = omap_lcd->height;
    298     if (omap_lcd->subpanel & (1 << 31)) {
    299         if (omap_lcd->subpanel & (1 << 29))
    300             first = (omap_lcd->subpanel >> 16) & 0x3ff;
    301         else
    302             height = (omap_lcd->subpanel >> 16) & 0x3ff;
    303         /* TODO: fill the rest of the panel with DPD */
    304     }
    305 
    306     step = width * bpp >> 3;
    307     linesize = surface_stride(surface);
    308     if (omap_lcd->invalidate) {
    309         framebuffer_update_memory_section(&omap_lcd->fbsection,
    310                                           omap_lcd->sysmem, frame_base,
    311                                           height, step);
    312     }
    313 
    314     framebuffer_update_display(surface, &omap_lcd->fbsection,
    315                                width, height,
    316                                step, linesize, 0,
    317                                omap_lcd->invalidate,
    318                                draw_line, omap_lcd->palette,
    319                                &first, &last);
    320 
    321     if (first >= 0) {
    322         dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
    323     }
    324     omap_lcd->invalidate = 0;
    325 }
    326 
    327 static void omap_invalidate_display(void *opaque) {
    328     struct omap_lcd_panel_s *omap_lcd = opaque;
    329     omap_lcd->invalidate = 1;
    330 }
    331 
    332 static void omap_lcd_update(struct omap_lcd_panel_s *s) {
    333     if (!s->enable) {
    334         s->dma->current_frame = -1;
    335         s->sync_error = 0;
    336         if (s->plm != 1)
    337             s->frame_done = 1;
    338         omap_lcd_interrupts(s);
    339         return;
    340     }
    341 
    342     if (s->dma->current_frame == -1) {
    343         s->frame_done = 0;
    344         s->palette_done = 0;
    345         s->dma->current_frame = 0;
    346     }
    347 
    348     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
    349                             s->dma->src_f1_top) ||
    350                     !s->dma->mpu->port[
    351                     s->dma->src].addr_valid(s->dma->mpu,
    352                             s->dma->src_f1_bottom) ||
    353                     (s->dma->dual &&
    354                      (!s->dma->mpu->port[
    355                       s->dma->src].addr_valid(s->dma->mpu,
    356                               s->dma->src_f2_top) ||
    357                       !s->dma->mpu->port[
    358                       s->dma->src].addr_valid(s->dma->mpu,
    359                               s->dma->src_f2_bottom)))) {
    360         s->dma->condition |= 1 << 2;
    361         if (s->dma->interrupts & (1 << 1))
    362             qemu_irq_raise(s->dma->irq);
    363         s->enable = 0;
    364         return;
    365     }
    366 
    367     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
    368     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
    369 
    370     if (s->plm != 2 && !s->palette_done) {
    371         cpu_physical_memory_read(
    372                             s->dma->phys_framebuffer[s->dma->current_frame],
    373                             s->palette, 0x200);
    374         s->palette_done = 1;
    375         omap_lcd_interrupts(s);
    376     }
    377 }
    378 
    379 static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
    380                                unsigned size)
    381 {
    382     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
    383 
    384     switch (addr) {
    385     case 0x00:	/* LCD_CONTROL */
    386         return (s->tft << 23) | (s->plm << 20) |
    387                 (s->tft << 7) | (s->interrupts << 3) |
    388                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
    389 
    390     case 0x04:	/* LCD_TIMING0 */
    391         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
    392 
    393     case 0x08:	/* LCD_TIMING1 */
    394         return (s->timing[1] << 10) | (s->height - 1);
    395 
    396     case 0x0c:	/* LCD_TIMING2 */
    397         return s->timing[2] | 0xfc000000;
    398 
    399     case 0x10:	/* LCD_STATUS */
    400         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
    401 
    402     case 0x14:	/* LCD_SUBPANEL */
    403         return s->subpanel;
    404 
    405     default:
    406         break;
    407     }
    408     OMAP_BAD_REG(addr);
    409     return 0;
    410 }
    411 
    412 static void omap_lcdc_write(void *opaque, hwaddr addr,
    413                             uint64_t value, unsigned size)
    414 {
    415     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
    416 
    417     switch (addr) {
    418     case 0x00:	/* LCD_CONTROL */
    419         s->plm = (value >> 20) & 3;
    420         s->tft = (value >> 7) & 1;
    421         s->interrupts = (value >> 3) & 3;
    422         s->mono = (value >> 1) & 1;
    423         s->ctrl = value & 0x01cff300;
    424         if (s->enable != (value & 1)) {
    425             s->enable = value & 1;
    426             omap_lcd_update(s);
    427         }
    428         break;
    429 
    430     case 0x04:	/* LCD_TIMING0 */
    431         s->timing[0] = value >> 10;
    432         s->width = (value & 0x3ff) + 1;
    433         break;
    434 
    435     case 0x08:	/* LCD_TIMING1 */
    436         s->timing[1] = value >> 10;
    437         s->height = (value & 0x3ff) + 1;
    438         break;
    439 
    440     case 0x0c:	/* LCD_TIMING2 */
    441         s->timing[2] = value;
    442         break;
    443 
    444     case 0x10:	/* LCD_STATUS */
    445         break;
    446 
    447     case 0x14:	/* LCD_SUBPANEL */
    448         s->subpanel = value & 0xa1ffffff;
    449         break;
    450 
    451     default:
    452         OMAP_BAD_REG(addr);
    453     }
    454 }
    455 
    456 static const MemoryRegionOps omap_lcdc_ops = {
    457     .read = omap_lcdc_read,
    458     .write = omap_lcdc_write,
    459     .endianness = DEVICE_NATIVE_ENDIAN,
    460 };
    461 
    462 void omap_lcdc_reset(struct omap_lcd_panel_s *s)
    463 {
    464     s->dma->current_frame = -1;
    465     s->plm = 0;
    466     s->tft = 0;
    467     s->mono = 0;
    468     s->enable = 0;
    469     s->width = 0;
    470     s->height = 0;
    471     s->interrupts = 0;
    472     s->timing[0] = 0;
    473     s->timing[1] = 0;
    474     s->timing[2] = 0;
    475     s->subpanel = 0;
    476     s->palette_done = 0;
    477     s->frame_done = 0;
    478     s->sync_error = 0;
    479     s->invalidate = 1;
    480     s->subpanel = 0;
    481     s->ctrl = 0;
    482 }
    483 
    484 static const GraphicHwOps omap_ops = {
    485     .invalidate  = omap_invalidate_display,
    486     .gfx_update  = omap_update_display,
    487 };
    488 
    489 struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
    490                                         hwaddr base,
    491                                         qemu_irq irq,
    492                                         struct omap_dma_lcd_channel_s *dma,
    493                                         omap_clk clk)
    494 {
    495     struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
    496 
    497     s->irq = irq;
    498     s->dma = dma;
    499     s->sysmem = sysmem;
    500     omap_lcdc_reset(s);
    501 
    502     memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
    503     memory_region_add_subregion(sysmem, base, &s->iomem);
    504 
    505     s->con = graphic_console_init(NULL, 0, &omap_ops, s);
    506 
    507     return s;
    508 }