]> Git Repo - qemu.git/blob - hw/omap_lcdc.c
tc6393xb: non-accelerated FB support (Dmitry Baryshkov).
[qemu.git] / hw / omap_lcdc.c
1 /*
2  * OMAP LCD controller.
3  *
4  * Copyright (C) 2006-2007 Andrzej Zaborowski  <[email protected]>
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
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19  * MA 02111-1307 USA
20  */
21 #include "hw.h"
22 #include "console.h"
23 #include "omap.h"
24
25 struct omap_lcd_panel_s {
26     target_phys_addr_t base;
27     qemu_irq irq;
28     DisplayState *state;
29     QEMUConsole *console;
30     ram_addr_t imif_base;
31     ram_addr_t emiff_base;
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 #include "pixel_ops.h"
73
74 typedef void draw_line_func(
75                 uint8_t *d, const uint8_t *s, int width, const uint16_t *pal);
76
77 #define DEPTH 8
78 #include "omap_lcd_template.h"
79 #define DEPTH 15
80 #include "omap_lcd_template.h"
81 #define DEPTH 16
82 #include "omap_lcd_template.h"
83 #define DEPTH 32
84 #include "omap_lcd_template.h"
85
86 static draw_line_func *draw_line_table2[33] = {
87     [0 ... 32]  = 0,
88     [8]         = draw_line2_8,
89     [15]        = draw_line2_15,
90     [16]        = draw_line2_16,
91     [32]        = draw_line2_32,
92 }, *draw_line_table4[33] = {
93     [0 ... 32]  = 0,
94     [8]         = draw_line4_8,
95     [15]        = draw_line4_15,
96     [16]        = draw_line4_16,
97     [32]        = draw_line4_32,
98 }, *draw_line_table8[33] = {
99     [0 ... 32]  = 0,
100     [8]         = draw_line8_8,
101     [15]        = draw_line8_15,
102     [16]        = draw_line8_16,
103     [32]        = draw_line8_32,
104 }, *draw_line_table12[33] = {
105     [0 ... 32]  = 0,
106     [8]         = draw_line12_8,
107     [15]        = draw_line12_15,
108     [16]        = draw_line12_16,
109     [32]        = draw_line12_32,
110 }, *draw_line_table16[33] = {
111     [0 ... 32]  = 0,
112     [8]         = draw_line16_8,
113     [15]        = draw_line16_15,
114     [16]        = draw_line16_16,
115     [32]        = draw_line16_32,
116 };
117
118 static void omap_update_display(void *opaque)
119 {
120     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
121     draw_line_func *draw_line;
122     int size, dirty[2], minline, maxline, height;
123     int line, width, linesize, step, bpp, frame_offset;
124     ram_addr_t frame_base, scanline, newline, x;
125     uint8_t *s, *d;
126
127     if (!omap_lcd || omap_lcd->plm == 1 ||
128                     !omap_lcd->enable || !omap_lcd->state->depth)
129         return;
130
131     frame_offset = 0;
132     if (omap_lcd->plm != 2) {
133         memcpy(omap_lcd->palette, phys_ram_base +
134                         omap_lcd->dma->phys_framebuffer[
135                         omap_lcd->dma->current_frame], 0x200);
136         switch (omap_lcd->palette[0] >> 12 & 7) {
137         case 3 ... 7:
138             frame_offset += 0x200;
139             break;
140         default:
141             frame_offset += 0x20;
142         }
143     }
144
145     /* Colour depth */
146     switch ((omap_lcd->palette[0] >> 12) & 7) {
147     case 1:
148         draw_line = draw_line_table2[omap_lcd->state->depth];
149         bpp = 2;
150         break;
151
152     case 2:
153         draw_line = draw_line_table4[omap_lcd->state->depth];
154         bpp = 4;
155         break;
156
157     case 3:
158         draw_line = draw_line_table8[omap_lcd->state->depth];
159         bpp = 8;
160         break;
161
162     case 4 ... 7:
163         if (!omap_lcd->tft)
164             draw_line = draw_line_table12[omap_lcd->state->depth];
165         else
166             draw_line = draw_line_table16[omap_lcd->state->depth];
167         bpp = 16;
168         break;
169
170     default:
171         /* Unsupported at the moment.  */
172         return;
173     }
174
175     /* Resolution */
176     width = omap_lcd->width;
177     if (width != omap_lcd->state->width ||
178             omap_lcd->height != omap_lcd->state->height) {
179         qemu_console_resize(omap_lcd->console,
180                             omap_lcd->width, omap_lcd->height);
181         omap_lcd->invalidate = 1;
182     }
183
184     if (omap_lcd->dma->current_frame == 0)
185         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
186     else
187         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
188
189     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
190         omap_lcd->sync_error = 1;
191         omap_lcd_interrupts(omap_lcd);
192         omap_lcd->enable = 0;
193         return;
194     }
195
196     /* Content */
197     frame_base = omap_lcd->dma->phys_framebuffer[
198             omap_lcd->dma->current_frame] + frame_offset;
199     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
200     if (omap_lcd->dma->interrupts & 1)
201         qemu_irq_raise(omap_lcd->dma->irq);
202     if (omap_lcd->dma->dual)
203         omap_lcd->dma->current_frame ^= 1;
204
205     if (!omap_lcd->state->depth)
206         return;
207
208     line = 0;
209     height = omap_lcd->height;
210     if (omap_lcd->subpanel & (1 << 31)) {
211         if (omap_lcd->subpanel & (1 << 29))
212             line = (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     step = width * bpp >> 3;
218     scanline = frame_base + step * line;
219     s = (uint8_t *) (phys_ram_base + scanline);
220     d = omap_lcd->state->data;
221     linesize = omap_lcd->state->linesize;
222
223     dirty[0] = dirty[1] =
224             cpu_physical_memory_get_dirty(scanline, VGA_DIRTY_FLAG);
225     minline = height;
226     maxline = line;
227     for (; line < height; line ++) {
228         newline = scanline + step;
229         for (x = scanline + TARGET_PAGE_SIZE; x < newline;
230                         x += TARGET_PAGE_SIZE) {
231             dirty[1] = cpu_physical_memory_get_dirty(x, VGA_DIRTY_FLAG);
232             dirty[0] |= dirty[1];
233         }
234         if (dirty[0] || omap_lcd->invalidate) {
235             draw_line(d, s, width, omap_lcd->palette);
236             if (line < minline)
237                 minline = line;
238             maxline = line + 1;
239         }
240         scanline = newline;
241         dirty[0] = dirty[1];
242         s += step;
243         d += linesize;
244     }
245
246     if (maxline >= minline) {
247         dpy_update(omap_lcd->state, 0, minline, width, maxline);
248         cpu_physical_memory_reset_dirty(frame_base + step * minline,
249                         frame_base + step * maxline, VGA_DIRTY_FLAG);
250     }
251 }
252
253 static int ppm_save(const char *filename, uint8_t *data,
254                 int w, int h, int linesize)
255 {
256     FILE *f;
257     uint8_t *d, *d1;
258     unsigned int v;
259     int y, x, bpp;
260
261     f = fopen(filename, "wb");
262     if (!f)
263         return -1;
264     fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
265     d1 = data;
266     bpp = linesize / w;
267     for (y = 0; y < h; y ++) {
268         d = d1;
269         for (x = 0; x < w; x ++) {
270             v = *(uint32_t *) d;
271             switch (bpp) {
272             case 2:
273                 fputc((v >> 8) & 0xf8, f);
274                 fputc((v >> 3) & 0xfc, f);
275                 fputc((v << 3) & 0xf8, f);
276                 break;
277             case 3:
278             case 4:
279             default:
280                 fputc((v >> 16) & 0xff, f);
281                 fputc((v >> 8) & 0xff, f);
282                 fputc((v) & 0xff, f);
283                 break;
284             }
285             d += bpp;
286         }
287         d1 += linesize;
288     }
289     fclose(f);
290     return 0;
291 }
292
293 static void omap_screen_dump(void *opaque, const char *filename) {
294     struct omap_lcd_panel_s *omap_lcd = opaque;
295     omap_update_display(opaque);
296     if (omap_lcd && omap_lcd->state->data)
297         ppm_save(filename, omap_lcd->state->data,
298                 omap_lcd->width, omap_lcd->height,
299                 omap_lcd->state->linesize);
300 }
301
302 static void omap_invalidate_display(void *opaque) {
303     struct omap_lcd_panel_s *omap_lcd = opaque;
304     omap_lcd->invalidate = 1;
305 }
306
307 static void omap_lcd_update(struct omap_lcd_panel_s *s) {
308     if (!s->enable) {
309         s->dma->current_frame = -1;
310         s->sync_error = 0;
311         if (s->plm != 1)
312             s->frame_done = 1;
313         omap_lcd_interrupts(s);
314         return;
315     }
316
317     if (s->dma->current_frame == -1) {
318         s->frame_done = 0;
319         s->palette_done = 0;
320         s->dma->current_frame = 0;
321     }
322
323     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
324                             s->dma->src_f1_top) ||
325                     !s->dma->mpu->port[
326                     s->dma->src].addr_valid(s->dma->mpu,
327                             s->dma->src_f1_bottom) ||
328                     (s->dma->dual &&
329                      (!s->dma->mpu->port[
330                       s->dma->src].addr_valid(s->dma->mpu,
331                               s->dma->src_f2_top) ||
332                       !s->dma->mpu->port[
333                       s->dma->src].addr_valid(s->dma->mpu,
334                               s->dma->src_f2_bottom)))) {
335         s->dma->condition |= 1 << 2;
336         if (s->dma->interrupts & (1 << 1))
337             qemu_irq_raise(s->dma->irq);
338         s->enable = 0;
339         return;
340     }
341
342      if (s->dma->src == imif) {
343         /* Framebuffers are in SRAM */
344         s->dma->phys_framebuffer[0] = s->imif_base +
345                 s->dma->src_f1_top - OMAP_IMIF_BASE;
346
347         s->dma->phys_framebuffer[1] = s->imif_base +
348                 s->dma->src_f2_top - OMAP_IMIF_BASE;
349     } else {
350         /* Framebuffers are in RAM */
351         s->dma->phys_framebuffer[0] = s->emiff_base +
352                 s->dma->src_f1_top - OMAP_EMIFF_BASE;
353
354         s->dma->phys_framebuffer[1] = s->emiff_base +
355                 s->dma->src_f2_top - OMAP_EMIFF_BASE;
356     }
357
358     if (s->plm != 2 && !s->palette_done) {
359         memcpy(s->palette, phys_ram_base +
360                 s->dma->phys_framebuffer[s->dma->current_frame], 0x200);
361         s->palette_done = 1;
362         omap_lcd_interrupts(s);
363     }
364 }
365
366 static uint32_t omap_lcdc_read(void *opaque, target_phys_addr_t addr)
367 {
368     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
369     int offset = addr - s->base;
370
371     switch (offset) {
372     case 0x00:  /* LCD_CONTROL */
373         return (s->tft << 23) | (s->plm << 20) |
374                 (s->tft << 7) | (s->interrupts << 3) |
375                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
376
377     case 0x04:  /* LCD_TIMING0 */
378         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
379
380     case 0x08:  /* LCD_TIMING1 */
381         return (s->timing[1] << 10) | (s->height - 1);
382
383     case 0x0c:  /* LCD_TIMING2 */
384         return s->timing[2] | 0xfc000000;
385
386     case 0x10:  /* LCD_STATUS */
387         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
388
389     case 0x14:  /* LCD_SUBPANEL */
390         return s->subpanel;
391
392     default:
393         break;
394     }
395     OMAP_BAD_REG(addr);
396     return 0;
397 }
398
399 static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
400                 uint32_t value)
401 {
402     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
403     int offset = addr - s->base;
404
405     switch (offset) {
406     case 0x00:  /* LCD_CONTROL */
407         s->plm = (value >> 20) & 3;
408         s->tft = (value >> 7) & 1;
409         s->interrupts = (value >> 3) & 3;
410         s->mono = (value >> 1) & 1;
411         s->ctrl = value & 0x01cff300;
412         if (s->enable != (value & 1)) {
413             s->enable = value & 1;
414             omap_lcd_update(s);
415         }
416         break;
417
418     case 0x04:  /* LCD_TIMING0 */
419         s->timing[0] = value >> 10;
420         s->width = (value & 0x3ff) + 1;
421         break;
422
423     case 0x08:  /* LCD_TIMING1 */
424         s->timing[1] = value >> 10;
425         s->height = (value & 0x3ff) + 1;
426         break;
427
428     case 0x0c:  /* LCD_TIMING2 */
429         s->timing[2] = value;
430         break;
431
432     case 0x10:  /* LCD_STATUS */
433         break;
434
435     case 0x14:  /* LCD_SUBPANEL */
436         s->subpanel = value & 0xa1ffffff;
437         break;
438
439     default:
440         OMAP_BAD_REG(addr);
441     }
442 }
443
444 static CPUReadMemoryFunc *omap_lcdc_readfn[] = {
445     omap_lcdc_read,
446     omap_lcdc_read,
447     omap_lcdc_read,
448 };
449
450 static CPUWriteMemoryFunc *omap_lcdc_writefn[] = {
451     omap_lcdc_write,
452     omap_lcdc_write,
453     omap_lcdc_write,
454 };
455
456 void omap_lcdc_reset(struct omap_lcd_panel_s *s)
457 {
458     s->dma->current_frame = -1;
459     s->plm = 0;
460     s->tft = 0;
461     s->mono = 0;
462     s->enable = 0;
463     s->width = 0;
464     s->height = 0;
465     s->interrupts = 0;
466     s->timing[0] = 0;
467     s->timing[1] = 0;
468     s->timing[2] = 0;
469     s->subpanel = 0;
470     s->palette_done = 0;
471     s->frame_done = 0;
472     s->sync_error = 0;
473     s->invalidate = 1;
474     s->subpanel = 0;
475     s->ctrl = 0;
476 }
477
478 struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
479                 struct omap_dma_lcd_channel_s *dma, DisplayState *ds,
480                 ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk)
481 {
482     int iomemtype;
483     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
484             qemu_mallocz(sizeof(struct omap_lcd_panel_s));
485
486     s->irq = irq;
487     s->dma = dma;
488     s->base = base;
489     s->state = ds;
490     s->imif_base = imif_base;
491     s->emiff_base = emiff_base;
492     omap_lcdc_reset(s);
493
494     iomemtype = cpu_register_io_memory(0, omap_lcdc_readfn,
495                     omap_lcdc_writefn, s);
496     cpu_register_physical_memory(s->base, 0x100, iomemtype);
497
498     s->console = graphic_console_init(ds, omap_update_display,
499                                       omap_invalidate_display,
500                                       omap_screen_dump, NULL, s);
501
502     return s;
503 }
This page took 0.054006 seconds and 4 git commands to generate.