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 #include "hw/hw.h" 20 #include "ui/console.h" 21 #include "hw/arm/omap.h" 22 #include "framebuffer.h" 23 #include "ui/pixel_ops.h" 24 25 struct omap_lcd_panel_s { 26 MemoryRegion *sysmem; 27 MemoryRegion iomem; 28 MemoryRegionSection fbsection; 29 qemu_irq irq; 30 QemuConsole *con; 31 32 int plm; 33 int tft; 34 int mono; 35 int enable; 36 int width; 37 int height; 38 int interrupts; 39 uint32_t timing[3]; 40 uint32_t subpanel; 41 uint32_t ctrl; 42 43 struct omap_dma_lcd_channel_s *dma; 44 uint16_t palette[256]; 45 int palette_done; 46 int frame_done; 47 int invalidate; 48 int sync_error; 49 }; 50 51 static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) 52 { 53 if (s->frame_done && (s->interrupts & 1)) { 54 qemu_irq_raise(s->irq); 55 return; 56 } 57 58 if (s->palette_done && (s->interrupts & 2)) { 59 qemu_irq_raise(s->irq); 60 return; 61 } 62 63 if (s->sync_error) { 64 qemu_irq_raise(s->irq); 65 return; 66 } 67 68 qemu_irq_lower(s->irq); 69 } 70 71 #define draw_line_func drawfn 72 73 #define DEPTH 8 74 #include "omap_lcd_template.h" 75 #define DEPTH 15 76 #include "omap_lcd_template.h" 77 #define DEPTH 16 78 #include "omap_lcd_template.h" 79 #define DEPTH 32 80 #include "omap_lcd_template.h" 81 82 static draw_line_func draw_line_table2[33] = { 83 [0 ... 32] = NULL, 84 [8] = draw_line2_8, 85 [15] = draw_line2_15, 86 [16] = draw_line2_16, 87 [32] = draw_line2_32, 88 }, draw_line_table4[33] = { 89 [0 ... 32] = NULL, 90 [8] = draw_line4_8, 91 [15] = draw_line4_15, 92 [16] = draw_line4_16, 93 [32] = draw_line4_32, 94 }, draw_line_table8[33] = { 95 [0 ... 32] = NULL, 96 [8] = draw_line8_8, 97 [15] = draw_line8_15, 98 [16] = draw_line8_16, 99 [32] = draw_line8_32, 100 }, draw_line_table12[33] = { 101 [0 ... 32] = NULL, 102 [8] = draw_line12_8, 103 [15] = draw_line12_15, 104 [16] = draw_line12_16, 105 [32] = draw_line12_32, 106 }, draw_line_table16[33] = { 107 [0 ... 32] = NULL, 108 [8] = draw_line16_8, 109 [15] = draw_line16_15, 110 [16] = draw_line16_16, 111 [32] = draw_line16_32, 112 }; 113 114 static void omap_update_display(void *opaque) 115 { 116 struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; 117 DisplaySurface *surface = qemu_console_surface(omap_lcd->con); 118 draw_line_func draw_line; 119 int size, height, first, last; 120 int width, linesize, step, bpp, frame_offset; 121 hwaddr frame_base; 122 123 if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || 124 !surface_bits_per_pixel(surface)) { 125 return; 126 } 127 128 frame_offset = 0; 129 if (omap_lcd->plm != 2) { 130 cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[ 131 omap_lcd->dma->current_frame], 132 (void *)omap_lcd->palette, 0x200); 133 switch (omap_lcd->palette[0] >> 12 & 7) { 134 case 3 ... 7: 135 frame_offset += 0x200; 136 break; 137 default: 138 frame_offset += 0x20; 139 } 140 } 141 142 /* Colour depth */ 143 switch ((omap_lcd->palette[0] >> 12) & 7) { 144 case 1: 145 draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; 146 bpp = 2; 147 break; 148 149 case 2: 150 draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; 151 bpp = 4; 152 break; 153 154 case 3: 155 draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; 156 bpp = 8; 157 break; 158 159 case 4 ... 7: 160 if (!omap_lcd->tft) 161 draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; 162 else 163 draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; 164 bpp = 16; 165 break; 166 167 default: 168 /* Unsupported at the moment. */ 169 return; 170 } 171 172 /* Resolution */ 173 width = omap_lcd->width; 174 if (width != surface_width(surface) || 175 omap_lcd->height != surface_height(surface)) { 176 qemu_console_resize(omap_lcd->con, 177 omap_lcd->width, omap_lcd->height); 178 surface = qemu_console_surface(omap_lcd->con); 179 omap_lcd->invalidate = 1; 180 } 181 182 if (omap_lcd->dma->current_frame == 0) 183 size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top; 184 else 185 size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top; 186 187 if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) { 188 omap_lcd->sync_error = 1; 189 omap_lcd_interrupts(omap_lcd); 190 omap_lcd->enable = 0; 191 return; 192 } 193 194 /* Content */ 195 frame_base = omap_lcd->dma->phys_framebuffer[ 196 omap_lcd->dma->current_frame] + frame_offset; 197 omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame; 198 if (omap_lcd->dma->interrupts & 1) 199 qemu_irq_raise(omap_lcd->dma->irq); 200 if (omap_lcd->dma->dual) 201 omap_lcd->dma->current_frame ^= 1; 202 203 if (!surface_bits_per_pixel(surface)) { 204 return; 205 } 206 207 first = 0; 208 height = omap_lcd->height; 209 if (omap_lcd->subpanel & (1 << 31)) { 210 if (omap_lcd->subpanel & (1 << 29)) 211 first = (omap_lcd->subpanel >> 16) & 0x3ff; 212 else 213 height = (omap_lcd->subpanel >> 16) & 0x3ff; 214 /* TODO: fill the rest of the panel with DPD */ 215 } 216 217 step = width * bpp >> 3; 218 linesize = surface_stride(surface); 219 if (omap_lcd->invalidate) { 220 framebuffer_update_memory_section(&omap_lcd->fbsection, 221 omap_lcd->sysmem, frame_base, 222 height, step); 223 } 224 225 framebuffer_update_display(surface, &omap_lcd->fbsection, 226 width, height, 227 step, linesize, 0, 228 omap_lcd->invalidate, 229 draw_line, omap_lcd->palette, 230 &first, &last); 231 232 if (first >= 0) { 233 dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); 234 } 235 omap_lcd->invalidate = 0; 236 } 237 238 static void omap_invalidate_display(void *opaque) { 239 struct omap_lcd_panel_s *omap_lcd = opaque; 240 omap_lcd->invalidate = 1; 241 } 242 243 static void omap_lcd_update(struct omap_lcd_panel_s *s) { 244 if (!s->enable) { 245 s->dma->current_frame = -1; 246 s->sync_error = 0; 247 if (s->plm != 1) 248 s->frame_done = 1; 249 omap_lcd_interrupts(s); 250 return; 251 } 252 253 if (s->dma->current_frame == -1) { 254 s->frame_done = 0; 255 s->palette_done = 0; 256 s->dma->current_frame = 0; 257 } 258 259 if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, 260 s->dma->src_f1_top) || 261 !s->dma->mpu->port[ 262 s->dma->src].addr_valid(s->dma->mpu, 263 s->dma->src_f1_bottom) || 264 (s->dma->dual && 265 (!s->dma->mpu->port[ 266 s->dma->src].addr_valid(s->dma->mpu, 267 s->dma->src_f2_top) || 268 !s->dma->mpu->port[ 269 s->dma->src].addr_valid(s->dma->mpu, 270 s->dma->src_f2_bottom)))) { 271 s->dma->condition |= 1 << 2; 272 if (s->dma->interrupts & (1 << 1)) 273 qemu_irq_raise(s->dma->irq); 274 s->enable = 0; 275 return; 276 } 277 278 s->dma->phys_framebuffer[0] = s->dma->src_f1_top; 279 s->dma->phys_framebuffer[1] = s->dma->src_f2_top; 280 281 if (s->plm != 2 && !s->palette_done) { 282 cpu_physical_memory_read( 283 s->dma->phys_framebuffer[s->dma->current_frame], 284 (void *)s->palette, 0x200); 285 s->palette_done = 1; 286 omap_lcd_interrupts(s); 287 } 288 } 289 290 static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, 291 unsigned size) 292 { 293 struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 294 295 switch (addr) { 296 case 0x00: /* LCD_CONTROL */ 297 return (s->tft << 23) | (s->plm << 20) | 298 (s->tft << 7) | (s->interrupts << 3) | 299 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; 300 301 case 0x04: /* LCD_TIMING0 */ 302 return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; 303 304 case 0x08: /* LCD_TIMING1 */ 305 return (s->timing[1] << 10) | (s->height - 1); 306 307 case 0x0c: /* LCD_TIMING2 */ 308 return s->timing[2] | 0xfc000000; 309 310 case 0x10: /* LCD_STATUS */ 311 return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; 312 313 case 0x14: /* LCD_SUBPANEL */ 314 return s->subpanel; 315 316 default: 317 break; 318 } 319 OMAP_BAD_REG(addr); 320 return 0; 321 } 322 323 static void omap_lcdc_write(void *opaque, hwaddr addr, 324 uint64_t value, unsigned size) 325 { 326 struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; 327 328 switch (addr) { 329 case 0x00: /* LCD_CONTROL */ 330 s->plm = (value >> 20) & 3; 331 s->tft = (value >> 7) & 1; 332 s->interrupts = (value >> 3) & 3; 333 s->mono = (value >> 1) & 1; 334 s->ctrl = value & 0x01cff300; 335 if (s->enable != (value & 1)) { 336 s->enable = value & 1; 337 omap_lcd_update(s); 338 } 339 break; 340 341 case 0x04: /* LCD_TIMING0 */ 342 s->timing[0] = value >> 10; 343 s->width = (value & 0x3ff) + 1; 344 break; 345 346 case 0x08: /* LCD_TIMING1 */ 347 s->timing[1] = value >> 10; 348 s->height = (value & 0x3ff) + 1; 349 break; 350 351 case 0x0c: /* LCD_TIMING2 */ 352 s->timing[2] = value; 353 break; 354 355 case 0x10: /* LCD_STATUS */ 356 break; 357 358 case 0x14: /* LCD_SUBPANEL */ 359 s->subpanel = value & 0xa1ffffff; 360 break; 361 362 default: 363 OMAP_BAD_REG(addr); 364 } 365 } 366 367 static const MemoryRegionOps omap_lcdc_ops = { 368 .read = omap_lcdc_read, 369 .write = omap_lcdc_write, 370 .endianness = DEVICE_NATIVE_ENDIAN, 371 }; 372 373 void omap_lcdc_reset(struct omap_lcd_panel_s *s) 374 { 375 s->dma->current_frame = -1; 376 s->plm = 0; 377 s->tft = 0; 378 s->mono = 0; 379 s->enable = 0; 380 s->width = 0; 381 s->height = 0; 382 s->interrupts = 0; 383 s->timing[0] = 0; 384 s->timing[1] = 0; 385 s->timing[2] = 0; 386 s->subpanel = 0; 387 s->palette_done = 0; 388 s->frame_done = 0; 389 s->sync_error = 0; 390 s->invalidate = 1; 391 s->subpanel = 0; 392 s->ctrl = 0; 393 } 394 395 static const GraphicHwOps omap_ops = { 396 .invalidate = omap_invalidate_display, 397 .gfx_update = omap_update_display, 398 }; 399 400 struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, 401 hwaddr base, 402 qemu_irq irq, 403 struct omap_dma_lcd_channel_s *dma, 404 omap_clk clk) 405 { 406 struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1); 407 408 s->irq = irq; 409 s->dma = dma; 410 s->sysmem = sysmem; 411 omap_lcdc_reset(s); 412 413 memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100); 414 memory_region_add_subregion(sysmem, base, &s->iomem); 415 416 s->con = graphic_console_init(NULL, 0, &omap_ops, s); 417 418 return s; 419 } 420