Deleted Added
sdiff udiff text old ( 12086:069c529a76fd ) new ( 12232:20817121988b )
full compact
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->set<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->get<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}