]> Git Repo - qemu.git/blob - hw/omap_lcdc.c
qom: add a reference count to qdev objects
[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 along
17  * with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "hw.h"
20 #include "console.h"
21 #include "omap.h"
22 #include "framebuffer.h"
23
24 struct omap_lcd_panel_s {
25     MemoryRegion iomem;
26     qemu_irq irq;
27     DisplayState *state;
28
29     int plm;
30     int tft;
31     int mono;
32     int enable;
33     int width;
34     int height;
35     int interrupts;
36     uint32_t timing[3];
37     uint32_t subpanel;
38     uint32_t ctrl;
39
40     struct omap_dma_lcd_channel_s *dma;
41     uint16_t palette[256];
42     int palette_done;
43     int frame_done;
44     int invalidate;
45     int sync_error;
46 };
47
48 static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
49 {
50     if (s->frame_done && (s->interrupts & 1)) {
51         qemu_irq_raise(s->irq);
52         return;
53     }
54
55     if (s->palette_done && (s->interrupts & 2)) {
56         qemu_irq_raise(s->irq);
57         return;
58     }
59
60     if (s->sync_error) {
61         qemu_irq_raise(s->irq);
62         return;
63     }
64
65     qemu_irq_lower(s->irq);
66 }
67
68 #include "pixel_ops.h"
69
70 #define draw_line_func drawfn
71
72 #define DEPTH 8
73 #include "omap_lcd_template.h"
74 #define DEPTH 15
75 #include "omap_lcd_template.h"
76 #define DEPTH 16
77 #include "omap_lcd_template.h"
78 #define DEPTH 32
79 #include "omap_lcd_template.h"
80
81 static draw_line_func draw_line_table2[33] = {
82     [0 ... 32]  = NULL,
83     [8]         = draw_line2_8,
84     [15]        = draw_line2_15,
85     [16]        = draw_line2_16,
86     [32]        = draw_line2_32,
87 }, draw_line_table4[33] = {
88     [0 ... 32]  = NULL,
89     [8]         = draw_line4_8,
90     [15]        = draw_line4_15,
91     [16]        = draw_line4_16,
92     [32]        = draw_line4_32,
93 }, draw_line_table8[33] = {
94     [0 ... 32]  = NULL,
95     [8]         = draw_line8_8,
96     [15]        = draw_line8_15,
97     [16]        = draw_line8_16,
98     [32]        = draw_line8_32,
99 }, draw_line_table12[33] = {
100     [0 ... 32]  = NULL,
101     [8]         = draw_line12_8,
102     [15]        = draw_line12_15,
103     [16]        = draw_line12_16,
104     [32]        = draw_line12_32,
105 }, draw_line_table16[33] = {
106     [0 ... 32]  = NULL,
107     [8]         = draw_line16_8,
108     [15]        = draw_line16_15,
109     [16]        = draw_line16_16,
110     [32]        = draw_line16_32,
111 };
112
113 static void omap_update_display(void *opaque)
114 {
115     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
116     draw_line_func draw_line;
117     int size, height, first, last;
118     int width, linesize, step, bpp, frame_offset;
119     target_phys_addr_t frame_base;
120
121     if (!omap_lcd || omap_lcd->plm == 1 ||
122                     !omap_lcd->enable || !ds_get_bits_per_pixel(omap_lcd->state))
123         return;
124
125     frame_offset = 0;
126     if (omap_lcd->plm != 2) {
127         cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
128                                   omap_lcd->dma->current_frame],
129                                  (void *)omap_lcd->palette, 0x200);
130         switch (omap_lcd->palette[0] >> 12 & 7) {
131         case 3 ... 7:
132             frame_offset += 0x200;
133             break;
134         default:
135             frame_offset += 0x20;
136         }
137     }
138
139     /* Colour depth */
140     switch ((omap_lcd->palette[0] >> 12) & 7) {
141     case 1:
142         draw_line = draw_line_table2[ds_get_bits_per_pixel(omap_lcd->state)];
143         bpp = 2;
144         break;
145
146     case 2:
147         draw_line = draw_line_table4[ds_get_bits_per_pixel(omap_lcd->state)];
148         bpp = 4;
149         break;
150
151     case 3:
152         draw_line = draw_line_table8[ds_get_bits_per_pixel(omap_lcd->state)];
153         bpp = 8;
154         break;
155
156     case 4 ... 7:
157         if (!omap_lcd->tft)
158             draw_line = draw_line_table12[ds_get_bits_per_pixel(omap_lcd->state)];
159         else
160             draw_line = draw_line_table16[ds_get_bits_per_pixel(omap_lcd->state)];
161         bpp = 16;
162         break;
163
164     default:
165         /* Unsupported at the moment.  */
166         return;
167     }
168
169     /* Resolution */
170     width = omap_lcd->width;
171     if (width != ds_get_width(omap_lcd->state) ||
172             omap_lcd->height != ds_get_height(omap_lcd->state)) {
173         qemu_console_resize(omap_lcd->state,
174                             omap_lcd->width, omap_lcd->height);
175         omap_lcd->invalidate = 1;
176     }
177
178     if (omap_lcd->dma->current_frame == 0)
179         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
180     else
181         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
182
183     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
184         omap_lcd->sync_error = 1;
185         omap_lcd_interrupts(omap_lcd);
186         omap_lcd->enable = 0;
187         return;
188     }
189
190     /* Content */
191     frame_base = omap_lcd->dma->phys_framebuffer[
192             omap_lcd->dma->current_frame] + frame_offset;
193     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
194     if (omap_lcd->dma->interrupts & 1)
195         qemu_irq_raise(omap_lcd->dma->irq);
196     if (omap_lcd->dma->dual)
197         omap_lcd->dma->current_frame ^= 1;
198
199     if (!ds_get_bits_per_pixel(omap_lcd->state))
200         return;
201
202     first = 0;
203     height = omap_lcd->height;
204     if (omap_lcd->subpanel & (1 << 31)) {
205         if (omap_lcd->subpanel & (1 << 29))
206             first = (omap_lcd->subpanel >> 16) & 0x3ff;
207         else
208             height = (omap_lcd->subpanel >> 16) & 0x3ff;
209         /* TODO: fill the rest of the panel with DPD */
210     }
211
212     step = width * bpp >> 3;
213     linesize = ds_get_linesize(omap_lcd->state);
214     framebuffer_update_display(omap_lcd->state,
215                                frame_base, width, height,
216                                step, linesize, 0,
217                                omap_lcd->invalidate,
218                                draw_line, omap_lcd->palette,
219                                &first, &last);
220     if (first >= 0) {
221         dpy_update(omap_lcd->state, 0, first, width, last - first + 1);
222     }
223     omap_lcd->invalidate = 0;
224 }
225
226 static int ppm_save(const char *filename, uint8_t *data,
227                 int w, int h, int linesize)
228 {
229     FILE *f;
230     uint8_t *d, *d1;
231     unsigned int v;
232     int y, x, bpp;
233
234     f = fopen(filename, "wb");
235     if (!f)
236         return -1;
237     fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
238     d1 = data;
239     bpp = linesize / w;
240     for (y = 0; y < h; y ++) {
241         d = d1;
242         for (x = 0; x < w; x ++) {
243             v = *(uint32_t *) d;
244             switch (bpp) {
245             case 2:
246                 fputc((v >> 8) & 0xf8, f);
247                 fputc((v >> 3) & 0xfc, f);
248                 fputc((v << 3) & 0xf8, f);
249                 break;
250             case 3:
251             case 4:
252             default:
253                 fputc((v >> 16) & 0xff, f);
254                 fputc((v >> 8) & 0xff, f);
255                 fputc((v) & 0xff, f);
256                 break;
257             }
258             d += bpp;
259         }
260         d1 += linesize;
261     }
262     fclose(f);
263     return 0;
264 }
265
266 static void omap_screen_dump(void *opaque, const char *filename) {
267     struct omap_lcd_panel_s *omap_lcd = opaque;
268     omap_update_display(opaque);
269     if (omap_lcd && ds_get_data(omap_lcd->state))
270         ppm_save(filename, ds_get_data(omap_lcd->state),
271                 omap_lcd->width, omap_lcd->height,
272                 ds_get_linesize(omap_lcd->state));
273 }
274
275 static void omap_invalidate_display(void *opaque) {
276     struct omap_lcd_panel_s *omap_lcd = opaque;
277     omap_lcd->invalidate = 1;
278 }
279
280 static void omap_lcd_update(struct omap_lcd_panel_s *s) {
281     if (!s->enable) {
282         s->dma->current_frame = -1;
283         s->sync_error = 0;
284         if (s->plm != 1)
285             s->frame_done = 1;
286         omap_lcd_interrupts(s);
287         return;
288     }
289
290     if (s->dma->current_frame == -1) {
291         s->frame_done = 0;
292         s->palette_done = 0;
293         s->dma->current_frame = 0;
294     }
295
296     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
297                             s->dma->src_f1_top) ||
298                     !s->dma->mpu->port[
299                     s->dma->src].addr_valid(s->dma->mpu,
300                             s->dma->src_f1_bottom) ||
301                     (s->dma->dual &&
302                      (!s->dma->mpu->port[
303                       s->dma->src].addr_valid(s->dma->mpu,
304                               s->dma->src_f2_top) ||
305                       !s->dma->mpu->port[
306                       s->dma->src].addr_valid(s->dma->mpu,
307                               s->dma->src_f2_bottom)))) {
308         s->dma->condition |= 1 << 2;
309         if (s->dma->interrupts & (1 << 1))
310             qemu_irq_raise(s->dma->irq);
311         s->enable = 0;
312         return;
313     }
314
315     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
316     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
317
318     if (s->plm != 2 && !s->palette_done) {
319         cpu_physical_memory_read(
320             s->dma->phys_framebuffer[s->dma->current_frame],
321             (void *)s->palette, 0x200);
322         s->palette_done = 1;
323         omap_lcd_interrupts(s);
324     }
325 }
326
327 static uint64_t omap_lcdc_read(void *opaque, target_phys_addr_t addr,
328                                unsigned size)
329 {
330     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
331
332     switch (addr) {
333     case 0x00:  /* LCD_CONTROL */
334         return (s->tft << 23) | (s->plm << 20) |
335                 (s->tft << 7) | (s->interrupts << 3) |
336                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
337
338     case 0x04:  /* LCD_TIMING0 */
339         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
340
341     case 0x08:  /* LCD_TIMING1 */
342         return (s->timing[1] << 10) | (s->height - 1);
343
344     case 0x0c:  /* LCD_TIMING2 */
345         return s->timing[2] | 0xfc000000;
346
347     case 0x10:  /* LCD_STATUS */
348         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
349
350     case 0x14:  /* LCD_SUBPANEL */
351         return s->subpanel;
352
353     default:
354         break;
355     }
356     OMAP_BAD_REG(addr);
357     return 0;
358 }
359
360 static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
361                             uint64_t value, unsigned size)
362 {
363     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
364
365     switch (addr) {
366     case 0x00:  /* LCD_CONTROL */
367         s->plm = (value >> 20) & 3;
368         s->tft = (value >> 7) & 1;
369         s->interrupts = (value >> 3) & 3;
370         s->mono = (value >> 1) & 1;
371         s->ctrl = value & 0x01cff300;
372         if (s->enable != (value & 1)) {
373             s->enable = value & 1;
374             omap_lcd_update(s);
375         }
376         break;
377
378     case 0x04:  /* LCD_TIMING0 */
379         s->timing[0] = value >> 10;
380         s->width = (value & 0x3ff) + 1;
381         break;
382
383     case 0x08:  /* LCD_TIMING1 */
384         s->timing[1] = value >> 10;
385         s->height = (value & 0x3ff) + 1;
386         break;
387
388     case 0x0c:  /* LCD_TIMING2 */
389         s->timing[2] = value;
390         break;
391
392     case 0x10:  /* LCD_STATUS */
393         break;
394
395     case 0x14:  /* LCD_SUBPANEL */
396         s->subpanel = value & 0xa1ffffff;
397         break;
398
399     default:
400         OMAP_BAD_REG(addr);
401     }
402 }
403
404 static const MemoryRegionOps omap_lcdc_ops = {
405     .read = omap_lcdc_read,
406     .write = omap_lcdc_write,
407     .endianness = DEVICE_NATIVE_ENDIAN,
408 };
409
410 void omap_lcdc_reset(struct omap_lcd_panel_s *s)
411 {
412     s->dma->current_frame = -1;
413     s->plm = 0;
414     s->tft = 0;
415     s->mono = 0;
416     s->enable = 0;
417     s->width = 0;
418     s->height = 0;
419     s->interrupts = 0;
420     s->timing[0] = 0;
421     s->timing[1] = 0;
422     s->timing[2] = 0;
423     s->subpanel = 0;
424     s->palette_done = 0;
425     s->frame_done = 0;
426     s->sync_error = 0;
427     s->invalidate = 1;
428     s->subpanel = 0;
429     s->ctrl = 0;
430 }
431
432 struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
433                                         target_phys_addr_t base,
434                                         qemu_irq irq,
435                                         struct omap_dma_lcd_channel_s *dma,
436                                         omap_clk clk)
437 {
438     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
439             g_malloc0(sizeof(struct omap_lcd_panel_s));
440
441     s->irq = irq;
442     s->dma = dma;
443     omap_lcdc_reset(s);
444
445     memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
446     memory_region_add_subregion(sysmem, base, &s->iomem);
447
448     s->state = graphic_console_init(omap_update_display,
449                                     omap_invalidate_display,
450                                     omap_screen_dump, NULL, s);
451
452     return s;
453 }
This page took 0.053205 seconds and 4 git commands to generate.