6c218662ac5e672625fbeec451ce05dccdc688d4
[gem5.git] / src / dev / arm / hdlcd.cc
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
38 #include "dev/arm/hdlcd.hh"
39
40 #include "base/output.hh"
41 #include "base/trace.hh"
42 #include "base/vnc/vncinput.hh"
43 #include "debug/Checkpoint.hh"
44 #include "debug/HDLcd.hh"
45 #include "dev/arm/amba_device.hh"
46 #include "dev/arm/base_gic.hh"
47 #include "enums/ImageFormat.hh"
48 #include "mem/packet.hh"
49 #include "mem/packet_access.hh"
50 #include "params/HDLcd.hh"
51 #include "sim/system.hh"
52
53 using std::vector;
54
55
56 // initialize hdlcd registers
57 HDLcd::HDLcd(const HDLcdParams &p)
58 : AmbaDmaDevice(p, 0xFFFF),
59 // Parameters
60 vnc(p.vnc),
61 workaroundSwapRB(p.workaround_swap_rb),
62 workaroundDmaLineCount(p.workaround_dma_line_count),
63 addrRanges{RangeSize(pioAddr, pioSize)},
64 enableCapture(p.enable_capture),
65 pixelBufferSize(p.pixel_buffer_size),
66 virtRefreshRate(p.virt_refresh_rate),
67
68 // Registers
69 version(VERSION_RESETV),
70 int_rawstat(0), int_mask(0),
71
72 fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0),
73 bus_options(BUS_OPTIONS_RESETV),
74
75 v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0),
76 h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0),
77 polarities(0),
78
79 command(0),
80
81 pixel_format(0),
82 red_select(0), green_select(0), blue_select(0),
83
84 virtRefreshEvent([this]{ virtRefresh(); }, name()),
85 // Other
86 imgFormat(p.frame_format), 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 imgWriter = createImgWriter(imgFormat, &pixelPump.fb);
93 }
94
95 HDLcd::~HDLcd()
96 {
97 }
98
99 void
100 HDLcd::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
113 void
114 HDLcd::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
150 void
151 HDLcd::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
201 void
202 HDLcd::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
226 void
227 HDLcd::virtRefresh()
228 {
229 pixelPump.renderFrame();
230 schedule(virtRefreshEvent, (curTick() + virtRefreshRate));
231 }
232
233 // read registers and frame buffer
234 Tick
235 HDLcd::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->setLE<uint32_t>(data);
249 pkt->makeAtomicResponse();
250 return pioDelay;
251 }
252
253 // write registers and frame buffer
254 Tick
255 HDLcd::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->getLE<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
273 uint32_t
274 HDLcd::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
312 void
313 HDLcd::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
433 PixelConverter
434 HDLcd::pixelConverter() const
435 {
436 ByteOrder byte_order(
437 pixel_format.big_endian ? ByteOrder::big : ByteOrder::little);
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
456 DisplayTimings
457 HDLcd::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
465 void
466 HDLcd::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
492 void
493 HDLcd::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
508 void
509 HDLcd::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
520 bool
521 HDLcd::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
533 void
534 HDLcd::pxlVSyncBegin()
535 {
536 DPRINTF(HDLcd, "Raising VSYNC interrupt.\n");
537 intRaise(INT_VSYNC);
538 }
539
540 void
541 HDLcd::pxlVSyncEnd()
542 {
543 DPRINTF(HDLcd, "End of VSYNC, starting DMA engine\n");
544 dmaEngine->startFrame(fb_base);
545 }
546
547 void
548 HDLcd::pxlUnderrun()
549 {
550 DPRINTF(HDLcd, "Buffer underrun, stopping DMA fill.\n");
551 ++stats.underruns;
552 intRaise(INT_UNDERRUN);
553 dmaEngine->abortFrame();
554 }
555
556 void
557 HDLcd::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.%s",
576 sys->name(), imgWriter->getImgExtension()),
577 true);
578 }
579
580 assert(pic);
581 pic->stream()->seekp(0);
582 imgWriter->write(*pic->stream());
583 }
584 }
585
586 void
587 HDLcd::setInterrupts(uint32_t ints, uint32_t mask)
588 {
589 const bool old_ints(intStatus());
590
591 int_mask = mask;
592 int_rawstat = ints;
593
594 if (!old_ints && intStatus()) {
595 interrupt->raise();
596 } else if (old_ints && !intStatus()) {
597 interrupt->clear();
598 }
599 }
600
601 HDLcd::DmaEngine::DmaEngine(HDLcd &_parent, size_t size,
602 unsigned request_size, unsigned max_pending,
603 size_t line_size, ssize_t line_pitch, unsigned num_lines)
604 : DmaReadFifo(
605 _parent.dmaPort, size, request_size, max_pending,
606 Request::UNCACHEABLE),
607 parent(_parent),
608 lineSize(line_size), linePitch(line_pitch), numLines(num_lines),
609 nextLineAddr(0)
610 {
611 }
612
613 void
614 HDLcd::DmaEngine::serialize(CheckpointOut &cp) const
615 {
616 DmaReadFifo::serialize(cp);
617
618 SERIALIZE_SCALAR(nextLineAddr);
619 SERIALIZE_SCALAR(frameEnd);
620 }
621
622 void
623 HDLcd::DmaEngine::unserialize(CheckpointIn &cp)
624 {
625 DmaReadFifo::unserialize(cp);
626
627 UNSERIALIZE_SCALAR(nextLineAddr);
628 UNSERIALIZE_SCALAR(frameEnd);
629 }
630
631 void
632 HDLcd::DmaEngine::startFrame(Addr fb_base)
633 {
634 nextLineAddr = fb_base;
635 frameEnd = fb_base + numLines * linePitch;
636
637 startFill(nextLineAddr, lineSize);
638 }
639
640 void
641 HDLcd::DmaEngine::abortFrame()
642 {
643 nextLineAddr = frameEnd;
644 stopFill();
645 flush();
646 }
647
648
649 void
650 HDLcd::DmaEngine::dumpSettings()
651 {
652 inform("DMA line size: %u bytes", lineSize);
653 inform("DMA line pitch: %i bytes", linePitch);
654 inform("DMA num lines: %u", numLines);
655 }
656
657 void
658 HDLcd::DmaEngine::onEndOfBlock()
659 {
660 if (nextLineAddr == frameEnd)
661 // We're done with this frame. Ignore calls to this method
662 // until the next frame has been started.
663 return;
664
665 nextLineAddr += linePitch;
666 if (nextLineAddr != frameEnd)
667 startFill(nextLineAddr, lineSize);
668 }
669
670 void
671 HDLcd::DmaEngine::onIdle()
672 {
673 parent.intRaise(INT_DMA_END);
674 }
675
676 void
677 HDLcd::PixelPump::dumpSettings()
678 {
679 const DisplayTimings &t(timings());
680
681 inform("PixelPump width: %u", t.width);
682 inform("PixelPump height: %u", t.height);
683
684 inform("PixelPump horizontal back porch: %u", t.hBackPorch);
685 inform("PixelPump horizontal fron porch: %u", t.hFrontPorch);
686 inform("PixelPump horizontal fron porch: %u", t.hSync);
687
688 inform("PixelPump vertical back porch: %u", t.vBackPorch);
689 inform("PixelPump vertical fron porch: %u", t.vFrontPorch);
690 inform("PixelPump vertical fron porch: %u", t.vSync);
691 }
692
693
694 HDLcd *
695 HDLcdParams::create() const
696 {
697 return new HDLcd(*this);
698 }