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