/*
- * Copyright (c) 2010 ARM Limited
+ * Copyright (c) 2010-2012, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* Ali Saidi
*/
-#include "base/bitmap.hh"
+#include "dev/arm/pl111.hh"
+
#include "base/output.hh"
#include "base/trace.hh"
-#include "base/vnc/vncserver.hh"
+#include "base/vnc/vncinput.hh"
+#include "debug/PL111.hh"
+#include "debug/Uart.hh"
#include "dev/arm/amba_device.hh"
-#include "dev/arm/gic.hh"
-#include "dev/arm/pl111.hh"
+#include "dev/arm/base_gic.hh"
#include "mem/packet.hh"
#include "mem/packet_access.hh"
+#include "sim/system.hh"
-using namespace AmbaDev;
+// clang complains about std::set being overloaded with Packet::set if
+// we open up the entire namespace std
+using std::vector;
// initialize clcd registers
Pl111::Pl111(const Params *p)
lcdRis(0), lcdMis(0),
clcdCrsrCtrl(0), clcdCrsrConfig(0), clcdCrsrPalette0(0),
clcdCrsrPalette1(0), clcdCrsrXY(0), clcdCrsrClip(0), clcdCrsrImsc(0),
- clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0), clock(p->clock),
- vncserver(p->vnc), bmp(NULL), width(LcdMaxWidth), height(LcdMaxHeight),
+ clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0),
+ pixelClock(p->pixel_clock),
+ converter(PixelConverter::rgba8888_le), fb(LcdMaxWidth, LcdMaxHeight),
+ vnc(p->vnc), bmp(&fb), pic(NULL),
+ width(LcdMaxWidth), height(LcdMaxHeight),
bytesPerPixel(4), startTime(0), startAddr(0), maxAddr(0), curAddr(0),
- waterMark(0), dmaPendingNum(0), readEvent(this), fillFifoEvent(this),
- dmaDoneEvent(maxOutstandingDma, this), intEvent(this)
+ waterMark(0), dmaPendingNum(0),
+ readEvent([this]{ readFramebuffer(); }, name()),
+ fillFifoEvent([this]{ fillFifo(); }, name()),
+ dmaDoneEventAll(maxOutstandingDma, this),
+ dmaDoneEventFree(maxOutstandingDma),
+ intEvent([this]{ generateInterrupt(); }, name()),
+ enableCapture(p->enable_capture)
{
pioSize = 0xFFFF;
- pic = simout.create("framebuffer.bmp", true);
-
- dmaBuffer = new uint8_t[LcdMaxWidth * LcdMaxHeight * sizeof(uint32_t)];
+ dmaBuffer = new uint8_t[buffer_size];
memset(lcdPalette, 0, sizeof(lcdPalette));
memset(cursorImage, 0, sizeof(cursorImage));
- memset(dmaBuffer, 0, sizeof(dmaBuffer));
+ memset(dmaBuffer, 0, buffer_size);
+
+ for (int i = 0; i < maxOutstandingDma; ++i)
+ dmaDoneEventFree[i] = &dmaDoneEventAll[i];
- if (vncserver)
- vncserver->setFramebufferAddr(dmaBuffer);
+ if (vnc)
+ vnc->setFrameBuffer(&fb);
+}
+
+Pl111::~Pl111()
+{
+ delete[] dmaBuffer;
}
// read registers and frame buffer
pkt->getAddr() < pioAddr + pioSize);
Addr daddr = pkt->getAddr() - pioAddr;
- pkt->allocate();
DPRINTF(PL111, " read register %#x size=%d\n", daddr, pkt->getSize());
data = clcdCrsrMis;
break;
default:
- if (AmbaDev::readId(pkt, AMBA_ID, pioAddr)) {
+ if (readId(pkt, AMBA_ID, pioAddr)) {
// Hack for variable size accesses
- data = pkt->get<uint32_t>();
+ data = pkt->getLE<uint32_t>();
break;
} else if (daddr >= CrsrImage && daddr <= 0xBFC) {
// CURSOR IMAGE
data = lcdPalette[index];
break;
} else {
- panic("Tried to read CLCD register at offset %#x that \
- doesn't exist\n", daddr);
+ panic("Tried to read CLCD register at offset %#x that "
+ "doesn't exist\n", daddr);
break;
}
}
switch(pkt->getSize()) {
case 1:
- pkt->set<uint8_t>(data);
+ pkt->setLE<uint8_t>(data);
break;
case 2:
- pkt->set<uint16_t>(data);
+ pkt->setLE<uint16_t>(data);
break;
case 4:
- pkt->set<uint32_t>(data);
+ pkt->setLE<uint32_t>(data);
break;
default:
panic("CLCD controller read size too big?\n");
switch(pkt->getSize()) {
case 1:
- data = pkt->get<uint8_t>();
+ data = pkt->getLE<uint8_t>();
break;
case 2:
- data = pkt->get<uint16_t>();
+ data = pkt->getLE<uint16_t>();
break;
case 4:
- data = pkt->get<uint32_t>();
+ data = pkt->getLE<uint32_t>();
break;
default:
panic("PL111 CLCD controller write size too big?\n");
Addr daddr = pkt->getAddr() - pioAddr;
DPRINTF(PL111, " write register %#x value %#x size=%d\n", daddr,
- pkt->get<uint8_t>(), pkt->getSize());
+ pkt->getLE<uint8_t>(), pkt->getSize());
switch (daddr) {
case LcdTiming0:
DPRINTF(PL111, "####### Upper panel base set to: %#x #######\n", lcdUpbase);
break;
case LcdLpBase:
- warn("LCD dual screen mode not supported\n");
+ warn_once("LCD dual screen mode not supported\n");
lcdLpbase = data;
DPRINTF(PL111, "###### Lower panel base set to: %#x #######\n", lcdLpbase);
break;
lcdPalette[index] = data;
break;
} else {
- panic("Tried to write PL111 register at offset %#x that \
- doesn't exist\n", daddr);
+ panic("Tried to write PL111 register at offset %#x that "
+ "doesn't exist\n", daddr);
break;
}
}
return pioDelay;
}
+PixelConverter
+Pl111::pixelConverter() const
+{
+ unsigned rw, gw, bw;
+ unsigned offsets[3];
+
+ switch (lcdControl.lcdbpp) {
+ case bpp24:
+ rw = gw = bw = 8;
+ offsets[0] = 0;
+ offsets[1] = 8;
+ offsets[2] = 16;
+ break;
+
+ case bpp16m565:
+ rw = 5;
+ gw = 6;
+ bw = 5;
+ offsets[0] = 0;
+ offsets[1] = 5;
+ offsets[2] = 11;
+ break;
+
+ default:
+ panic("Unimplemented video mode\n");
+ }
+
+ if (lcdControl.bgr) {
+ return PixelConverter(
+ bytesPerPixel,
+ offsets[2], offsets[1], offsets[0],
+ rw, gw, bw,
+ LittleEndianByteOrder);
+ } else {
+ return PixelConverter(
+ bytesPerPixel,
+ offsets[0], offsets[1], offsets[2],
+ rw, gw, bw,
+ LittleEndianByteOrder);
+ }
+}
+
void
Pl111::updateVideoParams()
{
- if (lcdControl.lcdbpp == bpp24) {
- bytesPerPixel = 4;
- } else if (lcdControl.lcdbpp == bpp16m565) {
- bytesPerPixel = 2;
- }
+ if (lcdControl.lcdbpp == bpp24) {
+ bytesPerPixel = 4;
+ } else if (lcdControl.lcdbpp == bpp16m565) {
+ bytesPerPixel = 2;
+ }
- if (vncserver) {
- if (lcdControl.lcdbpp == bpp24 && lcdControl.bgr)
- vncserver->setFrameBufferParams(VideoConvert::bgr8888, width,
- height);
- else if (lcdControl.lcdbpp == bpp24 && !lcdControl.bgr)
- vncserver->setFrameBufferParams(VideoConvert::rgb8888, width,
- height);
- else if (lcdControl.lcdbpp == bpp16m565 && lcdControl.bgr)
- vncserver->setFrameBufferParams(VideoConvert::bgr565, width,
- height);
- else if (lcdControl.lcdbpp == bpp16m565 && !lcdControl.bgr)
- vncserver->setFrameBufferParams(VideoConvert::rgb565, width,
- height);
- else
- panic("Unimplemented video mode\n");
- }
+ fb.resize(width, height);
+ converter = pixelConverter();
- if (bmp)
- delete bmp;
-
- if (lcdControl.lcdbpp == bpp24 && lcdControl.bgr)
- bmp = new Bitmap(VideoConvert::bgr8888, width, height, dmaBuffer);
- else if (lcdControl.lcdbpp == bpp24 && !lcdControl.bgr)
- bmp = new Bitmap(VideoConvert::rgb8888, width, height, dmaBuffer);
- else if (lcdControl.lcdbpp == bpp16m565 && lcdControl.bgr)
- bmp = new Bitmap(VideoConvert::bgr565, width, height, dmaBuffer);
- else if (lcdControl.lcdbpp == bpp16m565 && !lcdControl.bgr)
- bmp = new Bitmap(VideoConvert::rgb565, width, height, dmaBuffer);
- else
- panic("Unimplemented video mode\n");
+ // Workaround configuration bugs where multiple display
+ // controllers are attached to the same VNC server by reattaching
+ // enabled devices. This isn't ideal, but works as long as only
+ // one display controller is active at a time.
+ if (lcdControl.lcdpwr && vnc)
+ vnc->setFrameBuffer(&fb);
}
void
// Updating base address, interrupt if we're supposed to
lcdRis.baseaddr = 1;
if (!intEvent.scheduled())
- schedule(intEvent, nextCycle());
+ schedule(intEvent, clockEdge());
curAddr = 0;
startTime = curTick();
DPRINTF(PL111, " lcd frame buffer size of %d bytes \n", maxAddr);
- dmaPendingNum = 0;
-
fillFifo();
}
// due to assertion in scheduling state
++dmaPendingNum;
- assert(!dmaDoneEvent[dmaPendingNum-1].scheduled());
+ assert(!dmaDoneEventFree.empty());
+ DmaDoneEvent *event(dmaDoneEventFree.back());
+ dmaDoneEventFree.pop_back();
+ assert(!event->scheduled());
// We use a uncachable request here because the requests from the CPU
// will be uncacheable as well. If we have uncacheable and cacheable
// requests in the memory system for the same address it won't be
// pleased
- dmaPort->dmaAction(MemCmd::ReadReq, curAddr + startAddr, dmaSize,
- &dmaDoneEvent[dmaPendingNum-1], curAddr + dmaBuffer, 0,
- Request::UNCACHEABLE);
+ dmaPort.dmaAction(MemCmd::ReadReq, curAddr + startAddr, dmaSize,
+ event, curAddr + dmaBuffer,
+ 0, Request::UNCACHEABLE);
curAddr += dmaSize;
}
}
void
Pl111::dmaDone()
{
- Tick maxFrameTime = lcdTiming2.cpl * height * clock;
+ DPRINTF(PL111, "DMA Done\n");
+
+ Tick maxFrameTime = lcdTiming2.cpl * height * pixelClock;
--dmaPendingNum;
if (maxAddr == curAddr && !dmaPendingNum) {
if ((curTick() - startTime) > maxFrameTime) {
- warn("CLCD controller buffer underrun, took %d cycles when should"
+ warn("CLCD controller buffer underrun, took %d ticks when should"
" have taken %d\n", curTick() - startTime, maxFrameTime);
lcdRis.underflow = 1;
if (!intEvent.scheduled())
- schedule(intEvent, nextCycle());
+ schedule(intEvent, clockEdge());
}
assert(!readEvent.scheduled());
- if (vncserver)
- vncserver->setDirty();
+ fb.copyIn(dmaBuffer, converter);
+ if (vnc)
+ vnc->setDirty();
- DPRINTF(PL111, "-- write out frame buffer into bmp\n");
+ if (enableCapture) {
+ DPRINTF(PL111, "-- write out frame buffer into bmp\n");
- assert(bmp);
- pic->seekp(0);
- bmp->write(pic);
+ if (!pic)
+ pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()),
+ true);
- DPRINTF(PL111, "-- schedule next dma read event at %d tick \n",
- maxFrameTime + curTick());
+ assert(pic);
+ pic->stream()->seekp(0);
+ bmp.write(*pic->stream());
+ }
+ // schedule the next read based on when the last frame started
+ // and the desired fps (i.e. maxFrameTime), we turn the
+ // argument into a relative number of cycles in the future
if (lcdControl.lcden)
- schedule(readEvent, nextCycle(startTime + maxFrameTime));
+ schedule(readEvent, clockEdge(ticksToCycles(startTime -
+ curTick() +
+ maxFrameTime)));
}
if (dmaPendingNum > (maxOutstandingDma - waterMark))
return;
if (!fillFifoEvent.scheduled())
- schedule(fillFifoEvent, nextCycle());
-}
-
-
-Tick
-Pl111::nextCycle()
-{
- Tick nextTick = curTick() + clock - 1;
- nextTick -= nextTick%clock;
- return nextTick;
-}
-
-Tick
-Pl111::nextCycle(Tick beginTick)
-{
- Tick nextTick = beginTick;
- if (nextTick%clock!=0)
- nextTick = nextTick - (nextTick%clock) + clock;
-
- assert(nextTick >= curTick());
- return nextTick;
+ schedule(fillFifoEvent, clockEdge());
}
void
-Pl111::serialize(std::ostream &os)
+Pl111::serialize(CheckpointOut &cp) const
{
DPRINTF(PL111, "Serializing ARM PL111\n");
uint8_t clcdCrsrMis_serial = clcdCrsrMis;
SERIALIZE_SCALAR(clcdCrsrMis_serial);
- SERIALIZE_SCALAR(clock);
SERIALIZE_SCALAR(height);
SERIALIZE_SCALAR(width);
SERIALIZE_SCALAR(bytesPerPixel);
- SERIALIZE_ARRAY(dmaBuffer, height * width);
+ SERIALIZE_ARRAY(dmaBuffer, buffer_size);
SERIALIZE_SCALAR(startTime);
SERIALIZE_SCALAR(startAddr);
SERIALIZE_SCALAR(maxAddr);
vector<Tick> dma_done_event_tick;
dma_done_event_tick.resize(maxOutstandingDma);
for (int x = 0; x < maxOutstandingDma; x++) {
- dma_done_event_tick[x] = dmaDoneEvent[x].scheduled() ?
- dmaDoneEvent[x].when() : 0;
+ dma_done_event_tick[x] = dmaDoneEventAll[x].scheduled() ?
+ dmaDoneEventAll[x].when() : 0;
}
- arrayParamOut(os, "dma_done_event_tick", dma_done_event_tick);
+ SERIALIZE_CONTAINER(dma_done_event_tick);
}
void
-Pl111::unserialize(Checkpoint *cp, const std::string §ion)
+Pl111::unserialize(CheckpointIn &cp)
{
DPRINTF(PL111, "Unserializing ARM PL111\n");
UNSERIALIZE_SCALAR(clcdCrsrMis_serial);
clcdCrsrMis = clcdCrsrMis_serial;
- UNSERIALIZE_SCALAR(clock);
UNSERIALIZE_SCALAR(height);
UNSERIALIZE_SCALAR(width);
UNSERIALIZE_SCALAR(bytesPerPixel);
- UNSERIALIZE_ARRAY(dmaBuffer, height * width);
+ UNSERIALIZE_ARRAY(dmaBuffer, buffer_size);
UNSERIALIZE_SCALAR(startTime);
UNSERIALIZE_SCALAR(startAddr);
UNSERIALIZE_SCALAR(maxAddr);
vector<Tick> dma_done_event_tick;
dma_done_event_tick.resize(maxOutstandingDma);
- arrayParamIn(cp, section, "dma_done_event_tick", dma_done_event_tick);
+ UNSERIALIZE_CONTAINER(dma_done_event_tick);
+ dmaDoneEventFree.clear();
for (int x = 0; x < maxOutstandingDma; x++) {
if (dma_done_event_tick[x])
- schedule(dmaDoneEvent[x], dma_done_event_tick[x]);
+ schedule(dmaDoneEventAll[x], dma_done_event_tick[x]);
+ else
+ dmaDoneEventFree.push_back(&dmaDoneEventAll[x]);
}
+ assert(maxOutstandingDma - dmaDoneEventFree.size() == dmaPendingNum);
- updateVideoParams();
- if (vncserver)
- vncserver->setDirty();
+ if (lcdControl.lcdpwr) {
+ updateVideoParams();
+ fb.copyIn(dmaBuffer, converter);
+ if (vnc)
+ vnc->setDirty();
+ }
}
void
}
}
-void
-Pl111::addressRanges(AddrRangeList& range_list)
+AddrRangeList
+Pl111::getAddrRanges() const
{
- range_list.clear();
- range_list.push_back(RangeSize(pioAddr, pioSize));
+ AddrRangeList ranges;
+ ranges.push_back(RangeSize(pioAddr, pioSize));
+ return ranges;
}
Pl111 *