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