lm32: add Milkymist VGAFB support
This patch adds support for Milkymist's VGA framebuffer. Signed-off-by: Michael Walle <michael@walle.cc> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
This commit is contained in:
		
							parent
							
								
									883de16b46
								
							
						
					
					
						commit
						d23948b15a
					
				| 
						 | 
				
			
			@ -276,6 +276,8 @@ obj-lm32-y += milkymist-softusb.o
 | 
			
		|||
obj-lm32-y += milkymist-sysctl.o
 | 
			
		||||
obj-lm32-$(CONFIG_OPENGL) += milkymist-tmu2.o
 | 
			
		||||
obj-lm32-y += milkymist-uart.o
 | 
			
		||||
obj-lm32-y += milkymist-vgafb.o
 | 
			
		||||
obj-lm32-y += framebuffer.o
 | 
			
		||||
 | 
			
		||||
obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
 | 
			
		||||
obj-mips-y += mips_addr.o mips_timer.o mips_int.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,318 @@
 | 
			
		|||
 | 
			
		||||
/*
 | 
			
		||||
 *  QEMU model of the Milkymist VGA framebuffer.
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * Specification available at:
 | 
			
		||||
 *   http://www.milkymist.org/socdoc/vgafb.pdf
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw.h"
 | 
			
		||||
#include "sysbus.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "console.h"
 | 
			
		||||
#include "framebuffer.h"
 | 
			
		||||
#include "pixel_ops.h"
 | 
			
		||||
#include "qemu-error.h"
 | 
			
		||||
 | 
			
		||||
#define BITS 8
 | 
			
		||||
#include "milkymist-vgafb_template.h"
 | 
			
		||||
#define BITS 15
 | 
			
		||||
#include "milkymist-vgafb_template.h"
 | 
			
		||||
#define BITS 16
 | 
			
		||||
#include "milkymist-vgafb_template.h"
 | 
			
		||||
#define BITS 24
 | 
			
		||||
#include "milkymist-vgafb_template.h"
 | 
			
		||||
#define BITS 32
 | 
			
		||||
#include "milkymist-vgafb_template.h"
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    R_CTRL = 0,
 | 
			
		||||
    R_HRES,
 | 
			
		||||
    R_HSYNC_START,
 | 
			
		||||
    R_HSYNC_END,
 | 
			
		||||
    R_HSCAN,
 | 
			
		||||
    R_VRES,
 | 
			
		||||
    R_VSYNC_START,
 | 
			
		||||
    R_VSYNC_END,
 | 
			
		||||
    R_VSCAN,
 | 
			
		||||
    R_BASEADDRESS,
 | 
			
		||||
    R_BASEADDRESS_ACT,
 | 
			
		||||
    R_BURST_COUNT,
 | 
			
		||||
    R_SOURCE_CLOCK,
 | 
			
		||||
    R_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    CTRL_RESET = (1<<0),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct MilkymistVgafbState {
 | 
			
		||||
    SysBusDevice busdev;
 | 
			
		||||
    DisplayState *ds;
 | 
			
		||||
 | 
			
		||||
    int invalidate;
 | 
			
		||||
    uint32_t fb_offset;
 | 
			
		||||
    uint32_t fb_mask;
 | 
			
		||||
 | 
			
		||||
    uint32_t regs[R_MAX];
 | 
			
		||||
};
 | 
			
		||||
typedef struct MilkymistVgafbState MilkymistVgafbState;
 | 
			
		||||
 | 
			
		||||
static int vgafb_enabled(MilkymistVgafbState *s)
 | 
			
		||||
{
 | 
			
		||||
    return !(s->regs[R_CTRL] & CTRL_RESET);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vgafb_update_display(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    MilkymistVgafbState *s = opaque;
 | 
			
		||||
    int first = 0;
 | 
			
		||||
    int last = 0;
 | 
			
		||||
    drawfn fn;
 | 
			
		||||
 | 
			
		||||
    if (!vgafb_enabled(s)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int dest_width = s->regs[R_HRES];
 | 
			
		||||
 | 
			
		||||
    switch (ds_get_bits_per_pixel(s->ds)) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return;
 | 
			
		||||
    case 8:
 | 
			
		||||
        fn = draw_line_8;
 | 
			
		||||
        break;
 | 
			
		||||
    case 15:
 | 
			
		||||
        fn = draw_line_15;
 | 
			
		||||
        dest_width *= 2;
 | 
			
		||||
        break;
 | 
			
		||||
    case 16:
 | 
			
		||||
        fn = draw_line_16;
 | 
			
		||||
        dest_width *= 2;
 | 
			
		||||
        break;
 | 
			
		||||
    case 24:
 | 
			
		||||
        fn = draw_line_24;
 | 
			
		||||
        dest_width *= 3;
 | 
			
		||||
        break;
 | 
			
		||||
    case 32:
 | 
			
		||||
        fn = draw_line_32;
 | 
			
		||||
        dest_width *= 4;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        hw_error("milkymist_vgafb: bad color depth\n");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    framebuffer_update_display(s->ds,
 | 
			
		||||
                               s->regs[R_BASEADDRESS] + s->fb_offset,
 | 
			
		||||
                               s->regs[R_HRES],
 | 
			
		||||
                               s->regs[R_VRES],
 | 
			
		||||
                               s->regs[R_HRES] * 2,
 | 
			
		||||
                               dest_width,
 | 
			
		||||
                               0,
 | 
			
		||||
                               s->invalidate,
 | 
			
		||||
                               fn,
 | 
			
		||||
                               NULL,
 | 
			
		||||
                               &first, &last);
 | 
			
		||||
 | 
			
		||||
    if (first >= 0) {
 | 
			
		||||
        dpy_update(s->ds, 0, first, s->regs[R_HRES], last - first + 1);
 | 
			
		||||
    }
 | 
			
		||||
    s->invalidate = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vgafb_invalidate_display(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    MilkymistVgafbState *s = opaque;
 | 
			
		||||
    s->invalidate = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void vgafb_resize(MilkymistVgafbState *s)
 | 
			
		||||
{
 | 
			
		||||
    if (!vgafb_enabled(s)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_console_resize(s->ds, s->regs[R_HRES], s->regs[R_VRES]);
 | 
			
		||||
    s->invalidate = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t vgafb_read(void *opaque, target_phys_addr_t addr)
 | 
			
		||||
{
 | 
			
		||||
    MilkymistVgafbState *s = opaque;
 | 
			
		||||
    uint32_t r = 0;
 | 
			
		||||
 | 
			
		||||
    addr >>= 2;
 | 
			
		||||
    switch (addr) {
 | 
			
		||||
    case R_CTRL:
 | 
			
		||||
    case R_HRES:
 | 
			
		||||
    case R_HSYNC_START:
 | 
			
		||||
    case R_HSYNC_END:
 | 
			
		||||
    case R_HSCAN:
 | 
			
		||||
    case R_VRES:
 | 
			
		||||
    case R_VSYNC_START:
 | 
			
		||||
    case R_VSYNC_END:
 | 
			
		||||
    case R_VSCAN:
 | 
			
		||||
    case R_BASEADDRESS:
 | 
			
		||||
    case R_BURST_COUNT:
 | 
			
		||||
    case R_SOURCE_CLOCK:
 | 
			
		||||
        r = s->regs[addr];
 | 
			
		||||
    break;
 | 
			
		||||
    case R_BASEADDRESS_ACT:
 | 
			
		||||
        r = s->regs[R_BASEADDRESS];
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        error_report("milkymist_vgafb: read access to unknown register 0x"
 | 
			
		||||
                TARGET_FMT_plx, addr << 2);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_milkymist_vgafb_memory_read(addr << 2, r);
 | 
			
		||||
 | 
			
		||||
    return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
vgafb_write(void *opaque, target_phys_addr_t addr, uint32_t value)
 | 
			
		||||
{
 | 
			
		||||
    MilkymistVgafbState *s = opaque;
 | 
			
		||||
 | 
			
		||||
    trace_milkymist_vgafb_memory_write(addr, value);
 | 
			
		||||
 | 
			
		||||
    addr >>= 2;
 | 
			
		||||
    switch (addr) {
 | 
			
		||||
    case R_CTRL:
 | 
			
		||||
    case R_HSYNC_START:
 | 
			
		||||
    case R_HSYNC_END:
 | 
			
		||||
    case R_HSCAN:
 | 
			
		||||
    case R_VSYNC_START:
 | 
			
		||||
    case R_VSYNC_END:
 | 
			
		||||
    case R_VSCAN:
 | 
			
		||||
    case R_BURST_COUNT:
 | 
			
		||||
    case R_SOURCE_CLOCK:
 | 
			
		||||
        s->regs[addr] = value;
 | 
			
		||||
        break;
 | 
			
		||||
    case R_BASEADDRESS:
 | 
			
		||||
        if (value & 0x1f) {
 | 
			
		||||
            error_report("milkymist_vgafb: framebuffer base address have to "
 | 
			
		||||
                     "be 32 byte aligned");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        s->regs[addr] = value & s->fb_mask;
 | 
			
		||||
        s->invalidate = 1;
 | 
			
		||||
        break;
 | 
			
		||||
    case R_HRES:
 | 
			
		||||
    case R_VRES:
 | 
			
		||||
        s->regs[addr] = value;
 | 
			
		||||
        vgafb_resize(s);
 | 
			
		||||
        break;
 | 
			
		||||
    case R_BASEADDRESS_ACT:
 | 
			
		||||
        error_report("milkymist_vgafb: write to read-only register 0x"
 | 
			
		||||
                TARGET_FMT_plx, addr << 2);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        error_report("milkymist_vgafb: write access to unknown register 0x"
 | 
			
		||||
                TARGET_FMT_plx, addr << 2);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CPUReadMemoryFunc * const vgafb_read_fn[] = {
 | 
			
		||||
   NULL,
 | 
			
		||||
   NULL,
 | 
			
		||||
   &vgafb_read
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static CPUWriteMemoryFunc * const vgafb_write_fn[] = {
 | 
			
		||||
   NULL,
 | 
			
		||||
   NULL,
 | 
			
		||||
   &vgafb_write
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void milkymist_vgafb_reset(DeviceState *d)
 | 
			
		||||
{
 | 
			
		||||
    MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev);
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < R_MAX; i++) {
 | 
			
		||||
        s->regs[i] = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* defaults */
 | 
			
		||||
    s->regs[R_CTRL] = CTRL_RESET;
 | 
			
		||||
    s->regs[R_HRES] = 640;
 | 
			
		||||
    s->regs[R_VRES] = 480;
 | 
			
		||||
    s->regs[R_BASEADDRESS] = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int milkymist_vgafb_init(SysBusDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev);
 | 
			
		||||
    int vgafb_regs;
 | 
			
		||||
 | 
			
		||||
    vgafb_regs = cpu_register_io_memory(vgafb_read_fn, vgafb_write_fn, s,
 | 
			
		||||
            DEVICE_NATIVE_ENDIAN);
 | 
			
		||||
    sysbus_init_mmio(dev, R_MAX * 4, vgafb_regs);
 | 
			
		||||
 | 
			
		||||
    s->ds = graphic_console_init(vgafb_update_display,
 | 
			
		||||
                                 vgafb_invalidate_display,
 | 
			
		||||
                                 NULL, NULL, s);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vgafb_post_load(void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    vgafb_invalidate_display(opaque);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_milkymist_vgafb = {
 | 
			
		||||
    .name = "milkymist-vgafb",
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .minimum_version_id_old = 1,
 | 
			
		||||
    .post_load = vgafb_post_load,
 | 
			
		||||
    .fields      = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SysBusDeviceInfo milkymist_vgafb_info = {
 | 
			
		||||
    .init = milkymist_vgafb_init,
 | 
			
		||||
    .qdev.name  = "milkymist-vgafb",
 | 
			
		||||
    .qdev.size  = sizeof(MilkymistVgafbState),
 | 
			
		||||
    .qdev.vmsd  = &vmstate_milkymist_vgafb,
 | 
			
		||||
    .qdev.reset = milkymist_vgafb_reset,
 | 
			
		||||
    .qdev.props = (Property[]) {
 | 
			
		||||
        DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
 | 
			
		||||
        DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
 | 
			
		||||
        DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void milkymist_vgafb_register(void)
 | 
			
		||||
{
 | 
			
		||||
    sysbus_register_withprop(&milkymist_vgafb_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
device_init(milkymist_vgafb_register)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  QEMU model of the Milkymist VGA framebuffer.
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if BITS == 8
 | 
			
		||||
#define COPY_PIXEL(to, r, g, b)                    \
 | 
			
		||||
    do {                                           \
 | 
			
		||||
        *to = rgb_to_pixel8(r, g, b);              \
 | 
			
		||||
        to += 1;                                   \
 | 
			
		||||
    } while (0)
 | 
			
		||||
#elif BITS == 15
 | 
			
		||||
#define COPY_PIXEL(to, r, g, b)                    \
 | 
			
		||||
    do {                                           \
 | 
			
		||||
        *(uint16_t *)to = rgb_to_pixel15(r, g, b); \
 | 
			
		||||
        to += 2;                                   \
 | 
			
		||||
    } while (0)
 | 
			
		||||
#elif BITS == 16
 | 
			
		||||
#define COPY_PIXEL(to, r, g, b)                    \
 | 
			
		||||
    do {                                           \
 | 
			
		||||
        *(uint16_t *)to = rgb_to_pixel16(r, g, b); \
 | 
			
		||||
        to += 2;                                   \
 | 
			
		||||
    } while (0)
 | 
			
		||||
#elif BITS == 24
 | 
			
		||||
#define COPY_PIXEL(to, r, g, b)                    \
 | 
			
		||||
    do {                                           \
 | 
			
		||||
        uint32 tmp = rgb_to_pixel24(r, g, b);      \
 | 
			
		||||
        *(to++) =         tmp & 0xff;              \
 | 
			
		||||
        *(to++) =  (tmp >> 8) & 0xff;              \
 | 
			
		||||
        *(to++) = (tmp >> 16) & 0xff;              \
 | 
			
		||||
    } while (0)
 | 
			
		||||
#elif BITS == 32
 | 
			
		||||
#define COPY_PIXEL(to, r, g, b)                    \
 | 
			
		||||
    do {                                           \
 | 
			
		||||
        *(uint32_t *)to = rgb_to_pixel32(r, g, b); \
 | 
			
		||||
        to += 4;                                   \
 | 
			
		||||
    } while (0)
 | 
			
		||||
#else
 | 
			
		||||
#error unknown bit depth
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void glue(draw_line_, BITS)(void *opaque, uint8_t *d, const uint8_t *s,
 | 
			
		||||
        int width, int deststep)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t rgb565;
 | 
			
		||||
    uint8_t r, g, b;
 | 
			
		||||
 | 
			
		||||
    while (width--) {
 | 
			
		||||
        rgb565 = lduw_raw(s);
 | 
			
		||||
        r = ((rgb565 >> 11) & 0x1f) << 3;
 | 
			
		||||
        g = ((rgb565 >>  5) & 0x3f) << 2;
 | 
			
		||||
        b = ((rgb565 >>  0) & 0x1f) << 3;
 | 
			
		||||
        COPY_PIXEL(d, r, g, b);
 | 
			
		||||
        s += 2;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef BITS
 | 
			
		||||
#undef COPY_PIXEL
 | 
			
		||||
| 
						 | 
				
			
			@ -355,3 +355,7 @@ disable milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr %08x val
 | 
			
		|||
disable milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
 | 
			
		||||
disable milkymist_uart_pulse_irq_rx(void) "Pulse IRQ RX"
 | 
			
		||||
disable milkymist_uart_pulse_irq_tx(void) "Pulse IRQ TX"
 | 
			
		||||
 | 
			
		||||
# hw/milkymist-vgafb.c
 | 
			
		||||
disable milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
 | 
			
		||||
disable milkymist_vgafb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue