hdlcd.cc revision 11898:cada5b68fb12
1/*
2 * Copyright (c) 2010-2013, 2015, 2017 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/output.hh"
44#include "base/trace.hh"
45#include "base/vnc/vncinput.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      virtRefreshRate(p->virt_refresh_rate),
69
70      // Registers
71      version(VERSION_RESETV),
72      int_rawstat(0), int_mask(0),
73
74      fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0),
75      bus_options(BUS_OPTIONS_RESETV),
76
77      v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0),
78      h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0),
79      polarities(0),
80
81      command(0),
82
83      pixel_format(0),
84      red_select(0), green_select(0), blue_select(0),
85
86      virtRefreshEvent(this),
87      // Other
88      bmp(&pixelPump.fb), pic(NULL), conv(PixelConverter::rgba8888_le),
89      pixelPump(*this, *p->pxl_clk, p->pixel_chunk)
90{
91    if (vnc)
92        vnc->setFrameBuffer(&pixelPump.fb);
93}
94
95HDLcd::~HDLcd()
96{
97}
98
99void
100HDLcd::regStats()
101{
102    AmbaDmaDevice::regStats();
103
104    using namespace Stats;
105
106    stats.underruns
107        .name(name() + ".underruns")
108        .desc("number of buffer underruns")
109        .flags(nozero)
110        ;
111}
112
113void
114HDLcd::serialize(CheckpointOut &cp) const
115{
116    DPRINTF(Checkpoint, "Serializing ARM HDLCD\n");
117
118    SERIALIZE_SCALAR(int_rawstat);
119    SERIALIZE_SCALAR(int_mask);
120
121    SERIALIZE_SCALAR(fb_base);
122    SERIALIZE_SCALAR(fb_line_length);
123    SERIALIZE_SCALAR(fb_line_count);
124    SERIALIZE_SCALAR(fb_line_pitch);
125    SERIALIZE_SCALAR(bus_options);
126
127    SERIALIZE_SCALAR(v_sync);
128    SERIALIZE_SCALAR(v_back_porch);
129    SERIALIZE_SCALAR(v_data);
130    SERIALIZE_SCALAR(v_front_porch);
131
132    SERIALIZE_SCALAR(h_sync);
133    SERIALIZE_SCALAR(h_back_porch);
134    SERIALIZE_SCALAR(h_data);
135    SERIALIZE_SCALAR(h_front_porch);
136
137    SERIALIZE_SCALAR(polarities);
138
139    SERIALIZE_SCALAR(command);
140    SERIALIZE_SCALAR(pixel_format);
141    SERIALIZE_SCALAR(red_select);
142    SERIALIZE_SCALAR(green_select);
143    SERIALIZE_SCALAR(blue_select);
144
145    SERIALIZE_OBJ(pixelPump);
146    if (enabled())
147        dmaEngine->serializeSection(cp, "dmaEngine");
148}
149
150void
151HDLcd::unserialize(CheckpointIn &cp)
152{
153    DPRINTF(Checkpoint, "Unserializing ARM HDLCD\n");
154
155    UNSERIALIZE_SCALAR(int_rawstat);
156    UNSERIALIZE_SCALAR(int_mask);
157
158    UNSERIALIZE_SCALAR(fb_base);
159    UNSERIALIZE_SCALAR(fb_line_length);
160    UNSERIALIZE_SCALAR(fb_line_count);
161    UNSERIALIZE_SCALAR(fb_line_pitch);
162    UNSERIALIZE_SCALAR(bus_options);
163
164    UNSERIALIZE_SCALAR(v_sync);
165    UNSERIALIZE_SCALAR(v_back_porch);
166    UNSERIALIZE_SCALAR(v_data);
167    UNSERIALIZE_SCALAR(v_front_porch);
168
169    UNSERIALIZE_SCALAR(h_sync);
170    UNSERIALIZE_SCALAR(h_back_porch);
171    UNSERIALIZE_SCALAR(h_data);
172    UNSERIALIZE_SCALAR(h_front_porch);
173
174    UNSERIALIZE_SCALAR(polarities);
175
176    UNSERIALIZE_SCALAR(command);
177    UNSERIALIZE_SCALAR(pixel_format);
178    UNSERIALIZE_SCALAR(red_select);
179    UNSERIALIZE_SCALAR(green_select);
180    UNSERIALIZE_SCALAR(blue_select);
181
182    {
183        // Try to unserialize the pixel pump. It might not exist if
184        // we're unserializing an old checkpoint.
185        ScopedCheckpointSection sec(cp, "pixelPump");
186        if (cp.sectionExists(Serializable::currentSection()))
187            pixelPump.unserialize(cp);
188    }
189
190    if (enabled()) {
191        // Create the DMA engine and read its state from the
192        // checkpoint. We don't need to worry about the pixel pump as
193        // it is a proper SimObject.
194        createDmaEngine();
195        dmaEngine->unserializeSection(cp, "dmaEngine");
196
197        conv = pixelConverter();
198    }
199}
200
201void
202HDLcd::drainResume()
203{
204    AmbaDmaDevice::drainResume();
205
206    if (enabled()) {
207        if (sys->bypassCaches()) {
208            // We restart the HDLCD if we are in KVM mode. This
209            // ensures that we always use the fast refresh logic if we
210            // resume in KVM mode.
211            cmdDisable();
212            cmdEnable();
213        } else if (!pixelPump.active()) {
214            // We restored from an old checkpoint without a pixel
215            // pump, start an new refresh. This typically happens when
216            // restoring from old checkpoints.
217            cmdEnable();
218        }
219    }
220
221    // We restored from a checkpoint and need to update the VNC server
222    if (pixelPump.active() && vnc)
223        vnc->setDirty();
224}
225
226void
227HDLcd::virtRefresh()
228{
229    pixelPump.renderFrame();
230    schedule(virtRefreshEvent, (curTick() + virtRefreshRate));
231}
232
233// read registers and frame buffer
234Tick
235HDLcd::read(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
245    const uint32_t data(readReg(daddr));
246    DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data);
247
248    pkt->set<uint32_t>(data);
249    pkt->makeAtomicResponse();
250    return pioDelay;
251}
252
253// write registers and frame buffer
254Tick
255HDLcd::write(PacketPtr pkt)
256{
257    assert(pkt->getAddr() >= pioAddr &&
258           pkt->getAddr() < pioAddr + pioSize);
259
260    const Addr daddr(pkt->getAddr() - pioAddr);
261    panic_if(pkt->getSize() != 4,
262             "Unhandled read size (address: 0x.4x, size: %u)",
263             daddr, pkt->getSize());
264    const uint32_t data(pkt->get<uint32_t>());
265    DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data);
266
267    writeReg(daddr, data);
268
269    pkt->makeAtomicResponse();
270    return pioDelay;
271}
272
273uint32_t
274HDLcd::readReg(Addr offset)
275{
276    switch (offset) {
277      case Version: return version;
278
279      case Int_RawStat: return int_rawstat;
280      case Int_Clear:
281        panic("HDLCD INT_CLEAR register is Write-Only\n");
282      case Int_Mask: return int_mask;
283      case Int_Status: return intStatus();
284
285      case Fb_Base: return fb_base;
286      case Fb_Line_Length: return fb_line_length;
287      case Fb_Line_Count: return fb_line_count;
288      case Fb_Line_Pitch: return fb_line_pitch;
289      case Bus_Options: return bus_options;
290
291      case V_Sync: return v_sync;
292      case V_Back_Porch: return v_back_porch;
293      case V_Data: return v_data;
294      case V_Front_Porch: return v_front_porch;
295      case H_Sync: return h_sync;
296      case H_Back_Porch: return h_back_porch;
297      case H_Data: return h_data;
298      case H_Front_Porch: return h_front_porch;
299      case Polarities: return polarities;
300
301      case Command: return command;
302      case Pixel_Format: return pixel_format;
303      case Red_Select: return red_select;
304      case Green_Select: return green_select;
305      case Blue_Select: return blue_select;
306
307      default:
308        panic("Tried to read HDLCD register that doesn't  exist\n", offset);
309    }
310}
311
312void
313HDLcd::writeReg(Addr offset, uint32_t value)
314{
315    switch (offset) {
316      case Version:
317        panic("HDLCD VERSION register is read-Only\n");
318
319      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