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