xref: /openbmc/qemu/hw/display/omap_lcdc.c (revision 709395f8)
1 /*
2  * OMAP LCD controller.
3  *
4  * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "qemu/osdep.h"
20 #include "hw/hw.h"
21 #include "ui/console.h"
22 #include "hw/arm/omap.h"
23 #include "framebuffer.h"
24 #include "ui/pixel_ops.h"
25 
26 struct omap_lcd_panel_s {
27     MemoryRegion *sysmem;
28     MemoryRegion iomem;
29     MemoryRegionSection fbsection;
30     qemu_irq irq;
31     QemuConsole *con;
32 
33     int plm;
34     int tft;
35     int mono;
36     int enable;
37     int width;
38     int height;
39     int interrupts;
40     uint32_t timing[3];
41     uint32_t subpanel;
42     uint32_t ctrl;
43 
44     struct omap_dma_lcd_channel_s *dma;
45     uint16_t palette[256];
46     int palette_done;
47     int frame_done;
48     int invalidate;
49     int sync_error;
50 };
51 
52 static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
53 {
54     if (s->frame_done && (s->interrupts & 1)) {
55         qemu_irq_raise(s->irq);
56         return;
57     }
58 
59     if (s->palette_done && (s->interrupts & 2)) {
60         qemu_irq_raise(s->irq);
61         return;
62     }
63 
64     if (s->sync_error) {
65         qemu_irq_raise(s->irq);
66         return;
67     }
68 
69     qemu_irq_lower(s->irq);
70 }
71 
72 #define draw_line_func drawfn
73 
74 #define DEPTH 32
75 #include "omap_lcd_template.h"
76 
77 static void omap_update_display(void *opaque)
78 {
79     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
80     DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
81     draw_line_func draw_line;
82     int size, height, first, last;
83     int width, linesize, step, bpp, frame_offset;
84     hwaddr frame_base;
85 
86     if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable ||
87         !surface_bits_per_pixel(surface)) {
88         return;
89     }
90 
91     frame_offset = 0;
92     if (omap_lcd->plm != 2) {
93         cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
94                                   omap_lcd->dma->current_frame],
95                                  (void *)omap_lcd->palette, 0x200);
96         switch (omap_lcd->palette[0] >> 12 & 7) {
97         case 3 ... 7:
98             frame_offset += 0x200;
99             break;
100         default:
101             frame_offset += 0x20;
102         }
103     }
104 
105     /* Colour depth */
106     switch ((omap_lcd->palette[0] >> 12) & 7) {
107     case 1:
108         draw_line = draw_line2_32;
109         bpp = 2;
110         break;
111 
112     case 2:
113         draw_line = draw_line4_32;
114         bpp = 4;
115         break;
116 
117     case 3:
118         draw_line = draw_line8_32;
119         bpp = 8;
120         break;
121 
122     case 4 ... 7:
123         if (!omap_lcd->tft)
124             draw_line = draw_line12_32;
125         else
126             draw_line = draw_line16_32;
127         bpp = 16;
128         break;
129 
130     default:
131         /* Unsupported at the moment.  */
132         return;
133     }
134 
135     /* Resolution */
136     width = omap_lcd->width;
137     if (width != surface_width(surface) ||
138         omap_lcd->height != surface_height(surface)) {
139         qemu_console_resize(omap_lcd->con,
140                             omap_lcd->width, omap_lcd->height);
141         surface = qemu_console_surface(omap_lcd->con);
142         omap_lcd->invalidate = 1;
143     }
144 
145     if (omap_lcd->dma->current_frame == 0)
146         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
147     else
148         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
149 
150     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
151         omap_lcd->sync_error = 1;
152         omap_lcd_interrupts(omap_lcd);
153         omap_lcd->enable = 0;
154         return;
155     }
156 
157     /* Content */
158     frame_base = omap_lcd->dma->phys_framebuffer[
159             omap_lcd->dma->current_frame] + frame_offset;
160     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
161     if (omap_lcd->dma->interrupts & 1)
162         qemu_irq_raise(omap_lcd->dma->irq);
163     if (omap_lcd->dma->dual)
164         omap_lcd->dma->current_frame ^= 1;
165 
166     if (!surface_bits_per_pixel(surface)) {
167         return;
168     }
169 
170     first = 0;
171     height = omap_lcd->height;
172     if (omap_lcd->subpanel & (1 << 31)) {
173         if (omap_lcd->subpanel & (1 << 29))
174             first = (omap_lcd->subpanel >> 16) & 0x3ff;
175         else
176             height = (omap_lcd->subpanel >> 16) & 0x3ff;
177         /* TODO: fill the rest of the panel with DPD */
178     }
179 
180     step = width * bpp >> 3;
181     linesize = surface_stride(surface);
182     if (omap_lcd->invalidate) {
183         framebuffer_update_memory_section(&omap_lcd->fbsection,
184                                           omap_lcd->sysmem, frame_base,
185                                           height, step);
186     }
187 
188     framebuffer_update_display(surface, &omap_lcd->fbsection,
189                                width, height,
190                                step, linesize, 0,
191                                omap_lcd->invalidate,
192                                draw_line, omap_lcd->palette,
193                                &first, &last);
194 
195     if (first >= 0) {
196         dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
197     }
198     omap_lcd->invalidate = 0;
199 }
200 
201 static void omap_invalidate_display(void *opaque) {
202     struct omap_lcd_panel_s *omap_lcd = opaque;
203     omap_lcd->invalidate = 1;
204 }
205 
206 static void omap_lcd_update(struct omap_lcd_panel_s *s) {
207     if (!s->enable) {
208         s->dma->current_frame = -1;
209         s->sync_error = 0;
210         if (s->plm != 1)
211             s->frame_done = 1;
212         omap_lcd_interrupts(s);
213         return;
214     }
215 
216     if (s->dma->current_frame == -1) {
217         s->frame_done = 0;
218         s->palette_done = 0;
219         s->dma->current_frame = 0;
220     }
221 
222     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
223                             s->dma->src_f1_top) ||
224                     !s->dma->mpu->port[
225                     s->dma->src].addr_valid(s->dma->mpu,
226                             s->dma->src_f1_bottom) ||
227                     (s->dma->dual &&
228                      (!s->dma->mpu->port[
229                       s->dma->src].addr_valid(s->dma->mpu,
230                               s->dma->src_f2_top) ||
231                       !s->dma->mpu->port[
232                       s->dma->src].addr_valid(s->dma->mpu,
233                               s->dma->src_f2_bottom)))) {
234         s->dma->condition |= 1 << 2;
235         if (s->dma->interrupts & (1 << 1))
236             qemu_irq_raise(s->dma->irq);
237         s->enable = 0;
238         return;
239     }
240 
241     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
242     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
243 
244     if (s->plm != 2 && !s->palette_done) {
245         cpu_physical_memory_read(
246             s->dma->phys_framebuffer[s->dma->current_frame],
247             (void *)s->palette, 0x200);
248         s->palette_done = 1;
249         omap_lcd_interrupts(s);
250     }
251 }
252 
253 static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
254                                unsigned size)
255 {
256     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
257 
258     switch (addr) {
259     case 0x00:	/* LCD_CONTROL */
260         return (s->tft << 23) | (s->plm << 20) |
261                 (s->tft << 7) | (s->interrupts << 3) |
262                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
263 
264     case 0x04:	/* LCD_TIMING0 */
265         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
266 
267     case 0x08:	/* LCD_TIMING1 */
268         return (s->timing[1] << 10) | (s->height - 1);
269 
270     case 0x0c:	/* LCD_TIMING2 */
271         return s->timing[2] | 0xfc000000;
272 
273     case 0x10:	/* LCD_STATUS */
274         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
275 
276     case 0x14:	/* LCD_SUBPANEL */
277         return s->subpanel;
278 
279     default:
280         break;
281     }
282     OMAP_BAD_REG(addr);
283     return 0;
284 }
285 
286 static void omap_lcdc_write(void *opaque, hwaddr addr,
287                             uint64_t value, unsigned size)
288 {
289     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
290 
291     switch (addr) {
292     case 0x00:	/* LCD_CONTROL */
293         s->plm = (value >> 20) & 3;
294         s->tft = (value >> 7) & 1;
295         s->interrupts = (value >> 3) & 3;
296         s->mono = (value >> 1) & 1;
297         s->ctrl = value & 0x01cff300;
298         if (s->enable != (value & 1)) {
299             s->enable = value & 1;
300             omap_lcd_update(s);
301         }
302         break;
303 
304     case 0x04:	/* LCD_TIMING0 */
305         s->timing[0] = value >> 10;
306         s->width = (value & 0x3ff) + 1;
307         break;
308 
309     case 0x08:	/* LCD_TIMING1 */
310         s->timing[1] = value >> 10;
311         s->height = (value & 0x3ff) + 1;
312         break;
313 
314     case 0x0c:	/* LCD_TIMING2 */
315         s->timing[2] = value;
316         break;
317 
318     case 0x10:	/* LCD_STATUS */
319         break;
320 
321     case 0x14:	/* LCD_SUBPANEL */
322         s->subpanel = value & 0xa1ffffff;
323         break;
324 
325     default:
326         OMAP_BAD_REG(addr);
327     }
328 }
329 
330 static const MemoryRegionOps omap_lcdc_ops = {
331     .read = omap_lcdc_read,
332     .write = omap_lcdc_write,
333     .endianness = DEVICE_NATIVE_ENDIAN,
334 };
335 
336 void omap_lcdc_reset(struct omap_lcd_panel_s *s)
337 {
338     s->dma->current_frame = -1;
339     s->plm = 0;
340     s->tft = 0;
341     s->mono = 0;
342     s->enable = 0;
343     s->width = 0;
344     s->height = 0;
345     s->interrupts = 0;
346     s->timing[0] = 0;
347     s->timing[1] = 0;
348     s->timing[2] = 0;
349     s->subpanel = 0;
350     s->palette_done = 0;
351     s->frame_done = 0;
352     s->sync_error = 0;
353     s->invalidate = 1;
354     s->subpanel = 0;
355     s->ctrl = 0;
356 }
357 
358 static const GraphicHwOps omap_ops = {
359     .invalidate  = omap_invalidate_display,
360     .gfx_update  = omap_update_display,
361 };
362 
363 struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
364                                         hwaddr base,
365                                         qemu_irq irq,
366                                         struct omap_dma_lcd_channel_s *dma,
367                                         omap_clk clk)
368 {
369     struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
370 
371     s->irq = irq;
372     s->dma = dma;
373     s->sysmem = sysmem;
374     omap_lcdc_reset(s);
375 
376     memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
377     memory_region_add_subregion(sysmem, base, &s->iomem);
378 
379     s->con = graphic_console_init(NULL, 0, &omap_ops, s);
380 
381     return s;
382 }
383