hdlcd.cc revision 11898
12SN/A/*
21762SN/A * Copyright (c) 2010-2013, 2015, 2017 ARM Limited
37897Shestness@cs.utexas.edu * All rights reserved
42SN/A *
52SN/A * The license below extends only to copyright in the software and shall
62SN/A * not be construed as granting a license to any other intellectual
72SN/A * property including but not limited to intellectual property relating
82SN/A * to a hardware implementation of the functionality of the software
92SN/A * licensed hereunder.  You may use the software subject to the license
102SN/A * terms below provided that you ensure that this notice is replicated
112SN/A * unmodified and in its entirety in all distributions of the software,
122SN/A * modified or unmodified, in source code or in binary form.
132SN/A *
142SN/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
282665Ssaidi@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
292665Ssaidi@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
302665Ssaidi@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
312665Ssaidi@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
327897Shestness@cs.utexas.edu * 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
3975SN/A */
402SN/A
412439SN/A#include "dev/arm/hdlcd.hh"
422439SN/A
43603SN/A#include "base/output.hh"
442986Sgblack@eecs.umich.edu#include "base/trace.hh"
45603SN/A#include "base/vnc/vncinput.hh"
464762Snate@binkert.org#include "debug/Checkpoint.hh"
472520SN/A#include "debug/HDLcd.hh"
484762Snate@binkert.org#include "dev/arm/amba_device.hh"
492378SN/A#include "dev/arm/base_gic.hh"
506658Snate@binkert.org#include "mem/packet.hh"
512378SN/A#include "mem/packet_access.hh"
52722SN/A#include "params/HDLcd.hh"
532378SN/A#include "sim/system.hh"
54312SN/A
551634SN/Ausing std::vector;
562680Sktlim@umich.edu
571634SN/A
582521SN/A// initialize hdlcd registers
592378SN/AHDLcd::HDLcd(const HDLcdParams *p)
602378SN/A    : AmbaDmaDevice(p, 0xFFFF),
61803SN/A      // Parameters
627723SAli.Saidi@ARM.com      vnc(p->vnc),
637723SAli.Saidi@ARM.com      workaroundSwapRB(p->workaround_swap_rb),
643960Sgblack@eecs.umich.edu      workaroundDmaLineCount(p->workaround_dma_line_count),
652378SN/A      addrRanges{RangeSize(pioAddr, pioSize)},
666658Snate@binkert.org      enableCapture(p->enable_capture),
672SN/A      pixelBufferSize(p->pixel_buffer_size),
682SN/A      virtRefreshRate(p->virt_refresh_rate),
692SN/A
70603SN/A      // Registers
712901Ssaidi@eecs.umich.edu      version(VERSION_RESETV),
722902Ssaidi@eecs.umich.edu      int_rawstat(0), int_mask(0),
732902Ssaidi@eecs.umich.edu
744762Snate@binkert.org      fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0),
754762Snate@binkert.org      bus_options(BUS_OPTIONS_RESETV),
764762Snate@binkert.org
774762Snate@binkert.org      v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0),
784762Snate@binkert.org      h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0),
794762Snate@binkert.org      polarities(0),
802901Ssaidi@eecs.umich.edu
812901Ssaidi@eecs.umich.edu      command(0),
822901Ssaidi@eecs.umich.edu
832901Ssaidi@eecs.umich.edu      pixel_format(0),
842901Ssaidi@eecs.umich.edu      red_select(0), green_select(0), blue_select(0),
854762Snate@binkert.org
862901Ssaidi@eecs.umich.edu      virtRefreshEvent(this),
872521SN/A      // Other
882SN/A      bmp(&pixelPump.fb), pic(NULL), conv(PixelConverter::rgba8888_le),
892SN/A      pixelPump(*this, *p->pxl_clk, p->pixel_chunk)
902680Sktlim@umich.edu{
915714Shsul@eecs.umich.edu    if (vnc)
921806SN/A        vnc->setFrameBuffer(&pixelPump.fb);
936221Snate@binkert.org}
945713Shsul@eecs.umich.edu
955713Shsul@eecs.umich.eduHDLcd::~HDLcd()
965713Shsul@eecs.umich.edu{
975713Shsul@eecs.umich.edu}
985714Shsul@eecs.umich.edu
991806SN/Avoid
1006227Snate@binkert.orgHDLcd::regStats()
1015714Shsul@eecs.umich.edu{
1021806SN/A    AmbaDmaDevice::regStats();
103180SN/A
1046029Ssteve.reinhardt@amd.com    using namespace Stats;
1056029Ssteve.reinhardt@amd.com
1066029Ssteve.reinhardt@amd.com    stats.underruns
1076029Ssteve.reinhardt@amd.com        .name(name() + ".underruns")
1088460SAli.Saidi@ARM.com        .desc("number of buffer underruns")
1098460SAli.Saidi@ARM.com        .flags(nozero)
1108460SAli.Saidi@ARM.com        ;
1118460SAli.Saidi@ARM.com}
1128460SAli.Saidi@ARM.com
1138460SAli.Saidi@ARM.comvoid
1148460SAli.Saidi@ARM.comHDLcd::serialize(CheckpointOut &cp) const
1158460SAli.Saidi@ARM.com{
1162378SN/A    DPRINTF(Checkpoint, "Serializing ARM HDLCD\n");
1172378SN/A
1182378SN/A    SERIALIZE_SCALAR(int_rawstat);
1192378SN/A    SERIALIZE_SCALAR(int_mask);
1202520SN/A
1212520SN/A    SERIALIZE_SCALAR(fb_base);
1227723SAli.Saidi@ARM.com    SERIALIZE_SCALAR(fb_line_length);
1237723SAli.Saidi@ARM.com    SERIALIZE_SCALAR(fb_line_count);
1242520SN/A    SERIALIZE_SCALAR(fb_line_pitch);
1251885SN/A    SERIALIZE_SCALAR(bus_options);
1261070SN/A
127954SN/A    SERIALIZE_SCALAR(v_sync);
1281070SN/A    SERIALIZE_SCALAR(v_back_porch);
1291070SN/A    SERIALIZE_SCALAR(v_data);
1301070SN/A    SERIALIZE_SCALAR(v_front_porch);
1311070SN/A
1321070SN/A    SERIALIZE_SCALAR(h_sync);
1331070SN/A    SERIALIZE_SCALAR(h_back_porch);
1341070SN/A    SERIALIZE_SCALAR(h_data);
1351070SN/A    SERIALIZE_SCALAR(h_front_porch);
1361070SN/A
1371070SN/A    SERIALIZE_SCALAR(polarities);
1381070SN/A
1391070SN/A    SERIALIZE_SCALAR(command);
1407580SAli.Saidi@arm.com    SERIALIZE_SCALAR(pixel_format);
1417580SAli.Saidi@arm.com    SERIALIZE_SCALAR(red_select);
1427580SAli.Saidi@arm.com    SERIALIZE_SCALAR(green_select);
1437580SAli.Saidi@arm.com    SERIALIZE_SCALAR(blue_select);
1447580SAli.Saidi@arm.com
1457580SAli.Saidi@arm.com    SERIALIZE_OBJ(pixelPump);
1467580SAli.Saidi@arm.com    if (enabled())
1477580SAli.Saidi@arm.com        dmaEngine->serializeSection(cp, "dmaEngine");
1482378SN/A}
1492378SN/A
1507770SAli.Saidi@ARM.comvoid
1512378SN/AHDLcd::unserialize(CheckpointIn &cp)
1524997Sgblack@eecs.umich.edu{
1537770SAli.Saidi@ARM.com    DPRINTF(Checkpoint, "Unserializing ARM HDLCD\n");
1544997Sgblack@eecs.umich.edu
1554997Sgblack@eecs.umich.edu    UNSERIALIZE_SCALAR(int_rawstat);
1564997Sgblack@eecs.umich.edu    UNSERIALIZE_SCALAR(int_mask);
1574997Sgblack@eecs.umich.edu
1587770SAli.Saidi@ARM.com    UNSERIALIZE_SCALAR(fb_base);
1594997Sgblack@eecs.umich.edu    UNSERIALIZE_SCALAR(fb_line_length);
1604997Sgblack@eecs.umich.edu    UNSERIALIZE_SCALAR(fb_line_count);
1615795Ssaidi@eecs.umich.edu    UNSERIALIZE_SCALAR(fb_line_pitch);
1625795Ssaidi@eecs.umich.edu    UNSERIALIZE_SCALAR(bus_options);
1635795Ssaidi@eecs.umich.edu
1645795Ssaidi@eecs.umich.edu    UNSERIALIZE_SCALAR(v_sync);
1655795Ssaidi@eecs.umich.edu    UNSERIALIZE_SCALAR(v_back_porch);
1665795Ssaidi@eecs.umich.edu    UNSERIALIZE_SCALAR(v_data);
1672378SN/A    UNSERIALIZE_SCALAR(v_front_porch);
1682378SN/A
1692378SN/A    UNSERIALIZE_SCALAR(h_sync);
1701885SN/A    UNSERIALIZE_SCALAR(h_back_porch);
1714762Snate@binkert.org    UNSERIALIZE_SCALAR(h_data);
1727914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(h_front_porch);
1737914SBrad.Beckmann@amd.com
1747914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(polarities);
1757914SBrad.Beckmann@amd.com
1767914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(command);
1777914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(pixel_format);
1787914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(red_select);
1797914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(green_select);
1807914SBrad.Beckmann@amd.com    UNSERIALIZE_SCALAR(blue_select);
1817914SBrad.Beckmann@amd.com
1827914SBrad.Beckmann@amd.com    {
1837914SBrad.Beckmann@amd.com        // Try to unserialize the pixel pump. It might not exist if
1847914SBrad.Beckmann@amd.com        // we're unserializing an old checkpoint.
1857914SBrad.Beckmann@amd.com        ScopedCheckpointSection sec(cp, "pixelPump");
1867914SBrad.Beckmann@amd.com        if (cp.sectionExists(Serializable::currentSection()))
1877914SBrad.Beckmann@amd.com            pixelPump.unserialize(cp);
1887914SBrad.Beckmann@amd.com    }
1897914SBrad.Beckmann@amd.com
1907914SBrad.Beckmann@amd.com    if (enabled()) {
1917914SBrad.Beckmann@amd.com        // Create the DMA engine and read its state from the
1927914SBrad.Beckmann@amd.com        // checkpoint. We don't need to worry about the pixel pump as
1937914SBrad.Beckmann@amd.com        // it is a proper SimObject.
1947914SBrad.Beckmann@amd.com        createDmaEngine();
1957914SBrad.Beckmann@amd.com        dmaEngine->unserializeSection(cp, "dmaEngine");
1967914SBrad.Beckmann@amd.com
1977914SBrad.Beckmann@amd.com        conv = pixelConverter();
1987914SBrad.Beckmann@amd.com    }
1997914SBrad.Beckmann@amd.com}
2007914SBrad.Beckmann@amd.com
2017914SBrad.Beckmann@amd.comvoid
2027914SBrad.Beckmann@amd.comHDLcd::drainResume()
2037914SBrad.Beckmann@amd.com{
2047914SBrad.Beckmann@amd.com    AmbaDmaDevice::drainResume();
2057914SBrad.Beckmann@amd.com
2067914SBrad.Beckmann@amd.com    if (enabled()) {
2077914SBrad.Beckmann@amd.com        if (sys->bypassCaches()) {
2087914SBrad.Beckmann@amd.com            // We restart the HDLCD if we are in KVM mode. This
2097914SBrad.Beckmann@amd.com            // ensures that we always use the fast refresh logic if we
2107914SBrad.Beckmann@amd.com            // resume in KVM mode.
2117914SBrad.Beckmann@amd.com            cmdDisable();
2127914SBrad.Beckmann@amd.com            cmdEnable();
2137914SBrad.Beckmann@amd.com        } else if (!pixelPump.active()) {
2142901Ssaidi@eecs.umich.edu            // We restored from an old checkpoint without a pixel
2152424SN/A            // pump, start an new refresh. This typically happens when
2161885SN/A            // restoring from old checkpoints.
2171885SN/A            cmdEnable();
2181885SN/A        }
2191885SN/A    }
2201885SN/A
2212158SN/A    // We restored from a checkpoint and need to update the VNC server
2221885SN/A    if (pixelPump.active() && vnc)
2231885SN/A        vnc->setDirty();
2241885SN/A}
2251885SN/A
2261885SN/Avoid
2271885SN/AHDLcd::virtRefresh()
2282989Ssaidi@eecs.umich.edu{
2291885SN/A    pixelPump.renderFrame();
2301913SN/A    schedule(virtRefreshEvent, (curTick() + virtRefreshRate));
2311885SN/A}
2321885SN/A
2331885SN/A// read registers and frame buffer
2341885SN/ATick
2351885SN/AHDLcd::read(PacketPtr pkt)
2361885SN/A{
2371885SN/A    assert(pkt->getAddr() >= pioAddr &&
2381885SN/A           pkt->getAddr() < pioAddr + pioSize);
2391885SN/A
2401885SN/A    const Addr daddr(pkt->getAddr() - pioAddr);
2411885SN/A    panic_if(pkt->getSize() != 4,
2422989Ssaidi@eecs.umich.edu             "Unhandled read size (address: 0x.4x, size: %u)",
2431885SN/A             daddr, pkt->getSize());
2441885SN/A
2451885SN/A    const uint32_t data(readReg(daddr));
2461885SN/A    DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data);
2472378SN/A
24877SN/A    pkt->set<uint32_t>(data);
2496658Snate@binkert.org    pkt->makeAtomicResponse();
2501070SN/A    return pioDelay;
2513960Sgblack@eecs.umich.edu}
2521070SN/A
2531070SN/A// write registers and frame buffer
2544762Snate@binkert.orgTick
2551070SN/AHDLcd::write(PacketPtr pkt)
2562158SN/A{
2572158SN/A    assert(pkt->getAddr() >= pioAddr &&
2581070SN/A           pkt->getAddr() < pioAddr + pioSize);
2592158SN/A
2601070SN/A    const Addr daddr(pkt->getAddr() - pioAddr);
2612SN/A    panic_if(pkt->getSize() != 4,
2622SN/A             "Unhandled read size (address: 0x.4x, size: %u)",
2637733SAli.Saidi@ARM.com             daddr, pkt->getSize());
2641129SN/A    const uint32_t data(pkt->get<uint32_t>());
2652158SN/A    DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data);
2662158SN/A
2671070SN/A    writeReg(daddr, data);
2682378SN/A
2692378SN/A    pkt->makeAtomicResponse();
2701070SN/A    return pioDelay;
2711070SN/A}
2721070SN/A
2731070SN/Auint32_t
2741070SN/AHDLcd::readReg(Addr offset)
2751070SN/A{
2761070SN/A    switch (offset) {
2771070SN/A      case Version: return version;
2781070SN/A
2791070SN/A      case Int_RawStat: return int_rawstat;
2801070SN/A      case Int_Clear:
2811070SN/A        panic("HDLCD INT_CLEAR register is Write-Only\n");
2821070SN/A      case Int_Mask: return int_mask;
2831070SN/A      case Int_Status: return intStatus();
2841070SN/A
2851070SN/A      case Fb_Base: return fb_base;
2861070SN/A      case Fb_Line_Length: return fb_line_length;
2871070SN/A      case Fb_Line_Count: return fb_line_count;
2882378SN/A      case Fb_Line_Pitch: return fb_line_pitch;
2892378SN/A      case Bus_Options: return bus_options;
2902378SN/A
2912378SN/A      case V_Sync: return v_sync;
2922378SN/A      case V_Back_Porch: return v_back_porch;
2932378SN/A      case V_Data: return v_data;
2945718Shsul@eecs.umich.edu      case V_Front_Porch: return v_front_porch;
2955713Shsul@eecs.umich.edu      case H_Sync: return h_sync;
2961070SN/A      case H_Back_Porch: return h_back_porch;
2971070SN/A      case H_Data: return h_data;
2981070SN/A      case H_Front_Porch: return h_front_porch;
2997897Shestness@cs.utexas.edu      case Polarities: return polarities;
3002SN/A
30177SN/A      case Command: return command;
3027897Shestness@cs.utexas.edu      case Pixel_Format: return pixel_format;
3037897Shestness@cs.utexas.edu      case Red_Select: return red_select;
3047897Shestness@cs.utexas.edu      case Green_Select: return green_select;
3052SN/A      case Blue_Select: return blue_select;
3062SN/A
3072SN/A      default:
3082SN/A        panic("Tried to read HDLCD register that doesn't  exist\n", offset);
3092SN/A    }
3102SN/A}
3112SN/A
3122SN/Avoid
3132SN/AHDLcd::writeReg(Addr offset, uint32_t value)
3142SN/A{
3152158SN/A    switch (offset) {
3162158SN/A      case Version:
3172SN/A        panic("HDLCD VERSION register is read-Only\n");
3182SN/A
3192SN/A      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