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