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 */ 1964552b6bSMarkus Armbruster 2047df5154SPeter Maydell #include "qemu/osdep.h" 2164552b6bSMarkus Armbruster #include "hw/irq.h" 2228ecbaeeSPaolo Bonzini #include "ui/console.h" 230d09e41aSPaolo Bonzini #include "hw/arm/omap.h" 2447b43a1fSPaolo Bonzini #include "framebuffer.h" 2528ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 26c3d2689dSbalrog 27c3d2689dSbalrog struct omap_lcd_panel_s { 2875c9d6c2SAvi Kivity MemoryRegion *sysmem; 2930af1ec7SBenoît Canet MemoryRegion iomem; 30c1076c3eSPaolo Bonzini MemoryRegionSection fbsection; 31c3d2689dSbalrog qemu_irq irq; 32c78f7137SGerd Hoffmann QemuConsole *con; 33c3d2689dSbalrog 34c3d2689dSbalrog int plm; 35c3d2689dSbalrog int tft; 36c3d2689dSbalrog int mono; 37c3d2689dSbalrog int enable; 38c3d2689dSbalrog int width; 39c3d2689dSbalrog int height; 40c3d2689dSbalrog int interrupts; 41c3d2689dSbalrog uint32_t timing[3]; 42c3d2689dSbalrog uint32_t subpanel; 43c3d2689dSbalrog uint32_t ctrl; 44c3d2689dSbalrog 45c3d2689dSbalrog struct omap_dma_lcd_channel_s *dma; 46c3d2689dSbalrog uint16_t palette[256]; 47c3d2689dSbalrog int palette_done; 48c3d2689dSbalrog int frame_done; 49c3d2689dSbalrog int invalidate; 50c3d2689dSbalrog int sync_error; 51c3d2689dSbalrog }; 52c3d2689dSbalrog 53c3d2689dSbalrog static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) 54c3d2689dSbalrog { 55c3d2689dSbalrog if (s->frame_done && (s->interrupts & 1)) { 56c3d2689dSbalrog qemu_irq_raise(s->irq); 57c3d2689dSbalrog return; 58c3d2689dSbalrog } 59c3d2689dSbalrog 60c3d2689dSbalrog if (s->palette_done && (s->interrupts & 2)) { 61c3d2689dSbalrog qemu_irq_raise(s->irq); 62c3d2689dSbalrog return; 63c3d2689dSbalrog } 64c3d2689dSbalrog 65c3d2689dSbalrog if (s->sync_error) { 66c3d2689dSbalrog qemu_irq_raise(s->irq); 67c3d2689dSbalrog return; 68c3d2689dSbalrog } 69c3d2689dSbalrog 70c3d2689dSbalrog qemu_irq_lower(s->irq); 71c3d2689dSbalrog } 72c3d2689dSbalrog 73714fa308Spbrook #define draw_line_func drawfn 74c3d2689dSbalrog 75c3d2689dSbalrog #define DEPTH 32 7647b43a1fSPaolo Bonzini #include "omap_lcd_template.h" 77c3d2689dSbalrog 789596ebb7Spbrook static void omap_update_display(void *opaque) 79c3d2689dSbalrog { 80c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; 81c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(omap_lcd->con); 82714fa308Spbrook draw_line_func draw_line; 83714fa308Spbrook int size, height, first, last; 84714fa308Spbrook int width, linesize, step, bpp, frame_offset; 85a8170e5eSAvi Kivity hwaddr frame_base; 86c3d2689dSbalrog 87c78f7137SGerd Hoffmann if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || 88c78f7137SGerd Hoffmann !surface_bits_per_pixel(surface)) { 89c3d2689dSbalrog return; 90c78f7137SGerd Hoffmann } 91c3d2689dSbalrog 92c3d2689dSbalrog frame_offset = 0; 93c3d2689dSbalrog if (omap_lcd->plm != 2) { 94*0eeef0a4SPhilippe Mathieu-Daudé cpu_physical_memory_read( 95*0eeef0a4SPhilippe Mathieu-Daudé omap_lcd->dma->phys_framebuffer[omap_lcd->dma->current_frame], 96*0eeef0a4SPhilippe Mathieu-Daudé omap_lcd->palette, 0x200); 97c3d2689dSbalrog switch (omap_lcd->palette[0] >> 12 & 7) { 98c3d2689dSbalrog case 3 ... 7: 99c3d2689dSbalrog frame_offset += 0x200; 100c3d2689dSbalrog break; 101c3d2689dSbalrog default: 102c3d2689dSbalrog frame_offset += 0x20; 103c3d2689dSbalrog } 104c3d2689dSbalrog } 105c3d2689dSbalrog 106c3d2689dSbalrog /* Colour depth */ 107c3d2689dSbalrog switch ((omap_lcd->palette[0] >> 12) & 7) { 108c3d2689dSbalrog case 1: 109ea644cf3SPooja Dhannawat draw_line = draw_line2_32; 110c3d2689dSbalrog bpp = 2; 111c3d2689dSbalrog break; 112c3d2689dSbalrog 113c3d2689dSbalrog case 2: 114ea644cf3SPooja Dhannawat draw_line = draw_line4_32; 115c3d2689dSbalrog bpp = 4; 116c3d2689dSbalrog break; 117c3d2689dSbalrog 118c3d2689dSbalrog case 3: 119ea644cf3SPooja Dhannawat draw_line = draw_line8_32; 120c3d2689dSbalrog bpp = 8; 121c3d2689dSbalrog break; 122c3d2689dSbalrog 123c3d2689dSbalrog case 4 ... 7: 124c3d2689dSbalrog if (!omap_lcd->tft) 125ea644cf3SPooja Dhannawat draw_line = draw_line12_32; 126c3d2689dSbalrog else 127ea644cf3SPooja Dhannawat draw_line = draw_line16_32; 128c3d2689dSbalrog bpp = 16; 129c3d2689dSbalrog break; 130c3d2689dSbalrog 131c3d2689dSbalrog default: 132c3d2689dSbalrog /* Unsupported at the moment. */ 133c3d2689dSbalrog return; 134c3d2689dSbalrog } 135c3d2689dSbalrog 136c3d2689dSbalrog /* Resolution */ 137c3d2689dSbalrog width = omap_lcd->width; 138c78f7137SGerd Hoffmann if (width != surface_width(surface) || 139c78f7137SGerd Hoffmann omap_lcd->height != surface_height(surface)) { 140c78f7137SGerd Hoffmann qemu_console_resize(omap_lcd->con, 141c3d2689dSbalrog omap_lcd->width, omap_lcd->height); 142c78f7137SGerd Hoffmann surface = qemu_console_surface(omap_lcd->con); 143c3d2689dSbalrog omap_lcd->invalidate = 1; 144c3d2689dSbalrog } 145c3d2689dSbalrog 146c3d2689dSbalrog if (omap_lcd->dma->current_frame == 0) 147c3d2689dSbalrog size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top; 148c3d2689dSbalrog else 149c3d2689dSbalrog size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top; 150c3d2689dSbalrog 151c3d2689dSbalrog if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) { 152c3d2689dSbalrog omap_lcd->sync_error = 1; 153c3d2689dSbalrog omap_lcd_interrupts(omap_lcd); 154c3d2689dSbalrog omap_lcd->enable = 0; 155c3d2689dSbalrog return; 156c3d2689dSbalrog } 157c3d2689dSbalrog 158c3d2689dSbalrog /* Content */ 159c3d2689dSbalrog frame_base = omap_lcd->dma->phys_framebuffer[ 160c3d2689dSbalrog omap_lcd->dma->current_frame] + frame_offset; 161c3d2689dSbalrog omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame; 162c3d2689dSbalrog if (omap_lcd->dma->interrupts & 1) 163c3d2689dSbalrog qemu_irq_raise(omap_lcd->dma->irq); 164c3d2689dSbalrog if (omap_lcd->dma->dual) 165c3d2689dSbalrog omap_lcd->dma->current_frame ^= 1; 166c3d2689dSbalrog 167c78f7137SGerd Hoffmann if (!surface_bits_per_pixel(surface)) { 168c3d2689dSbalrog return; 169c78f7137SGerd Hoffmann } 170c3d2689dSbalrog 171714fa308Spbrook first = 0; 172c3d2689dSbalrog height = omap_lcd->height; 173c3d2689dSbalrog if (omap_lcd->subpanel & (1 << 31)) { 174c3d2689dSbalrog if (omap_lcd->subpanel & (1 << 29)) 175714fa308Spbrook first = (omap_lcd->subpanel >> 16) & 0x3ff; 176c3d2689dSbalrog else 177c3d2689dSbalrog height = (omap_lcd->subpanel >> 16) & 0x3ff; 178c3d2689dSbalrog /* TODO: fill the rest of the panel with DPD */ 179c3d2689dSbalrog } 180714fa308Spbrook 181c3d2689dSbalrog step = width * bpp >> 3; 182c78f7137SGerd Hoffmann linesize = surface_stride(surface); 183c1076c3eSPaolo Bonzini if (omap_lcd->invalidate) { 184c1076c3eSPaolo Bonzini framebuffer_update_memory_section(&omap_lcd->fbsection, 185c1076c3eSPaolo Bonzini omap_lcd->sysmem, frame_base, 186c1076c3eSPaolo Bonzini height, step); 187c1076c3eSPaolo Bonzini } 188c1076c3eSPaolo Bonzini 189c1076c3eSPaolo Bonzini framebuffer_update_display(surface, &omap_lcd->fbsection, 190c1076c3eSPaolo Bonzini width, height, 191714fa308Spbrook step, linesize, 0, 192714fa308Spbrook omap_lcd->invalidate, 193714fa308Spbrook draw_line, omap_lcd->palette, 194714fa308Spbrook &first, &last); 195c1076c3eSPaolo Bonzini 196714fa308Spbrook if (first >= 0) { 197c78f7137SGerd Hoffmann dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); 198c3d2689dSbalrog } 199714fa308Spbrook omap_lcd->invalidate = 0; 200c3d2689dSbalrog } 201c3d2689dSbalrog 2029596ebb7Spbrook static void omap_invalidate_display(void *opaque) { 203c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = opaque; 204c3d2689dSbalrog omap_lcd->invalidate = 1; 205c3d2689dSbalrog } 206c3d2689dSbalrog 2079596ebb7Spbrook static void omap_lcd_update(struct omap_lcd_panel_s *s) { 208c3d2689dSbalrog if (!s->enable) { 209c3d2689dSbalrog s->dma->current_frame = -1; 210c3d2689dSbalrog s->sync_error = 0; 211c3d2689dSbalrog if (s->plm != 1) 212c3d2689dSbalrog s->frame_done = 1; 213c3d2689dSbalrog omap_lcd_interrupts(s); 214c3d2689dSbalrog return; 215c3d2689dSbalrog } 216c3d2689dSbalrog 217c3d2689dSbalrog if (s->dma->current_frame == -1) { 218c3d2689dSbalrog s->frame_done = 0; 219c3d2689dSbalrog s->palette_done = 0; 220c3d2689dSbalrog s->dma->current_frame = 0; 221c3d2689dSbalrog } 222c3d2689dSbalrog 223c3d2689dSbalrog if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, 224c3d2689dSbalrog s->dma->src_f1_top) || 225c3d2689dSbalrog !s->dma->mpu->port[ 226c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 227c3d2689dSbalrog s->dma->src_f1_bottom) || 228c3d2689dSbalrog (s->dma->dual && 229c3d2689dSbalrog (!s->dma->mpu->port[ 230c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 231c3d2689dSbalrog s->dma->src_f2_top) || 232c3d2689dSbalrog !s->dma->mpu->port[ 233c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 234c3d2689dSbalrog s->dma->src_f2_bottom)))) { 235c3d2689dSbalrog s->dma->condition |= 1 << 2; 236c3d2689dSbalrog if (s->dma->interrupts & (1 << 1)) 237c3d2689dSbalrog qemu_irq_raise(s->dma->irq); 238c3d2689dSbalrog s->enable = 0; 239c3d2689dSbalrog return; 240c3d2689dSbalrog } 241c3d2689dSbalrog 242714fa308Spbrook s->dma->phys_framebuffer[0] = s->dma->src_f1_top; 243714fa308Spbrook s->dma->phys_framebuffer[1] = s->dma->src_f2_top; 244c3d2689dSbalrog 245c3d2689dSbalrog if (s->plm != 2 && !s->palette_done) { 246714fa308Spbrook cpu_physical_memory_read( 247714fa308Spbrook s->dma->phys_framebuffer[s->dma->current_frame], 248*0eeef0a4SPhilippe Mathieu-Daudé s->palette, 0x200); 249c3d2689dSbalrog s->palette_done = 1; 250c3d2689dSbalrog omap_lcd_interrupts(s); 251c3d2689dSbalrog } 252c3d2689dSbalrog } 253c3d2689dSbalrog 254a8170e5eSAvi Kivity static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, 25530af1ec7SBenoît Canet unsigned size) 256c3d2689dSbalrog { 257c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 258c3d2689dSbalrog 2598da3ff18Spbrook switch (addr) { 260c3d2689dSbalrog case 0x00: /* LCD_CONTROL */ 261c3d2689dSbalrog return (s->tft << 23) | (s->plm << 20) | 262c3d2689dSbalrog (s->tft << 7) | (s->interrupts << 3) | 263c3d2689dSbalrog (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; 264c3d2689dSbalrog 265c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */ 266c3d2689dSbalrog return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; 267c3d2689dSbalrog 268c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */ 269c3d2689dSbalrog return (s->timing[1] << 10) | (s->height - 1); 270c3d2689dSbalrog 271c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */ 272c3d2689dSbalrog return s->timing[2] | 0xfc000000; 273c3d2689dSbalrog 274c3d2689dSbalrog case 0x10: /* LCD_STATUS */ 275c3d2689dSbalrog return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; 276c3d2689dSbalrog 277c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */ 278c3d2689dSbalrog return s->subpanel; 279c3d2689dSbalrog 280c3d2689dSbalrog default: 281c3d2689dSbalrog break; 282c3d2689dSbalrog } 283c3d2689dSbalrog OMAP_BAD_REG(addr); 284c3d2689dSbalrog return 0; 285c3d2689dSbalrog } 286c3d2689dSbalrog 287a8170e5eSAvi Kivity static void omap_lcdc_write(void *opaque, hwaddr addr, 28830af1ec7SBenoît Canet uint64_t value, unsigned size) 289c3d2689dSbalrog { 290c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 291c3d2689dSbalrog 2928da3ff18Spbrook switch (addr) { 293c3d2689dSbalrog case 0x00: /* LCD_CONTROL */ 294c3d2689dSbalrog s->plm = (value >> 20) & 3; 295c3d2689dSbalrog s->tft = (value >> 7) & 1; 296c3d2689dSbalrog s->interrupts = (value >> 3) & 3; 297c3d2689dSbalrog s->mono = (value >> 1) & 1; 298c3d2689dSbalrog s->ctrl = value & 0x01cff300; 299c3d2689dSbalrog if (s->enable != (value & 1)) { 300c3d2689dSbalrog s->enable = value & 1; 301c3d2689dSbalrog omap_lcd_update(s); 302c3d2689dSbalrog } 303c3d2689dSbalrog break; 304c3d2689dSbalrog 305c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */ 306c3d2689dSbalrog s->timing[0] = value >> 10; 307c3d2689dSbalrog s->width = (value & 0x3ff) + 1; 308c3d2689dSbalrog break; 309c3d2689dSbalrog 310c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */ 311c3d2689dSbalrog s->timing[1] = value >> 10; 312c3d2689dSbalrog s->height = (value & 0x3ff) + 1; 313c3d2689dSbalrog break; 314c3d2689dSbalrog 315c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */ 316c3d2689dSbalrog s->timing[2] = value; 317c3d2689dSbalrog break; 318c3d2689dSbalrog 319c3d2689dSbalrog case 0x10: /* LCD_STATUS */ 320c3d2689dSbalrog break; 321c3d2689dSbalrog 322c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */ 323c3d2689dSbalrog s->subpanel = value & 0xa1ffffff; 324c3d2689dSbalrog break; 325c3d2689dSbalrog 326c3d2689dSbalrog default: 327c3d2689dSbalrog OMAP_BAD_REG(addr); 328c3d2689dSbalrog } 329c3d2689dSbalrog } 330c3d2689dSbalrog 33130af1ec7SBenoît Canet static const MemoryRegionOps omap_lcdc_ops = { 33230af1ec7SBenoît Canet .read = omap_lcdc_read, 33330af1ec7SBenoît Canet .write = omap_lcdc_write, 33430af1ec7SBenoît Canet .endianness = DEVICE_NATIVE_ENDIAN, 335c3d2689dSbalrog }; 336c3d2689dSbalrog 337c3d2689dSbalrog void omap_lcdc_reset(struct omap_lcd_panel_s *s) 338c3d2689dSbalrog { 339c3d2689dSbalrog s->dma->current_frame = -1; 340c3d2689dSbalrog s->plm = 0; 341c3d2689dSbalrog s->tft = 0; 342c3d2689dSbalrog s->mono = 0; 343c3d2689dSbalrog s->enable = 0; 344c3d2689dSbalrog s->width = 0; 345c3d2689dSbalrog s->height = 0; 346c3d2689dSbalrog s->interrupts = 0; 347c3d2689dSbalrog s->timing[0] = 0; 348c3d2689dSbalrog s->timing[1] = 0; 349c3d2689dSbalrog s->timing[2] = 0; 350c3d2689dSbalrog s->subpanel = 0; 351c3d2689dSbalrog s->palette_done = 0; 352c3d2689dSbalrog s->frame_done = 0; 353c3d2689dSbalrog s->sync_error = 0; 354c3d2689dSbalrog s->invalidate = 1; 355c3d2689dSbalrog s->subpanel = 0; 356c3d2689dSbalrog s->ctrl = 0; 357c3d2689dSbalrog } 358c3d2689dSbalrog 359380cd056SGerd Hoffmann static const GraphicHwOps omap_ops = { 360380cd056SGerd Hoffmann .invalidate = omap_invalidate_display, 361380cd056SGerd Hoffmann .gfx_update = omap_update_display, 362380cd056SGerd Hoffmann }; 363380cd056SGerd Hoffmann 36430af1ec7SBenoît Canet struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, 365a8170e5eSAvi Kivity hwaddr base, 36630af1ec7SBenoît Canet qemu_irq irq, 36730af1ec7SBenoît Canet struct omap_dma_lcd_channel_s *dma, 36830af1ec7SBenoît Canet omap_clk clk) 369c3d2689dSbalrog { 370b45c03f5SMarkus Armbruster struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1); 371c3d2689dSbalrog 372c3d2689dSbalrog s->irq = irq; 373c3d2689dSbalrog s->dma = dma; 37475c9d6c2SAvi Kivity s->sysmem = sysmem; 375c3d2689dSbalrog omap_lcdc_reset(s); 376c3d2689dSbalrog 3772c9b15caSPaolo Bonzini memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100); 37830af1ec7SBenoît Canet memory_region_add_subregion(sysmem, base, &s->iomem); 379c3d2689dSbalrog 3805643706aSGerd Hoffmann s->con = graphic_console_init(NULL, 0, &omap_ops, s); 381c3d2689dSbalrog 382c3d2689dSbalrog return s; 383c3d2689dSbalrog } 384