xref: /qemu/hw/display/omap_lcdc.c (revision a75ed3c4)
1fc97bb5bSPaolo Bonzini /*
2fc97bb5bSPaolo Bonzini  * OMAP LCD controller.
3fc97bb5bSPaolo Bonzini  *
4fc97bb5bSPaolo Bonzini  * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
5fc97bb5bSPaolo Bonzini  *
6fc97bb5bSPaolo Bonzini  * This program is free software; you can redistribute it and/or
7fc97bb5bSPaolo Bonzini  * modify it under the terms of the GNU General Public License as
8fc97bb5bSPaolo Bonzini  * published by the Free Software Foundation; either version 2 of
9fc97bb5bSPaolo Bonzini  * the License, or (at your option) any later version.
10fc97bb5bSPaolo Bonzini  *
11fc97bb5bSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
12fc97bb5bSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13fc97bb5bSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14fc97bb5bSPaolo Bonzini  * GNU General Public License for more details.
15fc97bb5bSPaolo Bonzini  *
16fc97bb5bSPaolo Bonzini  * You should have received a copy of the GNU General Public License along
17fc97bb5bSPaolo Bonzini  * with this program; if not, see <http://www.gnu.org/licenses/>.
18fc97bb5bSPaolo Bonzini  */
1964552b6bSMarkus Armbruster 
2047df5154SPeter Maydell #include "qemu/osdep.h"
2164552b6bSMarkus Armbruster #include "hw/irq.h"
22fc97bb5bSPaolo Bonzini #include "ui/console.h"
23fc97bb5bSPaolo Bonzini #include "hw/arm/omap.h"
2447b43a1fSPaolo Bonzini #include "framebuffer.h"
25fc97bb5bSPaolo Bonzini #include "ui/pixel_ops.h"
26fc97bb5bSPaolo Bonzini 
27fc97bb5bSPaolo Bonzini struct omap_lcd_panel_s {
28fc97bb5bSPaolo Bonzini     MemoryRegion *sysmem;
29fc97bb5bSPaolo Bonzini     MemoryRegion iomem;
30c1076c3eSPaolo Bonzini     MemoryRegionSection fbsection;
31fc97bb5bSPaolo Bonzini     qemu_irq irq;
32fc97bb5bSPaolo Bonzini     QemuConsole *con;
33fc97bb5bSPaolo Bonzini 
34fc97bb5bSPaolo Bonzini     int plm;
35fc97bb5bSPaolo Bonzini     int tft;
36fc97bb5bSPaolo Bonzini     int mono;
37fc97bb5bSPaolo Bonzini     int enable;
38fc97bb5bSPaolo Bonzini     int width;
39fc97bb5bSPaolo Bonzini     int height;
40fc97bb5bSPaolo Bonzini     int interrupts;
41fc97bb5bSPaolo Bonzini     uint32_t timing[3];
42fc97bb5bSPaolo Bonzini     uint32_t subpanel;
43fc97bb5bSPaolo Bonzini     uint32_t ctrl;
44fc97bb5bSPaolo Bonzini 
45fc97bb5bSPaolo Bonzini     struct omap_dma_lcd_channel_s *dma;
46fc97bb5bSPaolo Bonzini     uint16_t palette[256];
47fc97bb5bSPaolo Bonzini     int palette_done;
48fc97bb5bSPaolo Bonzini     int frame_done;
49fc97bb5bSPaolo Bonzini     int invalidate;
50fc97bb5bSPaolo Bonzini     int sync_error;
51fc97bb5bSPaolo Bonzini };
52fc97bb5bSPaolo Bonzini 
omap_lcd_interrupts(struct omap_lcd_panel_s * s)53fc97bb5bSPaolo Bonzini static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
54fc97bb5bSPaolo Bonzini {
55fc97bb5bSPaolo Bonzini     if (s->frame_done && (s->interrupts & 1)) {
56fc97bb5bSPaolo Bonzini         qemu_irq_raise(s->irq);
57fc97bb5bSPaolo Bonzini         return;
58fc97bb5bSPaolo Bonzini     }
59fc97bb5bSPaolo Bonzini 
60fc97bb5bSPaolo Bonzini     if (s->palette_done && (s->interrupts & 2)) {
61fc97bb5bSPaolo Bonzini         qemu_irq_raise(s->irq);
62fc97bb5bSPaolo Bonzini         return;
63fc97bb5bSPaolo Bonzini     }
64fc97bb5bSPaolo Bonzini 
65fc97bb5bSPaolo Bonzini     if (s->sync_error) {
66fc97bb5bSPaolo Bonzini         qemu_irq_raise(s->irq);
67fc97bb5bSPaolo Bonzini         return;
68fc97bb5bSPaolo Bonzini     }
69fc97bb5bSPaolo Bonzini 
70fc97bb5bSPaolo Bonzini     qemu_irq_lower(s->irq);
71fc97bb5bSPaolo Bonzini }
72fc97bb5bSPaolo Bonzini 
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 }
198fc97bb5bSPaolo Bonzini 
omap_update_display(void * opaque)199fc97bb5bSPaolo Bonzini static void omap_update_display(void *opaque)
200fc97bb5bSPaolo Bonzini {
201*a75ed3c4SPhilippe Mathieu-Daudé     struct omap_lcd_panel_s *omap_lcd = opaque;
2020080edc4SAlexChen     DisplaySurface *surface;
203cfb08215SPeter Maydell     drawfn draw_line;
204fc97bb5bSPaolo Bonzini     int size, height, first, last;
205fc97bb5bSPaolo Bonzini     int width, linesize, step, bpp, frame_offset;
206fc97bb5bSPaolo Bonzini     hwaddr frame_base;
207fc97bb5bSPaolo Bonzini 
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)) {
214fc97bb5bSPaolo Bonzini         return;
215fc97bb5bSPaolo Bonzini     }
216fc97bb5bSPaolo Bonzini 
217fc97bb5bSPaolo Bonzini     frame_offset = 0;
218fc97bb5bSPaolo Bonzini     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);
222fc97bb5bSPaolo Bonzini         switch (omap_lcd->palette[0] >> 12 & 7) {
223fc97bb5bSPaolo Bonzini         case 3 ... 7:
224fc97bb5bSPaolo Bonzini             frame_offset += 0x200;
225fc97bb5bSPaolo Bonzini             break;
226fc97bb5bSPaolo Bonzini         default:
227fc97bb5bSPaolo Bonzini             frame_offset += 0x20;
228fc97bb5bSPaolo Bonzini         }
229fc97bb5bSPaolo Bonzini     }
230fc97bb5bSPaolo Bonzini 
231fc97bb5bSPaolo Bonzini     /* Colour depth */
232fc97bb5bSPaolo Bonzini     switch ((omap_lcd->palette[0] >> 12) & 7) {
233fc97bb5bSPaolo Bonzini     case 1:
234ea644cf3SPooja Dhannawat         draw_line = draw_line2_32;
235fc97bb5bSPaolo Bonzini         bpp = 2;
236fc97bb5bSPaolo Bonzini         break;
237fc97bb5bSPaolo Bonzini 
238fc97bb5bSPaolo Bonzini     case 2:
239ea644cf3SPooja Dhannawat         draw_line = draw_line4_32;
240fc97bb5bSPaolo Bonzini         bpp = 4;
241fc97bb5bSPaolo Bonzini         break;
242fc97bb5bSPaolo Bonzini 
243fc97bb5bSPaolo Bonzini     case 3:
244ea644cf3SPooja Dhannawat         draw_line = draw_line8_32;
245fc97bb5bSPaolo Bonzini         bpp = 8;
246fc97bb5bSPaolo Bonzini         break;
247fc97bb5bSPaolo Bonzini 
248fc97bb5bSPaolo Bonzini     case 4 ... 7:
249fc97bb5bSPaolo Bonzini         if (!omap_lcd->tft)
250ea644cf3SPooja Dhannawat             draw_line = draw_line12_32;
251fc97bb5bSPaolo Bonzini         else
252ea644cf3SPooja Dhannawat             draw_line = draw_line16_32;
253fc97bb5bSPaolo Bonzini         bpp = 16;
254fc97bb5bSPaolo Bonzini         break;
255fc97bb5bSPaolo Bonzini 
256fc97bb5bSPaolo Bonzini     default:
257fc97bb5bSPaolo Bonzini         /* Unsupported at the moment.  */
258fc97bb5bSPaolo Bonzini         return;
259fc97bb5bSPaolo Bonzini     }
260fc97bb5bSPaolo Bonzini 
261fc97bb5bSPaolo Bonzini     /* Resolution */
262fc97bb5bSPaolo Bonzini     width = omap_lcd->width;
263fc97bb5bSPaolo Bonzini     if (width != surface_width(surface) ||
264fc97bb5bSPaolo Bonzini         omap_lcd->height != surface_height(surface)) {
265fc97bb5bSPaolo Bonzini         qemu_console_resize(omap_lcd->con,
266fc97bb5bSPaolo Bonzini                             omap_lcd->width, omap_lcd->height);
267fc97bb5bSPaolo Bonzini         surface = qemu_console_surface(omap_lcd->con);
268fc97bb5bSPaolo Bonzini         omap_lcd->invalidate = 1;
269fc97bb5bSPaolo Bonzini     }
270fc97bb5bSPaolo Bonzini 
271fc97bb5bSPaolo Bonzini     if (omap_lcd->dma->current_frame == 0)
272fc97bb5bSPaolo Bonzini         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
273fc97bb5bSPaolo Bonzini     else
274fc97bb5bSPaolo Bonzini         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
275fc97bb5bSPaolo Bonzini 
276fc97bb5bSPaolo Bonzini     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
277fc97bb5bSPaolo Bonzini         omap_lcd->sync_error = 1;
278fc97bb5bSPaolo Bonzini         omap_lcd_interrupts(omap_lcd);
279fc97bb5bSPaolo Bonzini         omap_lcd->enable = 0;
280fc97bb5bSPaolo Bonzini         return;
281fc97bb5bSPaolo Bonzini     }
282fc97bb5bSPaolo Bonzini 
283fc97bb5bSPaolo Bonzini     /* Content */
284fc97bb5bSPaolo Bonzini     frame_base = omap_lcd->dma->phys_framebuffer[
285fc97bb5bSPaolo Bonzini             omap_lcd->dma->current_frame] + frame_offset;
286fc97bb5bSPaolo Bonzini     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
287fc97bb5bSPaolo Bonzini     if (omap_lcd->dma->interrupts & 1)
288fc97bb5bSPaolo Bonzini         qemu_irq_raise(omap_lcd->dma->irq);
289fc97bb5bSPaolo Bonzini     if (omap_lcd->dma->dual)
290fc97bb5bSPaolo Bonzini         omap_lcd->dma->current_frame ^= 1;
291fc97bb5bSPaolo Bonzini 
292fc97bb5bSPaolo Bonzini     if (!surface_bits_per_pixel(surface)) {
293fc97bb5bSPaolo Bonzini         return;
294fc97bb5bSPaolo Bonzini     }
295fc97bb5bSPaolo Bonzini 
296fc97bb5bSPaolo Bonzini     first = 0;
297fc97bb5bSPaolo Bonzini     height = omap_lcd->height;
298fc97bb5bSPaolo Bonzini     if (omap_lcd->subpanel & (1 << 31)) {
299fc97bb5bSPaolo Bonzini         if (omap_lcd->subpanel & (1 << 29))
300fc97bb5bSPaolo Bonzini             first = (omap_lcd->subpanel >> 16) & 0x3ff;
301fc97bb5bSPaolo Bonzini         else
302fc97bb5bSPaolo Bonzini             height = (omap_lcd->subpanel >> 16) & 0x3ff;
303fc97bb5bSPaolo Bonzini         /* TODO: fill the rest of the panel with DPD */
304fc97bb5bSPaolo Bonzini     }
305fc97bb5bSPaolo Bonzini 
306fc97bb5bSPaolo Bonzini     step = width * bpp >> 3;
307fc97bb5bSPaolo Bonzini     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,
316fc97bb5bSPaolo Bonzini                                step, linesize, 0,
317fc97bb5bSPaolo Bonzini                                omap_lcd->invalidate,
318fc97bb5bSPaolo Bonzini                                draw_line, omap_lcd->palette,
319fc97bb5bSPaolo Bonzini                                &first, &last);
320c1076c3eSPaolo Bonzini 
321fc97bb5bSPaolo Bonzini     if (first >= 0) {
322fc97bb5bSPaolo Bonzini         dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
323fc97bb5bSPaolo Bonzini     }
324fc97bb5bSPaolo Bonzini     omap_lcd->invalidate = 0;
325fc97bb5bSPaolo Bonzini }
326fc97bb5bSPaolo Bonzini 
omap_invalidate_display(void * opaque)327fc97bb5bSPaolo Bonzini static void omap_invalidate_display(void *opaque) {
328fc97bb5bSPaolo Bonzini     struct omap_lcd_panel_s *omap_lcd = opaque;
329fc97bb5bSPaolo Bonzini     omap_lcd->invalidate = 1;
330fc97bb5bSPaolo Bonzini }
331fc97bb5bSPaolo Bonzini 
omap_lcd_update(struct omap_lcd_panel_s * s)332fc97bb5bSPaolo Bonzini static void omap_lcd_update(struct omap_lcd_panel_s *s) {
333fc97bb5bSPaolo Bonzini     if (!s->enable) {
334fc97bb5bSPaolo Bonzini         s->dma->current_frame = -1;
335fc97bb5bSPaolo Bonzini         s->sync_error = 0;
336fc97bb5bSPaolo Bonzini         if (s->plm != 1)
337fc97bb5bSPaolo Bonzini             s->frame_done = 1;
338fc97bb5bSPaolo Bonzini         omap_lcd_interrupts(s);
339fc97bb5bSPaolo Bonzini         return;
340fc97bb5bSPaolo Bonzini     }
341fc97bb5bSPaolo Bonzini 
342fc97bb5bSPaolo Bonzini     if (s->dma->current_frame == -1) {
343fc97bb5bSPaolo Bonzini         s->frame_done = 0;
344fc97bb5bSPaolo Bonzini         s->palette_done = 0;
345fc97bb5bSPaolo Bonzini         s->dma->current_frame = 0;
346fc97bb5bSPaolo Bonzini     }
347fc97bb5bSPaolo Bonzini 
348fc97bb5bSPaolo Bonzini     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
349fc97bb5bSPaolo Bonzini                             s->dma->src_f1_top) ||
350fc97bb5bSPaolo Bonzini                     !s->dma->mpu->port[
351fc97bb5bSPaolo Bonzini                     s->dma->src].addr_valid(s->dma->mpu,
352fc97bb5bSPaolo Bonzini                             s->dma->src_f1_bottom) ||
353fc97bb5bSPaolo Bonzini                     (s->dma->dual &&
354fc97bb5bSPaolo Bonzini                      (!s->dma->mpu->port[
355fc97bb5bSPaolo Bonzini                       s->dma->src].addr_valid(s->dma->mpu,
356fc97bb5bSPaolo Bonzini                               s->dma->src_f2_top) ||
357fc97bb5bSPaolo Bonzini                       !s->dma->mpu->port[
358fc97bb5bSPaolo Bonzini                       s->dma->src].addr_valid(s->dma->mpu,
359fc97bb5bSPaolo Bonzini                               s->dma->src_f2_bottom)))) {
360fc97bb5bSPaolo Bonzini         s->dma->condition |= 1 << 2;
361fc97bb5bSPaolo Bonzini         if (s->dma->interrupts & (1 << 1))
362fc97bb5bSPaolo Bonzini             qemu_irq_raise(s->dma->irq);
363fc97bb5bSPaolo Bonzini         s->enable = 0;
364fc97bb5bSPaolo Bonzini         return;
365fc97bb5bSPaolo Bonzini     }
366fc97bb5bSPaolo Bonzini 
367fc97bb5bSPaolo Bonzini     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
368fc97bb5bSPaolo Bonzini     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
369fc97bb5bSPaolo Bonzini 
370fc97bb5bSPaolo Bonzini     if (s->plm != 2 && !s->palette_done) {
371fc97bb5bSPaolo Bonzini         cpu_physical_memory_read(
372fc97bb5bSPaolo Bonzini                             s->dma->phys_framebuffer[s->dma->current_frame],
3730eeef0a4SPhilippe Mathieu-Daudé                             s->palette, 0x200);
374fc97bb5bSPaolo Bonzini         s->palette_done = 1;
375fc97bb5bSPaolo Bonzini         omap_lcd_interrupts(s);
376fc97bb5bSPaolo Bonzini     }
377fc97bb5bSPaolo Bonzini }
378fc97bb5bSPaolo Bonzini 
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)
380fc97bb5bSPaolo Bonzini {
381*a75ed3c4SPhilippe Mathieu-Daudé     struct omap_lcd_panel_s *s = opaque;
382fc97bb5bSPaolo Bonzini 
383fc97bb5bSPaolo Bonzini     switch (addr) {
384fc97bb5bSPaolo Bonzini     case 0x00:	/* LCD_CONTROL */
385fc97bb5bSPaolo Bonzini         return (s->tft << 23) | (s->plm << 20) |
386fc97bb5bSPaolo Bonzini                 (s->tft << 7) | (s->interrupts << 3) |
387fc97bb5bSPaolo Bonzini                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
388fc97bb5bSPaolo Bonzini 
389fc97bb5bSPaolo Bonzini     case 0x04:	/* LCD_TIMING0 */
390fc97bb5bSPaolo Bonzini         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
391fc97bb5bSPaolo Bonzini 
392fc97bb5bSPaolo Bonzini     case 0x08:	/* LCD_TIMING1 */
393fc97bb5bSPaolo Bonzini         return (s->timing[1] << 10) | (s->height - 1);
394fc97bb5bSPaolo Bonzini 
395fc97bb5bSPaolo Bonzini     case 0x0c:	/* LCD_TIMING2 */
396fc97bb5bSPaolo Bonzini         return s->timing[2] | 0xfc000000;
397fc97bb5bSPaolo Bonzini 
398fc97bb5bSPaolo Bonzini     case 0x10:	/* LCD_STATUS */
399fc97bb5bSPaolo Bonzini         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
400fc97bb5bSPaolo Bonzini 
401fc97bb5bSPaolo Bonzini     case 0x14:	/* LCD_SUBPANEL */
402fc97bb5bSPaolo Bonzini         return s->subpanel;
403fc97bb5bSPaolo Bonzini 
404fc97bb5bSPaolo Bonzini     default:
405fc97bb5bSPaolo Bonzini         break;
406fc97bb5bSPaolo Bonzini     }
407fc97bb5bSPaolo Bonzini     OMAP_BAD_REG(addr);
408fc97bb5bSPaolo Bonzini     return 0;
409fc97bb5bSPaolo Bonzini }
410fc97bb5bSPaolo Bonzini 
omap_lcdc_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)411fc97bb5bSPaolo Bonzini static void omap_lcdc_write(void *opaque, hwaddr addr,
412fc97bb5bSPaolo Bonzini                             uint64_t value, unsigned size)
413fc97bb5bSPaolo Bonzini {
414*a75ed3c4SPhilippe Mathieu-Daudé     struct omap_lcd_panel_s *s = opaque;
415fc97bb5bSPaolo Bonzini 
416fc97bb5bSPaolo Bonzini     switch (addr) {
417fc97bb5bSPaolo Bonzini     case 0x00:	/* LCD_CONTROL */
418fc97bb5bSPaolo Bonzini         s->plm = (value >> 20) & 3;
419fc97bb5bSPaolo Bonzini         s->tft = (value >> 7) & 1;
420fc97bb5bSPaolo Bonzini         s->interrupts = (value >> 3) & 3;
421fc97bb5bSPaolo Bonzini         s->mono = (value >> 1) & 1;
422fc97bb5bSPaolo Bonzini         s->ctrl = value & 0x01cff300;
423fc97bb5bSPaolo Bonzini         if (s->enable != (value & 1)) {
424fc97bb5bSPaolo Bonzini             s->enable = value & 1;
425fc97bb5bSPaolo Bonzini             omap_lcd_update(s);
426fc97bb5bSPaolo Bonzini         }
427fc97bb5bSPaolo Bonzini         break;
428fc97bb5bSPaolo Bonzini 
429fc97bb5bSPaolo Bonzini     case 0x04:	/* LCD_TIMING0 */
430fc97bb5bSPaolo Bonzini         s->timing[0] = value >> 10;
431fc97bb5bSPaolo Bonzini         s->width = (value & 0x3ff) + 1;
432fc97bb5bSPaolo Bonzini         break;
433fc97bb5bSPaolo Bonzini 
434fc97bb5bSPaolo Bonzini     case 0x08:	/* LCD_TIMING1 */
435fc97bb5bSPaolo Bonzini         s->timing[1] = value >> 10;
436fc97bb5bSPaolo Bonzini         s->height = (value & 0x3ff) + 1;
437fc97bb5bSPaolo Bonzini         break;
438fc97bb5bSPaolo Bonzini 
439fc97bb5bSPaolo Bonzini     case 0x0c:	/* LCD_TIMING2 */
440fc97bb5bSPaolo Bonzini         s->timing[2] = value;
441fc97bb5bSPaolo Bonzini         break;
442fc97bb5bSPaolo Bonzini 
443fc97bb5bSPaolo Bonzini     case 0x10:	/* LCD_STATUS */
444fc97bb5bSPaolo Bonzini         break;
445fc97bb5bSPaolo Bonzini 
446fc97bb5bSPaolo Bonzini     case 0x14:	/* LCD_SUBPANEL */
447fc97bb5bSPaolo Bonzini         s->subpanel = value & 0xa1ffffff;
448fc97bb5bSPaolo Bonzini         break;
449fc97bb5bSPaolo Bonzini 
450fc97bb5bSPaolo Bonzini     default:
451fc97bb5bSPaolo Bonzini         OMAP_BAD_REG(addr);
452fc97bb5bSPaolo Bonzini     }
453fc97bb5bSPaolo Bonzini }
454fc97bb5bSPaolo Bonzini 
455fc97bb5bSPaolo Bonzini static const MemoryRegionOps omap_lcdc_ops = {
456fc97bb5bSPaolo Bonzini     .read = omap_lcdc_read,
457fc97bb5bSPaolo Bonzini     .write = omap_lcdc_write,
458fc97bb5bSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
459fc97bb5bSPaolo Bonzini };
460fc97bb5bSPaolo Bonzini 
omap_lcdc_reset(struct omap_lcd_panel_s * s)461fc97bb5bSPaolo Bonzini void omap_lcdc_reset(struct omap_lcd_panel_s *s)
462fc97bb5bSPaolo Bonzini {
463fc97bb5bSPaolo Bonzini     s->dma->current_frame = -1;
464fc97bb5bSPaolo Bonzini     s->plm = 0;
465fc97bb5bSPaolo Bonzini     s->tft = 0;
466fc97bb5bSPaolo Bonzini     s->mono = 0;
467fc97bb5bSPaolo Bonzini     s->enable = 0;
468fc97bb5bSPaolo Bonzini     s->width = 0;
469fc97bb5bSPaolo Bonzini     s->height = 0;
470fc97bb5bSPaolo Bonzini     s->interrupts = 0;
471fc97bb5bSPaolo Bonzini     s->timing[0] = 0;
472fc97bb5bSPaolo Bonzini     s->timing[1] = 0;
473fc97bb5bSPaolo Bonzini     s->timing[2] = 0;
474fc97bb5bSPaolo Bonzini     s->subpanel = 0;
475fc97bb5bSPaolo Bonzini     s->palette_done = 0;
476fc97bb5bSPaolo Bonzini     s->frame_done = 0;
477fc97bb5bSPaolo Bonzini     s->sync_error = 0;
478fc97bb5bSPaolo Bonzini     s->invalidate = 1;
479fc97bb5bSPaolo Bonzini     s->subpanel = 0;
480fc97bb5bSPaolo Bonzini     s->ctrl = 0;
481fc97bb5bSPaolo Bonzini }
482fc97bb5bSPaolo Bonzini 
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)488fc97bb5bSPaolo Bonzini struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
489fc97bb5bSPaolo Bonzini                                         hwaddr base,
490fc97bb5bSPaolo Bonzini                                         qemu_irq irq,
491fc97bb5bSPaolo Bonzini                                         struct omap_dma_lcd_channel_s *dma,
492fc97bb5bSPaolo Bonzini                                         omap_clk clk)
493fc97bb5bSPaolo Bonzini {
494b45c03f5SMarkus Armbruster     struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
495fc97bb5bSPaolo Bonzini 
496fc97bb5bSPaolo Bonzini     s->irq = irq;
497fc97bb5bSPaolo Bonzini     s->dma = dma;
498fc97bb5bSPaolo Bonzini     s->sysmem = sysmem;
499fc97bb5bSPaolo Bonzini     omap_lcdc_reset(s);
500fc97bb5bSPaolo Bonzini 
5012c9b15caSPaolo Bonzini     memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
502fc97bb5bSPaolo Bonzini     memory_region_add_subregion(sysmem, base, &s->iomem);
503fc97bb5bSPaolo Bonzini 
5045643706aSGerd Hoffmann     s->con = graphic_console_init(NULL, 0, &omap_ops, s);
505fc97bb5bSPaolo Bonzini 
506fc97bb5bSPaolo Bonzini     return s;
507fc97bb5bSPaolo Bonzini }
508