1c3d2689dSbalrog /* 2c3d2689dSbalrog * OMAP LCD controller. 3c3d2689dSbalrog * 4c3d2689dSbalrog * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org> 5c3d2689dSbalrog * 6c3d2689dSbalrog * This program is free software; you can redistribute it and/or 7c3d2689dSbalrog * modify it under the terms of the GNU General Public License as 8c3d2689dSbalrog * published by the Free Software Foundation; either version 2 of 9c3d2689dSbalrog * the License, or (at your option) any later version. 10c3d2689dSbalrog * 11c3d2689dSbalrog * This program is distributed in the hope that it will be useful, 12c3d2689dSbalrog * but WITHOUT ANY WARRANTY; without even the implied warranty of 13c3d2689dSbalrog * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14c3d2689dSbalrog * GNU General Public License for more details. 15c3d2689dSbalrog * 16fad6cb1aSaurel32 * You should have received a copy of the GNU General Public License along 178167ee88SBlue Swirl * with this program; if not, see <http://www.gnu.org/licenses/>. 18c3d2689dSbalrog */ 1983c9f4caSPaolo Bonzini #include "hw/hw.h" 2028ecbaeeSPaolo Bonzini #include "ui/console.h" 2183c9f4caSPaolo Bonzini #include "hw/omap.h" 2283c9f4caSPaolo Bonzini #include "hw/framebuffer.h" 2328ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 24c3d2689dSbalrog 25c3d2689dSbalrog struct omap_lcd_panel_s { 2675c9d6c2SAvi Kivity MemoryRegion *sysmem; 2730af1ec7SBenoît Canet MemoryRegion iomem; 28c3d2689dSbalrog qemu_irq irq; 29*c78f7137SGerd Hoffmann QemuConsole *con; 30c3d2689dSbalrog 31c3d2689dSbalrog int plm; 32c3d2689dSbalrog int tft; 33c3d2689dSbalrog int mono; 34c3d2689dSbalrog int enable; 35c3d2689dSbalrog int width; 36c3d2689dSbalrog int height; 37c3d2689dSbalrog int interrupts; 38c3d2689dSbalrog uint32_t timing[3]; 39c3d2689dSbalrog uint32_t subpanel; 40c3d2689dSbalrog uint32_t ctrl; 41c3d2689dSbalrog 42c3d2689dSbalrog struct omap_dma_lcd_channel_s *dma; 43c3d2689dSbalrog uint16_t palette[256]; 44c3d2689dSbalrog int palette_done; 45c3d2689dSbalrog int frame_done; 46c3d2689dSbalrog int invalidate; 47c3d2689dSbalrog int sync_error; 48c3d2689dSbalrog }; 49c3d2689dSbalrog 50c3d2689dSbalrog static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) 51c3d2689dSbalrog { 52c3d2689dSbalrog if (s->frame_done && (s->interrupts & 1)) { 53c3d2689dSbalrog qemu_irq_raise(s->irq); 54c3d2689dSbalrog return; 55c3d2689dSbalrog } 56c3d2689dSbalrog 57c3d2689dSbalrog if (s->palette_done && (s->interrupts & 2)) { 58c3d2689dSbalrog qemu_irq_raise(s->irq); 59c3d2689dSbalrog return; 60c3d2689dSbalrog } 61c3d2689dSbalrog 62c3d2689dSbalrog if (s->sync_error) { 63c3d2689dSbalrog qemu_irq_raise(s->irq); 64c3d2689dSbalrog return; 65c3d2689dSbalrog } 66c3d2689dSbalrog 67c3d2689dSbalrog qemu_irq_lower(s->irq); 68c3d2689dSbalrog } 69c3d2689dSbalrog 70714fa308Spbrook #define draw_line_func drawfn 71c3d2689dSbalrog 72c3d2689dSbalrog #define DEPTH 8 7383c9f4caSPaolo Bonzini #include "hw/omap_lcd_template.h" 74c3d2689dSbalrog #define DEPTH 15 7583c9f4caSPaolo Bonzini #include "hw/omap_lcd_template.h" 76c3d2689dSbalrog #define DEPTH 16 7783c9f4caSPaolo Bonzini #include "hw/omap_lcd_template.h" 78c3d2689dSbalrog #define DEPTH 32 7983c9f4caSPaolo Bonzini #include "hw/omap_lcd_template.h" 80c3d2689dSbalrog 81714fa308Spbrook static draw_line_func draw_line_table2[33] = { 82b9d38e95SBlue Swirl [0 ... 32] = NULL, 83c3d2689dSbalrog [8] = draw_line2_8, 84c3d2689dSbalrog [15] = draw_line2_15, 85c3d2689dSbalrog [16] = draw_line2_16, 86c3d2689dSbalrog [32] = draw_line2_32, 87714fa308Spbrook }, draw_line_table4[33] = { 88b9d38e95SBlue Swirl [0 ... 32] = NULL, 89c3d2689dSbalrog [8] = draw_line4_8, 90c3d2689dSbalrog [15] = draw_line4_15, 91c3d2689dSbalrog [16] = draw_line4_16, 92c3d2689dSbalrog [32] = draw_line4_32, 93714fa308Spbrook }, draw_line_table8[33] = { 94b9d38e95SBlue Swirl [0 ... 32] = NULL, 95c3d2689dSbalrog [8] = draw_line8_8, 96c3d2689dSbalrog [15] = draw_line8_15, 97c3d2689dSbalrog [16] = draw_line8_16, 98c3d2689dSbalrog [32] = draw_line8_32, 99714fa308Spbrook }, draw_line_table12[33] = { 100b9d38e95SBlue Swirl [0 ... 32] = NULL, 101c3d2689dSbalrog [8] = draw_line12_8, 102c3d2689dSbalrog [15] = draw_line12_15, 103c3d2689dSbalrog [16] = draw_line12_16, 104c3d2689dSbalrog [32] = draw_line12_32, 105714fa308Spbrook }, draw_line_table16[33] = { 106b9d38e95SBlue Swirl [0 ... 32] = NULL, 107c3d2689dSbalrog [8] = draw_line16_8, 108c3d2689dSbalrog [15] = draw_line16_15, 109c3d2689dSbalrog [16] = draw_line16_16, 110c3d2689dSbalrog [32] = draw_line16_32, 111c3d2689dSbalrog }; 112c3d2689dSbalrog 1139596ebb7Spbrook static void omap_update_display(void *opaque) 114c3d2689dSbalrog { 115c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; 116*c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(omap_lcd->con); 117714fa308Spbrook draw_line_func draw_line; 118714fa308Spbrook int size, height, first, last; 119714fa308Spbrook int width, linesize, step, bpp, frame_offset; 120a8170e5eSAvi Kivity hwaddr frame_base; 121c3d2689dSbalrog 122*c78f7137SGerd Hoffmann if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || 123*c78f7137SGerd Hoffmann !surface_bits_per_pixel(surface)) { 124c3d2689dSbalrog return; 125*c78f7137SGerd Hoffmann } 126c3d2689dSbalrog 127c3d2689dSbalrog frame_offset = 0; 128c3d2689dSbalrog if (omap_lcd->plm != 2) { 129714fa308Spbrook cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[ 130714fa308Spbrook omap_lcd->dma->current_frame], 131714fa308Spbrook (void *)omap_lcd->palette, 0x200); 132c3d2689dSbalrog switch (omap_lcd->palette[0] >> 12 & 7) { 133c3d2689dSbalrog case 3 ... 7: 134c3d2689dSbalrog frame_offset += 0x200; 135c3d2689dSbalrog break; 136c3d2689dSbalrog default: 137c3d2689dSbalrog frame_offset += 0x20; 138c3d2689dSbalrog } 139c3d2689dSbalrog } 140c3d2689dSbalrog 141c3d2689dSbalrog /* Colour depth */ 142c3d2689dSbalrog switch ((omap_lcd->palette[0] >> 12) & 7) { 143c3d2689dSbalrog case 1: 144*c78f7137SGerd Hoffmann draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; 145c3d2689dSbalrog bpp = 2; 146c3d2689dSbalrog break; 147c3d2689dSbalrog 148c3d2689dSbalrog case 2: 149*c78f7137SGerd Hoffmann draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; 150c3d2689dSbalrog bpp = 4; 151c3d2689dSbalrog break; 152c3d2689dSbalrog 153c3d2689dSbalrog case 3: 154*c78f7137SGerd Hoffmann draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; 155c3d2689dSbalrog bpp = 8; 156c3d2689dSbalrog break; 157c3d2689dSbalrog 158c3d2689dSbalrog case 4 ... 7: 159c3d2689dSbalrog if (!omap_lcd->tft) 160*c78f7137SGerd Hoffmann draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; 161c3d2689dSbalrog else 162*c78f7137SGerd Hoffmann draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; 163c3d2689dSbalrog bpp = 16; 164c3d2689dSbalrog break; 165c3d2689dSbalrog 166c3d2689dSbalrog default: 167c3d2689dSbalrog /* Unsupported at the moment. */ 168c3d2689dSbalrog return; 169c3d2689dSbalrog } 170c3d2689dSbalrog 171c3d2689dSbalrog /* Resolution */ 172c3d2689dSbalrog width = omap_lcd->width; 173*c78f7137SGerd Hoffmann if (width != surface_width(surface) || 174*c78f7137SGerd Hoffmann omap_lcd->height != surface_height(surface)) { 175*c78f7137SGerd Hoffmann qemu_console_resize(omap_lcd->con, 176c3d2689dSbalrog omap_lcd->width, omap_lcd->height); 177*c78f7137SGerd Hoffmann surface = qemu_console_surface(omap_lcd->con); 178c3d2689dSbalrog omap_lcd->invalidate = 1; 179c3d2689dSbalrog } 180c3d2689dSbalrog 181c3d2689dSbalrog if (omap_lcd->dma->current_frame == 0) 182c3d2689dSbalrog size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top; 183c3d2689dSbalrog else 184c3d2689dSbalrog size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top; 185c3d2689dSbalrog 186c3d2689dSbalrog if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) { 187c3d2689dSbalrog omap_lcd->sync_error = 1; 188c3d2689dSbalrog omap_lcd_interrupts(omap_lcd); 189c3d2689dSbalrog omap_lcd->enable = 0; 190c3d2689dSbalrog return; 191c3d2689dSbalrog } 192c3d2689dSbalrog 193c3d2689dSbalrog /* Content */ 194c3d2689dSbalrog frame_base = omap_lcd->dma->phys_framebuffer[ 195c3d2689dSbalrog omap_lcd->dma->current_frame] + frame_offset; 196c3d2689dSbalrog omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame; 197c3d2689dSbalrog if (omap_lcd->dma->interrupts & 1) 198c3d2689dSbalrog qemu_irq_raise(omap_lcd->dma->irq); 199c3d2689dSbalrog if (omap_lcd->dma->dual) 200c3d2689dSbalrog omap_lcd->dma->current_frame ^= 1; 201c3d2689dSbalrog 202*c78f7137SGerd Hoffmann if (!surface_bits_per_pixel(surface)) { 203c3d2689dSbalrog return; 204*c78f7137SGerd Hoffmann } 205c3d2689dSbalrog 206714fa308Spbrook first = 0; 207c3d2689dSbalrog height = omap_lcd->height; 208c3d2689dSbalrog if (omap_lcd->subpanel & (1 << 31)) { 209c3d2689dSbalrog if (omap_lcd->subpanel & (1 << 29)) 210714fa308Spbrook first = (omap_lcd->subpanel >> 16) & 0x3ff; 211c3d2689dSbalrog else 212c3d2689dSbalrog height = (omap_lcd->subpanel >> 16) & 0x3ff; 213c3d2689dSbalrog /* TODO: fill the rest of the panel with DPD */ 214c3d2689dSbalrog } 215714fa308Spbrook 216c3d2689dSbalrog step = width * bpp >> 3; 217*c78f7137SGerd Hoffmann linesize = surface_stride(surface); 218*c78f7137SGerd Hoffmann framebuffer_update_display(surface, omap_lcd->sysmem, 219714fa308Spbrook frame_base, width, height, 220714fa308Spbrook step, linesize, 0, 221714fa308Spbrook omap_lcd->invalidate, 222714fa308Spbrook draw_line, omap_lcd->palette, 223714fa308Spbrook &first, &last); 224714fa308Spbrook if (first >= 0) { 225*c78f7137SGerd Hoffmann dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); 226c3d2689dSbalrog } 227714fa308Spbrook omap_lcd->invalidate = 0; 228c3d2689dSbalrog } 229c3d2689dSbalrog 230d9c7ebb1SLuiz Capitulino static void omap_ppm_save(const char *filename, uint8_t *data, 231d9c7ebb1SLuiz Capitulino int w, int h, int linesize, Error **errp) 232c3d2689dSbalrog { 233c3d2689dSbalrog FILE *f; 234c3d2689dSbalrog uint8_t *d, *d1; 235c3d2689dSbalrog unsigned int v; 236d9c7ebb1SLuiz Capitulino int ret, y, x, bpp; 237c3d2689dSbalrog 238c3d2689dSbalrog f = fopen(filename, "wb"); 239d9c7ebb1SLuiz Capitulino if (!f) { 240d9c7ebb1SLuiz Capitulino error_setg(errp, "failed to open file '%s': %s", filename, 241d9c7ebb1SLuiz Capitulino strerror(errno)); 242d9c7ebb1SLuiz Capitulino return; 243d9c7ebb1SLuiz Capitulino } 244d9c7ebb1SLuiz Capitulino ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); 245d9c7ebb1SLuiz Capitulino if (ret < 0) { 246d9c7ebb1SLuiz Capitulino goto write_err; 247d9c7ebb1SLuiz Capitulino } 248c3d2689dSbalrog d1 = data; 249c3d2689dSbalrog bpp = linesize / w; 250c3d2689dSbalrog for (y = 0; y < h; y ++) { 251c3d2689dSbalrog d = d1; 252c3d2689dSbalrog for (x = 0; x < w; x ++) { 253c3d2689dSbalrog v = *(uint32_t *) d; 254c3d2689dSbalrog switch (bpp) { 255c3d2689dSbalrog case 2: 256d9c7ebb1SLuiz Capitulino ret = fputc((v >> 8) & 0xf8, f); 257d9c7ebb1SLuiz Capitulino if (ret == EOF) { 258d9c7ebb1SLuiz Capitulino goto write_err; 259d9c7ebb1SLuiz Capitulino } 260d9c7ebb1SLuiz Capitulino ret = fputc((v >> 3) & 0xfc, f); 261d9c7ebb1SLuiz Capitulino if (ret == EOF) { 262d9c7ebb1SLuiz Capitulino goto write_err; 263d9c7ebb1SLuiz Capitulino } 264d9c7ebb1SLuiz Capitulino ret = fputc((v << 3) & 0xf8, f); 265d9c7ebb1SLuiz Capitulino if (ret == EOF) { 266d9c7ebb1SLuiz Capitulino goto write_err; 267d9c7ebb1SLuiz Capitulino } 268c3d2689dSbalrog break; 269c3d2689dSbalrog case 3: 270c3d2689dSbalrog case 4: 271c3d2689dSbalrog default: 272d9c7ebb1SLuiz Capitulino ret = fputc((v >> 16) & 0xff, f); 273d9c7ebb1SLuiz Capitulino if (ret == EOF) { 274d9c7ebb1SLuiz Capitulino goto write_err; 275d9c7ebb1SLuiz Capitulino } 276d9c7ebb1SLuiz Capitulino ret = fputc((v >> 8) & 0xff, f); 277d9c7ebb1SLuiz Capitulino if (ret == EOF) { 278d9c7ebb1SLuiz Capitulino goto write_err; 279d9c7ebb1SLuiz Capitulino } 280d9c7ebb1SLuiz Capitulino ret = fputc((v) & 0xff, f); 281d9c7ebb1SLuiz Capitulino if (ret == EOF) { 282d9c7ebb1SLuiz Capitulino goto write_err; 283d9c7ebb1SLuiz Capitulino } 284c3d2689dSbalrog break; 285c3d2689dSbalrog } 286c3d2689dSbalrog d += bpp; 287c3d2689dSbalrog } 288c3d2689dSbalrog d1 += linesize; 289c3d2689dSbalrog } 290d9c7ebb1SLuiz Capitulino out: 291c3d2689dSbalrog fclose(f); 292d9c7ebb1SLuiz Capitulino return; 293d9c7ebb1SLuiz Capitulino 294d9c7ebb1SLuiz Capitulino write_err: 295d9c7ebb1SLuiz Capitulino error_setg(errp, "failed to write to file '%s': %s", filename, 296d9c7ebb1SLuiz Capitulino strerror(errno)); 297d9c7ebb1SLuiz Capitulino unlink(filename); 298d9c7ebb1SLuiz Capitulino goto out; 299c3d2689dSbalrog } 300c3d2689dSbalrog 301d7098135SLuiz Capitulino static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, 302d7098135SLuiz Capitulino Error **errp) 30345efb161SGerd Hoffmann { 304c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = opaque; 305*c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(omap_lcd->con); 30608c4ea29SGerd Hoffmann 307c3d2689dSbalrog omap_update_display(opaque); 308*c78f7137SGerd Hoffmann if (omap_lcd && surface_data(surface)) 309*c78f7137SGerd Hoffmann omap_ppm_save(filename, surface_data(surface), 310c3d2689dSbalrog omap_lcd->width, omap_lcd->height, 311*c78f7137SGerd Hoffmann surface_stride(surface), errp); 312c3d2689dSbalrog } 313c3d2689dSbalrog 3149596ebb7Spbrook static void omap_invalidate_display(void *opaque) { 315c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = opaque; 316c3d2689dSbalrog omap_lcd->invalidate = 1; 317c3d2689dSbalrog } 318c3d2689dSbalrog 3199596ebb7Spbrook static void omap_lcd_update(struct omap_lcd_panel_s *s) { 320c3d2689dSbalrog if (!s->enable) { 321c3d2689dSbalrog s->dma->current_frame = -1; 322c3d2689dSbalrog s->sync_error = 0; 323c3d2689dSbalrog if (s->plm != 1) 324c3d2689dSbalrog s->frame_done = 1; 325c3d2689dSbalrog omap_lcd_interrupts(s); 326c3d2689dSbalrog return; 327c3d2689dSbalrog } 328c3d2689dSbalrog 329c3d2689dSbalrog if (s->dma->current_frame == -1) { 330c3d2689dSbalrog s->frame_done = 0; 331c3d2689dSbalrog s->palette_done = 0; 332c3d2689dSbalrog s->dma->current_frame = 0; 333c3d2689dSbalrog } 334c3d2689dSbalrog 335c3d2689dSbalrog if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, 336c3d2689dSbalrog s->dma->src_f1_top) || 337c3d2689dSbalrog !s->dma->mpu->port[ 338c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 339c3d2689dSbalrog s->dma->src_f1_bottom) || 340c3d2689dSbalrog (s->dma->dual && 341c3d2689dSbalrog (!s->dma->mpu->port[ 342c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 343c3d2689dSbalrog s->dma->src_f2_top) || 344c3d2689dSbalrog !s->dma->mpu->port[ 345c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 346c3d2689dSbalrog s->dma->src_f2_bottom)))) { 347c3d2689dSbalrog s->dma->condition |= 1 << 2; 348c3d2689dSbalrog if (s->dma->interrupts & (1 << 1)) 349c3d2689dSbalrog qemu_irq_raise(s->dma->irq); 350c3d2689dSbalrog s->enable = 0; 351c3d2689dSbalrog return; 352c3d2689dSbalrog } 353c3d2689dSbalrog 354714fa308Spbrook s->dma->phys_framebuffer[0] = s->dma->src_f1_top; 355714fa308Spbrook s->dma->phys_framebuffer[1] = s->dma->src_f2_top; 356c3d2689dSbalrog 357c3d2689dSbalrog if (s->plm != 2 && !s->palette_done) { 358714fa308Spbrook cpu_physical_memory_read( 359714fa308Spbrook s->dma->phys_framebuffer[s->dma->current_frame], 360714fa308Spbrook (void *)s->palette, 0x200); 361c3d2689dSbalrog s->palette_done = 1; 362c3d2689dSbalrog omap_lcd_interrupts(s); 363c3d2689dSbalrog } 364c3d2689dSbalrog } 365c3d2689dSbalrog 366a8170e5eSAvi Kivity static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, 36730af1ec7SBenoît Canet unsigned size) 368c3d2689dSbalrog { 369c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 370c3d2689dSbalrog 3718da3ff18Spbrook switch (addr) { 372c3d2689dSbalrog case 0x00: /* LCD_CONTROL */ 373c3d2689dSbalrog return (s->tft << 23) | (s->plm << 20) | 374c3d2689dSbalrog (s->tft << 7) | (s->interrupts << 3) | 375c3d2689dSbalrog (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; 376c3d2689dSbalrog 377c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */ 378c3d2689dSbalrog return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; 379c3d2689dSbalrog 380c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */ 381c3d2689dSbalrog return (s->timing[1] << 10) | (s->height - 1); 382c3d2689dSbalrog 383c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */ 384c3d2689dSbalrog return s->timing[2] | 0xfc000000; 385c3d2689dSbalrog 386c3d2689dSbalrog case 0x10: /* LCD_STATUS */ 387c3d2689dSbalrog return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; 388c3d2689dSbalrog 389c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */ 390c3d2689dSbalrog return s->subpanel; 391c3d2689dSbalrog 392c3d2689dSbalrog default: 393c3d2689dSbalrog break; 394c3d2689dSbalrog } 395c3d2689dSbalrog OMAP_BAD_REG(addr); 396c3d2689dSbalrog return 0; 397c3d2689dSbalrog } 398c3d2689dSbalrog 399a8170e5eSAvi Kivity static void omap_lcdc_write(void *opaque, hwaddr addr, 40030af1ec7SBenoît Canet uint64_t value, unsigned size) 401c3d2689dSbalrog { 402c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 403c3d2689dSbalrog 4048da3ff18Spbrook switch (addr) { 405c3d2689dSbalrog case 0x00: /* LCD_CONTROL */ 406c3d2689dSbalrog s->plm = (value >> 20) & 3; 407c3d2689dSbalrog s->tft = (value >> 7) & 1; 408c3d2689dSbalrog s->interrupts = (value >> 3) & 3; 409c3d2689dSbalrog s->mono = (value >> 1) & 1; 410c3d2689dSbalrog s->ctrl = value & 0x01cff300; 411c3d2689dSbalrog if (s->enable != (value & 1)) { 412c3d2689dSbalrog s->enable = value & 1; 413c3d2689dSbalrog omap_lcd_update(s); 414c3d2689dSbalrog } 415c3d2689dSbalrog break; 416c3d2689dSbalrog 417c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */ 418c3d2689dSbalrog s->timing[0] = value >> 10; 419c3d2689dSbalrog s->width = (value & 0x3ff) + 1; 420c3d2689dSbalrog break; 421c3d2689dSbalrog 422c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */ 423c3d2689dSbalrog s->timing[1] = value >> 10; 424c3d2689dSbalrog s->height = (value & 0x3ff) + 1; 425c3d2689dSbalrog break; 426c3d2689dSbalrog 427c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */ 428c3d2689dSbalrog s->timing[2] = value; 429c3d2689dSbalrog break; 430c3d2689dSbalrog 431c3d2689dSbalrog case 0x10: /* LCD_STATUS */ 432c3d2689dSbalrog break; 433c3d2689dSbalrog 434c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */ 435c3d2689dSbalrog s->subpanel = value & 0xa1ffffff; 436c3d2689dSbalrog break; 437c3d2689dSbalrog 438c3d2689dSbalrog default: 439c3d2689dSbalrog OMAP_BAD_REG(addr); 440c3d2689dSbalrog } 441c3d2689dSbalrog } 442c3d2689dSbalrog 44330af1ec7SBenoît Canet static const MemoryRegionOps omap_lcdc_ops = { 44430af1ec7SBenoît Canet .read = omap_lcdc_read, 44530af1ec7SBenoît Canet .write = omap_lcdc_write, 44630af1ec7SBenoît Canet .endianness = DEVICE_NATIVE_ENDIAN, 447c3d2689dSbalrog }; 448c3d2689dSbalrog 449c3d2689dSbalrog void omap_lcdc_reset(struct omap_lcd_panel_s *s) 450c3d2689dSbalrog { 451c3d2689dSbalrog s->dma->current_frame = -1; 452c3d2689dSbalrog s->plm = 0; 453c3d2689dSbalrog s->tft = 0; 454c3d2689dSbalrog s->mono = 0; 455c3d2689dSbalrog s->enable = 0; 456c3d2689dSbalrog s->width = 0; 457c3d2689dSbalrog s->height = 0; 458c3d2689dSbalrog s->interrupts = 0; 459c3d2689dSbalrog s->timing[0] = 0; 460c3d2689dSbalrog s->timing[1] = 0; 461c3d2689dSbalrog s->timing[2] = 0; 462c3d2689dSbalrog s->subpanel = 0; 463c3d2689dSbalrog s->palette_done = 0; 464c3d2689dSbalrog s->frame_done = 0; 465c3d2689dSbalrog s->sync_error = 0; 466c3d2689dSbalrog s->invalidate = 1; 467c3d2689dSbalrog s->subpanel = 0; 468c3d2689dSbalrog s->ctrl = 0; 469c3d2689dSbalrog } 470c3d2689dSbalrog 47130af1ec7SBenoît Canet struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, 472a8170e5eSAvi Kivity hwaddr base, 47330af1ec7SBenoît Canet qemu_irq irq, 47430af1ec7SBenoît Canet struct omap_dma_lcd_channel_s *dma, 47530af1ec7SBenoît Canet omap_clk clk) 476c3d2689dSbalrog { 477c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) 4787267c094SAnthony Liguori g_malloc0(sizeof(struct omap_lcd_panel_s)); 479c3d2689dSbalrog 480c3d2689dSbalrog s->irq = irq; 481c3d2689dSbalrog s->dma = dma; 48275c9d6c2SAvi Kivity s->sysmem = sysmem; 483c3d2689dSbalrog omap_lcdc_reset(s); 484c3d2689dSbalrog 48530af1ec7SBenoît Canet memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100); 48630af1ec7SBenoît Canet memory_region_add_subregion(sysmem, base, &s->iomem); 487c3d2689dSbalrog 488*c78f7137SGerd Hoffmann s->con = graphic_console_init(omap_update_display, 489c60e08d9Spbrook omap_invalidate_display, 490c60e08d9Spbrook omap_screen_dump, NULL, s); 491c3d2689dSbalrog 492c3d2689dSbalrog return s; 493c3d2689dSbalrog } 494