1/* 2 * Copyright (c) 2010-2013, 2015 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Chris Emmons 38 */ 39 40#include "dev/arm/hdlcd.hh" 41 42#include "base/vnc/vncinput.hh" 43#include "base/output.hh" 44#include "base/trace.hh" 45#include "debug/HDLcd.hh" 46#include "debug/Uart.hh" 47#include "dev/arm/amba_device.hh" 48#include "dev/arm/base_gic.hh" 49#include "mem/packet.hh" 50#include "mem/packet_access.hh" 51#include "sim/system.hh" 52 53using std::vector; 54 55 56// initialize hdlcd registers 57HDLcd::HDLcd(const Params *p) 58 : AmbaDmaDevice(p), version(VERSION_RESETV), 59 int_rawstat(0), int_clear(0), int_mask(0), int_status(0), 60 fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0), 61 bus_options(BUS_OPTIONS_RESETV), 62 v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0), 63 h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0), 64 polarities(0), command(0), pixel_format(0), 65 red_select(0), green_select(0), blue_select(0), 66 pixelClock(p->pixel_clock), 67 fb(0, 0), vnc(p->vnc), bmp(&fb), pic(NULL), 68 frameReadStartTime(0), 69 dmaStartAddr(0), dmaCurAddr(0), dmaMaxAddr(0), dmaPendingNum(0), 70 frameUnderrun(false), pixelBufferSize(0), 71 pixelIndex(0), doUpdateParams(false), frameUnderway(false), 72 dmaBytesInFlight(0), 73 startFrameEvent(this), endFrameEvent(this), renderPixelEvent(this), 74 fillPixelBufferEvent(this), intEvent(this), 75 dmaDoneEventAll(MAX_OUTSTANDING_DMA_REQ_CAPACITY, this), 76 dmaDoneEventFree(MAX_OUTSTANDING_DMA_REQ_CAPACITY), 77 enableCapture(p->enable_capture), 78 workaround_swap_rb(p->workaround_swap_rb) 79{ 80 pioSize = 0xFFFF; 81 82 for (int i = 0; i < MAX_OUTSTANDING_DMA_REQ_CAPACITY; ++i) 83 dmaDoneEventFree[i] = &dmaDoneEventAll[i]; 84 85 if (vnc) 86 vnc->setFrameBuffer(&fb); 87} 88 89HDLcd::~HDLcd() 90{ 91} 92 93// read registers and frame buffer 94Tick 95HDLcd::read(PacketPtr pkt) 96{ 97 uint32_t data = 0; 98 const Addr daddr = pkt->getAddr() - pioAddr; 99 100 DPRINTF(HDLcd, "read register BASE+0x%04x size=%d\n", daddr, 101 pkt->getSize()); 102 103 assert(pkt->getAddr() >= pioAddr && 104 pkt->getAddr() < pioAddr + pioSize && 105 pkt->getSize() == 4); 106 107 switch (daddr) { 108 case Version: 109 data = version; 110 break; 111 case Int_RawStat: 112 data = int_rawstat; 113 break; 114 case Int_Clear: 115 panic("HDLCD INT_CLEAR register is Write-Only\n"); 116 break; 117 case Int_Mask: 118 data = int_mask; 119 break; 120 case Int_Status: 121 data = int_status; 122 break; 123 case Fb_Base: 124 data = fb_base; 125 break; 126 case Fb_Line_Length: 127 data = fb_line_length; 128 break; 129 case Fb_Line_Count: 130 data = fb_line_count; 131 break; 132 case Fb_Line_Pitch: 133 data = fb_line_pitch; 134 break; 135 case Bus_Options: 136 data = bus_options; 137 break; 138 case V_Sync: 139 data = v_sync; 140 break; 141 case V_Back_Porch: 142 data = v_back_porch; 143 break; 144 case V_Data: 145 data = v_data; 146 break; 147 case V_Front_Porch: 148 data = v_front_porch; 149 break; 150 case H_Sync: 151 data = h_sync; 152 break; 153 case H_Back_Porch: 154 data = h_back_porch; 155 break; 156 case H_Data: 157 data = h_data; 158 break; 159 case H_Front_Porch: 160 data = h_front_porch; 161 break; 162 case Polarities: 163 data = polarities; 164 break; 165 case Command: 166 data = command; 167 break; 168 case Pixel_Format: 169 data = pixel_format; 170 break; 171 case Red_Select: 172 data = red_select; 173 break; 174 case Green_Select: 175 data = green_select; 176 break; 177 case Blue_Select: 178 data = blue_select; 179 break; 180 default: 181 panic("Tried to read HDLCD register that doesn't exist\n", daddr); 182 break; 183 } 184 185 pkt->set<uint32_t>(data); 186 pkt->makeAtomicResponse(); 187 return pioDelay; 188} 189 190// write registers and frame buffer 191Tick 192HDLcd::write(PacketPtr pkt) 193{ 194 assert(pkt->getAddr() >= pioAddr && 195 pkt->getAddr() < pioAddr + pioSize && 196 pkt->getSize() == 4); 197 198 const uint32_t data = pkt->get<uint32_t>(); 199 const Addr daddr = pkt->getAddr() - pioAddr; 200 201 DPRINTF(HDLcd, "write register BASE+%0x04x <= 0x%08x\n", daddr, 202 pkt->get<uint32_t>()); 203 204 switch (daddr) { 205 case Version: 206 panic("HDLCD VERSION register is read-Only\n"); 207 break; 208 case Int_RawStat: 209 int_rawstat = data; 210 break; 211 case Int_Clear: 212 int_clear = data; 213 break; 214 case Int_Mask: 215 int_mask = data; 216 break; 217 case Int_Status: 218 panic("HDLCD INT_STATUS register is read-Only\n"); 219 break; 220 case Fb_Base: 221 fb_base = data; 222 DPRINTF(HDLcd, "HDLCD Frame Buffer located at addr 0x%08x\n", fb_base); 223 break; 224 case Fb_Line_Length: 225 fb_line_length = data; 226 DPRINTF(HDLcd, "HDLCD res = %d x %d\n", width(), height()); 227 break; 228 case Fb_Line_Count: 229 fb_line_count = data; 230 DPRINTF(HDLcd, "HDLCD res = %d x %d\n", width(), height()); 231 break; 232 case Fb_Line_Pitch: 233 fb_line_pitch = data; 234 break; 235 case Bus_Options: { 236 BusOptsReg old_bus_options; 237 old_bus_options = bus_options; 238 bus_options = data; 239 if (bus_options.max_outstanding != old_bus_options.max_outstanding) 240 DPRINTF(HDLcd, 241 "Changing HDLcd outstanding dma transactions from %d to %d\n", 242 old_bus_options.max_outstanding, bus_options.max_outstanding); 243 if (bus_options.burst_len != old_bus_options.burst_len) 244 DPRINTF(HDLcd, 245 "Changing HDLcd dma burst length from %d bytes to %d bytes\n", 246 old_bus_options.burst_len, bus_options.burst_len); } 247 break; 248 case V_Sync: 249 v_sync = data; 250 break; 251 case V_Back_Porch: 252 v_back_porch = data; 253 break; 254 case V_Data: 255 v_data = data; 256 break; 257 case V_Front_Porch: 258 v_front_porch = data; 259 break; 260 case H_Sync: 261 h_sync = data; 262 break; 263 case H_Back_Porch: 264 h_back_porch = data; 265 break; 266 case H_Data: 267 h_data = data; 268 break; 269 case H_Front_Porch: 270 h_front_porch = data; 271 break; 272 case Polarities: 273 polarities = data; 274 break; 275 case Command: { 276 CommandReg new_command; 277 new_command = data; 278 if (new_command.enable != command.enable) { 279 DPRINTF(HDLcd, "HDLCD switched %s\n", 280 new_command.enable==0 ? "off" : "on"); 281 if (new_command.enable) { 282 doUpdateParams = true; 283 if (!frameUnderway) { 284 schedule(startFrameEvent, clockEdge()); 285 } 286 } 287 } 288 command = new_command; } 289 break; 290 case Pixel_Format: 291 pixel_format = data; 292 DPRINTF(HDLcd, "HDLCD res = %d x %d\n", width(), height()); 293 DPRINTF(HDLcd, "HDLCD bytes per pixel = %d\n", bytesPerPixel()); 294 DPRINTF(HDLcd, "HDLCD endianness = %s\n", 295 pixel_format.big_endian ? "big" : "little"); 296 break; 297 case Red_Select: 298 red_select = data; 299 break; 300 case Green_Select: 301 green_select = data; 302 break; 303 case Blue_Select: 304 blue_select = data; 305 break; 306 default: 307 panic("Tried to write HDLCD register that doesn't exist\n", daddr); 308 break; 309 } 310 311 pkt->makeAtomicResponse(); 312 return pioDelay; 313} 314 315void 316HDLcd::updateVideoParams(bool unserializing = false) 317{ 318 const uint16_t bpp M5_VAR_USED = bytesPerPixel() << 3; 319 320 // Workaround configuration bugs where multiple display 321 // controllers are attached to the same VNC server by reattaching 322 // enabled devices. This isn't ideal, but works as long as only 323 // one display controller is active at a time. 324 if (command.enable && vnc) 325 vnc->setFrameBuffer(&fb); 326 327 // updating these parameters while LCD is enabled is not supported 328 if (frameUnderway && !unserializing) 329 panic("Attempting to change some HDLCD parameters while the controller" 330 " is active is not allowed"); 331 332 // resize the virtualDisplayBuffer unless we are unserializing - it may 333 // have changed size 334 // there must be no outstanding DMA transactions for this to work 335 if (!unserializing) { 336 assert(dmaPendingNum == 0); 337 338 virtualDisplayBuffer.resize(bytesPerPixel() * area()); 339 fb.resize(width(), height()); 340 fb.clear(); 341 342 std::fill(virtualDisplayBuffer.begin(), virtualDisplayBuffer.end(), 343 0); 344 } 345 346 DPRINTF(HDLcd, "bpp = %d\n", bpp); 347 DPRINTF(HDLcd, "display size = %d x %d\n", width(), height()); 348#if TRACING_ON 349 const size_t totalLinesPerFrame = v_back_porch.val + 1 + 350 v_data.val + 1 + 351 v_front_porch.val + 1 + 352 v_sync.val + 1; 353 const double fps = (double)SimClock::Frequency / 354 (double)(PClksPerLine() * totalLinesPerFrame * pixelClock); 355#endif 356 DPRINTF(HDLcd, "simulated refresh rate ~ %.1ffps generating ~ %.1fMB/s " 357 "traffic ([%.1fMHz, T=%d sim clocks] pclk, %d bpp => %.1fMB/s peak requirement)\n", 358 fps, 359 fps * virtualDisplayBuffer.size() / 1024 / 1024, 360 (double)SimClock::Frequency / pixelClock / 1000000.0, 361 pixelClock, 362 bpp, 363 (double)(SimClock::Frequency / pixelClock * (bpp / 8)) / 1024 / 1024); 364} 365 366void 367HDLcd::startFrame() 368{ 369 // 0. Check that we are in the appropriate state 370 assert(!frameUnderway); 371 if (!command.enable) 372 return; 373 DPRINTF(HDLcd, "Frame read started\n"); 374 if (doUpdateParams) { 375 updateVideoParams(); 376 doUpdateParams = false; 377 } 378 frameUnderway = true; 379 assert(!virtualDisplayBuffer.empty()); 380 assert(pixelBufferSize == 0); 381 assert(dmaBytesInFlight == 0); 382 assert(dmaPendingNum == 0); 383 assert(dmaDoneEventFree.size() == dmaDoneEventAll.size()); 384 assert(!renderPixelEvent.scheduled()); 385 // currently only support positive line pitches equal to the line length 386 assert(width() * bytesPerPixel() == fb_line_pitch); 387 388 // 1. Start DMA'ing the frame; subsequent transactions created as we go 389 dmaCurAddr = dmaStartAddr = fb_base; 390 dmaMaxAddr = static_cast<Addr>(width() * height() * bytesPerPixel()) + 391 dmaCurAddr; 392 frameReadStartTime = curTick(); 393 pixelIndex = 0; 394 frameUnderrun = false; 395 fillPixelBuffer(); 396 397 // 2. Schedule first pixelclock read; subsequent reads generated as we go 398 Tick firstPixelReadTick = curTick() + pixelClock * ( 399 PClksPerLine() * (v_sync.val + 1 + 400 v_back_porch.val + 1) + 401 h_sync.val + 1 + 402 h_back_porch.val + 1); 403 schedule(renderPixelEvent, firstPixelReadTick); 404} 405 406void 407HDLcd::fillPixelBuffer() 408{ 409 // - am I under the LCD dma transaction total? 410 // - do I have more data to transfer? 411 // - have I not yet underrun for this frame? 412 // - is there room to put the data in the pixel buffer including any 413 // outstanding dma transfers in flight? 414 while ((dmaPendingNum < maxOutstandingDma()) && 415 (dmaMaxAddr > dmaCurAddr) && 416 !frameUnderrun && 417 bytesFreeInPixelBuffer() > dmaBurstLength() * AXI_PORT_WIDTH) { 418 // try largest transaction size allowed first but switch to smaller 419 // sizes for trailing bytes 420 size_t transaction_size = dmaBurstLength() * AXI_PORT_WIDTH; 421 while (transaction_size > (dmaMaxAddr - dmaCurAddr)) 422 transaction_size >>= 1; 423 assert(transaction_size > 0); 424 425 // concurrent dma reads need different dma done events 426 // due to assertion in scheduling state 427 ++dmaPendingNum; 428 429 assert(!dmaDoneEventFree.empty()); 430 DmaDoneEvent *event(dmaDoneEventFree.back()); 431 dmaDoneEventFree.pop_back(); 432 assert(event); 433 assert(!event->scheduled()); 434 435 // We use a uncachable request here because the requests from the CPU 436 // will be uncacheable as well. If we have uncacheable and cacheable 437 // requests in the memory system for the same address it won't be 438 // pleased 439 uint8_t *const dma_dst( 440 virtualDisplayBuffer.data() + dmaCurAddr - dmaStartAddr); 441 event->setTransactionSize(transaction_size); 442 dmaPort.dmaAction(MemCmd::ReadReq, dmaCurAddr, transaction_size, event, 443 dma_dst, 0, Request::UNCACHEABLE); 444 dmaCurAddr += transaction_size; 445 dmaBytesInFlight += transaction_size; 446 } 447} 448 449void 450HDLcd::renderPixel() 451{ 452 // try to handle multiple pixels at a time; doing so reduces the accuracy 453 // of the underrun detection but lowers simulation overhead 454 const size_t count = 32; 455 assert(width() % count == 0); // not set up to handle trailing pixels 456 457 // have we underrun on this frame anytime before? 458 if (frameUnderrun) { 459 // the LCD controller gives up on a frame if an underrun occurs and 460 // resumes regular operation on the next frame 461 pixelBufferSize = 0; 462 } else { 463 // did we underrun on this set of pixels? 464 if (pixelBufferSize < bytesPerPixel() * count) { 465 warn("HDLcd controller buffer underrun\n"); 466 frameUnderrun = true; 467 int_rawstat.underrun = 1; 468 if (!intEvent.scheduled()) 469 schedule(intEvent, clockEdge()); 470 } else { 471 // emulate the pixel read from the internal buffer 472 pixelBufferSize -= bytesPerPixel() * count; 473 } 474 } 475 476 // the DMA may have previously stalled due to the buffer being full; 477 // give it a kick; it knows not to fill if at end of frame, underrun, etc 478 if (!fillPixelBufferEvent.scheduled()) 479 schedule(fillPixelBufferEvent, clockEdge()); 480 481 // schedule the next pixel read according to where it is in the frame 482 pixelIndex += count; 483 assert(pixelIndex <= width() * height()); 484 size_t x = pixelIndex % width(); 485 Tick nextEventTick = curTick(); 486 if (x == 0) { 487 // start of new line 488 nextEventTick += pixelClock * ((h_front_porch.val + 1) + 489 (h_back_porch.val + 1) + 490 (h_sync.val + 1)); 491 if (pixelIndex == width() * height()) { 492 // end of frame 493 nextEventTick += PClksPerLine() * (v_front_porch.val + 1) * 494 pixelClock; 495 schedule(endFrameEvent, nextEventTick); 496 return; 497 } 498 } else { 499 nextEventTick += pixelClock * count; 500 } 501 502 schedule(renderPixelEvent, nextEventTick); 503} 504 505PixelConverter 506HDLcd::pixelConverter() const 507{ 508 ByteOrder byte_order( 509 pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder); 510 511 /* Some Linux kernels have a broken driver that swaps the red and 512 * blue color select registers. */ 513 if (!workaround_swap_rb) { 514 return PixelConverter( 515 bytesPerPixel(), 516 red_select.offset, green_select.offset, blue_select.offset, 517 red_select.size, green_select.size, blue_select.size, 518 byte_order); 519 } else { 520 return PixelConverter( 521 bytesPerPixel(), 522 blue_select.offset, green_select.offset, red_select.offset, 523 blue_select.size, green_select.size, red_select.size, 524 byte_order); 525 } 526} 527 528void 529HDLcd::endFrame() { 530 assert(pixelBufferSize == 0); 531 assert(dmaPendingNum == 0); 532 assert(dmaBytesInFlight == 0); 533 assert(dmaDoneEventFree.size() == dmaDoneEventAll.size()); 534 535 fb.copyIn(virtualDisplayBuffer, pixelConverter()); 536 537 if (vnc) 538 vnc->setDirty(); 539 540 if (enableCapture) { 541 if (!pic) 542 pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()), true); 543 544 assert(pic); 545 pic->seekp(0); 546 bmp.write(*pic); 547 } 548 549 // start the next frame 550 frameUnderway = false; 551 startFrame(); 552} 553 554void 555HDLcd::dmaDone(DmaDoneEvent *event) 556{ 557 const size_t transactionLength = event->getTransactionSize(); 558 assert(pixelBufferSize + transactionLength < PIXEL_BUFFER_CAPACITY); 559 assert(dmaCurAddr <= dmaMaxAddr); 560 561 dmaDoneEventFree.push_back(event); 562 --dmaPendingNum; 563 assert(MAX_OUTSTANDING_DMA_REQ_CAPACITY - dmaDoneEventFree.size() == 564 dmaPendingNum); 565 566 // add the data to the pixel buffer 567 dmaBytesInFlight -= transactionLength; 568 pixelBufferSize += transactionLength; 569 570 // schedule another dma transaction if: 571 // - we're not done reading the frame 572 // - there is sufficient room in the pixel buffer for another transaction 573 // - another fillPixelBufferEvent is not already scheduled 574 const size_t targetTransSize = dmaBurstLength() * AXI_PORT_WIDTH; 575 if ((dmaCurAddr < dmaMaxAddr) && 576 (bytesFreeInPixelBuffer() + targetTransSize < PIXEL_BUFFER_CAPACITY) && 577 !fillPixelBufferEvent.scheduled()) { 578 schedule(fillPixelBufferEvent, clockEdge()); 579 } 580} 581 582void
|