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