QEMU学习(四):LCD设备仿真及驱动开发

一.QEMU上创建LCD设备仿真

\qemu\hw\display\100ask_qemu_fb.c

1.在QEMU上创建LCD设备

static const TypeInfo ask100fb_info = {
    .name          = TYPE_100ASKFB,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(ASK100FbState),  /* object_new_with_type 创建设备时会malloc该结构体, 并传给instance_init */ 
    .class_init    = ask100fb_class_init,
    .instance_init = ask100fb_init,          /* object_init_with_type */
    
};

void create_100ask_qemu_fb(void)
{
    DeviceState *dev;

    dev = qdev_create(NULL, TYPE_100ASKFB);
    qdev_init_nofail(dev);
}

static void ask100fb_register_types(void)
{
    type_register_static(&ask100fb_info);
}

type_init(ask100fb_register_types)

2.给LCD控制器提供回调函数

ask100fb_class_init设备类初始化实现图像更新,ask100fb_init设备实例初始化实现读写lcd驱动的值。

// 设备类初始化回调函数
static void ask100fb_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);

    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
    dc->realize = ask100fb_realize;  /* ask100fb_realize 何时被调用 */

    /* Note: This device does not any state that we have to reset or migrate */
}

static void ask100fb_realize(DeviceState *dev, Error **errp)
{
    ASK100FbState *s = ASK100FB(dev);

	if (imx6ul_board_ui_backgroud_prepare())
		return;

    s->invalidate = 1;
	s->fb_xres = imx6ul_board_descs[selected_board].lcd.w;
	s->fb_yres = imx6ul_board_descs[selected_board].lcd.h;
	s->board_xres = board_mem_pixels.iWidth;
	s->board_yres = board_mem_pixels.iHeight;
	dev->id = "board";
    s->con = graphic_console_init(dev, 0, &ask100fb_ops, s); // 调用ops回调函数实现板子画面更新
    qemu_console_resize(s->con, s->board_xres, s->board_yres);

	s->input_state = qemu_input_handler_register(dev, &touchscreen_handler);
	qemu_input_handler_bind(s->input_state, dev->id, 0, NULL);
	qemu_input_handler_activate(s->input_state);
}

static const GraphicHwOps ask100fb_ops = {
    .invalidate  = ask100fb_invalidate,
    .gfx_update  = ask100fb_update,
};


static void ask100fb_update(void *opaque)
{
    ASK100FbState *s = ASK100FB(opaque);
    SysBusDevice *sbd;
    DisplaySurface *surface = qemu_console_surface(s->con);
	static int inited = 0;
	
    int dest_width;
    int src_width;
    int first = 0;
    int last  = 0;

	int fb_x, fb_y;

    src_width  = s->fb_xres * s->fb_bpp / 8;
    dest_width = s->board_xres * surface_bits_per_pixel(surface) / 8;

    sbd = SYS_BUS_DEVICE(s);

	if (inited)
	{
		imx_gpio_ui_update(opaque);

		if (!s->fb_base_phys)
			return;

		
	    //if (s->invalidate) {
	        framebuffer_update_memory_section(&s->fbsection, sysbus_address_space(sbd), s->fb_base_phys,
	                                          s->fb_yres, src_width);
	    //}

	    framebuffer_update_display(surface, &s->fbsection, s->fb_xres, s->fb_yres,
	                               src_width, dest_width, 0, 1, ask100fb_draw_line_src16,
	                               s, &first, &last); // 更新LCD要显示图片
	   	fb_x = imx6ul_board_descs[selected_board].lcd.x;
		fb_y = imx6ul_board_descs[selected_board].lcd.y;
        dpy_gfx_update(s->con, fb_x, fb_y, s->fb_xres, s->fb_yres); // 更新板子及LCD的图像
	    //}

	}
	else
	{
		framebuffer_update_region(surface, &board_mem_pixels, 0, 0, s->board_xres, s->board_yres);  // 更新板子图片
		dpy_gfx_update(s->con, 0, 0, s->board_xres, s->board_yres); // 首次显示只显示板子图片

		imx_gpio_ui_init();
		
		inited = 1;
	}
    s->invalidate = 0;
}

// lcd设备实例初始化
static void ask100fb_init(Object *obj)
{
    ASK100FbState *s = ASK100FB(obj);
    MemoryRegion *address_space = get_system_memory();

    memory_region_init_io(&s->iomem, obj, &ask100_lcdc_ops, s, TYPE_100ASKFB, 16);
    memory_region_add_subregion(address_space, FSL_IMX6UL_LCDIF_ADDR, &s->iomem);

	//(void)ask100_touchscreen_ops;
    memory_region_init_io(&s->iomem_touchscreen, obj, &ask100_touchscreen_ops, s, "100ask_qemu_touchscreen", 16);
    memory_region_add_subregion(address_space, FSL_IMX6UL_TOUCHSCREEN_ADDR, &s->iomem_touchscreen);
}

