#include "hw/arm/ipod_touch_lcd.h" #include "ui/pixel_ops.h" #include "ui/console.h" #include "hw/display/framebuffer.h" int lcd_brightness = 255; static uint64_t ipod_touch_lcd_read(void *opaque, hwaddr addr, unsigned size) { // printf("%s: read from location 0x%08x\n", __func__, addr); IPodTouchLCDState *s = (IPodTouchLCDState *)opaque; switch(addr) { case 0x0: return 2; case 0x4: return s->lcd_con; case 0x8: return s->int_enable; case 0xC: return s->int_status; case 0x20: return s->w1_display_depth_info; case 0x24: return s->w1_framebuffer_base; case 0x28: return s->w1_hspan; case 0x30: return s->w1_display_resolution_info; case 0x1b10: return 2; case 0x1b14: return 0x3; default: printf("%s: read invalid location 0x%08x.\n", __func__, addr); break; } return 0; } static void ipod_touch_lcd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { IPodTouchLCDState *s = (IPodTouchLCDState *)opaque; // printf("%s: writing 0x%08x to 0x%08x\n", __func__, val, addr); switch(addr) { case 0x4: s->lcd_con = val; break; case 0x8: s->int_enable = val; qemu_set_irq(s->irq, !!(s->int_status & s->int_enable)); break; case 0xC: /* Acquittement W1C, indépendant du masque d'interruptions. */ s->int_status &= ~val; qemu_set_irq(s->irq, !!(s->int_status & s->int_enable)); break; case 0x20: s->w1_display_depth_info = val; break; case 0x24: s->w1_framebuffer_base = val; break; case 0x28: s->w1_hspan = val; break; case 0x30: s->w1_display_resolution_info = val; break; } } void lcd_changebrightness(int brightness) { lcd_brightness = brightness & 0xFF; } static void lcd_invalidate(void *opaque) { IPodTouchLCDState *s = opaque; s->invalidate = 1; } static void draw_line32_32(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep) { // IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque; uint8_t r, g, b; do { //v = lduw_le_p((void *) s); //printf("V: %d\n", *s); b = s[0]; g = s[1]; r = s[2]; // printf("R: %d, G: %d, B: %d, A: %d\n", r, g, b, lcd_brightness); ((uint32_t *) d)[0] = rgb_to_pixel32(round((float)r * ((float)lcd_brightness / 255.0f)), round((float)g * ((float)lcd_brightness / 255.0f)), round((float)b * ((float)lcd_brightness / 255.0f))); s += 4; d += 4; } while (-- width != 0); } static void lcd_refresh(void *opaque) { // printf("%s: refreshing LCD screen\n", __func__); IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque; DisplaySurface *surface = qemu_console_surface(lcd->con); drawfn draw_line; int src_width, dest_width; int height, first, last; int width, linesize; if (!lcd || !lcd->con || !surface_bits_per_pixel(surface)) return; dest_width = 4; draw_line = draw_line32_32; /* Resolution */ first = last = 0; width = 320; height = 480; lcd->invalidate = 1; src_width = 4 * width; linesize = surface_stride(surface); if(lcd->invalidate) { framebuffer_update_memory_section(&lcd->fbsection, lcd->sysmem, lcd->w1_framebuffer_base, height, 4 * width); } framebuffer_update_display(surface, &lcd->fbsection, width, height, src_width, /* Length of source line, in bytes. */ linesize, /* Bytes between adjacent horizontal output pixels. */ dest_width, /* Bytes between adjacent vertical output pixels. */ lcd->invalidate, draw_line, NULL, &first, &last); if (first >= 0) { dpy_gfx_update(lcd->con, 0, first, width, last - first + 1); } lcd->invalidate = 0; } static const MemoryRegionOps lcd_ops = { .read = ipod_touch_lcd_read, .write = ipod_touch_lcd_write, .endianness = DEVICE_NATIVE_ENDIAN, }; static const GraphicHwOps gfx_ops = { .invalidate = lcd_invalidate, .gfx_update = lcd_refresh, }; static void ipod_touch_lcd_mouse_event(void *opaque, int x, int y, int z, int buttons_state) { // printf("x %d y %d z %d state %d\n", x, y, z, buttons_state); // convert x and y to fractional numbers float fx = x / pow(2, 15); float fy = 1 - y / pow(2, 15); IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque; lcd->mt->touch_x = fx; lcd->mt->touch_y = fy; if(buttons_state && !lcd->mt->touch_down) { ipod_touch_multitouch_on_touch(lcd->mt); } else if(!buttons_state && lcd->mt->touch_down) { ipod_touch_multitouch_on_release(lcd->mt); } } static void refresh_timer_tick(void *opaque) { IPodTouchLCDState *s = (IPodTouchLCDState *)opaque; s->int_status |= 1; qemu_set_irq(s->irq, !!(s->int_status & s->int_enable)); timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 60);//LCD_REFRESH_RATE_FREQUENCY); } static void ipod_touch_lcd_realize(DeviceState *dev, Error **errp) { IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev); s->con = graphic_console_init(dev, 0, &gfx_ops, s); qemu_console_resize(s->con, 320, 480); // add mouse handler qemu_add_mouse_event_handler(ipod_touch_lcd_mouse_event, s, 1, "iPod Touch Touchscreen"); // initialize the refresh timer s->refresh_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, refresh_timer_tick, s); timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY); } static void ipod_touch_lcd_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev); memory_region_init_io(&s->iomem, obj, &lcd_ops, s, "lcd", 0x10000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); } static void ipod_touch_lcd_reset(DeviceState *dev) { IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev); s->lcd_con = s->int_enable = s->int_status = 0; s->w1_display_resolution_info = s->w1_framebuffer_base = 0; s->w1_hspan = s->w1_display_depth_info = 0; s->invalidate = 1; lcd_brightness = 255; qemu_irq_lower(s->irq); } static void ipod_touch_lcd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ipod_touch_lcd_realize; dc->reset = ipod_touch_lcd_reset; } static const TypeInfo ipod_touch_lcd_info = { .name = TYPE_IPOD_TOUCH_LCD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IPodTouchLCDState), .instance_init = ipod_touch_lcd_init, .class_init = ipod_touch_lcd_class_init, }; static void ipod_touch_machine_types(void) { type_register_static(&ipod_touch_lcd_info); } type_init(ipod_touch_machine_types)