hdlcd.cc (11523:81332eb10367) hdlcd.cc (11793:ef606668d247)
1/*
2 * Copyright (c) 2010-2013, 2015 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
1/*
2 * Copyright (c) 2010-2013, 2015 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/vnc/vncinput.hh"
44#include "base/output.hh"
45#include "base/trace.hh"
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 pixelPump.start(displayTimings());
209
210 // We restored from a checkpoint and need to update the VNC server
211 if (pixelPump.active() && vnc)
212 vnc->setDirty();
213}
214
215// read registers and frame buffer
216Tick
217HDLcd::read(PacketPtr pkt)
218{
219 assert(pkt->getAddr() >= pioAddr &&
220 pkt->getAddr() < pioAddr + pioSize);
221
222 const Addr daddr(pkt->getAddr() - pioAddr);
223 panic_if(pkt->getSize() != 4,
224 "Unhandled read size (address: 0x.4x, size: %u)",
225 daddr, pkt->getSize());
226
227 const uint32_t data(readReg(daddr));
228 DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data);
229
230 pkt->set<uint32_t>(data);
231 pkt->makeAtomicResponse();
232 return pioDelay;
233}
234
235// write registers and frame buffer
236Tick
237HDLcd::write(PacketPtr pkt)
238{
239 assert(pkt->getAddr() >= pioAddr &&
240 pkt->getAddr() < pioAddr + pioSize);
241
242 const Addr daddr(pkt->getAddr() - pioAddr);
243 panic_if(pkt->getSize() != 4,
244 "Unhandled read size (address: 0x.4x, size: %u)",
245 daddr, pkt->getSize());
246 const uint32_t data(pkt->get<uint32_t>());
247 DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data);
248
249 writeReg(daddr, data);
250
251 pkt->makeAtomicResponse();
252 return pioDelay;
253}
254
255uint32_t
256HDLcd::readReg(Addr offset)
257{
258 switch (offset) {
259 case Version: return version;
260
261 case Int_RawStat: return int_rawstat;
262 case Int_Clear:
263 panic("HDLCD INT_CLEAR register is Write-Only\n");
264 case Int_Mask: return int_mask;
265 case Int_Status: return intStatus();
266
267 case Fb_Base: return fb_base;
268 case Fb_Line_Length: return fb_line_length;
269 case Fb_Line_Count: return fb_line_count;
270 case Fb_Line_Pitch: return fb_line_pitch;
271 case Bus_Options: return bus_options;
272
273 case V_Sync: return v_sync;
274 case V_Back_Porch: return v_back_porch;
275 case V_Data: return v_data;
276 case V_Front_Porch: return v_front_porch;
277 case H_Sync: return h_sync;
278 case H_Back_Porch: return h_back_porch;
279 case H_Data: return h_data;
280 case H_Front_Porch: return h_front_porch;
281 case Polarities: return polarities;
282
283 case Command: return command;
284 case Pixel_Format: return pixel_format;
285 case Red_Select: return red_select;
286 case Green_Select: return green_select;
287 case Blue_Select: return blue_select;
288
289 default:
290 panic("Tried to read HDLCD register that doesn't exist\n", offset);
291 }
292}
293
294void
295HDLcd::writeReg(Addr offset, uint32_t value)
296{
297 switch (offset) {
298 case Version:
299 panic("HDLCD VERSION register is read-Only\n");
300
301 case Int_RawStat:
302 intRaise(value);
303 return;
304 case Int_Clear:
305 intClear(value);
306 return;
307 case Int_Mask:
308 intMask(value);
309 return;
310 case Int_Status:
311 panic("HDLCD INT_STATUS register is read-Only\n");
312 break;
313
314 case Fb_Base:
315 fb_base = value;
316 return;
317
318 case Fb_Line_Length:
319 fb_line_length = value;
320 return;
321
322 case Fb_Line_Count:
323 fb_line_count = value;
324 return;
325
326 case Fb_Line_Pitch:
327 fb_line_pitch = value;
328 return;
329
330 case Bus_Options: {
331 const BusOptsReg old_bus_options(bus_options);
332 bus_options = value;
333
334 if (bus_options.max_outstanding != old_bus_options.max_outstanding) {
335 DPRINTF(HDLcd,
336 "Changing HDLcd outstanding DMA transactions: %d -> %d\n",
337 old_bus_options.max_outstanding,
338 bus_options.max_outstanding);
339
340 }
341
342 if (bus_options.burst_len != old_bus_options.burst_len) {
343 DPRINTF(HDLcd,
344 "Changing HDLcd DMA burst flags: 0x%x -> 0x%x\n",
345 old_bus_options.burst_len, bus_options.burst_len);
346 }
347 } return;
348
349 case V_Sync:
350 v_sync = value;
351 return;
352 case V_Back_Porch:
353 v_back_porch = value;
354 return;
355 case V_Data:
356 v_data = value;
357 return;
358 case V_Front_Porch:
359 v_front_porch = value;
360 return;
361
362 case H_Sync:
363 h_sync = value;
364 return;
365 case H_Back_Porch:
366 h_back_porch = value;
367 return;
368 case H_Data:
369 h_data = value;
370 return;
371 case H_Front_Porch:
372 h_front_porch = value;
373 return;
374
375 case Polarities:
376 polarities = value;
377 return;
378
379 case Command: {
380 const CommandReg new_command(value);
381
382 if (new_command.enable != command.enable) {
383 DPRINTF(HDLcd, "HDLCD switched %s\n",
384 new_command.enable ? "on" : "off");
385
386 if (new_command.enable) {
387 cmdEnable();
388 } else {
389 cmdDisable();
390 }
391 }
392 command = new_command;
393 } return;
394
395 case Pixel_Format:
396 pixel_format = value;
397 return;
398
399 case Red_Select:
400 red_select = value;
401 return;
402 case Green_Select:
403 green_select = value;
404 return;
405 case Blue_Select:
406 blue_select = value;
407 return;
408
409 default:
410 panic("Tried to write HDLCD register that doesn't exist\n", offset);
411 return;
412 }
413}
414
415PixelConverter
416HDLcd::pixelConverter() const
417{
418 ByteOrder byte_order(
419 pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder);
420
421 /* Some Linux kernels have a broken driver that swaps the red and
422 * blue color select registers. */
423 if (!workaroundSwapRB) {
424 return PixelConverter(
425 pixel_format.bytes_per_pixel + 1,
426 red_select.offset, green_select.offset, blue_select.offset,
427 red_select.size, green_select.size, blue_select.size,
428 byte_order);
429 } else {
430 return PixelConverter(
431 pixel_format.bytes_per_pixel + 1,
432 blue_select.offset, green_select.offset, red_select.offset,
433 blue_select.size, green_select.size, red_select.size,
434 byte_order);
435 }
436}
437
438DisplayTimings
439HDLcd::displayTimings() const
440{
441 return DisplayTimings(
442 h_data.val + 1, v_data.val + 1,
443 h_back_porch.val + 1, h_sync.val + 1, h_front_porch.val + 1,
444 v_back_porch.val + 1, v_sync.val + 1, v_front_porch.val + 1);
445}
446
447void
448HDLcd::createDmaEngine()
449{
450 if (bus_options.max_outstanding == 0) {
451 warn("Maximum number of outstanding DMA transfers set to 0.");
452 return;
453 }
454
455 const uint32_t dma_burst_flags(bus_options.burst_len);
456 const uint32_t dma_burst_len(
457 dma_burst_flags ?
458 (1UL << (findMsbSet(dma_burst_flags) - 1)) :
459 MAX_BURST_LEN);
460 // Some drivers seem to set the DMA line count incorrectly. This
461 // could either be a driver bug or a specification bug. Unlike for
462 // timings, the specification does not require 1 to be added to
463 // the DMA engine's line count.
464 const uint32_t dma_lines(
465 fb_line_count + (workaroundDmaLineCount ? 1 : 0));
466
467 dmaEngine.reset(new DmaEngine(
468 *this, pixelBufferSize,
469 AXI_PORT_WIDTH * dma_burst_len,
470 bus_options.max_outstanding,
471 fb_line_length, fb_line_pitch, dma_lines));
472}
473
474void
475HDLcd::cmdEnable()
476{
477 createDmaEngine();
478 conv = pixelConverter();
479 pixelPump.start(displayTimings());
480}
481
482void
483HDLcd::cmdDisable()
484{
485 pixelPump.stop();
486 dmaEngine->abortFrame();
487}
488
489bool
490HDLcd::pxlNext(Pixel &p)
491{
492 uint8_t pixel_data[MAX_PIXEL_SIZE];
493 assert(conv.length <= sizeof(pixel_data));
494 if (dmaEngine->tryGet(pixel_data, conv.length)) {
495 p = conv.toPixel(pixel_data);
496 return true;
497 } else {
498 return false;
499 }
500}
501
502void
503HDLcd::pxlVSyncBegin()
504{
505 DPRINTF(HDLcd, "Raising VSYNC interrupt.\n");
506 intRaise(INT_VSYNC);
507}
508
509void
510HDLcd::pxlVSyncEnd()
511{
512 DPRINTF(HDLcd, "End of VSYNC, starting DMA engine\n");
513 dmaEngine->startFrame(fb_base);
514}
515
516void
517HDLcd::pxlUnderrun()
518{
519 DPRINTF(HDLcd, "Buffer underrun, stopping DMA fill.\n");
520 ++stats.underruns;
521 intRaise(INT_UNDERRUN);
522 dmaEngine->abortFrame();
523}
524
525void
526HDLcd::pxlFrameDone()
527{
528 DPRINTF(HDLcd, "Reached end of last visible line.\n");
529
530 if (dmaEngine->size()) {
531 warn("HDLCD %u bytes still in FIFO after frame: Ensure that DMA "
532 "and PixelPump configuration is consistent\n",
533 dmaEngine->size());
534 dmaEngine->dumpSettings();
535 pixelPump.dumpSettings();
536 }
537
538 if (vnc)
539 vnc->setDirty();
540
541 if (enableCapture) {
542 if (!pic) {
543 pic = simout.create(
544 csprintf("%s.framebuffer.bmp", sys->name()),
545 true);
546 }
547
548 assert(pic);
549 pic->stream()->seekp(0);
550 bmp.write(*pic->stream());
551 }
552}
553
554void
555HDLcd::setInterrupts(uint32_t ints, uint32_t mask)
556{
557 const bool old_ints(intStatus());
558
559 int_mask = mask;
560 int_rawstat = ints;
561
562 if (!old_ints && intStatus()) {
563 gic->sendInt(intNum);
564 } else if (old_ints && !intStatus()) {
565 gic->clearInt(intNum);
566 }
567}
568
569HDLcd::DmaEngine::DmaEngine(HDLcd &_parent, size_t size,
570 unsigned request_size, unsigned max_pending,
571 size_t line_size, ssize_t line_pitch, unsigned num_lines)
572 : DmaReadFifo(
573 _parent.dmaPort, size, request_size, max_pending,
574 Request::UNCACHEABLE),
575 parent(_parent),
576 lineSize(line_size), linePitch(line_pitch), numLines(num_lines),
577 nextLineAddr(0)
578{
579}
580
581void
582HDLcd::DmaEngine::serialize(CheckpointOut &cp) const
583{
584 DmaReadFifo::serialize(cp);
585
586 SERIALIZE_SCALAR(nextLineAddr);
587 SERIALIZE_SCALAR(frameEnd);
588}
589
590void
591HDLcd::DmaEngine::unserialize(CheckpointIn &cp)
592{
593 DmaReadFifo::unserialize(cp);
594
595 UNSERIALIZE_SCALAR(nextLineAddr);
596 UNSERIALIZE_SCALAR(frameEnd);
597}
598
599void
600HDLcd::DmaEngine::startFrame(Addr fb_base)
601{
602 nextLineAddr = fb_base;
603 frameEnd = fb_base + numLines * linePitch;
604
605 startFill(nextLineAddr, lineSize);
606}
607
608void
609HDLcd::DmaEngine::abortFrame()
610{
611 nextLineAddr = frameEnd;
612 stopFill();
613 flush();
614}
615
616
617void
618HDLcd::DmaEngine::dumpSettings()
619{
620 inform("DMA line size: %u bytes", lineSize);
621 inform("DMA line pitch: %i bytes", linePitch);
622 inform("DMA num lines: %u", numLines);
623}
624
625void
626HDLcd::DmaEngine::onEndOfBlock()
627{
628 if (nextLineAddr == frameEnd)
629 // We're done with this frame. Ignore calls to this method
630 // until the next frame has been started.
631 return;
632
633 nextLineAddr += linePitch;
634 if (nextLineAddr != frameEnd)
635 startFill(nextLineAddr, lineSize);
636}
637
638void
639HDLcd::DmaEngine::onIdle()
640{
641 parent.intRaise(INT_DMA_END);
642}
643
644void
645HDLcd::PixelPump::dumpSettings()
646{
647 const DisplayTimings &t(timings());
648
649 inform("PixelPump width: %u", t.width);
650 inform("PixelPump height: %u", t.height);
651
652 inform("PixelPump horizontal back porch: %u", t.hBackPorch);
653 inform("PixelPump horizontal fron porch: %u", t.hFrontPorch);
654 inform("PixelPump horizontal fron porch: %u", t.hSync);
655
656 inform("PixelPump vertical back porch: %u", t.vBackPorch);
657 inform("PixelPump vertical fron porch: %u", t.vFrontPorch);
658 inform("PixelPump vertical fron porch: %u", t.vSync);
659}
660
661
662HDLcd *
663HDLcdParams::create()
664{
665 return new HDLcd(this);
666}
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 pixelPump.start(displayTimings());
209
210 // We restored from a checkpoint and need to update the VNC server
211 if (pixelPump.active() && vnc)
212 vnc->setDirty();
213}
214
215// read registers and frame buffer
216Tick
217HDLcd::read(PacketPtr pkt)
218{
219 assert(pkt->getAddr() >= pioAddr &&
220 pkt->getAddr() < pioAddr + pioSize);
221
222 const Addr daddr(pkt->getAddr() - pioAddr);
223 panic_if(pkt->getSize() != 4,
224 "Unhandled read size (address: 0x.4x, size: %u)",
225 daddr, pkt->getSize());
226
227 const uint32_t data(readReg(daddr));
228 DPRINTF(HDLcd, "read register 0x%04x: 0x%x\n", daddr, data);
229
230 pkt->set<uint32_t>(data);
231 pkt->makeAtomicResponse();
232 return pioDelay;
233}
234
235// write registers and frame buffer
236Tick
237HDLcd::write(PacketPtr pkt)
238{
239 assert(pkt->getAddr() >= pioAddr &&
240 pkt->getAddr() < pioAddr + pioSize);
241
242 const Addr daddr(pkt->getAddr() - pioAddr);
243 panic_if(pkt->getSize() != 4,
244 "Unhandled read size (address: 0x.4x, size: %u)",
245 daddr, pkt->getSize());
246 const uint32_t data(pkt->get<uint32_t>());
247 DPRINTF(HDLcd, "write register 0x%04x: 0x%x\n", daddr, data);
248
249 writeReg(daddr, data);
250
251 pkt->makeAtomicResponse();
252 return pioDelay;
253}
254
255uint32_t
256HDLcd::readReg(Addr offset)
257{
258 switch (offset) {
259 case Version: return version;
260
261 case Int_RawStat: return int_rawstat;
262 case Int_Clear:
263 panic("HDLCD INT_CLEAR register is Write-Only\n");
264 case Int_Mask: return int_mask;
265 case Int_Status: return intStatus();
266
267 case Fb_Base: return fb_base;
268 case Fb_Line_Length: return fb_line_length;
269 case Fb_Line_Count: return fb_line_count;
270 case Fb_Line_Pitch: return fb_line_pitch;
271 case Bus_Options: return bus_options;
272
273 case V_Sync: return v_sync;
274 case V_Back_Porch: return v_back_porch;
275 case V_Data: return v_data;
276 case V_Front_Porch: return v_front_porch;
277 case H_Sync: return h_sync;
278 case H_Back_Porch: return h_back_porch;
279 case H_Data: return h_data;
280 case H_Front_Porch: return h_front_porch;
281 case Polarities: return polarities;
282
283 case Command: return command;
284 case Pixel_Format: return pixel_format;
285 case Red_Select: return red_select;
286 case Green_Select: return green_select;
287 case Blue_Select: return blue_select;
288
289 default:
290 panic("Tried to read HDLCD register that doesn't exist\n", offset);
291 }
292}
293
294void
295HDLcd::writeReg(Addr offset, uint32_t value)
296{
297 switch (offset) {
298 case Version:
299 panic("HDLCD VERSION register is read-Only\n");
300
301 case Int_RawStat:
302 intRaise(value);
303 return;
304 case Int_Clear:
305 intClear(value);
306 return;
307 case Int_Mask:
308 intMask(value);
309 return;
310 case Int_Status:
311 panic("HDLCD INT_STATUS register is read-Only\n");
312 break;
313
314 case Fb_Base:
315 fb_base = value;
316 return;
317
318 case Fb_Line_Length:
319 fb_line_length = value;
320 return;
321
322 case Fb_Line_Count:
323 fb_line_count = value;
324 return;
325
326 case Fb_Line_Pitch:
327 fb_line_pitch = value;
328 return;
329
330 case Bus_Options: {
331 const BusOptsReg old_bus_options(bus_options);
332 bus_options = value;
333
334 if (bus_options.max_outstanding != old_bus_options.max_outstanding) {
335 DPRINTF(HDLcd,
336 "Changing HDLcd outstanding DMA transactions: %d -> %d\n",
337 old_bus_options.max_outstanding,
338 bus_options.max_outstanding);
339
340 }
341
342 if (bus_options.burst_len != old_bus_options.burst_len) {
343 DPRINTF(HDLcd,
344 "Changing HDLcd DMA burst flags: 0x%x -> 0x%x\n",
345 old_bus_options.burst_len, bus_options.burst_len);
346 }
347 } return;
348
349 case V_Sync:
350 v_sync = value;
351 return;
352 case V_Back_Porch:
353 v_back_porch = value;
354 return;
355 case V_Data:
356 v_data = value;
357 return;
358 case V_Front_Porch:
359 v_front_porch = value;
360 return;
361
362 case H_Sync:
363 h_sync = value;
364 return;
365 case H_Back_Porch:
366 h_back_porch = value;
367 return;
368 case H_Data:
369 h_data = value;
370 return;
371 case H_Front_Porch:
372 h_front_porch = value;
373 return;
374
375 case Polarities:
376 polarities = value;
377 return;
378
379 case Command: {
380 const CommandReg new_command(value);
381
382 if (new_command.enable != command.enable) {
383 DPRINTF(HDLcd, "HDLCD switched %s\n",
384 new_command.enable ? "on" : "off");
385
386 if (new_command.enable) {
387 cmdEnable();
388 } else {
389 cmdDisable();
390 }
391 }
392 command = new_command;
393 } return;
394
395 case Pixel_Format:
396 pixel_format = value;
397 return;
398
399 case Red_Select:
400 red_select = value;
401 return;
402 case Green_Select:
403 green_select = value;
404 return;
405 case Blue_Select:
406 blue_select = value;
407 return;
408
409 default:
410 panic("Tried to write HDLCD register that doesn't exist\n", offset);
411 return;
412 }
413}
414
415PixelConverter
416HDLcd::pixelConverter() const
417{
418 ByteOrder byte_order(
419 pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder);
420
421 /* Some Linux kernels have a broken driver that swaps the red and
422 * blue color select registers. */
423 if (!workaroundSwapRB) {
424 return PixelConverter(
425 pixel_format.bytes_per_pixel + 1,
426 red_select.offset, green_select.offset, blue_select.offset,
427 red_select.size, green_select.size, blue_select.size,
428 byte_order);
429 } else {
430 return PixelConverter(
431 pixel_format.bytes_per_pixel + 1,
432 blue_select.offset, green_select.offset, red_select.offset,
433 blue_select.size, green_select.size, red_select.size,
434 byte_order);
435 }
436}
437
438DisplayTimings
439HDLcd::displayTimings() const
440{
441 return DisplayTimings(
442 h_data.val + 1, v_data.val + 1,
443 h_back_porch.val + 1, h_sync.val + 1, h_front_porch.val + 1,
444 v_back_porch.val + 1, v_sync.val + 1, v_front_porch.val + 1);
445}
446
447void
448HDLcd::createDmaEngine()
449{
450 if (bus_options.max_outstanding == 0) {
451 warn("Maximum number of outstanding DMA transfers set to 0.");
452 return;
453 }
454
455 const uint32_t dma_burst_flags(bus_options.burst_len);
456 const uint32_t dma_burst_len(
457 dma_burst_flags ?
458 (1UL << (findMsbSet(dma_burst_flags) - 1)) :
459 MAX_BURST_LEN);
460 // Some drivers seem to set the DMA line count incorrectly. This
461 // could either be a driver bug or a specification bug. Unlike for
462 // timings, the specification does not require 1 to be added to
463 // the DMA engine's line count.
464 const uint32_t dma_lines(
465 fb_line_count + (workaroundDmaLineCount ? 1 : 0));
466
467 dmaEngine.reset(new DmaEngine(
468 *this, pixelBufferSize,
469 AXI_PORT_WIDTH * dma_burst_len,
470 bus_options.max_outstanding,
471 fb_line_length, fb_line_pitch, dma_lines));
472}
473
474void
475HDLcd::cmdEnable()
476{
477 createDmaEngine();
478 conv = pixelConverter();
479 pixelPump.start(displayTimings());
480}
481
482void
483HDLcd::cmdDisable()
484{
485 pixelPump.stop();
486 dmaEngine->abortFrame();
487}
488
489bool
490HDLcd::pxlNext(Pixel &p)
491{
492 uint8_t pixel_data[MAX_PIXEL_SIZE];
493 assert(conv.length <= sizeof(pixel_data));
494 if (dmaEngine->tryGet(pixel_data, conv.length)) {
495 p = conv.toPixel(pixel_data);
496 return true;
497 } else {
498 return false;
499 }
500}
501
502void
503HDLcd::pxlVSyncBegin()
504{
505 DPRINTF(HDLcd, "Raising VSYNC interrupt.\n");
506 intRaise(INT_VSYNC);
507}
508
509void
510HDLcd::pxlVSyncEnd()
511{
512 DPRINTF(HDLcd, "End of VSYNC, starting DMA engine\n");
513 dmaEngine->startFrame(fb_base);
514}
515
516void
517HDLcd::pxlUnderrun()
518{
519 DPRINTF(HDLcd, "Buffer underrun, stopping DMA fill.\n");
520 ++stats.underruns;
521 intRaise(INT_UNDERRUN);
522 dmaEngine->abortFrame();
523}
524
525void
526HDLcd::pxlFrameDone()
527{
528 DPRINTF(HDLcd, "Reached end of last visible line.\n");
529
530 if (dmaEngine->size()) {
531 warn("HDLCD %u bytes still in FIFO after frame: Ensure that DMA "
532 "and PixelPump configuration is consistent\n",
533 dmaEngine->size());
534 dmaEngine->dumpSettings();
535 pixelPump.dumpSettings();
536 }
537
538 if (vnc)
539 vnc->setDirty();
540
541 if (enableCapture) {
542 if (!pic) {
543 pic = simout.create(
544 csprintf("%s.framebuffer.bmp", sys->name()),
545 true);
546 }
547
548 assert(pic);
549 pic->stream()->seekp(0);
550 bmp.write(*pic->stream());
551 }
552}
553
554void
555HDLcd::setInterrupts(uint32_t ints, uint32_t mask)
556{
557 const bool old_ints(intStatus());
558
559 int_mask = mask;
560 int_rawstat = ints;
561
562 if (!old_ints && intStatus()) {
563 gic->sendInt(intNum);
564 } else if (old_ints && !intStatus()) {
565 gic->clearInt(intNum);
566 }
567}
568
569HDLcd::DmaEngine::DmaEngine(HDLcd &_parent, size_t size,
570 unsigned request_size, unsigned max_pending,
571 size_t line_size, ssize_t line_pitch, unsigned num_lines)
572 : DmaReadFifo(
573 _parent.dmaPort, size, request_size, max_pending,
574 Request::UNCACHEABLE),
575 parent(_parent),
576 lineSize(line_size), linePitch(line_pitch), numLines(num_lines),
577 nextLineAddr(0)
578{
579}
580
581void
582HDLcd::DmaEngine::serialize(CheckpointOut &cp) const
583{
584 DmaReadFifo::serialize(cp);
585
586 SERIALIZE_SCALAR(nextLineAddr);
587 SERIALIZE_SCALAR(frameEnd);
588}
589
590void
591HDLcd::DmaEngine::unserialize(CheckpointIn &cp)
592{
593 DmaReadFifo::unserialize(cp);
594
595 UNSERIALIZE_SCALAR(nextLineAddr);
596 UNSERIALIZE_SCALAR(frameEnd);
597}
598
599void
600HDLcd::DmaEngine::startFrame(Addr fb_base)
601{
602 nextLineAddr = fb_base;
603 frameEnd = fb_base + numLines * linePitch;
604
605 startFill(nextLineAddr, lineSize);
606}
607
608void
609HDLcd::DmaEngine::abortFrame()
610{
611 nextLineAddr = frameEnd;
612 stopFill();
613 flush();
614}
615
616
617void
618HDLcd::DmaEngine::dumpSettings()
619{
620 inform("DMA line size: %u bytes", lineSize);
621 inform("DMA line pitch: %i bytes", linePitch);
622 inform("DMA num lines: %u", numLines);
623}
624
625void
626HDLcd::DmaEngine::onEndOfBlock()
627{
628 if (nextLineAddr == frameEnd)
629 // We're done with this frame. Ignore calls to this method
630 // until the next frame has been started.
631 return;
632
633 nextLineAddr += linePitch;
634 if (nextLineAddr != frameEnd)
635 startFill(nextLineAddr, lineSize);
636}
637
638void
639HDLcd::DmaEngine::onIdle()
640{
641 parent.intRaise(INT_DMA_END);
642}
643
644void
645HDLcd::PixelPump::dumpSettings()
646{
647 const DisplayTimings &t(timings());
648
649 inform("PixelPump width: %u", t.width);
650 inform("PixelPump height: %u", t.height);
651
652 inform("PixelPump horizontal back porch: %u", t.hBackPorch);
653 inform("PixelPump horizontal fron porch: %u", t.hFrontPorch);
654 inform("PixelPump horizontal fron porch: %u", t.hSync);
655
656 inform("PixelPump vertical back porch: %u", t.vBackPorch);
657 inform("PixelPump vertical fron porch: %u", t.vFrontPorch);
658 inform("PixelPump vertical fron porch: %u", t.vSync);
659}
660
661
662HDLcd *
663HDLcdParams::create()
664{
665 return new HDLcd(this);
666}