hdlcd.cc revision 11898
1/* 2 * Copyright (c) 2010-2013, 2015, 2017 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 * Andreas Sandberg 39 */ 40 41#include "dev/arm/hdlcd.hh" 42 43#include "base/output.hh" 44#include "base/trace.hh" 45#include "base/vnc/vncinput.hh" 46#include "debug/Checkpoint.hh" 47#include "debug/HDLcd.hh" 48#include "dev/arm/amba_device.hh" 49#include "dev/arm/base_gic.hh" 50#include "mem/packet.hh" 51#include "mem/packet_access.hh" 52#include "params/HDLcd.hh" 53#include "sim/system.hh" 54 55using std::vector; 56 57 58// initialize hdlcd registers 59HDLcd::HDLcd(const HDLcdParams *p) 60 : AmbaDmaDevice(p, 0xFFFF), 61 // Parameters 62 vnc(p->vnc), 63 workaroundSwapRB(p->workaround_swap_rb), 64 workaroundDmaLineCount(p->workaround_dma_line_count), 65 addrRanges{RangeSize(pioAddr, pioSize)}, 66 enableCapture(p->enable_capture), 67 pixelBufferSize(p->pixel_buffer_size), 68 virtRefreshRate(p->virt_refresh_rate), 69 70 // Registers 71 version(VERSION_RESETV), 72 int_rawstat(0), int_mask(0), 73 74 fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0), 75 bus_options(BUS_OPTIONS_RESETV), 76 77 v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0), 78 h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0), 79 polarities(0), 80 81 command(0), 82 83 pixel_format(0), 84 red_select(0), green_select(0), blue_select(0), 85 86 virtRefreshEvent(this), 87 // Other 88 bmp(&pixelPump.fb), pic(NULL), conv(PixelConverter::rgba8888_le), 89 pixelPump(*this, *p->pxl_clk, p->pixel_chunk) 90{ 91 if (vnc) 92 vnc->setFrameBuffer(&pixelPump.fb); 93} 94 95HDLcd::~HDLcd() 96{ 97} 98 99void 100HDLcd::regStats() 101{ 102 AmbaDmaDevice::regStats(); 103 104 using namespace Stats; 105 106 stats.underruns 107 .name(name() + ".underruns") 108 .desc("number of buffer underruns") 109 .flags(nozero) 110 ; 111} 112 113void 114HDLcd::serialize(CheckpointOut &cp) const 115{ 116 DPRINTF(Checkpoint, "Serializing ARM HDLCD\n"); 117 118 SERIALIZE_SCALAR(int_rawstat); 119 SERIALIZE_SCALAR(int_mask); 120 121 SERIALIZE_SCALAR(fb_base); 122 SERIALIZE_SCALAR(fb_line_length); 123 SERIALIZE_SCALAR(fb_line_count); 124 SERIALIZE_SCALAR(fb_line_pitch); 125 SERIALIZE_SCALAR(bus_options); 126 127 SERIALIZE_SCALAR(v_sync); 128 SERIALIZE_SCALAR(v_back_porch); 129 SERIALIZE_SCALAR(v_data); 130 SERIALIZE_SCALAR(v_front_porch); 131 132 SERIALIZE_SCALAR(h_sync); 133 SERIALIZE_SCALAR(h_back_porch); 134 SERIALIZE_SCALAR(h_data); 135 SERIALIZE_SCALAR(h_front_porch); 136 137 SERIALIZE_SCALAR(polarities); 138 139 SERIALIZE_SCALAR(command); 140 SERIALIZE_SCALAR(pixel_format); 141 SERIALIZE_SCALAR(red_select); 142 SERIALIZE_SCALAR(green_select); 143 SERIALIZE_SCALAR(blue_select); 144 145 SERIALIZE_OBJ(pixelPump); 146 if (enabled()) 147 dmaEngine->serializeSection(cp, "dmaEngine"); 148} 149 150void 151HDLcd::unserialize(CheckpointIn &cp) 152{ 153 DPRINTF(Checkpoint, "Unserializing ARM HDLCD\n"); 154 155 UNSERIALIZE_SCALAR(int_rawstat); 156 UNSERIALIZE_SCALAR(int_mask); 157 158 UNSERIALIZE_SCALAR(fb_base); 159 UNSERIALIZE_SCALAR(fb_line_length); 160 UNSERIALIZE_SCALAR(fb_line_count); 161 UNSERIALIZE_SCALAR(fb_line_pitch); 162 UNSERIALIZE_SCALAR(bus_options); 163 164 UNSERIALIZE_SCALAR(v_sync); 165 UNSERIALIZE_SCALAR(v_back_porch); 166 UNSERIALIZE_SCALAR(v_data); 167 UNSERIALIZE_SCALAR(v_front_porch); 168 169 UNSERIALIZE_SCALAR(h_sync); 170 UNSERIALIZE_SCALAR(h_back_porch); 171 UNSERIALIZE_SCALAR(h_data); 172 UNSERIALIZE_SCALAR(h_front_porch); 173 174 UNSERIALIZE_SCALAR(polarities); 175 176 UNSERIALIZE_SCALAR(command); 177 UNSERIALIZE_SCALAR(pixel_format); 178 UNSERIALIZE_SCALAR(red_select); 179 UNSERIALIZE_SCALAR(green_select); 180 UNSERIALIZE_SCALAR(blue_select); 181 182 { 183 // Try to unserialize the pixel pump. It might not exist if 184 // we're unserializing an old checkpoint. 185 ScopedCheckpointSection sec(cp, "pixelPump"); 186 if (cp.sectionExists(Serializable::currentSection())) 187 pixelPump.unserialize(cp); 188 } 189 190 if (enabled()) { 191 // Create the DMA engine and read its state from the 192 // checkpoint. We don't need to worry about the pixel pump as 193 // it is a proper SimObject. 194 createDmaEngine(); 195 dmaEngine->unserializeSection(cp, "dmaEngine"); 196 197 conv = pixelConverter(); 198 } 199} 200 201void 202HDLcd::drainResume() 203{ 204 AmbaDmaDevice::drainResume(); 205 206 if (enabled()) { 207 if (sys->bypassCaches()) { 208 // We restart the HDLCD if we are in KVM mode. This 209 // ensures that we always use the fast refresh logic if we 210 // resume in KVM mode. 211 cmdDisable(); 212 cmdEnable(); 213 } else if (!pixelPump.active()) { 214 // We restored from an old checkpoint without a pixel 215 // pump, start an new refresh. This typically happens when 216 // restoring from old checkpoints. 217 cmdEnable(); 218 } 219 } 220 221 // We restored from a checkpoint and need to update the VNC server 222 if (pixelPump.active() && vnc) 223 vnc->setDirty(); 224} 225 226void 227HDLcd::virtRefresh() 228{ 229 pixelPump.renderFrame(); 230 schedule(virtRefreshEvent, (curTick() + virtRefreshRate)); 231} 232 233// read registers and frame buffer 234Tick 235HDLcd::read(PacketPtr pkt) 236{ 237 assert(pkt->getAddr() >= pioAddr && 238 pkt->getAddr() < pioAddr + pioSize); 239 240 const Addr daddr(pkt->getAddr() - pioAddr); 241 panic_if(pkt->getSize() != 4, 242 "Unhandled read size (address: 0x.4x, size: %u)", 243 daddr, pkt->getSize()); 244 245 const uint32_t data(readReg(daddr)); 246 DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data); 247 248 pkt->set<uint32_t>(data); 249 pkt->makeAtomicResponse(); 250 return pioDelay; 251} 252 253// write registers and frame buffer 254Tick 255HDLcd::write(PacketPtr pkt) 256{ 257 assert(pkt->getAddr() >= pioAddr && 258 pkt->getAddr() < pioAddr + pioSize); 259 260 const Addr daddr(pkt->getAddr() - pioAddr); 261 panic_if(pkt->getSize() != 4, 262 "Unhandled read size (address: 0x.4x, size: %u)", 263 daddr, pkt->getSize()); 264 const uint32_t data(pkt->get<uint32_t>()); 265 DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data); 266 267 writeReg(daddr, data); 268 269 pkt->makeAtomicResponse(); 270 return pioDelay; 271} 272 273uint32_t 274HDLcd::readReg(Addr offset) 275{ 276 switch (offset) { 277 case Version: return version; 278 279 case Int_RawStat: return int_rawstat; 280 case Int_Clear: 281 panic("HDLCD INT_CLEAR register is Write-Only\n"); 282 case Int_Mask: return int_mask; 283 case Int_Status: return intStatus(); 284 285 case Fb_Base: return fb_base; 286 case Fb_Line_Length: return fb_line_length; 287 case Fb_Line_Count: return fb_line_count; 288 case Fb_Line_Pitch: return fb_line_pitch; 289 case Bus_Options: return bus_options; 290 291 case V_Sync: return v_sync; 292 case V_Back_Porch: return v_back_porch; 293 case V_Data: return v_data; 294 case V_Front_Porch: return v_front_porch; 295 case H_Sync: return h_sync; 296 case H_Back_Porch: return h_back_porch; 297 case H_Data: return h_data; 298 case H_Front_Porch: return h_front_porch; 299 case Polarities: return polarities; 300 301 case Command: return command; 302 case Pixel_Format: return pixel_format; 303 case Red_Select: return red_select; 304 case Green_Select: return green_select; 305 case Blue_Select: return blue_select; 306 307 default: 308 panic("Tried to read HDLCD register that doesn't exist\n", offset); 309 } 310} 311 312void 313HDLcd::writeReg(Addr offset, uint32_t value) 314{ 315 switch (offset) { 316 case Version: 317 panic("HDLCD VERSION register is read-Only\n"); 318 319 case Int_RawStat: 320 intRaise(value); 321 return; 322 case Int_Clear: 323 intClear(value); 324 return; 325 case Int_Mask: 326 intMask(value); 327 return; 328 case Int_Status: 329 panic("HDLCD INT_STATUS register is read-Only\n"); 330 break; 331 332 case Fb_Base: 333 fb_base = value; 334 return; 335 336 case Fb_Line_Length: 337 fb_line_length = value; 338 return; 339 340 case Fb_Line_Count: 341 fb_line_count = value; 342 return; 343 344 case Fb_Line_Pitch: 345 fb_line_pitch = value; 346 return; 347 348 case Bus_Options: { 349 const BusOptsReg old_bus_options(bus_options); 350 bus_options = value; 351 352 if (bus_options.max_outstanding != old_bus_options.max_outstanding) { 353 DPRINTF(HDLcd, 354 "Changing HDLcd outstanding DMA transactions: %d -> %d\n", 355 old_bus_options.max_outstanding, 356 bus_options.max_outstanding); 357 358 } 359 360 if (bus_options.burst_len != old_bus_options.burst_len) { 361 DPRINTF(HDLcd, 362 "Changing HDLcd DMA burst flags: 0x%x -> 0x%x\n", 363 old_bus_options.burst_len, bus_options.burst_len); 364 } 365 } return; 366 367 case V_Sync: 368 v_sync = value; 369 return; 370 case V_Back_Porch: 371 v_back_porch = value; 372 return; 373 case V_Data: 374 v_data = value; 375 return; 376 case V_Front_Porch: 377 v_front_porch = value; 378 return; 379 380 case H_Sync: 381 h_sync = value; 382 return; 383 case H_Back_Porch: 384 h_back_porch = value; 385 return; 386 case H_Data: 387 h_data = value; 388 return; 389 case H_Front_Porch: 390 h_front_porch = value; 391 return; 392 393 case Polarities: 394 polarities = value; 395 return; 396 397 case Command: { 398 const CommandReg new_command(value); 399 400 if (new_command.enable != command.enable) { 401 DPRINTF(HDLcd, "HDLCD switched %s\n", 402 new_command.enable ? "on" : "off"); 403 404 if (new_command.enable) { 405 cmdEnable(); 406 } else { 407 cmdDisable(); 408 } 409 } 410 command = new_command; 411 } return; 412 413 case Pixel_Format: 414 pixel_format = value; 415 return; 416 417 case Red_Select: 418 red_select = value; 419 return; 420 case Green_Select: 421 green_select = value; 422 return; 423 case Blue_Select: 424 blue_select = value; 425 return; 426 427 default: 428 panic("Tried to write HDLCD register that doesn't exist\n", offset); 429 return; 430 } 431} 432 433PixelConverter 434HDLcd::pixelConverter() const 435{ 436 ByteOrder byte_order( 437 pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder); 438 439 /* Some Linux kernels have a broken driver that swaps the red and 440 * blue color select registers. */ 441 if (!workaroundSwapRB) { 442 return PixelConverter( 443 pixel_format.bytes_per_pixel + 1, 444 red_select.offset, green_select.offset, blue_select.offset, 445 red_select.size, green_select.size, blue_select.size, 446 byte_order); 447 } else { 448 return PixelConverter( 449 pixel_format.bytes_per_pixel + 1, 450 blue_select.offset, green_select.offset, red_select.offset, 451 blue_select.size, green_select.size, red_select.size, 452 byte_order); 453 } 454} 455 456DisplayTimings 457HDLcd::displayTimings() const 458{ 459 return DisplayTimings( 460 h_data.val + 1, v_data.val + 1, 461 h_back_porch.val + 1, h_sync.val + 1, h_front_porch.val + 1, 462 v_back_porch.val + 1, v_sync.val + 1, v_front_porch.val + 1); 463} 464 465void 466HDLcd::createDmaEngine() 467{ 468 if (bus_options.max_outstanding == 0) { 469 warn("Maximum number of outstanding DMA transfers set to 0."); 470 return; 471 } 472 473 const uint32_t dma_burst_flags(bus_options.burst_len); 474 const uint32_t dma_burst_len( 475 dma_burst_flags ? 476 (1UL << (findMsbSet(dma_burst_flags) - 1)) : 477 MAX_BURST_LEN); 478 // Some drivers seem to set the DMA line count incorrectly. This 479 // could either be a driver bug or a specification bug. Unlike for 480 // timings, the specification does not require 1 to be added to 481 // the DMA engine's line count. 482 const uint32_t dma_lines( 483 fb_line_count + (workaroundDmaLineCount ? 1 : 0)); 484 485 dmaEngine.reset(new DmaEngine( 486 *this, pixelBufferSize, 487 AXI_PORT_WIDTH * dma_burst_len, 488 bus_options.max_outstanding, 489 fb_line_length, fb_line_pitch, dma_lines)); 490} 491 492void 493HDLcd::cmdEnable() 494{ 495 createDmaEngine(); 496 conv = pixelConverter(); 497 498 // Update timing parameter before rendering frames 499 pixelPump.updateTimings(displayTimings()); 500 501 if (sys->bypassCaches()) { 502 schedule(virtRefreshEvent, clockEdge()); 503 } else { 504 pixelPump.start(); 505 } 506} 507 508void 509HDLcd::cmdDisable() 510{ 511 pixelPump.stop(); 512 // Disable the virtual refresh event 513 if (virtRefreshEvent.scheduled()) { 514 assert(sys->bypassCaches()); 515 deschedule(virtRefreshEvent); 516 } 517 dmaEngine->abortFrame(); 518} 519 520bool 521HDLcd::pxlNext(Pixel &p) 522{ 523 uint8_t pixel_data[MAX_PIXEL_SIZE]; 524 assert(conv.length <= sizeof(pixel_data)); 525 if (dmaEngine->tryGet(pixel_data, conv.length)) { 526 p = conv.toPixel(pixel_data); 527 return true; 528 } else { 529 return false; 530 } 531} 532 533void 534HDLcd::pxlVSyncBegin() 535{ 536 DPRINTF(HDLcd, "Raising VSYNC interrupt.\n"); 537 intRaise(INT_VSYNC); 538} 539 540void 541HDLcd::pxlVSyncEnd() 542{ 543 DPRINTF(HDLcd, "End of VSYNC, starting DMA engine\n"); 544 dmaEngine->startFrame(fb_base); 545} 546 547void 548HDLcd::pxlUnderrun() 549{ 550 DPRINTF(HDLcd, "Buffer underrun, stopping DMA fill.\n"); 551 ++stats.underruns; 552 intRaise(INT_UNDERRUN); 553 dmaEngine->abortFrame(); 554} 555 556void 557HDLcd::pxlFrameDone() 558{ 559 DPRINTF(HDLcd, "Reached end of last visible line.\n"); 560 561 if (dmaEngine->size()) { 562 warn("HDLCD %u bytes still in FIFO after frame: Ensure that DMA " 563 "and PixelPump configuration is consistent\n", 564 dmaEngine->size()); 565 dmaEngine->dumpSettings(); 566 pixelPump.dumpSettings(); 567 } 568 569 if (vnc) 570 vnc->setDirty(); 571 572 if (enableCapture) { 573 if (!pic) { 574 pic = simout.create( 575 csprintf("%s.framebuffer.bmp", sys->name()), 576 true); 577 } 578 579 assert(pic); 580 pic->stream()->seekp(0); 581 bmp.write(*pic->stream()); 582 } 583} 584 585void 586HDLcd::setInterrupts(uint32_t ints, uint32_t mask) 587{ 588 const bool old_ints(intStatus()); 589 590 int_mask = mask; 591 int_rawstat = ints; 592 593 if (!old_ints && intStatus()) { 594 gic->sendInt(intNum); 595 } else if (old_ints && !intStatus()) { 596 gic->clearInt(intNum); 597 } 598} 599 600HDLcd::DmaEngine::DmaEngine(HDLcd &_parent, size_t size, 601 unsigned request_size, unsigned max_pending, 602 size_t line_size, ssize_t line_pitch, unsigned num_lines) 603 : DmaReadFifo( 604 _parent.dmaPort, size, request_size, max_pending, 605 Request::UNCACHEABLE), 606 parent(_parent), 607 lineSize(line_size), linePitch(line_pitch), numLines(num_lines), 608 nextLineAddr(0) 609{ 610} 611 612void 613HDLcd::DmaEngine::serialize(CheckpointOut &cp) const 614{ 615 DmaReadFifo::serialize(cp); 616 617 SERIALIZE_SCALAR(nextLineAddr); 618 SERIALIZE_SCALAR(frameEnd); 619} 620 621void 622HDLcd::DmaEngine::unserialize(CheckpointIn &cp) 623{ 624 DmaReadFifo::unserialize(cp); 625 626 UNSERIALIZE_SCALAR(nextLineAddr); 627 UNSERIALIZE_SCALAR(frameEnd); 628} 629 630void 631HDLcd::DmaEngine::startFrame(Addr fb_base) 632{ 633 nextLineAddr = fb_base; 634 frameEnd = fb_base + numLines * linePitch; 635 636 startFill(nextLineAddr, lineSize); 637} 638 639void 640HDLcd::DmaEngine::abortFrame() 641{ 642 nextLineAddr = frameEnd; 643 stopFill(); 644 flush(); 645} 646 647 648void 649HDLcd::DmaEngine::dumpSettings() 650{ 651 inform("DMA line size: %u bytes", lineSize); 652 inform("DMA line pitch: %i bytes", linePitch); 653 inform("DMA num lines: %u", numLines); 654} 655 656void 657HDLcd::DmaEngine::onEndOfBlock() 658{ 659 if (nextLineAddr == frameEnd) 660 // We're done with this frame. Ignore calls to this method 661 // until the next frame has been started. 662 return; 663 664 nextLineAddr += linePitch; 665 if (nextLineAddr != frameEnd) 666 startFill(nextLineAddr, lineSize); 667} 668 669void 670HDLcd::DmaEngine::onIdle() 671{ 672 parent.intRaise(INT_DMA_END); 673} 674 675void 676HDLcd::PixelPump::dumpSettings() 677{ 678 const DisplayTimings &t(timings()); 679 680 inform("PixelPump width: %u", t.width); 681 inform("PixelPump height: %u", t.height); 682 683 inform("PixelPump horizontal back porch: %u", t.hBackPorch); 684 inform("PixelPump horizontal fron porch: %u", t.hFrontPorch); 685 inform("PixelPump horizontal fron porch: %u", t.hSync); 686 687 inform("PixelPump vertical back porch: %u", t.vBackPorch); 688 inform("PixelPump vertical fron porch: %u", t.vFrontPorch); 689 inform("PixelPump vertical fron porch: %u", t.vSync); 690} 691 692 693HDLcd * 694HDLcdParams::create() 695{ 696 return new HDLcd(this); 697} 698