hdlcd.cc revision 11422:4f749e00b667
14202Sbinkertn@umich.edu/* 24202Sbinkertn@umich.edu * Copyright (c) 2010-2013, 2015 ARM Limited 34202Sbinkertn@umich.edu * All rights reserved 44202Sbinkertn@umich.edu * 54202Sbinkertn@umich.edu * The license below extends only to copyright in the software and shall 64202Sbinkertn@umich.edu * not be construed as granting a license to any other intellectual 74202Sbinkertn@umich.edu * property including but not limited to intellectual property relating 84202Sbinkertn@umich.edu * to a hardware implementation of the functionality of the software 94202Sbinkertn@umich.edu * licensed hereunder. You may use the software subject to the license 104202Sbinkertn@umich.edu * terms below provided that you ensure that this notice is replicated 114202Sbinkertn@umich.edu * unmodified and in its entirety in all distributions of the software, 124202Sbinkertn@umich.edu * modified or unmodified, in source code or in binary form. 134202Sbinkertn@umich.edu * 144202Sbinkertn@umich.edu * Redistribution and use in source and binary forms, with or without 154202Sbinkertn@umich.edu * modification, are permitted provided that the following conditions are 164202Sbinkertn@umich.edu * met: redistributions of source code must retain the above copyright 174202Sbinkertn@umich.edu * notice, this list of conditions and the following disclaimer; 184202Sbinkertn@umich.edu * redistributions in binary form must reproduce the above copyright 194202Sbinkertn@umich.edu * notice, this list of conditions and the following disclaimer in the 204202Sbinkertn@umich.edu * documentation and/or other materials provided with the distribution; 214202Sbinkertn@umich.edu * neither the name of the copyright holders nor the names of its 224202Sbinkertn@umich.edu * contributors may be used to endorse or promote products derived from 234202Sbinkertn@umich.edu * this software without specific prior written permission. 244202Sbinkertn@umich.edu * 254202Sbinkertn@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 264202Sbinkertn@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 274202Sbinkertn@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 284202Sbinkertn@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 294202Sbinkertn@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 304202Sbinkertn@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 314202Sbinkertn@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 324202Sbinkertn@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 334486Sbinkertn@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 344486Sbinkertn@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 354486Sbinkertn@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 364486Sbinkertn@umich.edu * 374486Sbinkertn@umich.edu * Authors: Chris Emmons 384202Sbinkertn@umich.edu * Andreas Sandberg 394202Sbinkertn@umich.edu */ 404202Sbinkertn@umich.edu 414202Sbinkertn@umich.edu#include "dev/arm/hdlcd.hh" 424202Sbinkertn@umich.edu 434202Sbinkertn@umich.edu#include "base/vnc/vncinput.hh" 444202Sbinkertn@umich.edu#include "base/output.hh" 454202Sbinkertn@umich.edu#include "base/trace.hh" 464202Sbinkertn@umich.edu#include "debug/Checkpoint.hh" 474202Sbinkertn@umich.edu#include "debug/HDLcd.hh" 484202Sbinkertn@umich.edu#include "dev/arm/amba_device.hh" 494202Sbinkertn@umich.edu#include "dev/arm/base_gic.hh" 504202Sbinkertn@umich.edu#include "mem/packet.hh" 514202Sbinkertn@umich.edu#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 69 // Registers 70 version(VERSION_RESETV), 71 int_rawstat(0), int_mask(0), 72 73 fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0), 74 bus_options(BUS_OPTIONS_RESETV), 75 76 v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0), 77 h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0), 78 polarities(0), 79 80 command(0), 81 82 pixel_format(0), 83 red_select(0), green_select(0), blue_select(0), 84 85 // Other 86 bmp(&pixelPump.fb), pic(NULL), conv(PixelConverter::rgba8888_le), 87 pixelPump(*this, *p->pxl_clk, p->pixel_chunk) 88{ 89 if (vnc) 90 vnc->setFrameBuffer(&pixelPump.fb); 91} 92 93HDLcd::~HDLcd() 94{ 95} 96 97void 98HDLcd::regStats() 99{ 100 AmbaDmaDevice::regStats(); 101 102 using namespace Stats; 103 104 stats.underruns 105 .name(name() + ".underruns") 106 .desc("number of buffer underruns") 107 .flags(nozero) 108 ; 109} 110 111void 112HDLcd::serialize(CheckpointOut &cp) const 113{ 114 DPRINTF(Checkpoint, "Serializing ARM HDLCD\n"); 115 116 SERIALIZE_SCALAR(int_rawstat); 117 SERIALIZE_SCALAR(int_mask); 118 119 SERIALIZE_SCALAR(fb_base); 120 SERIALIZE_SCALAR(fb_line_length); 121 SERIALIZE_SCALAR(fb_line_count); 122 SERIALIZE_SCALAR(fb_line_pitch); 123 SERIALIZE_SCALAR(bus_options); 124 125 SERIALIZE_SCALAR(v_sync); 126 SERIALIZE_SCALAR(v_back_porch); 127 SERIALIZE_SCALAR(v_data); 128 SERIALIZE_SCALAR(v_front_porch); 129 130 SERIALIZE_SCALAR(h_sync); 131 SERIALIZE_SCALAR(h_back_porch); 132 SERIALIZE_SCALAR(h_data); 133 SERIALIZE_SCALAR(h_front_porch); 134 135 SERIALIZE_SCALAR(polarities); 136 137 SERIALIZE_SCALAR(command); 138 SERIALIZE_SCALAR(pixel_format); 139 SERIALIZE_SCALAR(red_select); 140 SERIALIZE_SCALAR(green_select); 141 SERIALIZE_SCALAR(blue_select); 142 143 SERIALIZE_OBJ(pixelPump); 144 if (enabled()) 145 dmaEngine->serializeSection(cp, "dmaEngine"); 146} 147 148void 149HDLcd::unserialize(CheckpointIn &cp) 150{ 151 DPRINTF(Checkpoint, "Unserializing ARM HDLCD\n"); 152 153 UNSERIALIZE_SCALAR(int_rawstat); 154 UNSERIALIZE_SCALAR(int_mask); 155 156 UNSERIALIZE_SCALAR(fb_base); 157 UNSERIALIZE_SCALAR(fb_line_length); 158 UNSERIALIZE_SCALAR(fb_line_count); 159 UNSERIALIZE_SCALAR(fb_line_pitch); 160 UNSERIALIZE_SCALAR(bus_options); 161 162 UNSERIALIZE_SCALAR(v_sync); 163 UNSERIALIZE_SCALAR(v_back_porch); 164 UNSERIALIZE_SCALAR(v_data); 165 UNSERIALIZE_SCALAR(v_front_porch); 166 167 UNSERIALIZE_SCALAR(h_sync); 168 UNSERIALIZE_SCALAR(h_back_porch); 169 UNSERIALIZE_SCALAR(h_data); 170 UNSERIALIZE_SCALAR(h_front_porch); 171 172 UNSERIALIZE_SCALAR(polarities); 173 174 UNSERIALIZE_SCALAR(command); 175 UNSERIALIZE_SCALAR(pixel_format); 176 UNSERIALIZE_SCALAR(red_select); 177 UNSERIALIZE_SCALAR(green_select); 178 UNSERIALIZE_SCALAR(blue_select); 179 180 { 181 // Try to unserialize the pixel pump. It might not exist if 182 // we're unserializing an old checkpoint. 183 ScopedCheckpointSection sec(cp, "pixelPump"); 184 if (cp.sectionExists(Serializable::currentSection())) 185 pixelPump.unserialize(cp); 186 } 187 188 if (enabled()) { 189 // Create the DMA engine and read its state from the 190 // checkpoint. We don't need to worry about the pixel pump as 191 // it is a proper SimObject. 192 createDmaEngine(); 193 dmaEngine->unserializeSection(cp, "dmaEngine"); 194 195 conv = pixelConverter(); 196 } 197} 198 199void 200HDLcd::drainResume() 201{ 202 AmbaDmaDevice::drainResume(); 203 204 // We restored from an old checkpoint without a pixel pump, start 205 // an new refresh. This typically happens when restoring from old 206 // checkpoints. 207 if (enabled() && !pixelPump.active()) 208 pixelPump.start(displayTimings()); 209 210 // We restored from a checkpoint and need to update the VNC server 211 if (pixelPump.active() && vnc) 212 vnc->setDirty(); 213} 214 215// read registers and frame buffer 216Tick 217HDLcd::read(PacketPtr pkt) 218{ 219 assert(pkt->getAddr() >= pioAddr && 220 pkt->getAddr() < pioAddr + pioSize); 221 222 const Addr daddr(pkt->getAddr() - pioAddr); 223 panic_if(pkt->getSize() != 4, 224 "Unhandled read size (address: 0x.4x, size: %u)", 225 daddr, pkt->getSize()); 226 227 const uint32_t data(readReg(daddr)); 228 DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data); 229 230 pkt->set<uint32_t>(data); 231 pkt->makeAtomicResponse(); 232 return pioDelay; 233} 234 235// write registers and frame buffer 236Tick 237HDLcd::write(PacketPtr pkt) 238{ 239 assert(pkt->getAddr() >= pioAddr && 240 pkt->getAddr() < pioAddr + pioSize); 241 242 const Addr daddr(pkt->getAddr() - pioAddr); 243 panic_if(pkt->getSize() != 4, 244 "Unhandled read size (address: 0x.4x, size: %u)", 245 daddr, pkt->getSize()); 246 const uint32_t data(pkt->get<uint32_t>()); 247 DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data); 248 249 writeReg(daddr, data); 250 251 pkt->makeAtomicResponse(); 252 return pioDelay; 253} 254 255uint32_t 256HDLcd::readReg(Addr offset) 257{ 258 switch (offset) { 259 case Version: return version; 260 261 case Int_RawStat: return int_rawstat; 262 case Int_Clear: 263 panic("HDLCD INT_CLEAR register is Write-Only\n"); 264 case Int_Mask: return int_mask; 265 case Int_Status: return intStatus(); 266 267 case Fb_Base: return fb_base; 268 case Fb_Line_Length: return fb_line_length; 269 case Fb_Line_Count: return fb_line_count; 270 case Fb_Line_Pitch: return fb_line_pitch; 271 case Bus_Options: return bus_options; 272 273 case V_Sync: return v_sync; 274 case V_Back_Porch: return v_back_porch; 275 case V_Data: return v_data; 276 case V_Front_Porch: return v_front_porch; 277 case H_Sync: return h_sync; 278 case H_Back_Porch: return h_back_porch; 279 case H_Data: return h_data; 280 case H_Front_Porch: return h_front_porch; 281 case Polarities: return polarities; 282 283 case Command: return command; 284 case Pixel_Format: return pixel_format; 285 case Red_Select: return red_select; 286 case Green_Select: return green_select; 287 case Blue_Select: return blue_select; 288 289 default: 290 panic("Tried to read HDLCD register that doesn't exist\n", offset); 291 } 292} 293 294void 295HDLcd::writeReg(Addr offset, uint32_t value) 296{ 297 switch (offset) { 298 case Version: 299 panic("HDLCD VERSION register is read-Only\n"); 300 301 case Int_RawStat: 302 intRaise(value); 303 return; 304 case Int_Clear: 305 intClear(value); 306 return; 307 case Int_Mask: 308 intMask(value); 309 return; 310 case Int_Status: 311 panic("HDLCD INT_STATUS register is read-Only\n"); 312 break; 313 314 case Fb_Base: 315 fb_base = value; 316 return; 317 318 case Fb_Line_Length: 319 fb_line_length = value; 320 return; 321 322 case Fb_Line_Count: 323 fb_line_count = value; 324 return; 325 326 case Fb_Line_Pitch: 327 fb_line_pitch = value; 328 return; 329 330 case Bus_Options: { 331 const BusOptsReg old_bus_options(bus_options); 332 bus_options = value; 333 334 if (bus_options.max_outstanding != old_bus_options.max_outstanding) { 335 DPRINTF(HDLcd, 336 "Changing HDLcd outstanding DMA transactions: %d -> %d\n", 337 old_bus_options.max_outstanding, 338 bus_options.max_outstanding); 339 340 } 341 342 if (bus_options.burst_len != old_bus_options.burst_len) { 343 DPRINTF(HDLcd, 344 "Changing HDLcd DMA burst flags: 0x%x -> 0x%x\n", 345 old_bus_options.burst_len, bus_options.burst_len); 346 } 347 } return; 348 349 case V_Sync: 350 v_sync = value; 351 return; 352 case V_Back_Porch: 353 v_back_porch = value; 354 return; 355 case V_Data: 356 v_data = value; 357 return; 358 case V_Front_Porch: 359 v_front_porch = value; 360 return; 361 362 case H_Sync: 363 h_sync = value; 364 return; 365 case H_Back_Porch: 366 h_back_porch = value; 367 return; 368 case H_Data: 369 h_data = value; 370 return; 371 case H_Front_Porch: 372 h_front_porch = value; 373 return; 374 375 case Polarities: 376 polarities = value; 377 return; 378 379 case Command: { 380 const CommandReg new_command(value); 381 382 if (new_command.enable != command.enable) { 383 DPRINTF(HDLcd, "HDLCD switched %s\n", 384 new_command.enable ? "on" : "off"); 385 386 if (new_command.enable) { 387 cmdEnable(); 388 } else { 389 cmdDisable(); 390 } 391 } 392 command = new_command; 393 } return; 394 395 case Pixel_Format: 396 pixel_format = value; 397 return; 398 399 case Red_Select: 400 red_select = value; 401 return; 402 case Green_Select: 403 green_select = value; 404 return; 405 case Blue_Select: 406 blue_select = value; 407 return; 408 409 default: 410 panic("Tried to write HDLCD register that doesn't exist\n", offset); 411 return; 412 } 413} 414 415PixelConverter 416HDLcd::pixelConverter() const 417{ 418 ByteOrder byte_order( 419 pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder); 420 421 /* Some Linux kernels have a broken driver that swaps the red and 422 * blue color select registers. */ 423 if (!workaroundSwapRB) { 424 return PixelConverter( 425 pixel_format.bytes_per_pixel + 1, 426 red_select.offset, green_select.offset, blue_select.offset, 427 red_select.size, green_select.size, blue_select.size, 428 byte_order); 429 } else { 430 return PixelConverter( 431 pixel_format.bytes_per_pixel + 1, 432 blue_select.offset, green_select.offset, red_select.offset, 433 blue_select.size, green_select.size, red_select.size, 434 byte_order); 435 } 436} 437 438DisplayTimings 439HDLcd::displayTimings() const 440{ 441 return DisplayTimings( 442 h_data.val + 1, v_data.val + 1, 443 h_back_porch.val + 1, h_sync.val + 1, h_front_porch.val + 1, 444 v_back_porch.val + 1, v_sync.val + 1, v_front_porch.val + 1); 445} 446 447void 448HDLcd::createDmaEngine() 449{ 450 if (bus_options.max_outstanding == 0) { 451 warn("Maximum number of outstanding DMA transfers set to 0."); 452 return; 453 } 454 455 const uint32_t dma_burst_flags(bus_options.burst_len); 456 const uint32_t dma_burst_len( 457 dma_burst_flags ? 458 (1UL << (findMsbSet(dma_burst_flags) - 1)) : 459 MAX_BURST_LEN); 460 // Some drivers seem to set the DMA line count incorrectly. This 461 // could either be a driver bug or a specification bug. Unlike for 462 // timings, the specification does not require 1 to be added to 463 // the DMA engine's line count. 464 const uint32_t dma_lines( 465 fb_line_count + (workaroundDmaLineCount ? 1 : 0)); 466 467 dmaEngine.reset(new DmaEngine( 468 *this, pixelBufferSize, 469 AXI_PORT_WIDTH * dma_burst_len, 470 bus_options.max_outstanding, 471 fb_line_length, fb_line_pitch, dma_lines)); 472} 473 474void 475HDLcd::cmdEnable() 476{ 477 createDmaEngine(); 478 conv = pixelConverter(); 479 pixelPump.start(displayTimings()); 480} 481 482void 483HDLcd::cmdDisable() 484{ 485 pixelPump.stop(); 486 dmaEngine->abortFrame(); 487} 488 489bool 490HDLcd::pxlNext(Pixel &p) 491{ 492 uint8_t pixel_data[MAX_PIXEL_SIZE]; 493 assert(conv.length <= sizeof(pixel_data)); 494 if (dmaEngine->tryGet(pixel_data, conv.length)) { 495 p = conv.toPixel(pixel_data); 496 return true; 497 } else { 498 return false; 499 } 500} 501 502void 503HDLcd::pxlVSyncBegin() 504{ 505 DPRINTF(HDLcd, "Raising VSYNC interrupt.\n"); 506 intRaise(INT_VSYNC); 507} 508 509void 510HDLcd::pxlVSyncEnd() 511{ 512 DPRINTF(HDLcd, "End of VSYNC, starting DMA engine\n"); 513 dmaEngine->startFrame(fb_base); 514} 515 516void 517HDLcd::pxlUnderrun() 518{ 519 DPRINTF(HDLcd, "Buffer underrun, stopping DMA fill.\n"); 520 ++stats.underruns; 521 intRaise(INT_UNDERRUN); 522 dmaEngine->abortFrame(); 523} 524 525void 526HDLcd::pxlFrameDone() 527{ 528 DPRINTF(HDLcd, "Reached end of last visible line.\n"); 529 530 if (dmaEngine->size()) { 531 warn("HDLCD %u bytes still in FIFO after frame: Ensure that DMA " 532 "and PixelPump configuration is consistent\n", 533 dmaEngine->size()); 534 dmaEngine->dumpSettings(); 535 pixelPump.dumpSettings(); 536 } 537 538 if (vnc) 539 vnc->setDirty(); 540 541 if (enableCapture) { 542 if (!pic) { 543 pic = simout.create( 544 csprintf("%s.framebuffer.bmp", sys->name()), 545 true); 546 } 547 548 assert(pic); 549 pic->stream()->seekp(0); 550 bmp.write(*pic->stream()); 551 } 552} 553 554void 555HDLcd::setInterrupts(uint32_t ints, uint32_t mask) 556{ 557 const bool old_ints(intStatus()); 558 559 int_mask = mask; 560 int_rawstat = ints; 561 562 if (!old_ints && intStatus()) { 563 gic->sendInt(intNum); 564 } else if (old_ints && !intStatus()) { 565 gic->clearInt(intNum); 566 } 567} 568 569HDLcd::DmaEngine::DmaEngine(HDLcd &_parent, size_t size, 570 unsigned request_size, unsigned max_pending, 571 size_t line_size, ssize_t line_pitch, unsigned num_lines) 572 : DmaReadFifo( 573 _parent.dmaPort, size, request_size, max_pending, 574 Request::UNCACHEABLE), 575 parent(_parent), 576 lineSize(line_size), linePitch(line_pitch), numLines(num_lines), 577 nextLineAddr(0) 578{ 579} 580 581void 582HDLcd::DmaEngine::serialize(CheckpointOut &cp) const 583{ 584 DmaReadFifo::serialize(cp); 585 586 SERIALIZE_SCALAR(nextLineAddr); 587 SERIALIZE_SCALAR(frameEnd); 588} 589 590void 591HDLcd::DmaEngine::unserialize(CheckpointIn &cp) 592{ 593 DmaReadFifo::unserialize(cp); 594 595 UNSERIALIZE_SCALAR(nextLineAddr); 596 UNSERIALIZE_SCALAR(frameEnd); 597} 598 599void 600HDLcd::DmaEngine::startFrame(Addr fb_base) 601{ 602 nextLineAddr = fb_base; 603 frameEnd = fb_base + numLines * linePitch; 604 605 startFill(nextLineAddr, lineSize); 606} 607 608void 609HDLcd::DmaEngine::abortFrame() 610{ 611 nextLineAddr = frameEnd; 612 stopFill(); 613 flush(); 614} 615 616 617void 618HDLcd::DmaEngine::dumpSettings() 619{ 620 inform("DMA line size: %u bytes", lineSize); 621 inform("DMA line pitch: %i bytes", linePitch); 622 inform("DMA num lines: %u", numLines); 623} 624 625void 626HDLcd::DmaEngine::onEndOfBlock() 627{ 628 if (nextLineAddr == frameEnd) 629 // We're done with this frame. Ignore calls to this method 630 // until the next frame has been started. 631 return; 632 633 nextLineAddr += linePitch; 634 if (nextLineAddr != frameEnd) 635 startFill(nextLineAddr, lineSize); 636} 637 638void 639HDLcd::DmaEngine::onIdle() 640{ 641 parent.intRaise(INT_DMA_END); 642} 643 644void 645HDLcd::PixelPump::dumpSettings() 646{ 647 const DisplayTimings &t(timings()); 648 649 inform("PixelPump width: %u", t.width); 650 inform("PixelPump height: %u", t.height); 651 652 inform("PixelPump horizontal back porch: %u", t.hBackPorch); 653 inform("PixelPump horizontal fron porch: %u", t.hFrontPorch); 654 inform("PixelPump horizontal fron porch: %u", t.hSync); 655 656 inform("PixelPump vertical back porch: %u", t.vBackPorch); 657 inform("PixelPump vertical fron porch: %u", t.vFrontPorch); 658 inform("PixelPump vertical fron porch: %u", t.vSync); 659} 660 661 662HDLcd * 663HDLcdParams::create() 664{ 665 return new HDLcd(this); 666} 667