1c3d2689dSbalrog /*
2c3d2689dSbalrog * OMAP LCD controller.
3c3d2689dSbalrog *
4c3d2689dSbalrog * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
5c3d2689dSbalrog *
6c3d2689dSbalrog * This program is free software; you can redistribute it and/or
7c3d2689dSbalrog * modify it under the terms of the GNU General Public License as
8c3d2689dSbalrog * published by the Free Software Foundation; either version 2 of
9c3d2689dSbalrog * the License, or (at your option) any later version.
10c3d2689dSbalrog *
11c3d2689dSbalrog * This program is distributed in the hope that it will be useful,
12c3d2689dSbalrog * but WITHOUT ANY WARRANTY; without even the implied warranty of
13c3d2689dSbalrog * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14c3d2689dSbalrog * GNU General Public License for more details.
15c3d2689dSbalrog *
16fad6cb1aSaurel32 * You should have received a copy of the GNU General Public License along
178167ee88SBlue Swirl * with this program; if not, see <http://www.gnu.org/licenses/>.
18c3d2689dSbalrog */
1964552b6bSMarkus Armbruster
2047df5154SPeter Maydell #include "qemu/osdep.h"
2164552b6bSMarkus Armbruster #include "hw/irq.h"
2228ecbaeeSPaolo Bonzini #include "ui/console.h"
230d09e41aSPaolo Bonzini #include "hw/arm/omap.h"
2447b43a1fSPaolo Bonzini #include "framebuffer.h"
2528ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h"
26c3d2689dSbalrog
27c3d2689dSbalrog struct omap_lcd_panel_s {
2875c9d6c2SAvi Kivity MemoryRegion *sysmem;
2930af1ec7SBenoît Canet MemoryRegion iomem;
30c1076c3eSPaolo Bonzini MemoryRegionSection fbsection;
31c3d2689dSbalrog qemu_irq irq;
32c78f7137SGerd Hoffmann QemuConsole *con;
33c3d2689dSbalrog
34c3d2689dSbalrog int plm;
35c3d2689dSbalrog int tft;
36c3d2689dSbalrog int mono;
37c3d2689dSbalrog int enable;
38c3d2689dSbalrog int width;
39c3d2689dSbalrog int height;
40c3d2689dSbalrog int interrupts;
41c3d2689dSbalrog uint32_t timing[3];
42c3d2689dSbalrog uint32_t subpanel;
43c3d2689dSbalrog uint32_t ctrl;
44c3d2689dSbalrog
45c3d2689dSbalrog struct omap_dma_lcd_channel_s *dma;
46c3d2689dSbalrog uint16_t palette[256];
47c3d2689dSbalrog int palette_done;
48c3d2689dSbalrog int frame_done;
49c3d2689dSbalrog int invalidate;
50c3d2689dSbalrog int sync_error;
51c3d2689dSbalrog };
52c3d2689dSbalrog
omap_lcd_interrupts(struct omap_lcd_panel_s * s)53c3d2689dSbalrog static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
54c3d2689dSbalrog {
55c3d2689dSbalrog if (s->frame_done && (s->interrupts & 1)) {
56c3d2689dSbalrog qemu_irq_raise(s->irq);
57c3d2689dSbalrog return;
58c3d2689dSbalrog }
59c3d2689dSbalrog
60c3d2689dSbalrog if (s->palette_done && (s->interrupts & 2)) {
61c3d2689dSbalrog qemu_irq_raise(s->irq);
62c3d2689dSbalrog return;
63c3d2689dSbalrog }
64c3d2689dSbalrog
65c3d2689dSbalrog if (s->sync_error) {
66c3d2689dSbalrog qemu_irq_raise(s->irq);
67c3d2689dSbalrog return;
68c3d2689dSbalrog }
69c3d2689dSbalrog
70c3d2689dSbalrog qemu_irq_lower(s->irq);
71c3d2689dSbalrog }
72c3d2689dSbalrog
731cccdd18SPeter Maydell /*
741cccdd18SPeter Maydell * 2-bit colour
751cccdd18SPeter Maydell */
draw_line2_32(void * opaque,uint8_t * d,const uint8_t * s,int width,int deststep)761cccdd18SPeter Maydell static void draw_line2_32(void *opaque, uint8_t *d, const uint8_t *s,
771cccdd18SPeter Maydell int width, int deststep)
781cccdd18SPeter Maydell {
791cccdd18SPeter Maydell uint16_t *pal = opaque;
801cccdd18SPeter Maydell uint8_t v, r, g, b;
811cccdd18SPeter Maydell
821cccdd18SPeter Maydell do {
831cccdd18SPeter Maydell v = ldub_p((void *) s);
841cccdd18SPeter Maydell r = (pal[v & 3] >> 4) & 0xf0;
851cccdd18SPeter Maydell g = pal[v & 3] & 0xf0;
861cccdd18SPeter Maydell b = (pal[v & 3] << 4) & 0xf0;
871cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
881cccdd18SPeter Maydell d += 4;
891cccdd18SPeter Maydell v >>= 2;
901cccdd18SPeter Maydell r = (pal[v & 3] >> 4) & 0xf0;
911cccdd18SPeter Maydell g = pal[v & 3] & 0xf0;
921cccdd18SPeter Maydell b = (pal[v & 3] << 4) & 0xf0;
931cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
941cccdd18SPeter Maydell d += 4;
951cccdd18SPeter Maydell v >>= 2;
961cccdd18SPeter Maydell r = (pal[v & 3] >> 4) & 0xf0;
971cccdd18SPeter Maydell g = pal[v & 3] & 0xf0;
981cccdd18SPeter Maydell b = (pal[v & 3] << 4) & 0xf0;
991cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1001cccdd18SPeter Maydell d += 4;
1011cccdd18SPeter Maydell v >>= 2;
1021cccdd18SPeter Maydell r = (pal[v & 3] >> 4) & 0xf0;
1031cccdd18SPeter Maydell g = pal[v & 3] & 0xf0;
1041cccdd18SPeter Maydell b = (pal[v & 3] << 4) & 0xf0;
1051cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1061cccdd18SPeter Maydell d += 4;
1071cccdd18SPeter Maydell s++;
1081cccdd18SPeter Maydell width -= 4;
1091cccdd18SPeter Maydell } while (width > 0);
1101cccdd18SPeter Maydell }
1111cccdd18SPeter Maydell
1121cccdd18SPeter Maydell /*
1131cccdd18SPeter Maydell * 4-bit colour
1141cccdd18SPeter Maydell */
draw_line4_32(void * opaque,uint8_t * d,const uint8_t * s,int width,int deststep)1151cccdd18SPeter Maydell static void draw_line4_32(void *opaque, uint8_t *d, const uint8_t *s,
1161cccdd18SPeter Maydell int width, int deststep)
1171cccdd18SPeter Maydell {
1181cccdd18SPeter Maydell uint16_t *pal = opaque;
1191cccdd18SPeter Maydell uint8_t v, r, g, b;
1201cccdd18SPeter Maydell
1211cccdd18SPeter Maydell do {
1221cccdd18SPeter Maydell v = ldub_p((void *) s);
1231cccdd18SPeter Maydell r = (pal[v & 0xf] >> 4) & 0xf0;
1241cccdd18SPeter Maydell g = pal[v & 0xf] & 0xf0;
1251cccdd18SPeter Maydell b = (pal[v & 0xf] << 4) & 0xf0;
1261cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1271cccdd18SPeter Maydell d += 4;
1281cccdd18SPeter Maydell v >>= 4;
1291cccdd18SPeter Maydell r = (pal[v & 0xf] >> 4) & 0xf0;
1301cccdd18SPeter Maydell g = pal[v & 0xf] & 0xf0;
1311cccdd18SPeter Maydell b = (pal[v & 0xf] << 4) & 0xf0;
1321cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1331cccdd18SPeter Maydell d += 4;
1341cccdd18SPeter Maydell s++;
1351cccdd18SPeter Maydell width -= 2;
1361cccdd18SPeter Maydell } while (width > 0);
1371cccdd18SPeter Maydell }
1381cccdd18SPeter Maydell
1391cccdd18SPeter Maydell /*
1401cccdd18SPeter Maydell * 8-bit colour
1411cccdd18SPeter Maydell */
draw_line8_32(void * opaque,uint8_t * d,const uint8_t * s,int width,int deststep)1421cccdd18SPeter Maydell static void draw_line8_32(void *opaque, uint8_t *d, const uint8_t *s,
1431cccdd18SPeter Maydell int width, int deststep)
1441cccdd18SPeter Maydell {
1451cccdd18SPeter Maydell uint16_t *pal = opaque;
1461cccdd18SPeter Maydell uint8_t v, r, g, b;
1471cccdd18SPeter Maydell
1481cccdd18SPeter Maydell do {
1491cccdd18SPeter Maydell v = ldub_p((void *) s);
1501cccdd18SPeter Maydell r = (pal[v] >> 4) & 0xf0;
1511cccdd18SPeter Maydell g = pal[v] & 0xf0;
1521cccdd18SPeter Maydell b = (pal[v] << 4) & 0xf0;
1531cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1541cccdd18SPeter Maydell s++;
1551cccdd18SPeter Maydell d += 4;
1561cccdd18SPeter Maydell } while (-- width != 0);
1571cccdd18SPeter Maydell }
1581cccdd18SPeter Maydell
1591cccdd18SPeter Maydell /*
1601cccdd18SPeter Maydell * 12-bit colour
1611cccdd18SPeter Maydell */
draw_line12_32(void * opaque,uint8_t * d,const uint8_t * s,int width,int deststep)1621cccdd18SPeter Maydell static void draw_line12_32(void *opaque, uint8_t *d, const uint8_t *s,
1631cccdd18SPeter Maydell int width, int deststep)
1641cccdd18SPeter Maydell {
1651cccdd18SPeter Maydell uint16_t v;
1661cccdd18SPeter Maydell uint8_t r, g, b;
1671cccdd18SPeter Maydell
1681cccdd18SPeter Maydell do {
1691cccdd18SPeter Maydell v = lduw_le_p((void *) s);
1701cccdd18SPeter Maydell r = (v >> 4) & 0xf0;
1711cccdd18SPeter Maydell g = v & 0xf0;
1721cccdd18SPeter Maydell b = (v << 4) & 0xf0;
1731cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1741cccdd18SPeter Maydell s += 2;
1751cccdd18SPeter Maydell d += 4;
1761cccdd18SPeter Maydell } while (-- width != 0);
1771cccdd18SPeter Maydell }
1781cccdd18SPeter Maydell
1791cccdd18SPeter Maydell /*
1801cccdd18SPeter Maydell * 16-bit colour
1811cccdd18SPeter Maydell */
draw_line16_32(void * opaque,uint8_t * d,const uint8_t * s,int width,int deststep)1821cccdd18SPeter Maydell static void draw_line16_32(void *opaque, uint8_t *d, const uint8_t *s,
1831cccdd18SPeter Maydell int width, int deststep)
1841cccdd18SPeter Maydell {
1851cccdd18SPeter Maydell uint16_t v;
1861cccdd18SPeter Maydell uint8_t r, g, b;
1871cccdd18SPeter Maydell
1881cccdd18SPeter Maydell do {
1891cccdd18SPeter Maydell v = lduw_le_p((void *) s);
1901cccdd18SPeter Maydell r = (v >> 8) & 0xf8;
1911cccdd18SPeter Maydell g = (v >> 3) & 0xfc;
1921cccdd18SPeter Maydell b = (v << 3) & 0xf8;
1931cccdd18SPeter Maydell ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
1941cccdd18SPeter Maydell s += 2;
1951cccdd18SPeter Maydell d += 4;
1961cccdd18SPeter Maydell } while (-- width != 0);
1971cccdd18SPeter Maydell }
198c3d2689dSbalrog
omap_update_display(void * opaque)1999596ebb7Spbrook static void omap_update_display(void *opaque)
200c3d2689dSbalrog {
201*a75ed3c4SPhilippe Mathieu-Daudé struct omap_lcd_panel_s *omap_lcd = opaque;
2020080edc4SAlexChen DisplaySurface *surface;
203cfb08215SPeter Maydell drawfn draw_line;
204714fa308Spbrook int size, height, first, last;
205714fa308Spbrook int width, linesize, step, bpp, frame_offset;
206a8170e5eSAvi Kivity hwaddr frame_base;
207c3d2689dSbalrog
2080080edc4SAlexChen if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable) {
2090080edc4SAlexChen return;
2100080edc4SAlexChen }
2110080edc4SAlexChen
2120080edc4SAlexChen surface = qemu_console_surface(omap_lcd->con);
2130080edc4SAlexChen if (!surface_bits_per_pixel(surface)) {
214c3d2689dSbalrog return;
215c78f7137SGerd Hoffmann }
216c3d2689dSbalrog
217c3d2689dSbalrog frame_offset = 0;
218c3d2689dSbalrog if (omap_lcd->plm != 2) {
2190eeef0a4SPhilippe Mathieu-Daudé cpu_physical_memory_read(
2200eeef0a4SPhilippe Mathieu-Daudé omap_lcd->dma->phys_framebuffer[omap_lcd->dma->current_frame],
2210eeef0a4SPhilippe Mathieu-Daudé omap_lcd->palette, 0x200);
222c3d2689dSbalrog switch (omap_lcd->palette[0] >> 12 & 7) {
223c3d2689dSbalrog case 3 ... 7:
224c3d2689dSbalrog frame_offset += 0x200;
225c3d2689dSbalrog break;
226c3d2689dSbalrog default:
227c3d2689dSbalrog frame_offset += 0x20;
228c3d2689dSbalrog }
229c3d2689dSbalrog }
230c3d2689dSbalrog
231c3d2689dSbalrog /* Colour depth */
232c3d2689dSbalrog switch ((omap_lcd->palette[0] >> 12) & 7) {
233c3d2689dSbalrog case 1:
234ea644cf3SPooja Dhannawat draw_line = draw_line2_32;
235c3d2689dSbalrog bpp = 2;
236c3d2689dSbalrog break;
237c3d2689dSbalrog
238c3d2689dSbalrog case 2:
239ea644cf3SPooja Dhannawat draw_line = draw_line4_32;
240c3d2689dSbalrog bpp = 4;
241c3d2689dSbalrog break;
242c3d2689dSbalrog
243c3d2689dSbalrog case 3:
244ea644cf3SPooja Dhannawat draw_line = draw_line8_32;
245c3d2689dSbalrog bpp = 8;
246c3d2689dSbalrog break;
247c3d2689dSbalrog
248c3d2689dSbalrog case 4 ... 7:
249c3d2689dSbalrog if (!omap_lcd->tft)
250ea644cf3SPooja Dhannawat draw_line = draw_line12_32;
251c3d2689dSbalrog else
252ea644cf3SPooja Dhannawat draw_line = draw_line16_32;
253c3d2689dSbalrog bpp = 16;
254c3d2689dSbalrog break;
255c3d2689dSbalrog
256c3d2689dSbalrog default:
257c3d2689dSbalrog /* Unsupported at the moment. */
258c3d2689dSbalrog return;
259c3d2689dSbalrog }
260c3d2689dSbalrog
261c3d2689dSbalrog /* Resolution */
262c3d2689dSbalrog width = omap_lcd->width;
263c78f7137SGerd Hoffmann if (width != surface_width(surface) ||
264c78f7137SGerd Hoffmann omap_lcd->height != surface_height(surface)) {
265c78f7137SGerd Hoffmann qemu_console_resize(omap_lcd->con,
266c3d2689dSbalrog omap_lcd->width, omap_lcd->height);
267c78f7137SGerd Hoffmann surface = qemu_console_surface(omap_lcd->con);
268c3d2689dSbalrog omap_lcd->invalidate = 1;
269c3d2689dSbalrog }
270c3d2689dSbalrog
271c3d2689dSbalrog if (omap_lcd->dma->current_frame == 0)
272c3d2689dSbalrog size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
273c3d2689dSbalrog else
274c3d2689dSbalrog size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
275c3d2689dSbalrog
276c3d2689dSbalrog if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
277c3d2689dSbalrog omap_lcd->sync_error = 1;
278c3d2689dSbalrog omap_lcd_interrupts(omap_lcd);
279c3d2689dSbalrog omap_lcd->enable = 0;
280c3d2689dSbalrog return;
281c3d2689dSbalrog }
282c3d2689dSbalrog
283c3d2689dSbalrog /* Content */
284c3d2689dSbalrog frame_base = omap_lcd->dma->phys_framebuffer[
285c3d2689dSbalrog omap_lcd->dma->current_frame] + frame_offset;
286c3d2689dSbalrog omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
287c3d2689dSbalrog if (omap_lcd->dma->interrupts & 1)
288c3d2689dSbalrog qemu_irq_raise(omap_lcd->dma->irq);
289c3d2689dSbalrog if (omap_lcd->dma->dual)
290c3d2689dSbalrog omap_lcd->dma->current_frame ^= 1;
291c3d2689dSbalrog
292c78f7137SGerd Hoffmann if (!surface_bits_per_pixel(surface)) {
293c3d2689dSbalrog return;
294c78f7137SGerd Hoffmann }
295c3d2689dSbalrog
296714fa308Spbrook first = 0;
297c3d2689dSbalrog height = omap_lcd->height;
298c3d2689dSbalrog if (omap_lcd->subpanel & (1 << 31)) {
299c3d2689dSbalrog if (omap_lcd->subpanel & (1 << 29))
300714fa308Spbrook first = (omap_lcd->subpanel >> 16) & 0x3ff;
301c3d2689dSbalrog else
302c3d2689dSbalrog height = (omap_lcd->subpanel >> 16) & 0x3ff;
303c3d2689dSbalrog /* TODO: fill the rest of the panel with DPD */
304c3d2689dSbalrog }
305714fa308Spbrook
306c3d2689dSbalrog step = width * bpp >> 3;
307c78f7137SGerd Hoffmann linesize = surface_stride(surface);
308c1076c3eSPaolo Bonzini if (omap_lcd->invalidate) {
309c1076c3eSPaolo Bonzini framebuffer_update_memory_section(&omap_lcd->fbsection,
310c1076c3eSPaolo Bonzini omap_lcd->sysmem, frame_base,
311c1076c3eSPaolo Bonzini height, step);
312c1076c3eSPaolo Bonzini }
313c1076c3eSPaolo Bonzini
314c1076c3eSPaolo Bonzini framebuffer_update_display(surface, &omap_lcd->fbsection,
315c1076c3eSPaolo Bonzini width, height,
316714fa308Spbrook step, linesize, 0,
317714fa308Spbrook omap_lcd->invalidate,
318714fa308Spbrook draw_line, omap_lcd->palette,
319714fa308Spbrook &first, &last);
320c1076c3eSPaolo Bonzini
321714fa308Spbrook if (first >= 0) {
322c78f7137SGerd Hoffmann dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
323c3d2689dSbalrog }
324714fa308Spbrook omap_lcd->invalidate = 0;
325c3d2689dSbalrog }
326c3d2689dSbalrog
omap_invalidate_display(void * opaque)3279596ebb7Spbrook static void omap_invalidate_display(void *opaque) {
328c3d2689dSbalrog struct omap_lcd_panel_s *omap_lcd = opaque;
329c3d2689dSbalrog omap_lcd->invalidate = 1;
330c3d2689dSbalrog }
331c3d2689dSbalrog
omap_lcd_update(struct omap_lcd_panel_s * s)3329596ebb7Spbrook static void omap_lcd_update(struct omap_lcd_panel_s *s) {
333c3d2689dSbalrog if (!s->enable) {
334c3d2689dSbalrog s->dma->current_frame = -1;
335c3d2689dSbalrog s->sync_error = 0;
336c3d2689dSbalrog if (s->plm != 1)
337c3d2689dSbalrog s->frame_done = 1;
338c3d2689dSbalrog omap_lcd_interrupts(s);
339c3d2689dSbalrog return;
340c3d2689dSbalrog }
341c3d2689dSbalrog
342c3d2689dSbalrog if (s->dma->current_frame == -1) {
343c3d2689dSbalrog s->frame_done = 0;
344c3d2689dSbalrog s->palette_done = 0;
345c3d2689dSbalrog s->dma->current_frame = 0;
346c3d2689dSbalrog }
347c3d2689dSbalrog
348c3d2689dSbalrog if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
349c3d2689dSbalrog s->dma->src_f1_top) ||
350c3d2689dSbalrog !s->dma->mpu->port[
351c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu,
352c3d2689dSbalrog s->dma->src_f1_bottom) ||
353c3d2689dSbalrog (s->dma->dual &&
354c3d2689dSbalrog (!s->dma->mpu->port[
355c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu,
356c3d2689dSbalrog s->dma->src_f2_top) ||
357c3d2689dSbalrog !s->dma->mpu->port[
358c3d2689dSbalrog s->dma->src].addr_valid(s->dma->mpu,
359c3d2689dSbalrog s->dma->src_f2_bottom)))) {
360c3d2689dSbalrog s->dma->condition |= 1 << 2;
361c3d2689dSbalrog if (s->dma->interrupts & (1 << 1))
362c3d2689dSbalrog qemu_irq_raise(s->dma->irq);
363c3d2689dSbalrog s->enable = 0;
364c3d2689dSbalrog return;
365c3d2689dSbalrog }
366c3d2689dSbalrog
367714fa308Spbrook s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
368714fa308Spbrook s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
369c3d2689dSbalrog
370c3d2689dSbalrog if (s->plm != 2 && !s->palette_done) {
371714fa308Spbrook cpu_physical_memory_read(
372714fa308Spbrook s->dma->phys_framebuffer[s->dma->current_frame],
3730eeef0a4SPhilippe Mathieu-Daudé s->palette, 0x200);
374c3d2689dSbalrog s->palette_done = 1;
375c3d2689dSbalrog omap_lcd_interrupts(s);
376c3d2689dSbalrog }
377c3d2689dSbalrog }
378c3d2689dSbalrog
omap_lcdc_read(void * opaque,hwaddr addr,unsigned size)379*a75ed3c4SPhilippe Mathieu-Daudé static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, unsigned size)
380c3d2689dSbalrog {
381*a75ed3c4SPhilippe Mathieu-Daudé struct omap_lcd_panel_s *s = opaque;
382c3d2689dSbalrog
3838da3ff18Spbrook switch (addr) {
384c3d2689dSbalrog case 0x00: /* LCD_CONTROL */
385c3d2689dSbalrog return (s->tft << 23) | (s->plm << 20) |
386c3d2689dSbalrog (s->tft << 7) | (s->interrupts << 3) |
387c3d2689dSbalrog (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
388c3d2689dSbalrog
389c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */
390c3d2689dSbalrog return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
391c3d2689dSbalrog
392c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */
393c3d2689dSbalrog return (s->timing[1] << 10) | (s->height - 1);
394c3d2689dSbalrog
395c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */
396c3d2689dSbalrog return s->timing[2] | 0xfc000000;
397c3d2689dSbalrog
398c3d2689dSbalrog case 0x10: /* LCD_STATUS */
399c3d2689dSbalrog return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
400c3d2689dSbalrog
401c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */
402c3d2689dSbalrog return s->subpanel;
403c3d2689dSbalrog
404c3d2689dSbalrog default:
405c3d2689dSbalrog break;
406c3d2689dSbalrog }
407c3d2689dSbalrog OMAP_BAD_REG(addr);
408c3d2689dSbalrog return 0;
409c3d2689dSbalrog }
410c3d2689dSbalrog
omap_lcdc_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)411a8170e5eSAvi Kivity static void omap_lcdc_write(void *opaque, hwaddr addr,
41230af1ec7SBenoît Canet uint64_t value, unsigned size)
413c3d2689dSbalrog {
414*a75ed3c4SPhilippe Mathieu-Daudé struct omap_lcd_panel_s *s = opaque;
415c3d2689dSbalrog
4168da3ff18Spbrook switch (addr) {
417c3d2689dSbalrog case 0x00: /* LCD_CONTROL */
418c3d2689dSbalrog s->plm = (value >> 20) & 3;
419c3d2689dSbalrog s->tft = (value >> 7) & 1;
420c3d2689dSbalrog s->interrupts = (value >> 3) & 3;
421c3d2689dSbalrog s->mono = (value >> 1) & 1;
422c3d2689dSbalrog s->ctrl = value & 0x01cff300;
423c3d2689dSbalrog if (s->enable != (value & 1)) {
424c3d2689dSbalrog s->enable = value & 1;
425c3d2689dSbalrog omap_lcd_update(s);
426c3d2689dSbalrog }
427c3d2689dSbalrog break;
428c3d2689dSbalrog
429c3d2689dSbalrog case 0x04: /* LCD_TIMING0 */
430c3d2689dSbalrog s->timing[0] = value >> 10;
431c3d2689dSbalrog s->width = (value & 0x3ff) + 1;
432c3d2689dSbalrog break;
433c3d2689dSbalrog
434c3d2689dSbalrog case 0x08: /* LCD_TIMING1 */
435c3d2689dSbalrog s->timing[1] = value >> 10;
436c3d2689dSbalrog s->height = (value & 0x3ff) + 1;
437c3d2689dSbalrog break;
438c3d2689dSbalrog
439c3d2689dSbalrog case 0x0c: /* LCD_TIMING2 */
440c3d2689dSbalrog s->timing[2] = value;
441c3d2689dSbalrog break;
442c3d2689dSbalrog
443c3d2689dSbalrog case 0x10: /* LCD_STATUS */
444c3d2689dSbalrog break;
445c3d2689dSbalrog
446c3d2689dSbalrog case 0x14: /* LCD_SUBPANEL */
447c3d2689dSbalrog s->subpanel = value & 0xa1ffffff;
448c3d2689dSbalrog break;
449c3d2689dSbalrog
450c3d2689dSbalrog default:
451c3d2689dSbalrog OMAP_BAD_REG(addr);
452c3d2689dSbalrog }
453c3d2689dSbalrog }
454c3d2689dSbalrog
45530af1ec7SBenoît Canet static const MemoryRegionOps omap_lcdc_ops = {
45630af1ec7SBenoît Canet .read = omap_lcdc_read,
45730af1ec7SBenoît Canet .write = omap_lcdc_write,
45830af1ec7SBenoît Canet .endianness = DEVICE_NATIVE_ENDIAN,
459c3d2689dSbalrog };
460c3d2689dSbalrog
omap_lcdc_reset(struct omap_lcd_panel_s * s)461c3d2689dSbalrog void omap_lcdc_reset(struct omap_lcd_panel_s *s)
462c3d2689dSbalrog {
463c3d2689dSbalrog s->dma->current_frame = -1;
464c3d2689dSbalrog s->plm = 0;
465c3d2689dSbalrog s->tft = 0;
466c3d2689dSbalrog s->mono = 0;
467c3d2689dSbalrog s->enable = 0;
468c3d2689dSbalrog s->width = 0;
469c3d2689dSbalrog s->height = 0;
470c3d2689dSbalrog s->interrupts = 0;
471c3d2689dSbalrog s->timing[0] = 0;
472c3d2689dSbalrog s->timing[1] = 0;
473c3d2689dSbalrog s->timing[2] = 0;
474c3d2689dSbalrog s->subpanel = 0;
475c3d2689dSbalrog s->palette_done = 0;
476c3d2689dSbalrog s->frame_done = 0;
477c3d2689dSbalrog s->sync_error = 0;
478c3d2689dSbalrog s->invalidate = 1;
479c3d2689dSbalrog s->subpanel = 0;
480c3d2689dSbalrog s->ctrl = 0;
481c3d2689dSbalrog }
482c3d2689dSbalrog
483380cd056SGerd Hoffmann static const GraphicHwOps omap_ops = {
484380cd056SGerd Hoffmann .invalidate = omap_invalidate_display,
485380cd056SGerd Hoffmann .gfx_update = omap_update_display,
486380cd056SGerd Hoffmann };
487380cd056SGerd Hoffmann
omap_lcdc_init(MemoryRegion * sysmem,hwaddr base,qemu_irq irq,struct omap_dma_lcd_channel_s * dma,omap_clk clk)48830af1ec7SBenoît Canet struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
489a8170e5eSAvi Kivity hwaddr base,
49030af1ec7SBenoît Canet qemu_irq irq,
49130af1ec7SBenoît Canet struct omap_dma_lcd_channel_s *dma,
49230af1ec7SBenoît Canet omap_clk clk)
493c3d2689dSbalrog {
494b45c03f5SMarkus Armbruster struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
495c3d2689dSbalrog
496c3d2689dSbalrog s->irq = irq;
497c3d2689dSbalrog s->dma = dma;
49875c9d6c2SAvi Kivity s->sysmem = sysmem;
499c3d2689dSbalrog omap_lcdc_reset(s);
500c3d2689dSbalrog
5012c9b15caSPaolo Bonzini memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
50230af1ec7SBenoît Canet memory_region_add_subregion(sysmem, base, &s->iomem);
503c3d2689dSbalrog
5045643706aSGerd Hoffmann s->con = graphic_console_init(NULL, 0, &omap_ops, s);
505c3d2689dSbalrog
506c3d2689dSbalrog return s;
507c3d2689dSbalrog }
508