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