hdlcd.cc revision 11090:f37a6b82f98f
12SN/A/* 211071SN/A * Copyright (c) 2010-2013, 2015 ARM Limited 311071SN/A * All rights reserved 411071SN/A * 511071SN/A * The license below extends only to copyright in the software and shall 611071SN/A * not be construed as granting a license to any other intellectual 711071SN/A * property including but not limited to intellectual property relating 811071SN/A * to a hardware implementation of the functionality of the software 911071SN/A * licensed hereunder. You may use the software subject to the license 1011071SN/A * terms below provided that you ensure that this notice is replicated 1111071SN/A * unmodified and in its entirety in all distributions of the software, 1211071SN/A * modified or unmodified, in source code or in binary form. 1311071SN/A * 141762SN/A * Redistribution and use in source and binary forms, with or without 152SN/A * modification, are permitted provided that the following conditions are 162SN/A * met: redistributions of source code must retain the above copyright 172SN/A * notice, this list of conditions and the following disclaimer; 182SN/A * redistributions in binary form must reproduce the above copyright 192SN/A * notice, this list of conditions and the following disclaimer in the 202SN/A * documentation and/or other materials provided with the distribution; 212SN/A * neither the name of the copyright holders nor the names of its 222SN/A * contributors may be used to endorse or promote products derived from 232SN/A * this software without specific prior written permission. 242SN/A * 252SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 262SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 272SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 282SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 292SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 302SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 312SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 322SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 332SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 342SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 352SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 362SN/A * 372SN/A * Authors: Chris Emmons 382SN/A * Andreas Sandberg 392665SN/A */ 402665SN/A 412665SN/A#include "dev/arm/hdlcd.hh" 422SN/A 432SN/A#include "base/vnc/vncinput.hh" 442SN/A#include "base/output.hh" 452SN/A#include "base/trace.hh" 462SN/A#include "debug/Checkpoint.hh" 472SN/A#include "debug/HDLcd.hh" 4811263Sandreas.sandberg@arm.com#include "dev/arm/amba_device.hh" 4911263Sandreas.sandberg@arm.com#include "dev/arm/base_gic.hh" 50146SN/A#include "mem/packet.hh" 512SN/A#include "mem/packet_access.hh" 522SN/A#include "params/HDLcd.hh" 532SN/A#include "sim/system.hh" 542SN/A 551954SN/Ausing std::vector; 56146SN/A 578232SN/A 588232SN/A// initialize hdlcd registers 5911263Sandreas.sandberg@arm.comHDLcd::HDLcd(const HDLcdParams *p) 6011263Sandreas.sandberg@arm.com : AmbaDmaDevice(p, 0xFFFF), 6111263Sandreas.sandberg@arm.com // Parameters 624762SN/A vnc(p->vnc), 638229SN/A workaroundSwapRB(p->workaround_swap_rb), 641078SN/A workaroundDmaLineCount(p->workaround_dma_line_count), 651078SN/A addrRanges{RangeSize(pioAddr, pioSize)}, 662SN/A enableCapture(p->enable_capture), 672SN/A pixelBufferSize(p->pixel_buffer_size), 682SN/A 694981SN/A // Registers 704981SN/A version(VERSION_RESETV), 712SN/A int_rawstat(0), int_mask(0), 725034SN/A 735034SN/A fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0), 745034SN/A bus_options(BUS_OPTIONS_RESETV), 755034SN/A 762SN/A v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0), 774981SN/A h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0), 784981SN/A polarities(0), 794981SN/A 802SN/A command(0), 812SN/A 822SN/A pixel_format(0), 832SN/A red_select(0), green_select(0), blue_select(0), 841435SN/A 851435SN/A // Other 862SN/A bmp(&pixelPump.fb), pic(NULL), conv(PixelConverter::rgba8888_le), 871435SN/A pixelPump(*this, *p->pxl_clk, p->pixel_chunk) 881435SN/A{ 892SN/A if (vnc) 902SN/A vnc->setFrameBuffer(&pixelPump.fb); 914981SN/A} 924981SN/A 934981SN/AHDLcd::~HDLcd() 944981SN/A{ 954981SN/A} 964981SN/A 974981SN/Avoid 984981SN/AHDLcd::serialize(CheckpointOut &cp) const 994981SN/A{ 1004981SN/A DPRINTF(Checkpoint, "Serializing ARM HDLCD\n"); 1014981SN/A 1024981SN/A SERIALIZE_SCALAR(int_rawstat); 1034981SN/A SERIALIZE_SCALAR(int_mask); 1044981SN/A 1054981SN/A SERIALIZE_SCALAR(fb_base); 1064981SN/A SERIALIZE_SCALAR(fb_line_length); 1074981SN/A SERIALIZE_SCALAR(fb_line_count); 108633SN/A SERIALIZE_SCALAR(fb_line_pitch); 1092SN/A SERIALIZE_SCALAR(bus_options); 1102SN/A 1112SN/A SERIALIZE_SCALAR(v_sync); 1122SN/A SERIALIZE_SCALAR(v_back_porch); 1132SN/A SERIALIZE_SCALAR(v_data); 1142SN/A SERIALIZE_SCALAR(v_front_porch); 1151435SN/A 1161954SN/A SERIALIZE_SCALAR(h_sync); 1171435SN/A SERIALIZE_SCALAR(h_back_porch); 1181954SN/A SERIALIZE_SCALAR(h_data); 11911071SN/A SERIALIZE_SCALAR(h_front_porch); 1201435SN/A 1212SN/A SERIALIZE_SCALAR(polarities); 1222SN/A 12310905SN/A SERIALIZE_SCALAR(command); 124558SN/A SERIALIZE_SCALAR(pixel_format); 12510905SN/A SERIALIZE_SCALAR(red_select); 12610905SN/A SERIALIZE_SCALAR(green_select); 127558SN/A SERIALIZE_SCALAR(blue_select); 128558SN/A 129558SN/A SERIALIZE_OBJ(pixelPump); 13010905SN/A if (enabled()) 131558SN/A dmaEngine->serializeSection(cp, "dmaEngine"); 13210905SN/A} 13310905SN/A 134558SN/Avoid 135558SN/AHDLcd::unserialize(CheckpointIn &cp) 136558SN/A{ 1372566SN/A DPRINTF(Checkpoint, "Unserializing ARM HDLCD\n"); 138633SN/A 139633SN/A UNSERIALIZE_SCALAR(int_rawstat); 140633SN/A UNSERIALIZE_SCALAR(int_mask); 141633SN/A 142633SN/A UNSERIALIZE_SCALAR(fb_base); 143633SN/A UNSERIALIZE_SCALAR(fb_line_length); 144633SN/A UNSERIALIZE_SCALAR(fb_line_count); 1452SN/A UNSERIALIZE_SCALAR(fb_line_pitch); 1462SN/A UNSERIALIZE_SCALAR(bus_options); 1472SN/A 1482SN/A UNSERIALIZE_SCALAR(v_sync); 1492SN/A UNSERIALIZE_SCALAR(v_back_porch); 150633SN/A UNSERIALIZE_SCALAR(v_data); 151633SN/A UNSERIALIZE_SCALAR(v_front_porch); 15211071SN/A 15311071SN/A UNSERIALIZE_SCALAR(h_sync); 15411071SN/A UNSERIALIZE_SCALAR(h_back_porch); 155633SN/A UNSERIALIZE_SCALAR(h_data); 15611071SN/A UNSERIALIZE_SCALAR(h_front_porch); 157633SN/A 158633SN/A UNSERIALIZE_SCALAR(polarities); 159195SN/A 1602SN/A UNSERIALIZE_SCALAR(command); 1612SN/A UNSERIALIZE_SCALAR(pixel_format); 1622SN/A UNSERIALIZE_SCALAR(red_select); 1632SN/A UNSERIALIZE_SCALAR(green_select); 1642SN/A UNSERIALIZE_SCALAR(blue_select); 1652SN/A 16611071SN/A { 16711071SN/A // Try to unserialize the pixel pump. It might not exist if 16811071SN/A // we're unserializing an old checkpoint. 16911071SN/A ScopedCheckpointSection sec(cp, "pixelPump"); 17011071SN/A if (cp.sectionExists(Serializable::currentSection())) 17111071SN/A pixelPump.unserialize(cp); 17211071SN/A } 17311071SN/A 17411071SN/A if (enabled()) { 17511071SN/A // Create the DMA engine and read its state from the 17611071SN/A // checkpoint. We don't need to worry about the pixel pump as 17711071SN/A // it is a proper SimObject. 17811071SN/A createDmaEngine(); 17911071SN/A dmaEngine->unserializeSection(cp, "dmaEngine"); 18011071SN/A 18111071SN/A conv = pixelConverter(); 18211071SN/A } 1832SN/A} 1842566SN/A 1852SN/Avoid 1862SN/AHDLcd::drainResume() 187633SN/A{ 1882SN/A AmbaDmaDevice::drainResume(); 1892SN/A 1902SN/A // We restored from an old checkpoint without a pixel pump, start 191633SN/A // an new refresh. This typically happens when restoring from old 1922SN/A // checkpoints. 1932SN/A if (enabled() && !pixelPump.active()) 1942SN/A pixelPump.start(displayTimings()); 19511701Smichael.lebeane@amd.com 1965190SN/A // We restored from a checkpoint and need to update the VNC server 1975190SN/A if (pixelPump.active() && vnc) 1985190SN/A vnc->setDirty(); 199633SN/A} 200633SN/A 2017823SN/A// read registers and frame buffer 2022SN/ATick 2032SN/AHDLcd::read(PacketPtr pkt) 2042SN/A{ 2052SN/A assert(pkt->getAddr() >= pioAddr && 206558SN/A pkt->getAddr() < pioAddr + pioSize); 20710905SN/A 208558SN/A const Addr daddr(pkt->getAddr() - pioAddr); 20910469SN/A panic_if(pkt->getSize() != 4, 21010905SN/A "Unhandled read size (address: 0x.4x, size: %u)", 2111435SN/A daddr, pkt->getSize()); 21210905SN/A 213558SN/A const uint32_t data(readReg(daddr)); 214633SN/A DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data); 21510905SN/A 216558SN/A pkt->set<uint32_t>(data); 217633SN/A pkt->makeAtomicResponse(); 21810905SN/A return pioDelay; 219574SN/A} 220574SN/A 22111071SN/A// write registers and frame buffer 22211071SN/ATick 22311071SN/AHDLcd::write(PacketPtr pkt) 22411071SN/A{ 22511071SN/A assert(pkt->getAddr() >= pioAddr && 22611071SN/A pkt->getAddr() < pioAddr + pioSize); 22711071SN/A 22811071SN/A const Addr daddr(pkt->getAddr() - pioAddr); 22911071SN/A panic_if(pkt->getSize() != 4, 230558SN/A "Unhandled read size (address: 0x.4x, size: %u)", 231558SN/A daddr, pkt->getSize()); 232558SN/A const uint32_t data(pkt->get<uint32_t>()); 23310905SN/A DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data); 234558SN/A 235574SN/A writeReg(daddr, data); 23610905SN/A 237574SN/A pkt->makeAtomicResponse(); 23811701Smichael.lebeane@amd.com return pioDelay; 23910905SN/A} 240558SN/A 241558SN/Auint32_t 242574SN/AHDLcd::readReg(Addr offset) 24310905SN/A{ 244558SN/A switch (offset) { 245574SN/A case Version: return version; 24610905SN/A 2475606SN/A case Int_RawStat: return int_rawstat; 248558SN/A case Int_Clear: 24911071SN/A panic("HDLCD INT_CLEAR register is Write-Only\n"); 25011071SN/A case Int_Mask: return int_mask; 25111071SN/A case Int_Status: return intStatus(); 25211071SN/A 25311071SN/A case Fb_Base: return fb_base; 25411701Smichael.lebeane@amd.com case Fb_Line_Length: return fb_line_length; 25511071SN/A case Fb_Line_Count: return fb_line_count; 25611071SN/A case Fb_Line_Pitch: return fb_line_pitch; 25711071SN/A case Bus_Options: return bus_options; 25811071SN/A 25911071SN/A case V_Sync: return v_sync; 26011071SN/A case V_Back_Porch: return v_back_porch; 26111071SN/A case V_Data: return v_data; 26211071SN/A case V_Front_Porch: return v_front_porch; 26311071SN/A case H_Sync: return h_sync; 26411071SN/A case H_Back_Porch: return h_back_porch; 26511071SN/A case H_Data: return h_data; 26611071SN/A case H_Front_Porch: return h_front_porch; 26711071SN/A case Polarities: return polarities; 26811071SN/A 26911071SN/A case Command: return command; 27011071SN/A case Pixel_Format: return pixel_format; 27111071SN/A case Red_Select: return red_select; 27211071SN/A case Green_Select: return green_select; 27311071SN/A case Blue_Select: return blue_select; 274558SN/A 275558SN/A default: 2764762SN/A panic("Tried to read HDLCD register that doesn't exist\n", offset); 2774762SN/A } 2782SN/A} 2794981SN/A 2802SN/Avoid 281HDLcd::writeReg(Addr offset, uint32_t value) 282{ 283 switch (offset) { 284 case Version: 285 panic("HDLCD VERSION register is read-Only\n"); 286 287 case Int_RawStat: 288 intRaise(value); 289 return; 290 case Int_Clear: 291 intClear(value); 292 return; 293 case Int_Mask: 294 intMask(value); 295 return; 296 case Int_Status: 297 panic("HDLCD INT_STATUS register is read-Only\n"); 298 break; 299 300 case Fb_Base: 301 fb_base = value; 302 return; 303 304 case Fb_Line_Length: 305 fb_line_length = value; 306 return; 307 308 case Fb_Line_Count: 309 fb_line_count = value; 310 return; 311 312 case Fb_Line_Pitch: 313 fb_line_pitch = value; 314 return; 315 316 case Bus_Options: { 317 const BusOptsReg old_bus_options(bus_options); 318 bus_options = value; 319 320 if (bus_options.max_outstanding != old_bus_options.max_outstanding) { 321 DPRINTF(HDLcd, 322 "Changing HDLcd outstanding DMA transactions: %d -> %d\n", 323 old_bus_options.max_outstanding, 324 bus_options.max_outstanding); 325 326 } 327 328 if (bus_options.burst_len != old_bus_options.burst_len) { 329 DPRINTF(HDLcd, 330 "Changing HDLcd DMA burst flags: 0x%x -> 0x%x\n", 331 old_bus_options.burst_len, bus_options.burst_len); 332 } 333 } return; 334 335 case V_Sync: 336 v_sync = value; 337 return; 338 case V_Back_Porch: 339 v_back_porch = value; 340 return; 341 case V_Data: 342 v_data = value; 343 return; 344 case V_Front_Porch: 345 v_front_porch = value; 346 return; 347 348 case H_Sync: 349 h_sync = value; 350 return; 351 case H_Back_Porch: 352 h_back_porch = value; 353 return; 354 case H_Data: 355 h_data = value; 356 return; 357 case H_Front_Porch: 358 h_front_porch = value; 359 return; 360 361 case Polarities: 362 polarities = value; 363 return; 364 365 case Command: { 366 const CommandReg new_command(value); 367 368 if (new_command.enable != command.enable) { 369 DPRINTF(HDLcd, "HDLCD switched %s\n", 370 new_command.enable ? "on" : "off"); 371 372 if (new_command.enable) { 373 cmdEnable(); 374 } else { 375 cmdDisable(); 376 } 377 } 378 command = new_command; 379 } return; 380 381 case Pixel_Format: 382 pixel_format = value; 383 return; 384 385 case Red_Select: 386 red_select = value; 387 return; 388 case Green_Select: 389 green_select = value; 390 return; 391 case Blue_Select: 392 blue_select = value; 393 return; 394 395 default: 396 panic("Tried to write HDLCD register that doesn't exist\n", offset); 397 return; 398 } 399} 400 401PixelConverter 402HDLcd::pixelConverter() const 403{ 404 ByteOrder byte_order( 405 pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder); 406 407 /* Some Linux kernels have a broken driver that swaps the red and 408 * blue color select registers. */ 409 if (!workaroundSwapRB) { 410 return PixelConverter( 411 pixel_format.bytes_per_pixel + 1, 412 red_select.offset, green_select.offset, blue_select.offset, 413 red_select.size, green_select.size, blue_select.size, 414 byte_order); 415 } else { 416 return PixelConverter( 417 pixel_format.bytes_per_pixel + 1, 418 blue_select.offset, green_select.offset, red_select.offset, 419 blue_select.size, green_select.size, red_select.size, 420 byte_order); 421 } 422} 423 424DisplayTimings 425HDLcd::displayTimings() const 426{ 427 return DisplayTimings( 428 h_data.val + 1, v_data.val + 1, 429 h_back_porch.val + 1, h_sync.val + 1, h_front_porch.val + 1, 430 v_back_porch.val + 1, v_sync.val + 1, v_front_porch.val + 1); 431} 432 433void 434HDLcd::createDmaEngine() 435{ 436 if (bus_options.max_outstanding == 0) { 437 warn("Maximum number of outstanding DMA transfers set to 0."); 438 return; 439 } 440 441 const uint32_t dma_burst_flags(bus_options.burst_len); 442 const uint32_t dma_burst_len( 443 dma_burst_flags ? 444 (1UL << (findMsbSet(dma_burst_flags) - 1)) : 445 MAX_BURST_LEN); 446 // Some drivers seem to set the DMA line count incorrectly. This 447 // could either be a driver bug or a specification bug. Unlike for 448 // timings, the specification does not require 1 to be added to 449 // the DMA engine's line count. 450 const uint32_t dma_lines( 451 fb_line_count + (workaroundDmaLineCount ? 1 : 0)); 452 453 dmaEngine.reset(new DmaEngine( 454 *this, pixelBufferSize, 455 AXI_PORT_WIDTH * dma_burst_len, 456 bus_options.max_outstanding, 457 fb_line_length, fb_line_pitch, dma_lines)); 458} 459 460void 461HDLcd::cmdEnable() 462{ 463 createDmaEngine(); 464 conv = pixelConverter(); 465 pixelPump.start(displayTimings()); 466} 467 468void 469HDLcd::cmdDisable() 470{ 471 pixelPump.stop(); 472 dmaEngine->abortFrame(); 473} 474 475bool 476HDLcd::pxlNext(Pixel &p) 477{ 478 uint8_t pixel_data[MAX_PIXEL_SIZE]; 479 assert(conv.length <= sizeof(pixel_data)); 480 if (dmaEngine->tryGet(pixel_data, conv.length)) { 481 p = conv.toPixel(pixel_data); 482 return true; 483 } else { 484 return false; 485 } 486} 487 488void 489HDLcd::pxlVSyncBegin() 490{ 491 DPRINTF(HDLcd, "Raising VSYNC interrupt.\n"); 492 intRaise(INT_VSYNC); 493} 494 495void 496HDLcd::pxlVSyncEnd() 497{ 498 DPRINTF(HDLcd, "End of VSYNC, starting DMA engine\n"); 499 dmaEngine->startFrame(fb_base); 500} 501 502void 503HDLcd::pxlUnderrun() 504{ 505 DPRINTF(HDLcd, "Buffer underrun, stopping DMA fill.\n"); 506 intRaise(INT_UNDERRUN); 507 dmaEngine->abortFrame(); 508} 509 510void 511HDLcd::pxlFrameDone() 512{ 513 DPRINTF(HDLcd, "Reached end of last visible line.\n"); 514 515 if (dmaEngine->size()) { 516 warn("HDLCD %u bytes still in FIFO after frame: Ensure that DMA " 517 "and PixelPump configuration is consistent\n", 518 dmaEngine->size()); 519 dmaEngine->dumpSettings(); 520 pixelPump.dumpSettings(); 521 } 522 523 if (vnc) 524 vnc->setDirty(); 525 526 if (enableCapture) { 527 if (!pic) { 528 pic = simout.create( 529 csprintf("%s.framebuffer.bmp", sys->name()), 530 true); 531 } 532 533 assert(pic); 534 pic->seekp(0); 535 bmp.write(*pic); 536 } 537} 538 539void 540HDLcd::setInterrupts(uint32_t ints, uint32_t mask) 541{ 542 const bool old_ints(intStatus()); 543 544 int_mask = mask; 545 int_rawstat = ints; 546 547 if (!old_ints && intStatus()) { 548 gic->sendInt(intNum); 549 } else if (old_ints && !intStatus()) { 550 gic->clearInt(intNum); 551 } 552} 553 554HDLcd::DmaEngine::DmaEngine(HDLcd &_parent, size_t size, 555 unsigned request_size, unsigned max_pending, 556 size_t line_size, ssize_t line_pitch, unsigned num_lines) 557 : DmaReadFifo( 558 _parent.dmaPort, size, request_size, max_pending, 559 Request::UNCACHEABLE), 560 parent(_parent), 561 lineSize(line_size), linePitch(line_pitch), numLines(num_lines), 562 nextLineAddr(0) 563{ 564} 565 566void 567HDLcd::DmaEngine::serialize(CheckpointOut &cp) const 568{ 569 DmaReadFifo::serialize(cp); 570 571 SERIALIZE_SCALAR(nextLineAddr); 572 SERIALIZE_SCALAR(frameEnd); 573} 574 575void 576HDLcd::DmaEngine::unserialize(CheckpointIn &cp) 577{ 578 DmaReadFifo::unserialize(cp); 579 580 UNSERIALIZE_SCALAR(nextLineAddr); 581 UNSERIALIZE_SCALAR(frameEnd); 582} 583 584void 585HDLcd::DmaEngine::startFrame(Addr fb_base) 586{ 587 nextLineAddr = fb_base; 588 frameEnd = fb_base + numLines * linePitch; 589 590 startFill(nextLineAddr, lineSize); 591} 592 593void 594HDLcd::DmaEngine::abortFrame() 595{ 596 nextLineAddr = frameEnd; 597 stopFill(); 598 flush(); 599} 600 601 602void 603HDLcd::DmaEngine::dumpSettings() 604{ 605 inform("DMA line size: %u bytes", lineSize); 606 inform("DMA line pitch: %i bytes", linePitch); 607 inform("DMA num lines: %u", numLines); 608} 609 610void 611HDLcd::DmaEngine::onEndOfBlock() 612{ 613 if (nextLineAddr == frameEnd) 614 // We're done with this frame. Ignore calls to this method 615 // until the next frame has been started. 616 return; 617 618 nextLineAddr += linePitch; 619 if (nextLineAddr != frameEnd) 620 startFill(nextLineAddr, lineSize); 621} 622 623void 624HDLcd::DmaEngine::onIdle() 625{ 626 parent.intRaise(INT_DMA_END); 627} 628 629void 630HDLcd::PixelPump::dumpSettings() 631{ 632 const DisplayTimings &t(timings()); 633 634 inform("PixelPump width: %u", t.width); 635 inform("PixelPump height: %u", t.height); 636 637 inform("PixelPump horizontal back porch: %u", t.hBackPorch); 638 inform("PixelPump horizontal fron porch: %u", t.hFrontPorch); 639 inform("PixelPump horizontal fron porch: %u", t.hSync); 640 641 inform("PixelPump vertical back porch: %u", t.vBackPorch); 642 inform("PixelPump vertical fron porch: %u", t.vFrontPorch); 643 inform("PixelPump vertical fron porch: %u", t.vSync); 644} 645 646 647HDLcd * 648HDLcdParams::create() 649{ 650 return new HDLcd(this); 651} 652