TWL92230 qdev conversion
[qemu] / hw / omap_lcdc.c
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, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 #include "hw.h"
21 #include "console.h"
22 #include "omap.h"
23 #include "framebuffer.h"
24
25 struct omap_lcd_panel_s {
26     qemu_irq irq;
27     DisplayState *state;
28     ram_addr_t imif_base;
29     ram_addr_t emiff_base;
30
31     int plm;
32     int tft;
33     int mono;
34     int enable;
35     int width;
36     int height;
37     int interrupts;
38     uint32_t timing[3];
39     uint32_t subpanel;
40     uint32_t ctrl;
41
42     struct omap_dma_lcd_channel_s *dma;
43     uint16_t palette[256];
44     int palette_done;
45     int frame_done;
46     int invalidate;
47     int sync_error;
48 };
49
50 static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
51 {
52     if (s->frame_done && (s->interrupts & 1)) {
53         qemu_irq_raise(s->irq);
54         return;
55     }
56
57     if (s->palette_done && (s->interrupts & 2)) {
58         qemu_irq_raise(s->irq);
59         return;
60     }
61
62     if (s->sync_error) {
63         qemu_irq_raise(s->irq);
64         return;
65     }
66
67     qemu_irq_lower(s->irq);
68 }
69
70 #include "pixel_ops.h"
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]  = 0,
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]  = 0,
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]  = 0,
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]  = 0,
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]  = 0,
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     draw_line_func draw_line;
119     int size, height, first, last;
120     int width, linesize, step, bpp, frame_offset;
121     target_phys_addr_t frame_base;
122
123     if (!omap_lcd || omap_lcd->plm == 1 ||
124                     !omap_lcd->enable || !ds_get_bits_per_pixel(omap_lcd->state))
125         return;
126
127     frame_offset = 0;
128     if (omap_lcd->plm != 2) {
129         cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
130                                   omap_lcd->dma->current_frame],
131                                  (void *)omap_lcd->palette, 0x200);
132         switch (omap_lcd->palette[0] >> 12 & 7) {
133         case 3 ... 7:
134             frame_offset += 0x200;
135             break;
136         default:
137             frame_offset += 0x20;
138         }
139     }
140
141     /* Colour depth */
142     switch ((omap_lcd->palette[0] >> 12) & 7) {
143     case 1:
144         draw_line = draw_line_table2[ds_get_bits_per_pixel(omap_lcd->state)];
145         bpp = 2;
146         break;
147
148     case 2:
149         draw_line = draw_line_table4[ds_get_bits_per_pixel(omap_lcd->state)];
150         bpp = 4;
151         break;
152
153     case 3:
154         draw_line = draw_line_table8[ds_get_bits_per_pixel(omap_lcd->state)];
155         bpp = 8;
156         break;
157
158     case 4 ... 7:
159         if (!omap_lcd->tft)
160             draw_line = draw_line_table12[ds_get_bits_per_pixel(omap_lcd->state)];
161         else
162             draw_line = draw_line_table16[ds_get_bits_per_pixel(omap_lcd->state)];
163         bpp = 16;
164         break;
165
166     default:
167         /* Unsupported at the moment.  */
168         return;
169     }
170
171     /* Resolution */
172     width = omap_lcd->width;
173     if (width != ds_get_width(omap_lcd->state) ||
174             omap_lcd->height != ds_get_height(omap_lcd->state)) {
175         qemu_console_resize(omap_lcd->state,
176                             omap_lcd->width, omap_lcd->height);
177         omap_lcd->invalidate = 1;
178     }
179
180     if (omap_lcd->dma->current_frame == 0)
181         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
182     else
183         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
184
185     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
186         omap_lcd->sync_error = 1;
187         omap_lcd_interrupts(omap_lcd);
188         omap_lcd->enable = 0;
189         return;
190     }
191
192     /* Content */
193     frame_base = omap_lcd->dma->phys_framebuffer[
194             omap_lcd->dma->current_frame] + frame_offset;
195     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
196     if (omap_lcd->dma->interrupts & 1)
197         qemu_irq_raise(omap_lcd->dma->irq);
198     if (omap_lcd->dma->dual)
199         omap_lcd->dma->current_frame ^= 1;
200
201     if (!ds_get_bits_per_pixel(omap_lcd->state))
202         return;
203
204     first = 0;
205     height = omap_lcd->height;
206     if (omap_lcd->subpanel & (1 << 31)) {
207         if (omap_lcd->subpanel & (1 << 29))
208             first = (omap_lcd->subpanel >> 16) & 0x3ff;
209         else
210             height = (omap_lcd->subpanel >> 16) & 0x3ff;
211         /* TODO: fill the rest of the panel with DPD */
212     }
213
214     step = width * bpp >> 3;
215     linesize = ds_get_linesize(omap_lcd->state);
216     framebuffer_update_display(omap_lcd->state,
217                                frame_base, width, height,
218                                step, linesize, 0,
219                                omap_lcd->invalidate,
220                                draw_line, omap_lcd->palette,
221                                &first, &last);
222     if (first >= 0) {
223         dpy_update(omap_lcd->state, 0, first, width, last - first + 1);
224     }
225     omap_lcd->invalidate = 0;
226 }
227
228 static int ppm_save(const char *filename, uint8_t *data,
229                 int w, int h, int linesize)
230 {
231     FILE *f;
232     uint8_t *d, *d1;
233     unsigned int v;
234     int y, x, bpp;
235
236     f = fopen(filename, "wb");
237     if (!f)
238         return -1;
239     fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
240     d1 = data;
241     bpp = linesize / w;
242     for (y = 0; y < h; y ++) {
243         d = d1;
244         for (x = 0; x < w; x ++) {
245             v = *(uint32_t *) d;
246             switch (bpp) {
247             case 2:
248                 fputc((v >> 8) & 0xf8, f);
249                 fputc((v >> 3) & 0xfc, f);
250                 fputc((v << 3) & 0xf8, f);
251                 break;
252             case 3:
253             case 4:
254             default:
255                 fputc((v >> 16) & 0xff, f);
256                 fputc((v >> 8) & 0xff, f);
257                 fputc((v) & 0xff, f);
258                 break;
259             }
260             d += bpp;
261         }
262         d1 += linesize;
263     }
264     fclose(f);
265     return 0;
266 }
267
268 static void omap_screen_dump(void *opaque, const char *filename) {
269     struct omap_lcd_panel_s *omap_lcd = opaque;
270     omap_update_display(opaque);
271     if (omap_lcd && ds_get_data(omap_lcd->state))
272         ppm_save(filename, ds_get_data(omap_lcd->state),
273                 omap_lcd->width, omap_lcd->height,
274                 ds_get_linesize(omap_lcd->state));
275 }
276
277 static void omap_invalidate_display(void *opaque) {
278     struct omap_lcd_panel_s *omap_lcd = opaque;
279     omap_lcd->invalidate = 1;
280 }
281
282 static void omap_lcd_update(struct omap_lcd_panel_s *s) {
283     if (!s->enable) {
284         s->dma->current_frame = -1;
285         s->sync_error = 0;
286         if (s->plm != 1)
287             s->frame_done = 1;
288         omap_lcd_interrupts(s);
289         return;
290     }
291
292     if (s->dma->current_frame == -1) {
293         s->frame_done = 0;
294         s->palette_done = 0;
295         s->dma->current_frame = 0;
296     }
297
298     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
299                             s->dma->src_f1_top) ||
300                     !s->dma->mpu->port[
301                     s->dma->src].addr_valid(s->dma->mpu,
302                             s->dma->src_f1_bottom) ||
303                     (s->dma->dual &&
304                      (!s->dma->mpu->port[
305                       s->dma->src].addr_valid(s->dma->mpu,
306                               s->dma->src_f2_top) ||
307                       !s->dma->mpu->port[
308                       s->dma->src].addr_valid(s->dma->mpu,
309                               s->dma->src_f2_bottom)))) {
310         s->dma->condition |= 1 << 2;
311         if (s->dma->interrupts & (1 << 1))
312             qemu_irq_raise(s->dma->irq);
313         s->enable = 0;
314         return;
315     }
316
317     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
318     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
319
320     if (s->plm != 2 && !s->palette_done) {
321         cpu_physical_memory_read(
322             s->dma->phys_framebuffer[s->dma->current_frame],
323             (void *)s->palette, 0x200);
324         s->palette_done = 1;
325         omap_lcd_interrupts(s);
326     }
327 }
328
329 static uint32_t omap_lcdc_read(void *opaque, target_phys_addr_t addr)
330 {
331     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
332
333     switch (addr) {
334     case 0x00:  /* LCD_CONTROL */
335         return (s->tft << 23) | (s->plm << 20) |
336                 (s->tft << 7) | (s->interrupts << 3) |
337                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
338
339     case 0x04:  /* LCD_TIMING0 */
340         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
341
342     case 0x08:  /* LCD_TIMING1 */
343         return (s->timing[1] << 10) | (s->height - 1);
344
345     case 0x0c:  /* LCD_TIMING2 */
346         return s->timing[2] | 0xfc000000;
347
348     case 0x10:  /* LCD_STATUS */
349         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
350
351     case 0x14:  /* LCD_SUBPANEL */
352         return s->subpanel;
353
354     default:
355         break;
356     }
357     OMAP_BAD_REG(addr);
358     return 0;
359 }
360
361 static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
362                 uint32_t value)
363 {
364     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
365
366     switch (addr) {
367     case 0x00:  /* LCD_CONTROL */
368         s->plm = (value >> 20) & 3;
369         s->tft = (value >> 7) & 1;
370         s->interrupts = (value >> 3) & 3;
371         s->mono = (value >> 1) & 1;
372         s->ctrl = value & 0x01cff300;
373         if (s->enable != (value & 1)) {
374             s->enable = value & 1;
375             omap_lcd_update(s);
376         }
377         break;
378
379     case 0x04:  /* LCD_TIMING0 */
380         s->timing[0] = value >> 10;
381         s->width = (value & 0x3ff) + 1;
382         break;
383
384     case 0x08:  /* LCD_TIMING1 */
385         s->timing[1] = value >> 10;
386         s->height = (value & 0x3ff) + 1;
387         break;
388
389     case 0x0c:  /* LCD_TIMING2 */
390         s->timing[2] = value;
391         break;
392
393     case 0x10:  /* LCD_STATUS */
394         break;
395
396     case 0x14:  /* LCD_SUBPANEL */
397         s->subpanel = value & 0xa1ffffff;
398         break;
399
400     default:
401         OMAP_BAD_REG(addr);
402     }
403 }
404
405 static CPUReadMemoryFunc *omap_lcdc_readfn[] = {
406     omap_lcdc_read,
407     omap_lcdc_read,
408     omap_lcdc_read,
409 };
410
411 static CPUWriteMemoryFunc *omap_lcdc_writefn[] = {
412     omap_lcdc_write,
413     omap_lcdc_write,
414     omap_lcdc_write,
415 };
416
417 void omap_lcdc_reset(struct omap_lcd_panel_s *s)
418 {
419     s->dma->current_frame = -1;
420     s->plm = 0;
421     s->tft = 0;
422     s->mono = 0;
423     s->enable = 0;
424     s->width = 0;
425     s->height = 0;
426     s->interrupts = 0;
427     s->timing[0] = 0;
428     s->timing[1] = 0;
429     s->timing[2] = 0;
430     s->subpanel = 0;
431     s->palette_done = 0;
432     s->frame_done = 0;
433     s->sync_error = 0;
434     s->invalidate = 1;
435     s->subpanel = 0;
436     s->ctrl = 0;
437 }
438
439 struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
440                 struct omap_dma_lcd_channel_s *dma,
441                 ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk)
442 {
443     int iomemtype;
444     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
445             qemu_mallocz(sizeof(struct omap_lcd_panel_s));
446
447     s->irq = irq;
448     s->dma = dma;
449     s->imif_base = imif_base;
450     s->emiff_base = emiff_base;
451     omap_lcdc_reset(s);
452
453     iomemtype = cpu_register_io_memory(0, omap_lcdc_readfn,
454                     omap_lcdc_writefn, s);
455     cpu_register_physical_memory(base, 0x100, iomemtype);
456
457     s->state = graphic_console_init(omap_update_display,
458                                     omap_invalidate_display,
459                                     omap_screen_dump, NULL, s);
460
461     return s;
462 }