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" 210d09e41aSPaolo Bonzini #include "hw/arm/omap.h" 2247b43a1fSPaolo Bonzini #include "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; 29c78f7137SGerd 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 7347b43a1fSPaolo Bonzini #include "omap_lcd_template.h" 74c3d2689dSbalrog #define DEPTH 15 7547b43a1fSPaolo Bonzini #include "omap_lcd_template.h" 76c3d2689dSbalrog #define DEPTH 16 7747b43a1fSPaolo Bonzini #include "omap_lcd_template.h" 78c3d2689dSbalrog #define DEPTH 32 7947b43a1fSPaolo Bonzini #include "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; 116c78f7137SGerd 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 122c78f7137SGerd Hoffmann if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || 123c78f7137SGerd Hoffmann !surface_bits_per_pixel(surface)) { 124c3d2689dSbalrog return; 125c78f7137SGerd 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: 144c78f7137SGerd Hoffmann draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; 145c3d2689dSbalrog bpp = 2; 146c3d2689dSbalrog break; 147c3d2689dSbalrog 148c3d2689dSbalrog case 2: 149c78f7137SGerd Hoffmann draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; 150c3d2689dSbalrog bpp = 4; 151c3d2689dSbalrog break; 152c3d2689dSbalrog 153c3d2689dSbalrog case 3: 154c78f7137SGerd 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) 160c78f7137SGerd Hoffmann draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; 161c3d2689dSbalrog else 162c78f7137SGerd 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; 173c78f7137SGerd Hoffmann if (width != surface_width(surface) || 174c78f7137SGerd Hoffmann omap_lcd->height != surface_height(surface)) { 175c78f7137SGerd Hoffmann qemu_console_resize(omap_lcd->con, 176c3d2689dSbalrog omap_lcd->width, omap_lcd->height); 177c78f7137SGerd 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 202c78f7137SGerd Hoffmann if (!surface_bits_per_pixel(surface)) { 203c3d2689dSbalrog return; 204c78f7137SGerd 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; 217c78f7137SGerd Hoffmann linesize = surface_stride(surface); 218c78f7137SGerd 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) { 225c78f7137SGerd Hoffmann dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); 226c3d2689dSbalrog } 227714fa308Spbrook omap_lcd->invalidate = 0; 228c3d2689dSbalrog } 229c3d2689dSbalrog 2309596ebb7Spbrook static void omap_invalidate_display(void *opaque) { 231c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = opaque; 232c3d2689dSbalrog omap_lcd->invalidate = 1; 233c3d2689dSbalrog } 234c3d2689dSbalrog 2359596ebb7Spbrook static void omap_lcd_update(struct omap_lcd_panel_s *s) { 236c3d2689dSbalrog if (!s->enable) { 237c3d2689dSbalrog s->dma->current_frame = -1; 238c3d2689dSbalrog s->sync_error = 0; 239c3d2689dSbalrog if (s->plm != 1) 240c3d2689dSbalrog s->frame_done = 1; 241c3d2689dSbalrog omap_lcd_interrupts(s); 242c3d2689dSbalrog return; 243c3d2689dSbalrog } 244c3d2689dSbalrog 245c3d2689dSbalrog if (s->dma->current_frame == -1) { 246c3d2689dSbalrog s->frame_done = 0; 247c3d2689dSbalrog s->palette_done = 0; 248c3d2689dSbalrog s->dma->current_frame = 0; 249c3d2689dSbalrog } 250c3d2689dSbalrog 251c3d2689dSbalrog if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, 252c3d2689dSbalrog s->dma->src_f1_top) || 253c3d2689dSbalrog !s->dma->mpu->port[ 254c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 255c3d2689dSbalrog s->dma->src_f1_bottom) || 256c3d2689dSbalrog (s->dma->dual && 257c3d2689dSbalrog (!s->dma->mpu->port[ 258c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 259c3d2689dSbalrog s->dma->src_f2_top) || 260c3d2689dSbalrog !s->dma->mpu->port[ 261c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu, 262c3d2689dSbalrog s->dma->src_f2_bottom)))) { 263c3d2689dSbalrog s->dma->condition |= 1 << 2; 264c3d2689dSbalrog if (s->dma->interrupts & (1 << 1)) 265c3d2689dSbalrog qemu_irq_raise(s->dma->irq); 266c3d2689dSbalrog s->enable = 0; 267c3d2689dSbalrog return; 268c3d2689dSbalrog } 269c3d2689dSbalrog 270714fa308Spbrook s->dma->phys_framebuffer[0] = s->dma->src_f1_top; 271714fa308Spbrook s->dma->phys_framebuffer[1] = s->dma->src_f2_top; 272c3d2689dSbalrog 273c3d2689dSbalrog if (s->plm != 2 && !s->palette_done) { 274714fa308Spbrook cpu_physical_memory_read( 275714fa308Spbrook s->dma->phys_framebuffer[s->dma->current_frame], 276714fa308Spbrook (void *)s->palette, 0x200); 277c3d2689dSbalrog s->palette_done = 1; 278c3d2689dSbalrog omap_lcd_interrupts(s); 279c3d2689dSbalrog } 280c3d2689dSbalrog } 281c3d2689dSbalrog 282a8170e5eSAvi Kivity static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, 28330af1ec7SBenoît Canet unsigned size) 284c3d2689dSbalrog { 285c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 286c3d2689dSbalrog 2878da3ff18Spbrook switch (addr) { 288c3d2689dSbalrog case 0x00: /* LCD_CONTROL */ 289c3d2689dSbalrog return (s->tft << 23) | (s->plm << 20) | 290c3d2689dSbalrog (s->tft << 7) | (s->interrupts << 3) | 291c3d2689dSbalrog (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; 292c3d2689dSbalrog 293c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */ 294c3d2689dSbalrog return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; 295c3d2689dSbalrog 296c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */ 297c3d2689dSbalrog return (s->timing[1] << 10) | (s->height - 1); 298c3d2689dSbalrog 299c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */ 300c3d2689dSbalrog return s->timing[2] | 0xfc000000; 301c3d2689dSbalrog 302c3d2689dSbalrog case 0x10: /* LCD_STATUS */ 303c3d2689dSbalrog return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; 304c3d2689dSbalrog 305c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */ 306c3d2689dSbalrog return s->subpanel; 307c3d2689dSbalrog 308c3d2689dSbalrog default: 309c3d2689dSbalrog break; 310c3d2689dSbalrog } 311c3d2689dSbalrog OMAP_BAD_REG(addr); 312c3d2689dSbalrog return 0; 313c3d2689dSbalrog } 314c3d2689dSbalrog 315a8170e5eSAvi Kivity static void omap_lcdc_write(void *opaque, hwaddr addr, 31630af1ec7SBenoît Canet uint64_t value, unsigned size) 317c3d2689dSbalrog { 318c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 319c3d2689dSbalrog 3208da3ff18Spbrook switch (addr) { 321c3d2689dSbalrog case 0x00: /* LCD_CONTROL */ 322c3d2689dSbalrog s->plm = (value >> 20) & 3; 323c3d2689dSbalrog s->tft = (value >> 7) & 1; 324c3d2689dSbalrog s->interrupts = (value >> 3) & 3; 325c3d2689dSbalrog s->mono = (value >> 1) & 1; 326c3d2689dSbalrog s->ctrl = value & 0x01cff300; 327c3d2689dSbalrog if (s->enable != (value & 1)) { 328c3d2689dSbalrog s->enable = value & 1; 329c3d2689dSbalrog omap_lcd_update(s); 330c3d2689dSbalrog } 331c3d2689dSbalrog break; 332c3d2689dSbalrog 333c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */ 334c3d2689dSbalrog s->timing[0] = value >> 10; 335c3d2689dSbalrog s->width = (value & 0x3ff) + 1; 336c3d2689dSbalrog break; 337c3d2689dSbalrog 338c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */ 339c3d2689dSbalrog s->timing[1] = value >> 10; 340c3d2689dSbalrog s->height = (value & 0x3ff) + 1; 341c3d2689dSbalrog break; 342c3d2689dSbalrog 343c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */ 344c3d2689dSbalrog s->timing[2] = value; 345c3d2689dSbalrog break; 346c3d2689dSbalrog 347c3d2689dSbalrog case 0x10: /* LCD_STATUS */ 348c3d2689dSbalrog break; 349c3d2689dSbalrog 350c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */ 351c3d2689dSbalrog s->subpanel = value & 0xa1ffffff; 352c3d2689dSbalrog break; 353c3d2689dSbalrog 354c3d2689dSbalrog default: 355c3d2689dSbalrog OMAP_BAD_REG(addr); 356c3d2689dSbalrog } 357c3d2689dSbalrog } 358c3d2689dSbalrog 35930af1ec7SBenoît Canet static const MemoryRegionOps omap_lcdc_ops = { 36030af1ec7SBenoît Canet .read = omap_lcdc_read, 36130af1ec7SBenoît Canet .write = omap_lcdc_write, 36230af1ec7SBenoît Canet .endianness = DEVICE_NATIVE_ENDIAN, 363c3d2689dSbalrog }; 364c3d2689dSbalrog 365c3d2689dSbalrog void omap_lcdc_reset(struct omap_lcd_panel_s *s) 366c3d2689dSbalrog { 367c3d2689dSbalrog s->dma->current_frame = -1; 368c3d2689dSbalrog s->plm = 0; 369c3d2689dSbalrog s->tft = 0; 370c3d2689dSbalrog s->mono = 0; 371c3d2689dSbalrog s->enable = 0; 372c3d2689dSbalrog s->width = 0; 373c3d2689dSbalrog s->height = 0; 374c3d2689dSbalrog s->interrupts = 0; 375c3d2689dSbalrog s->timing[0] = 0; 376c3d2689dSbalrog s->timing[1] = 0; 377c3d2689dSbalrog s->timing[2] = 0; 378c3d2689dSbalrog s->subpanel = 0; 379c3d2689dSbalrog s->palette_done = 0; 380c3d2689dSbalrog s->frame_done = 0; 381c3d2689dSbalrog s->sync_error = 0; 382c3d2689dSbalrog s->invalidate = 1; 383c3d2689dSbalrog s->subpanel = 0; 384c3d2689dSbalrog s->ctrl = 0; 385c3d2689dSbalrog } 386c3d2689dSbalrog 387*380cd056SGerd Hoffmann static const GraphicHwOps omap_ops = { 388*380cd056SGerd Hoffmann .invalidate = omap_invalidate_display, 389*380cd056SGerd Hoffmann .gfx_update = omap_update_display, 390*380cd056SGerd Hoffmann }; 391*380cd056SGerd Hoffmann 39230af1ec7SBenoît Canet struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, 393a8170e5eSAvi Kivity hwaddr base, 39430af1ec7SBenoît Canet qemu_irq irq, 39530af1ec7SBenoît Canet struct omap_dma_lcd_channel_s *dma, 39630af1ec7SBenoît Canet omap_clk clk) 397c3d2689dSbalrog { 398c3d2689dSbalrog struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) 3997267c094SAnthony Liguori g_malloc0(sizeof(struct omap_lcd_panel_s)); 400c3d2689dSbalrog 401c3d2689dSbalrog s->irq = irq; 402c3d2689dSbalrog s->dma = dma; 40375c9d6c2SAvi Kivity s->sysmem = sysmem; 404c3d2689dSbalrog omap_lcdc_reset(s); 405c3d2689dSbalrog 40630af1ec7SBenoît Canet memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100); 40730af1ec7SBenoît Canet memory_region_add_subregion(sysmem, base, &s->iomem); 408c3d2689dSbalrog 409*380cd056SGerd Hoffmann s->con = graphic_console_init(&omap_ops, s); 410c3d2689dSbalrog 411c3d2689dSbalrog return s; 412c3d2689dSbalrog } 413