static const MemoryRegionOps ask100_lcdc_ops = {
    .read = ask100_lcdc_read,
    .write = ask100_lcdc_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

static uint64_t ask100_lcdc_read(void *opaque, hwaddr addr,
                               unsigned size)
{
    ASK100FbState *s = ASK100FB(opaque);

    switch (addr) {
    case 0x00:	
        return s->fb_base_phys;

    case 0x04:	
        return s->fb_xres;

    case 0x08:	
        return s->fb_yres;

    case 0x0c:	
        return s->fb_bpp;

    default:
        break;
    }
    return 0;
}
// lcd驱动写数据时,此函数被调用
static void ask100_lcdc_write(void *opaque, hwaddr addr,
                            uint64_t value, unsigned size)
{
    ASK100FbState *s = ASK100FB(opaque);

    switch (addr) {
    case 0x00:
        s->fb_base_phys = value;
        break;

    case 0x04:
		s->fb_xres = value;
	    //qemu_console_resize(s->con, s->fb_xres, s->fb_yres);
        break;

    case 0x08:
		s->fb_yres = value;
	    //qemu_console_resize(s->con, s->fb_xres, s->fb_yres);
        break;

    case 0x0c:
		s->fb_bpp  = value;
        break;
    default:
		break;
    }
}

二.LCD设备驱动

QEMU自带/dev/fb0,也可以自己编写驱动模块。

既然操作的不是真实的 LCD 控制器,那么 LCD 驱动程序可以极大精简。
① 对于 LCD 控制器,只需要操作 4 个寄存器:
分别用来保存:framebuffer 的物理地址、宽度、高度、BPP。
你需要记住这些寄存器的物理地址(可以自己指定地址是什么)。
② 对于 FrameBuffer:
驱动程序分配得到 FrameBuffer 后,要把它的物理地址写到上述第 1 个寄存器里。
部分代码如下,其他的时钟使能、GPIO 设置等等都不再需要:

 

三.应用层写LCD

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/time.h>
#include "fb.h"
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>

 
#define ALLOW_OS_CODE 1
/*#include "../rua/include/rua.h"*/

#if 0
#define DEB(f) (f)
#else
#define DEB(f)
#endif
typedef unsigned int u32;
typedef unsigned short u16;
typedef u32 color_t;

typedef unsigned char RMuint8;
typedef unsigned short RMuint16;
typedef unsigned int RMuint32;

struct fb_var_screeninfo fb_var;
struct fb_fix_screeninfo fb_fix;
char * fb_base_addr = NULL;


struct ldm_info {
    int fd;
    struct fb_var_screeninfo var;
    struct fb_fix_screeninfo fix;
    size_t pixel_size;
    size_t x, y; //通过fix成员计算得到的实际分辨率
    size_t fb_size; //显存大小
    size_t addr;
    /* @x, y: 像素点的坐标
     * @color: 不管屏幕是什么色深,该参数都是标准RGB888的32位色的颜色值
     */
    void (*draw_pixel)(struct ldm_info *, size_t x, size_t y, color_t color);
};

struct files {
    size_t offset;
};

struct infos {
    size_t width;
    size_t height;
    size_t count;
    size_t sizeimage;
};

struct bmp_head {
    struct files file;
    struct infos info;
};

/* Set a pixel color
 * @param p_osd osd descriptor
 * @param x
 * @param y
 * @color 0xAARRGGBB (AA = alpha : 0 transparent)
 */
static void set_pixel(RMuint32 x, RMuint32 y, RMuint32 color)
{
	/*static RMuint32 i=0;*/
	/* TODO We assume for now we have contigus regions */
	switch (fb_var.bits_per_pixel){
		case 32:
			{
				RMuint32 *addr= (RMuint32 *)fb_base_addr+(y*fb_var.xres+x);
				*addr = color;
			}
			break;
		case 24:
			{
				/*RMuint32 *addr= (RMuint32 *)fb_base_addr+(y*fb_var.xres+x);
				*addr = (0x00FFFFFF) & color;*/
				RMuint8 *addr = (RMuint8 *)fb_base_addr+(y*fb_var.xres+x)*3;
				*addr = (RMuint8) (color & 0xFF);
				*(addr+1) = (RMuint8) (color >> 8 & 0xFF);
				*(addr+2) = (RMuint8) (color >> 16 & 0xFF);
			}
			break;
		case 16:
			{
				RMuint16 *addr = (RMuint16 *) fb_base_addr+(y*fb_var.xres+x);
				*addr = (RMuint16) color;
			}
			break;
		case 8:
			{
				RMuint8 *addr = (RMuint8 *)fb_base_addr+(y*fb_var.xres+x);
				*addr = (RMuint8) color;
			}
			break;
		case 4:
			{
				RMuint8 *addr = (RMuint8 *)fb_base_addr+(y*fb_var.xres+x) / 2;
				RMuint32 bit = (RMuint32) ((RMuint8 *)fb_base_addr+(y*fb_var.xres+x) % 2);
				RMuint8 pixel = *addr;
				pixel = pixel & ( 0x0F << bit * 4 );
				color = ( color & 0x0000000F ) << 4;
				*addr = pixel | ( (RMuint8) color >> bit * 4 );
			}
			break;
		case 2:
			{
				RMuint8 *addr = (RMuint8 *)fb_base_addr+(y*fb_var.xres+x) / 4;
				RMuint32 bit = (RMuint32) ((RMuint8 *)fb_base_addr+(y*fb_var.xres+x) % 4);
				RMuint8 pixel = *addr;
				pixel = pixel & ( 0xFF ^ ( 0x3 << bit * 2));
				color = color & 0x00000003;
				*addr = pixel | ( (RMuint8) color << bit * 2 );
			}
			break;
		case 1:
			{
				/*RMuint8 *addr = (RMuint8 *)fb_base_addr+(y*fb_var.xres+x);
				*addr = (RMuint8) color;*/
				RMuint8 *addr = (RMuint8 *)fb_base_addr+(y*fb_var.xres+x) / 8;
				RMuint32 bit = (RMuint32) ((RMuint8 *)fb_base_addr+(y*fb_var.xres+x) % 8);
				RMuint8 pixel = *addr;
				pixel = pixel & ( 0xFF ^ ( 0x1 << bit ));
				color = color & 0x00000001;
				*addr = pixel | ( (RMuint8) color << bit );
			}
			break;
		default:
			fprintf(stderr,"Unknown bpp : %d\n",fb_var.bits_per_pixel);
			break;
	}
	/*if (i<10){
		DEB(fprintf(stderr,"(%ld,%ld) [%p] <- %lX\n",x,y,addr,*addr));
		i++;
	}*/
}

static void mire()
{
	RMuint32 x,y;
	RMuint32 color;
	RMuint8 red,green,blue,alpha;

	DEB(fprintf(stderr,"begin mire\n"));
	for (y=0;y<fb_var.yres;y++)
		for (x=0;x<fb_var.xres;x++){
			color = ((x-fb_var.xres/2)*(x-fb_var.xres/2) + (y-fb_var.yres/2)*(y-fb_var.yres/2))/64;
			red   = (color/8) % 256;
			green = (color/4) % 256;
			blue  = (color/2) % 256;
			alpha = (color*2) % 256;
			/*alpha = 0xFF;*/

			color |= ((RMuint32)alpha << 24);
			color |= ((RMuint32)red   << 16);
			color |= ((RMuint32)green << 8 );
			color |= ((RMuint32)blue       );

			set_pixel(x,y,color);
		}

	DEB(fprintf(stderr,"end mire\n"));
}
int main(int argc, char** argv)
{
	int fd = 0;
	long int screensize = 0;

	unsigned int width = 0;
	unsigned int height = 0;
	unsigned int bpp = 0;
    unsigned int *pdwAddr = 0;
    unsigned short *pwAddr = 0;
    unsigned char *pcAddr = 0;
    unsigned int dwOffset;
    unsigned int dwVal;
    unsigned int num;
    int i;
	
	if (argc < 2) {
        printusage(argv[0]);
		return -1;
	}
 
	fd = open(argv[1],O_RDWR);
	if (fd <0){
		printf("error opening %s\n",argv[1]);
		exit(1);
	}


	/* Get fixed screen information */
	if (ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix)) {
		printf("Error reading fb fixed information.\n");
		exit(1);
	}

	/* Get variable screen information 	*/
	if (ioctl(fd, FBIOGET_VSCREENINFO, &fb_var)) {
		printf("Error reading fb variable information.\n");
		exit(1);
	}
    if ((argc == 2) || (argc > 2 && !strcmp(argv[2], "set")))
    {
    	/*blank	*/
    	memset(fb_base_addr,0x00,screensize);

    	usleep(50000);
    	mire();
    }
    return 0;
}

实际效果

[root@qemu_imx6ul:~]# myfb-test /dev/fb0

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值