xref: /qemu/hw/display/omap_lcdc.c (revision 886fb67020e32ce6a2cf7049c6f017acf1f0d69a)
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