pl111.cc (7753:d3e613312953) pl111.cc (7823:dac01f14f20f)
1/*
2 * Copyright (c) 2010 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: William Wang
38 */
39
40#include "base/trace.hh"
41#include "dev/arm/amba_device.hh"
42#include "dev/arm/gic.hh"
43#include "dev/arm/pl111.hh"
44#include "mem/packet.hh"
45#include "mem/packet_access.hh"
46
47using namespace AmbaDev;
48
49// initialize clcd registers
50Pl111::Pl111(const Params *p)
51 : AmbaDmaDevice(p), lcdTiming0(0), lcdTiming1(0), lcdTiming2(0),
52 lcdTiming3(0), lcdUpbase(0), lcdLpbase(0), lcdControl(0), lcdImsc(0),
53 lcdRis(0), lcdMis(0), lcdIcr(0), lcdUpcurr(0), lcdLpcurr(0),
54 clcdCrsrCtrl(0), clcdCrsrConfig(0), clcdCrsrPalette0(0),
55 clcdCrsrPalette1(0), clcdCrsrXY(0), clcdCrsrClip(0), clcdCrsrImsc(0),
56 clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0), clock(p->clock),
57 height(0), width(0), startTime(0), startAddr(0), maxAddr(0), curAddr(0),
58 waterMark(0), dmaPendingNum(0), readEvent(this), fillFifoEvent(this),
59 dmaDoneEvent(maxOutstandingDma, this), intEvent(this)
60{
61 pioSize = 0xFFFF;
62
63 memset(lcdPalette, 0, sizeof(lcdPalette));
64 memset(cursorImage, 0, sizeof(cursorImage));
65 memset(dmaBuffer, 0, sizeof(dmaBuffer));
66 memset(frameBuffer, 0, sizeof(frameBuffer));
67}
68
69// read registers and frame buffer
70Tick
71Pl111::read(PacketPtr pkt)
72{
73 // use a temporary data since the LCD registers are read/written with
74 // different size operations
75
76 uint32_t data = 0;
77
78 if ((pkt->getAddr()& 0xffff0000) == pioAddr) {
79
80 assert(pkt->getAddr() >= pioAddr &&
81 pkt->getAddr() < pioAddr + pioSize);
82
83 Addr daddr = pkt->getAddr()&0xFFFF;
84 pkt->allocate();
85
86 DPRINTF(PL111, " read register %#x size=%d\n", daddr, pkt->getSize());
87
88 switch (daddr) {
89 case LcdTiming0:
90 data = lcdTiming0;
91 break;
92 case LcdTiming1:
93 data = lcdTiming1;
94 break;
95 case LcdTiming2:
96 data = lcdTiming2;
97 break;
98 case LcdTiming3:
99 data = lcdTiming3;
100 break;
101 case LcdUpBase:
102 data = lcdUpbase;
103 break;
104 case LcdLpBase:
105 data = lcdLpbase;
106 break;
107 case LcdControl:
108 data = lcdControl;
109 break;
110 case LcdImsc:
111 warn("LCD interrupt set/clear function not supported\n");
112 data = lcdImsc;
113 break;
114 case LcdRis:
115 warn("LCD Raw interrupt status function not supported\n");
116 data = lcdRis;
117 break;
118 case LcdMis:
119 warn("LCD Masked interrupt status function not supported\n");
120 data = lcdMis;
121 break;
122 case LcdIcr:
123 panic("LCD register at offset %#x is Write-Only\n", daddr);
124 break;
125 case LcdUpCurr:
126 data = lcdUpcurr;
127 break;
128 case LcdLpCurr:
129 data = lcdLpcurr;
130 break;
131 case ClcdCrsrCtrl:
132 data = clcdCrsrCtrl;
133 break;
134 case ClcdCrsrConfig:
135 data = clcdCrsrConfig;
136 break;
137 case ClcdCrsrPalette0:
138 data = clcdCrsrPalette0;
139 break;
140 case ClcdCrsrPalette1:
141 data = clcdCrsrPalette1;
142 break;
143 case ClcdCrsrXY:
144 data = clcdCrsrXY;
145 break;
146 case ClcdCrsrClip:
147 data = clcdCrsrClip;
148 break;
149 case ClcdCrsrImsc:
150 data = clcdCrsrImsc;
151 break;
152 case ClcdCrsrIcr:
153 panic("CLCD register at offset %#x is Write-Only\n", daddr);
154 break;
155 case ClcdCrsrRis:
156 data = clcdCrsrRis;
157 break;
158 case ClcdCrsrMis:
159 data = clcdCrsrMis;
160 break;
161 default:
162 if (AmbaDev::readId(pkt, AMBA_ID, pioAddr)) {
163 // Hack for variable size accesses
164 data = pkt->get<uint32_t>();
165 break;
166 } else if (daddr >= CrsrImage && daddr <= 0xBFC) {
167 // CURSOR IMAGE
168 int index;
169 index = (daddr - CrsrImage) >> 2;
170 data= cursorImage[index];
171 break;
172 } else if (daddr >= LcdPalette && daddr <= 0x3FC) {
173 // LCD Palette
174 int index;
175 index = (daddr - LcdPalette) >> 2;
176 data = lcdPalette[index];
177 break;
178 } else {
179 panic("Tried to read CLCD register at offset %#x that \
180 doesn't exist\n", daddr);
181 break;
182 }
183 }
184 }
185
186 switch(pkt->getSize()) {
187 case 1:
188 pkt->set<uint8_t>(data);
189 break;
190 case 2:
191 pkt->set<uint16_t>(data);
192 break;
193 case 4:
194 pkt->set<uint32_t>(data);
195 break;
196 default:
197 panic("CLCD controller read size too big?\n");
198 break;
199 }
200
201 pkt->makeAtomicResponse();
202 return pioDelay;
203}
204
205// write registers and frame buffer
206Tick
207Pl111::write(PacketPtr pkt)
208{
209 // use a temporary data since the LCD registers are read/written with
210 // different size operations
211 //
212 uint32_t data = 0;
213
214 switch(pkt->getSize()) {
215 case 1:
216 data = pkt->get<uint8_t>();
217 break;
218 case 2:
219 data = pkt->get<uint16_t>();
220 break;
221 case 4:
222 data = pkt->get<uint32_t>();
223 break;
224 default:
225 panic("PL111 CLCD controller write size too big?\n");
226 break;
227 }
228
229 if ((pkt->getAddr()& 0xffff0000) == pioAddr) {
230
231 assert(pkt->getAddr() >= pioAddr &&
232 pkt->getAddr() < pioAddr + pioSize);
233
234 Addr daddr = pkt->getAddr() - pioAddr;
235
236 DPRINTF(PL111, " write register %#x value %#x size=%d\n", daddr,
237 pkt->get<uint8_t>(), pkt->getSize());
238
239 switch (daddr) {
240 case LcdTiming0:
241 lcdTiming0 = data;
242 // width = 16 * (PPL+1)
243 width = (lcdTiming0.ppl + 1) << 4;
244 break;
245 case LcdTiming1:
246 lcdTiming1 = data;
247 // height = LPP + 1
248 height = (lcdTiming1.lpp) + 1;
249 break;
250 case LcdTiming2:
251 lcdTiming2 = data;
252 break;
253 case LcdTiming3:
254 lcdTiming3 = data;
255 break;
256 case LcdUpBase:
257 lcdUpbase = data;
258 break;
259 case LcdLpBase:
260 warn("LCD dual screen mode not supported\n");
261 lcdLpbase = data;
262 break;
263 case LcdControl:
264 int old_lcdpwr;
265 old_lcdpwr = lcdControl.lcdpwr;
266 lcdControl = data;
267 // LCD power enable
268 if (lcdControl.lcdpwr&&!old_lcdpwr) {
269 DPRINTF(PL111, " lcd size: height %d width %d\n", height, width);
270 waterMark = lcdControl.watermark ? 8 : 4;
271 readFramebuffer();
272 }
273 break;
274 case LcdImsc:
275 warn("LCD interrupt mask set/clear not supported\n");
276 lcdImsc = data;
277 break;
278 case LcdRis:
279 warn("LCD register at offset %#x is Read-Only\n", daddr);
280 break;
281 case LcdMis:
282 warn("LCD register at offset %#x is Read-Only\n", daddr);
283 break;
284 case LcdIcr:
285 warn("LCD interrupt clear not supported\n");
286 lcdIcr = data;
287 break;
288 case LcdUpCurr:
289 warn("LCD register at offset %#x is Read-Only\n", daddr);
290 break;
291 case LcdLpCurr:
292 warn("LCD register at offset %#x is Read-Only\n", daddr);
293 break;
294 case ClcdCrsrCtrl:
295 clcdCrsrCtrl = data;
296 break;
297 case ClcdCrsrConfig:
298 clcdCrsrConfig = data;
299 break;
300 case ClcdCrsrPalette0:
301 clcdCrsrPalette0 = data;
302 break;
303 case ClcdCrsrPalette1:
304 clcdCrsrPalette1 = data;
305 break;
306 case ClcdCrsrXY:
307 clcdCrsrXY = data;
308 break;
309 case ClcdCrsrClip:
310 clcdCrsrClip = data;
311 break;
312 case ClcdCrsrImsc:
313 clcdCrsrImsc = data;
314 break;
315 case ClcdCrsrIcr:
316 clcdCrsrIcr = data;
317 break;
318 case ClcdCrsrRis:
319 warn("CLCD register at offset %#x is Read-Only\n", daddr);
320 break;
321 case ClcdCrsrMis:
322 warn("CLCD register at offset %#x is Read-Only\n", daddr);
323 break;
324 default:
325 if (daddr >= CrsrImage && daddr <= 0xBFC) {
326 // CURSOR IMAGE
327 int index;
328 index = (daddr - CrsrImage) >> 2;
329 cursorImage[index] = data;
330 break;
331 } else if (daddr >= LcdPalette && daddr <= 0x3FC) {
332 // LCD Palette
333 int index;
334 index = (daddr - LcdPalette) >> 2;
335 lcdPalette[index] = data;
336 break;
337 } else {
338 panic("Tried to write PL111 register at offset %#x that \
339 doesn't exist\n", daddr);
340 break;
341 }
342 }
343 }
344
345 pkt->makeAtomicResponse();
346 return pioDelay;
347}
348
349void
350Pl111::readFramebuffer()
351{
352 // initialization for dma read from frame buffer to dma buffer
353 uint32_t length = height*width;
354 if (startAddr != lcdUpbase) {
355 startAddr = lcdUpbase;
356 }
357 curAddr = 0;
1/*
2 * Copyright (c) 2010 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: William Wang
38 */
39
40#include "base/trace.hh"
41#include "dev/arm/amba_device.hh"
42#include "dev/arm/gic.hh"
43#include "dev/arm/pl111.hh"
44#include "mem/packet.hh"
45#include "mem/packet_access.hh"
46
47using namespace AmbaDev;
48
49// initialize clcd registers
50Pl111::Pl111(const Params *p)
51 : AmbaDmaDevice(p), lcdTiming0(0), lcdTiming1(0), lcdTiming2(0),
52 lcdTiming3(0), lcdUpbase(0), lcdLpbase(0), lcdControl(0), lcdImsc(0),
53 lcdRis(0), lcdMis(0), lcdIcr(0), lcdUpcurr(0), lcdLpcurr(0),
54 clcdCrsrCtrl(0), clcdCrsrConfig(0), clcdCrsrPalette0(0),
55 clcdCrsrPalette1(0), clcdCrsrXY(0), clcdCrsrClip(0), clcdCrsrImsc(0),
56 clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0), clock(p->clock),
57 height(0), width(0), startTime(0), startAddr(0), maxAddr(0), curAddr(0),
58 waterMark(0), dmaPendingNum(0), readEvent(this), fillFifoEvent(this),
59 dmaDoneEvent(maxOutstandingDma, this), intEvent(this)
60{
61 pioSize = 0xFFFF;
62
63 memset(lcdPalette, 0, sizeof(lcdPalette));
64 memset(cursorImage, 0, sizeof(cursorImage));
65 memset(dmaBuffer, 0, sizeof(dmaBuffer));
66 memset(frameBuffer, 0, sizeof(frameBuffer));
67}
68
69// read registers and frame buffer
70Tick
71Pl111::read(PacketPtr pkt)
72{
73 // use a temporary data since the LCD registers are read/written with
74 // different size operations
75
76 uint32_t data = 0;
77
78 if ((pkt->getAddr()& 0xffff0000) == pioAddr) {
79
80 assert(pkt->getAddr() >= pioAddr &&
81 pkt->getAddr() < pioAddr + pioSize);
82
83 Addr daddr = pkt->getAddr()&0xFFFF;
84 pkt->allocate();
85
86 DPRINTF(PL111, " read register %#x size=%d\n", daddr, pkt->getSize());
87
88 switch (daddr) {
89 case LcdTiming0:
90 data = lcdTiming0;
91 break;
92 case LcdTiming1:
93 data = lcdTiming1;
94 break;
95 case LcdTiming2:
96 data = lcdTiming2;
97 break;
98 case LcdTiming3:
99 data = lcdTiming3;
100 break;
101 case LcdUpBase:
102 data = lcdUpbase;
103 break;
104 case LcdLpBase:
105 data = lcdLpbase;
106 break;
107 case LcdControl:
108 data = lcdControl;
109 break;
110 case LcdImsc:
111 warn("LCD interrupt set/clear function not supported\n");
112 data = lcdImsc;
113 break;
114 case LcdRis:
115 warn("LCD Raw interrupt status function not supported\n");
116 data = lcdRis;
117 break;
118 case LcdMis:
119 warn("LCD Masked interrupt status function not supported\n");
120 data = lcdMis;
121 break;
122 case LcdIcr:
123 panic("LCD register at offset %#x is Write-Only\n", daddr);
124 break;
125 case LcdUpCurr:
126 data = lcdUpcurr;
127 break;
128 case LcdLpCurr:
129 data = lcdLpcurr;
130 break;
131 case ClcdCrsrCtrl:
132 data = clcdCrsrCtrl;
133 break;
134 case ClcdCrsrConfig:
135 data = clcdCrsrConfig;
136 break;
137 case ClcdCrsrPalette0:
138 data = clcdCrsrPalette0;
139 break;
140 case ClcdCrsrPalette1:
141 data = clcdCrsrPalette1;
142 break;
143 case ClcdCrsrXY:
144 data = clcdCrsrXY;
145 break;
146 case ClcdCrsrClip:
147 data = clcdCrsrClip;
148 break;
149 case ClcdCrsrImsc:
150 data = clcdCrsrImsc;
151 break;
152 case ClcdCrsrIcr:
153 panic("CLCD register at offset %#x is Write-Only\n", daddr);
154 break;
155 case ClcdCrsrRis:
156 data = clcdCrsrRis;
157 break;
158 case ClcdCrsrMis:
159 data = clcdCrsrMis;
160 break;
161 default:
162 if (AmbaDev::readId(pkt, AMBA_ID, pioAddr)) {
163 // Hack for variable size accesses
164 data = pkt->get<uint32_t>();
165 break;
166 } else if (daddr >= CrsrImage && daddr <= 0xBFC) {
167 // CURSOR IMAGE
168 int index;
169 index = (daddr - CrsrImage) >> 2;
170 data= cursorImage[index];
171 break;
172 } else if (daddr >= LcdPalette && daddr <= 0x3FC) {
173 // LCD Palette
174 int index;
175 index = (daddr - LcdPalette) >> 2;
176 data = lcdPalette[index];
177 break;
178 } else {
179 panic("Tried to read CLCD register at offset %#x that \
180 doesn't exist\n", daddr);
181 break;
182 }
183 }
184 }
185
186 switch(pkt->getSize()) {
187 case 1:
188 pkt->set<uint8_t>(data);
189 break;
190 case 2:
191 pkt->set<uint16_t>(data);
192 break;
193 case 4:
194 pkt->set<uint32_t>(data);
195 break;
196 default:
197 panic("CLCD controller read size too big?\n");
198 break;
199 }
200
201 pkt->makeAtomicResponse();
202 return pioDelay;
203}
204
205// write registers and frame buffer
206Tick
207Pl111::write(PacketPtr pkt)
208{
209 // use a temporary data since the LCD registers are read/written with
210 // different size operations
211 //
212 uint32_t data = 0;
213
214 switch(pkt->getSize()) {
215 case 1:
216 data = pkt->get<uint8_t>();
217 break;
218 case 2:
219 data = pkt->get<uint16_t>();
220 break;
221 case 4:
222 data = pkt->get<uint32_t>();
223 break;
224 default:
225 panic("PL111 CLCD controller write size too big?\n");
226 break;
227 }
228
229 if ((pkt->getAddr()& 0xffff0000) == pioAddr) {
230
231 assert(pkt->getAddr() >= pioAddr &&
232 pkt->getAddr() < pioAddr + pioSize);
233
234 Addr daddr = pkt->getAddr() - pioAddr;
235
236 DPRINTF(PL111, " write register %#x value %#x size=%d\n", daddr,
237 pkt->get<uint8_t>(), pkt->getSize());
238
239 switch (daddr) {
240 case LcdTiming0:
241 lcdTiming0 = data;
242 // width = 16 * (PPL+1)
243 width = (lcdTiming0.ppl + 1) << 4;
244 break;
245 case LcdTiming1:
246 lcdTiming1 = data;
247 // height = LPP + 1
248 height = (lcdTiming1.lpp) + 1;
249 break;
250 case LcdTiming2:
251 lcdTiming2 = data;
252 break;
253 case LcdTiming3:
254 lcdTiming3 = data;
255 break;
256 case LcdUpBase:
257 lcdUpbase = data;
258 break;
259 case LcdLpBase:
260 warn("LCD dual screen mode not supported\n");
261 lcdLpbase = data;
262 break;
263 case LcdControl:
264 int old_lcdpwr;
265 old_lcdpwr = lcdControl.lcdpwr;
266 lcdControl = data;
267 // LCD power enable
268 if (lcdControl.lcdpwr&&!old_lcdpwr) {
269 DPRINTF(PL111, " lcd size: height %d width %d\n", height, width);
270 waterMark = lcdControl.watermark ? 8 : 4;
271 readFramebuffer();
272 }
273 break;
274 case LcdImsc:
275 warn("LCD interrupt mask set/clear not supported\n");
276 lcdImsc = data;
277 break;
278 case LcdRis:
279 warn("LCD register at offset %#x is Read-Only\n", daddr);
280 break;
281 case LcdMis:
282 warn("LCD register at offset %#x is Read-Only\n", daddr);
283 break;
284 case LcdIcr:
285 warn("LCD interrupt clear not supported\n");
286 lcdIcr = data;
287 break;
288 case LcdUpCurr:
289 warn("LCD register at offset %#x is Read-Only\n", daddr);
290 break;
291 case LcdLpCurr:
292 warn("LCD register at offset %#x is Read-Only\n", daddr);
293 break;
294 case ClcdCrsrCtrl:
295 clcdCrsrCtrl = data;
296 break;
297 case ClcdCrsrConfig:
298 clcdCrsrConfig = data;
299 break;
300 case ClcdCrsrPalette0:
301 clcdCrsrPalette0 = data;
302 break;
303 case ClcdCrsrPalette1:
304 clcdCrsrPalette1 = data;
305 break;
306 case ClcdCrsrXY:
307 clcdCrsrXY = data;
308 break;
309 case ClcdCrsrClip:
310 clcdCrsrClip = data;
311 break;
312 case ClcdCrsrImsc:
313 clcdCrsrImsc = data;
314 break;
315 case ClcdCrsrIcr:
316 clcdCrsrIcr = data;
317 break;
318 case ClcdCrsrRis:
319 warn("CLCD register at offset %#x is Read-Only\n", daddr);
320 break;
321 case ClcdCrsrMis:
322 warn("CLCD register at offset %#x is Read-Only\n", daddr);
323 break;
324 default:
325 if (daddr >= CrsrImage && daddr <= 0xBFC) {
326 // CURSOR IMAGE
327 int index;
328 index = (daddr - CrsrImage) >> 2;
329 cursorImage[index] = data;
330 break;
331 } else if (daddr >= LcdPalette && daddr <= 0x3FC) {
332 // LCD Palette
333 int index;
334 index = (daddr - LcdPalette) >> 2;
335 lcdPalette[index] = data;
336 break;
337 } else {
338 panic("Tried to write PL111 register at offset %#x that \
339 doesn't exist\n", daddr);
340 break;
341 }
342 }
343 }
344
345 pkt->makeAtomicResponse();
346 return pioDelay;
347}
348
349void
350Pl111::readFramebuffer()
351{
352 // initialization for dma read from frame buffer to dma buffer
353 uint32_t length = height*width;
354 if (startAddr != lcdUpbase) {
355 startAddr = lcdUpbase;
356 }
357 curAddr = 0;
358 startTime = curTick;
358 startTime = curTick();
359 maxAddr = static_cast<Addr>(length*sizeof(uint32_t));
360 dmaPendingNum =0 ;
361
362 fillFifo();
363}
364
365void
366Pl111::fillFifo()
367{
368 while ((dmaPendingNum < maxOutstandingDma) && (maxAddr >= curAddr + dmaSize )) {
369 // concurrent dma reads need different dma done events
370 // due to assertion in scheduling state
371 ++dmaPendingNum;
372 DPRINTF(PL111, " ++ DMA pending number %d read addr %#x\n",
373 dmaPendingNum, curAddr);
374 assert(!dmaDoneEvent[dmaPendingNum-1].scheduled());
375 dmaRead(curAddr + startAddr, dmaSize, &dmaDoneEvent[dmaPendingNum-1],
376 curAddr + dmaBuffer);
377 curAddr += dmaSize;
378 }
379}
380
381void
382Pl111::dmaDone()
383{
384 Tick maxFrameTime = lcdTiming2.cpl*height*clock;
385
386 --dmaPendingNum;
387
388 DPRINTF(PL111, " -- DMA pending number %d\n", dmaPendingNum);
389
390 if (maxAddr == curAddr && !dmaPendingNum) {
359 maxAddr = static_cast<Addr>(length*sizeof(uint32_t));
360 dmaPendingNum =0 ;
361
362 fillFifo();
363}
364
365void
366Pl111::fillFifo()
367{
368 while ((dmaPendingNum < maxOutstandingDma) && (maxAddr >= curAddr + dmaSize )) {
369 // concurrent dma reads need different dma done events
370 // due to assertion in scheduling state
371 ++dmaPendingNum;
372 DPRINTF(PL111, " ++ DMA pending number %d read addr %#x\n",
373 dmaPendingNum, curAddr);
374 assert(!dmaDoneEvent[dmaPendingNum-1].scheduled());
375 dmaRead(curAddr + startAddr, dmaSize, &dmaDoneEvent[dmaPendingNum-1],
376 curAddr + dmaBuffer);
377 curAddr += dmaSize;
378 }
379}
380
381void
382Pl111::dmaDone()
383{
384 Tick maxFrameTime = lcdTiming2.cpl*height*clock;
385
386 --dmaPendingNum;
387
388 DPRINTF(PL111, " -- DMA pending number %d\n", dmaPendingNum);
389
390 if (maxAddr == curAddr && !dmaPendingNum) {
391 if ((curTick - startTime) > maxFrameTime)
391 if ((curTick() - startTime) > maxFrameTime)
392 warn("CLCD controller buffer underrun, took %d cycles when should"
392 warn("CLCD controller buffer underrun, took %d cycles when should"
393 " have taken %d\n", curTick - startTime, maxFrameTime);
393 " have taken %d\n", curTick() - startTime, maxFrameTime);
394
395 // double buffering so the vnc server doesn't see a tear in the screen
396 memcpy(frameBuffer, dmaBuffer, maxAddr);
397 assert(!readEvent.scheduled());
398
399 DPRINTF(PL111, "-- write out frame buffer into bmp\n");
400 writeBMP(frameBuffer);
401
402 DPRINTF(PL111, "-- schedule next dma read event at %d tick \n",
394
395 // double buffering so the vnc server doesn't see a tear in the screen
396 memcpy(frameBuffer, dmaBuffer, maxAddr);
397 assert(!readEvent.scheduled());
398
399 DPRINTF(PL111, "-- write out frame buffer into bmp\n");
400 writeBMP(frameBuffer);
401
402 DPRINTF(PL111, "-- schedule next dma read event at %d tick \n",
403 maxFrameTime + curTick);
403 maxFrameTime + curTick());
404 schedule(readEvent, nextCycle(startTime + maxFrameTime));
405 }
406
407 if (dmaPendingNum > (maxOutstandingDma - waterMark))
408 return;
409
410 if (!fillFifoEvent.scheduled())
411 schedule(fillFifoEvent, nextCycle());
412
413}
414
415Tick
416Pl111::nextCycle()
417{
404 schedule(readEvent, nextCycle(startTime + maxFrameTime));
405 }
406
407 if (dmaPendingNum > (maxOutstandingDma - waterMark))
408 return;
409
410 if (!fillFifoEvent.scheduled())
411 schedule(fillFifoEvent, nextCycle());
412
413}
414
415Tick
416Pl111::nextCycle()
417{
418 Tick nextTick = curTick + clock - 1;
418 Tick nextTick = curTick() + clock - 1;
419 nextTick -= nextTick%clock;
420 return nextTick;
421}
422
423Tick
424Pl111::nextCycle(Tick beginTick)
425{
426 Tick nextTick = beginTick;
427 if (nextTick%clock!=0)
428 nextTick = nextTick - (nextTick%clock) + clock;
429
419 nextTick -= nextTick%clock;
420 return nextTick;
421}
422
423Tick
424Pl111::nextCycle(Tick beginTick)
425{
426 Tick nextTick = beginTick;
427 if (nextTick%clock!=0)
428 nextTick = nextTick - (nextTick%clock) + clock;
429
430 assert(nextTick >= curTick);
430 assert(nextTick >= curTick());
431 return nextTick;
432}
433
434// write out the frame buffer into a bitmap file
435void
436Pl111::writeBMP(uint32_t* frameBuffer)
437{
438 fstream pic;
439
440 // write out bmp head
441 std::string filename = "./m5out/frameBuffer.bmp";
442 pic.open(filename.c_str(), ios::out|ios::binary);
443 Bitmap bm(pic, height, width);
444
445 DPRINTF(PL111, "-- write out data into bmp\n");
446
447 // write out frame buffer data
448 for (int i = height -1; i >= 0; --i) {
449 for (int j = 0; j< width; ++j) {
450 uint32_t pixel = frameBuffer[i*width + j];
451 pic.write(reinterpret_cast<char*>(&pixel),
452 sizeof(uint32_t));
453 DPRINTF(PL111, " write pixel data %#x at addr %#x\n",
454 pixel, i*width + j);
455 }
456 }
457
458 pic.close();
459}
460
461void
462Pl111::serialize(std::ostream &os)
463{
464 DPRINTF(PL111, "Serializing ARM PL111\n");
465
466 uint32_t lcdTiming0_serial = lcdTiming0;
467 SERIALIZE_SCALAR(lcdTiming0_serial);
468
469 uint32_t lcdTiming1_serial = lcdTiming1;
470 SERIALIZE_SCALAR(lcdTiming1_serial);
471
472 uint32_t lcdTiming2_serial = lcdTiming2;
473 SERIALIZE_SCALAR(lcdTiming2_serial);
474
475 uint32_t lcdTiming3_serial = lcdTiming3;
476 SERIALIZE_SCALAR(lcdTiming3_serial);
477
478 SERIALIZE_SCALAR(lcdUpbase);
479 SERIALIZE_SCALAR(lcdLpbase);
480
481 uint32_t lcdControl_serial = lcdControl;
482 SERIALIZE_SCALAR(lcdControl_serial);
483
484 uint8_t lcdImsc_serial = lcdImsc;
485 SERIALIZE_SCALAR(lcdImsc_serial);
486
487 uint8_t lcdRis_serial = lcdRis;
488 SERIALIZE_SCALAR(lcdRis_serial);
489
490 uint8_t lcdMis_serial = lcdMis;
491 SERIALIZE_SCALAR(lcdMis_serial);
492
493 uint8_t lcdIcr_serial = lcdIcr;
494 SERIALIZE_SCALAR(lcdIcr_serial);
495
496 SERIALIZE_ARRAY(lcdPalette, LcdPaletteSize);
497 SERIALIZE_ARRAY(cursorImage, CrsrImageSize);
498
499 SERIALIZE_SCALAR(clcdCrsrCtrl);
500 SERIALIZE_SCALAR(clcdCrsrConfig);
501 SERIALIZE_SCALAR(clcdCrsrPalette0);
502 SERIALIZE_SCALAR(clcdCrsrPalette1);
503 SERIALIZE_SCALAR(clcdCrsrXY);
504 SERIALIZE_SCALAR(clcdCrsrClip);
505
506 uint8_t clcdCrsrImsc_serial = clcdCrsrImsc;
507 SERIALIZE_SCALAR(clcdCrsrImsc_serial);
508
509 uint8_t clcdCrsrIcr_serial = clcdCrsrIcr;
510 SERIALIZE_SCALAR(clcdCrsrIcr_serial);
511
512 uint8_t clcdCrsrRis_serial = clcdCrsrRis;
513 SERIALIZE_SCALAR(clcdCrsrRis_serial);
514
515 uint8_t clcdCrsrMis_serial = clcdCrsrMis;
516 SERIALIZE_SCALAR(clcdCrsrMis_serial);
517
518 SERIALIZE_SCALAR(clock);
519 SERIALIZE_SCALAR(height);
520 SERIALIZE_SCALAR(width);
521
522 SERIALIZE_ARRAY(dmaBuffer, height*width);
523 SERIALIZE_ARRAY(frameBuffer, height*width);
524 SERIALIZE_SCALAR(startTime);
525 SERIALIZE_SCALAR(startAddr);
526 SERIALIZE_SCALAR(maxAddr);
527 SERIALIZE_SCALAR(curAddr);
528 SERIALIZE_SCALAR(waterMark);
529 SERIALIZE_SCALAR(dmaPendingNum);
530}
531
532void
533Pl111::unserialize(Checkpoint *cp, const std::string &section)
534{
535 DPRINTF(PL111, "Unserializing ARM PL111\n");
536
537 uint32_t lcdTiming0_serial;
538 UNSERIALIZE_SCALAR(lcdTiming0_serial);
539 lcdTiming0 = lcdTiming0_serial;
540
541 uint32_t lcdTiming1_serial;
542 UNSERIALIZE_SCALAR(lcdTiming1_serial);
543 lcdTiming1 = lcdTiming1_serial;
544
545 uint32_t lcdTiming2_serial;
546 UNSERIALIZE_SCALAR(lcdTiming2_serial);
547 lcdTiming2 = lcdTiming2_serial;
548
549 uint32_t lcdTiming3_serial;
550 UNSERIALIZE_SCALAR(lcdTiming3_serial);
551 lcdTiming3 = lcdTiming3_serial;
552
553 UNSERIALIZE_SCALAR(lcdUpbase);
554 UNSERIALIZE_SCALAR(lcdLpbase);
555
556 uint32_t lcdControl_serial;
557 UNSERIALIZE_SCALAR(lcdControl_serial);
558 lcdControl = lcdControl_serial;
559
560 uint8_t lcdImsc_serial;
561 UNSERIALIZE_SCALAR(lcdImsc_serial);
562 lcdImsc = lcdImsc_serial;
563
564 uint8_t lcdRis_serial;
565 UNSERIALIZE_SCALAR(lcdRis_serial);
566 lcdRis = lcdRis_serial;
567
568 uint8_t lcdMis_serial;
569 UNSERIALIZE_SCALAR(lcdMis_serial);
570 lcdMis = lcdMis_serial;
571
572 uint8_t lcdIcr_serial;
573 UNSERIALIZE_SCALAR(lcdIcr_serial);
574 lcdIcr = lcdIcr_serial;
575
576 UNSERIALIZE_ARRAY(lcdPalette, LcdPaletteSize);
577 UNSERIALIZE_ARRAY(cursorImage, CrsrImageSize);
578
579 UNSERIALIZE_SCALAR(clcdCrsrCtrl);
580 UNSERIALIZE_SCALAR(clcdCrsrConfig);
581 UNSERIALIZE_SCALAR(clcdCrsrPalette0);
582 UNSERIALIZE_SCALAR(clcdCrsrPalette1);
583 UNSERIALIZE_SCALAR(clcdCrsrXY);
584 UNSERIALIZE_SCALAR(clcdCrsrClip);
585
586 uint8_t clcdCrsrImsc_serial;
587 UNSERIALIZE_SCALAR(clcdCrsrImsc_serial);
588 clcdCrsrImsc = clcdCrsrImsc_serial;
589
590 uint8_t clcdCrsrIcr_serial;
591 UNSERIALIZE_SCALAR(clcdCrsrIcr_serial);
592 clcdCrsrIcr = clcdCrsrIcr_serial;
593
594 uint8_t clcdCrsrRis_serial;
595 UNSERIALIZE_SCALAR(clcdCrsrRis_serial);
596 clcdCrsrRis = clcdCrsrRis_serial;
597
598 uint8_t clcdCrsrMis_serial;
599 UNSERIALIZE_SCALAR(clcdCrsrMis_serial);
600 clcdCrsrMis = clcdCrsrMis_serial;
601
602 UNSERIALIZE_SCALAR(clock);
603 UNSERIALIZE_SCALAR(height);
604 UNSERIALIZE_SCALAR(width);
605
606 UNSERIALIZE_ARRAY(dmaBuffer, height*width);
607 UNSERIALIZE_ARRAY(frameBuffer, height*width);
608 UNSERIALIZE_SCALAR(startTime);
609 UNSERIALIZE_SCALAR(startAddr);
610 UNSERIALIZE_SCALAR(maxAddr);
611 UNSERIALIZE_SCALAR(curAddr);
612 UNSERIALIZE_SCALAR(waterMark);
613 UNSERIALIZE_SCALAR(dmaPendingNum);
614}
615
616void
617Pl111::generateInterrupt()
618{
619 DPRINTF(PL111, "Generate Interrupt: lcdImsc=0x%x lcdRis=0x%x lcdMis=0x%x\n",
620 lcdImsc, lcdRis, lcdMis);
621 lcdMis = lcdImsc & lcdRis;
622
623 if (lcdMis.ffufie || lcdMis.nbupie || lcdMis.vtcpie || lcdMis.ahmeie) {
624 gic->sendInt(intNum);
625 DPRINTF(PL111, " -- Generated\n");
626 }
627}
628
629void
630Pl111::addressRanges(AddrRangeList& range_list)
631{
632 range_list.clear();
633 range_list.push_back(RangeSize(pioAddr, pioSize));
634}
635
636Pl111 *
637Pl111Params::create()
638{
639 return new Pl111(this);
640}
641
642// bitmap class ctor
643Bitmap::Bitmap(std::fstream& bmp, uint16_t h, uint16_t w)
644{
645 Magic magic = {{'B','M'}};
646 Header header = {sizeof(Color)*w*h , 0, 0, 54};
647 Info info = {sizeof(Info), w, h, 1, sizeof(Color)*8, 0,
648 ( sizeof(Color) *(w*h) ), 1, 1, 0, 0};
649
650 bmp.write(reinterpret_cast<char*>(&magic), sizeof(magic));
651 bmp.write(reinterpret_cast<char*>(&header), sizeof(header));
652 bmp.write(reinterpret_cast<char*>(&info), sizeof(info));
653}
431 return nextTick;
432}
433
434// write out the frame buffer into a bitmap file
435void
436Pl111::writeBMP(uint32_t* frameBuffer)
437{
438 fstream pic;
439
440 // write out bmp head
441 std::string filename = "./m5out/frameBuffer.bmp";
442 pic.open(filename.c_str(), ios::out|ios::binary);
443 Bitmap bm(pic, height, width);
444
445 DPRINTF(PL111, "-- write out data into bmp\n");
446
447 // write out frame buffer data
448 for (int i = height -1; i >= 0; --i) {
449 for (int j = 0; j< width; ++j) {
450 uint32_t pixel = frameBuffer[i*width + j];
451 pic.write(reinterpret_cast<char*>(&pixel),
452 sizeof(uint32_t));
453 DPRINTF(PL111, " write pixel data %#x at addr %#x\n",
454 pixel, i*width + j);
455 }
456 }
457
458 pic.close();
459}
460
461void
462Pl111::serialize(std::ostream &os)
463{
464 DPRINTF(PL111, "Serializing ARM PL111\n");
465
466 uint32_t lcdTiming0_serial = lcdTiming0;
467 SERIALIZE_SCALAR(lcdTiming0_serial);
468
469 uint32_t lcdTiming1_serial = lcdTiming1;
470 SERIALIZE_SCALAR(lcdTiming1_serial);
471
472 uint32_t lcdTiming2_serial = lcdTiming2;
473 SERIALIZE_SCALAR(lcdTiming2_serial);
474
475 uint32_t lcdTiming3_serial = lcdTiming3;
476 SERIALIZE_SCALAR(lcdTiming3_serial);
477
478 SERIALIZE_SCALAR(lcdUpbase);
479 SERIALIZE_SCALAR(lcdLpbase);
480
481 uint32_t lcdControl_serial = lcdControl;
482 SERIALIZE_SCALAR(lcdControl_serial);
483
484 uint8_t lcdImsc_serial = lcdImsc;
485 SERIALIZE_SCALAR(lcdImsc_serial);
486
487 uint8_t lcdRis_serial = lcdRis;
488 SERIALIZE_SCALAR(lcdRis_serial);
489
490 uint8_t lcdMis_serial = lcdMis;
491 SERIALIZE_SCALAR(lcdMis_serial);
492
493 uint8_t lcdIcr_serial = lcdIcr;
494 SERIALIZE_SCALAR(lcdIcr_serial);
495
496 SERIALIZE_ARRAY(lcdPalette, LcdPaletteSize);
497 SERIALIZE_ARRAY(cursorImage, CrsrImageSize);
498
499 SERIALIZE_SCALAR(clcdCrsrCtrl);
500 SERIALIZE_SCALAR(clcdCrsrConfig);
501 SERIALIZE_SCALAR(clcdCrsrPalette0);
502 SERIALIZE_SCALAR(clcdCrsrPalette1);
503 SERIALIZE_SCALAR(clcdCrsrXY);
504 SERIALIZE_SCALAR(clcdCrsrClip);
505
506 uint8_t clcdCrsrImsc_serial = clcdCrsrImsc;
507 SERIALIZE_SCALAR(clcdCrsrImsc_serial);
508
509 uint8_t clcdCrsrIcr_serial = clcdCrsrIcr;
510 SERIALIZE_SCALAR(clcdCrsrIcr_serial);
511
512 uint8_t clcdCrsrRis_serial = clcdCrsrRis;
513 SERIALIZE_SCALAR(clcdCrsrRis_serial);
514
515 uint8_t clcdCrsrMis_serial = clcdCrsrMis;
516 SERIALIZE_SCALAR(clcdCrsrMis_serial);
517
518 SERIALIZE_SCALAR(clock);
519 SERIALIZE_SCALAR(height);
520 SERIALIZE_SCALAR(width);
521
522 SERIALIZE_ARRAY(dmaBuffer, height*width);
523 SERIALIZE_ARRAY(frameBuffer, height*width);
524 SERIALIZE_SCALAR(startTime);
525 SERIALIZE_SCALAR(startAddr);
526 SERIALIZE_SCALAR(maxAddr);
527 SERIALIZE_SCALAR(curAddr);
528 SERIALIZE_SCALAR(waterMark);
529 SERIALIZE_SCALAR(dmaPendingNum);
530}
531
532void
533Pl111::unserialize(Checkpoint *cp, const std::string &section)
534{
535 DPRINTF(PL111, "Unserializing ARM PL111\n");
536
537 uint32_t lcdTiming0_serial;
538 UNSERIALIZE_SCALAR(lcdTiming0_serial);
539 lcdTiming0 = lcdTiming0_serial;
540
541 uint32_t lcdTiming1_serial;
542 UNSERIALIZE_SCALAR(lcdTiming1_serial);
543 lcdTiming1 = lcdTiming1_serial;
544
545 uint32_t lcdTiming2_serial;
546 UNSERIALIZE_SCALAR(lcdTiming2_serial);
547 lcdTiming2 = lcdTiming2_serial;
548
549 uint32_t lcdTiming3_serial;
550 UNSERIALIZE_SCALAR(lcdTiming3_serial);
551 lcdTiming3 = lcdTiming3_serial;
552
553 UNSERIALIZE_SCALAR(lcdUpbase);
554 UNSERIALIZE_SCALAR(lcdLpbase);
555
556 uint32_t lcdControl_serial;
557 UNSERIALIZE_SCALAR(lcdControl_serial);
558 lcdControl = lcdControl_serial;
559
560 uint8_t lcdImsc_serial;
561 UNSERIALIZE_SCALAR(lcdImsc_serial);
562 lcdImsc = lcdImsc_serial;
563
564 uint8_t lcdRis_serial;
565 UNSERIALIZE_SCALAR(lcdRis_serial);
566 lcdRis = lcdRis_serial;
567
568 uint8_t lcdMis_serial;
569 UNSERIALIZE_SCALAR(lcdMis_serial);
570 lcdMis = lcdMis_serial;
571
572 uint8_t lcdIcr_serial;
573 UNSERIALIZE_SCALAR(lcdIcr_serial);
574 lcdIcr = lcdIcr_serial;
575
576 UNSERIALIZE_ARRAY(lcdPalette, LcdPaletteSize);
577 UNSERIALIZE_ARRAY(cursorImage, CrsrImageSize);
578
579 UNSERIALIZE_SCALAR(clcdCrsrCtrl);
580 UNSERIALIZE_SCALAR(clcdCrsrConfig);
581 UNSERIALIZE_SCALAR(clcdCrsrPalette0);
582 UNSERIALIZE_SCALAR(clcdCrsrPalette1);
583 UNSERIALIZE_SCALAR(clcdCrsrXY);
584 UNSERIALIZE_SCALAR(clcdCrsrClip);
585
586 uint8_t clcdCrsrImsc_serial;
587 UNSERIALIZE_SCALAR(clcdCrsrImsc_serial);
588 clcdCrsrImsc = clcdCrsrImsc_serial;
589
590 uint8_t clcdCrsrIcr_serial;
591 UNSERIALIZE_SCALAR(clcdCrsrIcr_serial);
592 clcdCrsrIcr = clcdCrsrIcr_serial;
593
594 uint8_t clcdCrsrRis_serial;
595 UNSERIALIZE_SCALAR(clcdCrsrRis_serial);
596 clcdCrsrRis = clcdCrsrRis_serial;
597
598 uint8_t clcdCrsrMis_serial;
599 UNSERIALIZE_SCALAR(clcdCrsrMis_serial);
600 clcdCrsrMis = clcdCrsrMis_serial;
601
602 UNSERIALIZE_SCALAR(clock);
603 UNSERIALIZE_SCALAR(height);
604 UNSERIALIZE_SCALAR(width);
605
606 UNSERIALIZE_ARRAY(dmaBuffer, height*width);
607 UNSERIALIZE_ARRAY(frameBuffer, height*width);
608 UNSERIALIZE_SCALAR(startTime);
609 UNSERIALIZE_SCALAR(startAddr);
610 UNSERIALIZE_SCALAR(maxAddr);
611 UNSERIALIZE_SCALAR(curAddr);
612 UNSERIALIZE_SCALAR(waterMark);
613 UNSERIALIZE_SCALAR(dmaPendingNum);
614}
615
616void
617Pl111::generateInterrupt()
618{
619 DPRINTF(PL111, "Generate Interrupt: lcdImsc=0x%x lcdRis=0x%x lcdMis=0x%x\n",
620 lcdImsc, lcdRis, lcdMis);
621 lcdMis = lcdImsc & lcdRis;
622
623 if (lcdMis.ffufie || lcdMis.nbupie || lcdMis.vtcpie || lcdMis.ahmeie) {
624 gic->sendInt(intNum);
625 DPRINTF(PL111, " -- Generated\n");
626 }
627}
628
629void
630Pl111::addressRanges(AddrRangeList& range_list)
631{
632 range_list.clear();
633 range_list.push_back(RangeSize(pioAddr, pioSize));
634}
635
636Pl111 *
637Pl111Params::create()
638{
639 return new Pl111(this);
640}
641
642// bitmap class ctor
643Bitmap::Bitmap(std::fstream& bmp, uint16_t h, uint16_t w)
644{
645 Magic magic = {{'B','M'}};
646 Header header = {sizeof(Color)*w*h , 0, 0, 54};
647 Info info = {sizeof(Info), w, h, 1, sizeof(Color)*8, 0,
648 ( sizeof(Color) *(w*h) ), 1, 1, 0, 0};
649
650 bmp.write(reinterpret_cast<char*>(&magic), sizeof(magic));
651 bmp.write(reinterpret_cast<char*>(&header), sizeof(header));
652 bmp.write(reinterpret_cast<char*>(&info), sizeof(info));
653}