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