实验环境:
正点原子alpha 开发板
调试自己编写的framebuffer 驱动,加载到内核之后,显示出小企鹅
1. Framebufer 总体框架
fbmem.c 作为Framebuffer的核心层,向上提供app使用的接口,向下屏蔽了底层各种硬件的差异;
准确来说fbmem.c 就是一个字符设备驱动框架的程序,对于字符设备驱动框架的程序如下:
(1) 分配主次设备号:如果主设备号为0,则内核自动分配;
(2)构造file_ops 结构体,提供应用层使用到的常用接口:drv_open, drv_read, drv_write, drv_ioctrl等;
(3)将主次设备号和构造的结构体注册到内核:使用register_char_dev()接口;
(4) 通过module_init(), module_exit() 完成驱动程序的自动化调用;
(5)使用其他的一些函数class_create 和 device_create 自动创建出设备节点;
fbmem.c 就是完成了以上字符设备驱动的通用的操作,但是具体对硬件的操作对于imx6ull开发板来说就是 mxsfb.c驱动。
fbmem.c 对下提供了注册fbinfo 的接口函数:register_framebuffer(struct fb_info *fb_info);
底层硬件驱动通过调用此函数向 fbmem.c 提供了统一的接口操作函数。
2. mxsfb.c 驱动框架
mxsfb.c 通过平台总线的方式被内核管理,即通过platform_driver, 如下:
static struct platform_driver mxsfb_driver = {
.probe = mxsfb_probe,
.remove = mxsfb_remove,
.shutdown = mxsfb_shutdown,
.id_table = mxsfb_devtype,
.driver = {
.name = DRIVER_NAME,
.of_match_table = mxsfb_dt_ids,
.pm = &mxsfb_pm_ops,
},
};
module_platform_driver(mxsfb_driver);
当of_match_table与内核中某个设备节点匹配之后,mxsfb_probe 就会被调用;
mxsfb_probe主要完成的功能如下:
1. 申请fb_info
2. 初始化fbinfo 结构体中的各个成员变量
3. 初始化lcdif控制器
4. 使用register_framebuffer() 函数向linux内核注册初始化号的fbinfo.
3. 结合以上分析 自己实现的驱动如下:
#include <linux/busfreq-imx.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/pinctrl/consumer.h>
#include <linux/fb.h>
#include <linux/mxcfb.h>
#include <linux/regulator/consumer.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
#include <linux/uaccess.h>
static struct fb_info *lcdfb_info;
static struct imx6ull_elcdif *elcdif;
static struct clk *clk_pix;
static struct clk *clk_axi;
static uint32_t pseudo_palette[16];
/** LCDIF - Register Layout Typedef */
struct imx6ull_elcdif{
volatile unsigned int CTRL; /**< eLCDIF General Control Register, offset: 0x0 */
volatile unsigned int CTRL_SET; /**< eLCDIF General Control Register, offset: 0x4 */
volatile unsigned int CTRL_CLR; /**< eLCDIF General Control Register, offset: 0x8 */
volatile unsigned int CTRL_TOG; /**< eLCDIF General Control Register, offset: 0xC */
volatile unsigned int CTRL1; /**< eLCDIF General Control1 Register, offset: 0x10 */
volatile unsigned int CTRL1_SET; /**< eLCDIF General Control1 Register, offset: 0x14 */
volatile unsigned int CTRL1_CLR; /**< eLCDIF General Control1 Register, offset: 0x18 */
volatile unsigned int CTRL1_TOG; /**< eLCDIF General Control1 Register, offset: 0x1C */
volatile unsigned int CTRL2; /**< eLCDIF General Control2 Register, offset: 0x20 */
volatile unsigned int CTRL2_SET; /**< eLCDIF General Control2 Register, offset: 0x24 */
volatile unsigned int CTRL2_CLR; /**< eLCDIF General Control2 Register, offset: 0x28 */
volatile unsigned int CTRL2_TOG; /**< eLCDIF General Control2 Register, offset: 0x2C */
volatile unsigned int TRANSFER_COUNT; /**< eLCDIF Horizontal and Vertical Valid Data Count Register, offset: 0x30 */
unsigned char RESERVED_0[12];
volatile unsigned int CUR_BUF; /**< LCD Interface Current Buffer Address R