xref: /openbmc/qemu/hw/display/omap_lcdc.c (revision d38ea87a)
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 8
75 #include "omap_lcd_template.h"
76 #define DEPTH 15
77 #include "omap_lcd_template.h"
78 #define DEPTH 16
79 #include "omap_lcd_template.h"
80 #define DEPTH 32
81 #include "omap_lcd_template.h"
82 
83 static draw_line_func draw_line_table2[33] = {
84     [0 ... 32]	= NULL,
85     [8]		= draw_line2_8,
86     [15]	= draw_line2_15,
87     [16]	= draw_line2_16,
88     [32]	= draw_line2_32,
89 }, draw_line_table4[33] = {
90     [0 ... 32]	= NULL,
91     [8]		= draw_line4_8,
92     [15]	= draw_line4_15,
93     [16]	= draw_line4_16,
94     [32]	= draw_line4_32,
95 }, draw_line_table8[33] = {
96     [0 ... 32]	= NULL,
97     [8]		= draw_line8_8,
98     [15]	= draw_line8_15,
99     [16]	= draw_line8_16,
100     [32]	= draw_line8_32,
101 }, draw_line_table12[33] = {
102     [0 ... 32]	= NULL,
103     [8]		= draw_line12_8,
104     [15]	= draw_line12_15,
105     [16]	= draw_line12_16,
106     [32]	= draw_line12_32,
107 }, draw_line_table16[33] = {
108     [0 ... 32]	= NULL,
109     [8]		= draw_line16_8,
110     [15]	= draw_line16_15,
111     [16]	= draw_line16_16,
112     [32]	= draw_line16_32,
113 };
114 
115 static void omap_update_display(void *opaque)
116 {
117     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
118     DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
119     draw_line_func draw_line;
120     int size, height, first, last;
121     int width, linesize, step, bpp, frame_offset;
122     hwaddr frame_base;
123 
124     if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable ||
125         !surface_bits_per_pixel(surface)) {
126         return;
127     }
128 
129     frame_offset = 0;
130     if (omap_lcd->plm != 2) {
131         cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
132                                   omap_lcd->dma->current_frame],
133                                  (void *)omap_lcd->palette, 0x200);
134         switch (omap_lcd->palette[0] >> 12 & 7) {
135         case 3 ... 7:
136             frame_offset += 0x200;
137             break;
138         default:
139             frame_offset += 0x20;
140         }
141     }
142 
143     /* Colour depth */
144     switch ((omap_lcd->palette[0] >> 12) & 7) {
145     case 1:
146         draw_line = draw_line_table2[surface_bits_per_pixel(surface)];
147         bpp = 2;
148         break;
149 
150     case 2:
151         draw_line = draw_line_table4[surface_bits_per_pixel(surface)];
152         bpp = 4;
153         break;
154 
155     case 3:
156         draw_line = draw_line_table8[surface_bits_per_pixel(surface)];
157         bpp = 8;
158         break;
159 
160     case 4 ... 7:
161         if (!omap_lcd->tft)
162             draw_line = draw_line_table12[surface_bits_per_pixel(surface)];
163         else
164             draw_line = draw_line_table16[surface_bits_per_pixel(surface)];
165         bpp = 16;
166         break;
167 
168     default:
169         /* Unsupported at the moment.  */
170         return;
171     }
172 
173     /* Resolution */
174     width = omap_lcd->width;
175     if (width != surface_width(surface) ||
176         omap_lcd->height != surface_height(surface)) {
177         qemu_console_resize(omap_lcd->con,
178                             omap_lcd->width, omap_lcd->height);
179         surface = qemu_console_surface(omap_lcd->con);
180         omap_lcd->invalidate = 1;
181     }
182 
183     if (omap_lcd->dma->current_frame == 0)
184         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
185     else
186         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
187 
188     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
189         omap_lcd->sync_error = 1;
190         omap_lcd_interrupts(omap_lcd);
191         omap_lcd->enable = 0;
192         return;
193     }
194 
195     /* Content */
196     frame_base = omap_lcd->dma->phys_framebuffer[
197             omap_lcd->dma->current_frame] + frame_offset;
198     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
199     if (omap_lcd->dma->interrupts & 1)
200         qemu_irq_raise(omap_lcd->dma->irq);
201     if (omap_lcd->dma->dual)
202         omap_lcd->dma->current_frame ^= 1;
203 
204     if (!surface_bits_per_pixel(surface)) {
205         return;
206     }
207 
208     first = 0;
209     height = omap_lcd->height;
210     if (omap_lcd->subpanel & (1 << 31)) {
211         if (omap_lcd->subpanel & (1 << 29))
212             first = (omap_lcd->subpanel >> 16) & 0x3ff;
213         else
214             height = (omap_lcd->subpanel >> 16) & 0x3ff;
215         /* TODO: fill the rest of the panel with DPD */
216     }
217 
218     step = width * bpp >> 3;
219     linesize = surface_stride(surface);
220     if (omap_lcd->invalidate) {
221         framebuffer_update_memory_section(&omap_lcd->fbsection,
222                                           omap_lcd->sysmem, frame_base,
223                                           height, step);
224     }
225 
226     framebuffer_update_display(surface, &omap_lcd->fbsection,
227                                width, height,
228                                step, linesize, 0,
229                                omap_lcd->invalidate,
230                                draw_line, omap_lcd->palette,
231                                &first, &last);
232 
233     if (first >= 0) {
234         dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
235     }
236     omap_lcd->invalidate = 0;
237 }
238 
239 static void omap_invalidate_display(void *opaque) {
240     struct omap_lcd_panel_s *omap_lcd = opaque;
241     omap_lcd->invalidate = 1;
242 }
243 
244 static void omap_lcd_update(struct omap_lcd_panel_s *s) {
245     if (!s->enable) {
246         s->dma->current_frame = -1;
247         s->sync_error = 0;
248         if (s->plm != 1)
249             s->frame_done = 1;
250         omap_lcd_interrupts(s);
251         return;
252     }
253 
254     if (s->dma->current_frame == -1) {
255         s->frame_done = 0;
256         s->palette_done = 0;
257         s->dma->current_frame = 0;
258     }
259 
260     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
261                             s->dma->src_f1_top) ||
262                     !s->dma->mpu->port[
263                     s->dma->src].addr_valid(s->dma->mpu,
264                             s->dma->src_f1_bottom) ||
265                     (s->dma->dual &&
266                      (!s->dma->mpu->port[
267                       s->dma->src].addr_valid(s->dma->mpu,
268                               s->dma->src_f2_top) ||
269                       !s->dma->mpu->port[
270                       s->dma->src].addr_valid(s->dma->mpu,
271                               s->dma->src_f2_bottom)))) {
272         s->dma->condition |= 1 << 2;
273         if (s->dma->interrupts & (1 << 1))
274             qemu_irq_raise(s->dma->irq);
275         s->enable = 0;
276         return;
277     }
278 
279     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
280     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
281 
282     if (s->plm != 2 && !s->palette_done) {
283         cpu_physical_memory_read(
284             s->dma->phys_framebuffer[s->dma->current_frame],
285             (void *)s->palette, 0x200);
286         s->palette_done = 1;
287         omap_lcd_interrupts(s);
288     }
289 }
290 
291 static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
292                                unsigned size)
293 {
294     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
295 
296     switch (addr) {
297     case 0x00:	/* LCD_CONTROL */
298         return (s->tft << 23) | (s->plm << 20) |
299                 (s->tft << 7) | (s->interrupts << 3) |
300                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
301 
302     case 0x04:	/* LCD_TIMING0 */
303         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
304 
305     case 0x08:	/* LCD_TIMING1 */
306         return (s->timing[1] << 10) | (s->height - 1);
307 
308     case 0x0c:	/* LCD_TIMING2 */
309         return s->timing[2] | 0xfc000000;
310 
311     case 0x10:	/* LCD_STATUS */
312         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
313 
314     case 0x14:	/* LCD_SUBPANEL */
315         return s->subpanel;
316 
317     default:
318         break;
319     }
320     OMAP_BAD_REG(addr);
321     return 0;
322 }
323 
324 static void omap_lcdc_write(void *opaque, hwaddr addr,
325                             uint64_t value, unsigned size)
326 {
327     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
328 
329     switch (addr) {
330     case 0x00:	/* LCD_CONTROL */
331         s->plm = (value >> 20) & 3;
332         s->tft = (value >> 7) & 1;
333         s->interrupts = (value >> 3) & 3;
334         s->mono = (value >> 1) & 1;
335         s->ctrl = value & 0x01cff300;
336         if (s->enable != (value & 1)) {
337             s->enable = value & 1;
338             omap_lcd_update(s);
339         }
340         break;
341 
342     case 0x04:	/* LCD_TIMING0 */
343         s->timing[0] = value >> 10;
344         s->width = (value & 0x3ff) + 1;
345         break;
346 
347     case 0x08:	/* LCD_TIMING1 */
348         s->timing[1] = value >> 10;
349         s->height = (value & 0x3ff) + 1;
350         break;
351 
352     case 0x0c:	/* LCD_TIMING2 */
353         s->timing[2] = value;
354         break;
355 
356     case 0x10:	/* LCD_STATUS */
357         break;
358 
359     case 0x14:	/* LCD_SUBPANEL */
360         s->subpanel = value & 0xa1ffffff;
361         break;
362 
363     default:
364         OMAP_BAD_REG(addr);
365     }
366 }
367 
368 static const MemoryRegionOps omap_lcdc_ops = {
369     .read = omap_lcdc_read,
370     .write = omap_lcdc_write,
371     .endianness = DEVICE_NATIVE_ENDIAN,
372 };
373 
374 void omap_lcdc_reset(struct omap_lcd_panel_s *s)
375 {
376     s->dma->current_frame = -1;
377     s->plm = 0;
378     s->tft = 0;
379     s->mono = 0;
380     s->enable = 0;
381     s->width = 0;
382     s->height = 0;
383     s->interrupts = 0;
384     s->timing[0] = 0;
385     s->timing[1] = 0;
386     s->timing[2] = 0;
387     s->subpanel = 0;
388     s->palette_done = 0;
389     s->frame_done = 0;
390     s->sync_error = 0;
391     s->invalidate = 1;
392     s->subpanel = 0;
393     s->ctrl = 0;
394 }
395 
396 static const GraphicHwOps omap_ops = {
397     .invalidate  = omap_invalidate_display,
398     .gfx_update  = omap_update_display,
399 };
400 
401 struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
402                                         hwaddr base,
403                                         qemu_irq irq,
404                                         struct omap_dma_lcd_channel_s *dma,
405                                         omap_clk clk)
406 {
407     struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
408 
409     s->irq = irq;
410     s->dma = dma;
411     s->sysmem = sysmem;
412     omap_lcdc_reset(s);
413 
414     memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
415     memory_region_add_subregion(sysmem, base, &s->iomem);
416 
417     s->con = graphic_console_init(NULL, 0, &omap_ops, s);
418 
419     return s;
420 }
421