Framebuffer設備
在裸機開發LCD的時候要初始化eLCDIF控制器,重點是LCD屏幕width、height、hspw、hbp、hfp、vspw、vbp、vfp
Linux中的應用程序最終通過操作LCD的顯存來實現在LCD上顯示字符、圖片等信息,因爲虛擬內存的存在,驅動程序設置的顯存和應用程序訪問的顯存要是同一片物理內存
Framebuffer子系統,幀緩衝,簡稱fb,fb是一種機制,將系統中所有跟顯示有關的硬件以及軟件集合起來,虛擬出一個 fb設備,當我們編寫好 LCD驅動以後會生成一個名爲 /dev/fbX(X=0~n)的設備,應用程序通過訪問 /dev/fbX這個設備就可以訪問 LCD
fb的file_operations操作集爲fb_fop
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
.llseek = default_llseek,
};
LCD 驅動
不同分辨率的LCD屏幕區別在於設置屏幕參數,這部分要放到設備樹中
打開 imx6ull.dtsi 中的lcdif節點
lcdif: lcdif@021c8000 {
compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
reg = <0x021c8000 0x4000>;
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
<&clks IMX6UL_CLK_LCDIF_APB>,
<&clks IMX6UL_CLK_DUMMY>;
clock-names = "pix", "axi", "disp_axi";
status = "disabled";
};
以上信息是所有使用imx6ull芯片的板子共有的,並不完整,在板級dtsi文件中會對屏幕參數等信息進行添加
\drivers\video\fbdev\mxsfb.c 是 imx6ull的LCD驅動文件
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,
},
};
可以看出這是標準的platform驅動
Framebuffer驅動編寫流程
Linux內核將所有的Framebuffer抽象爲一個fb_info結構體,包含了Framebuffer設備的完整屬性和操作集合,每一個Framebuffer設備都必須有一個fb_info
LCD的驅動就是構建fb_info並向系統註冊fb_info的過程
定義在fb.h,如下
struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs互斥鎖 */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields 用於fb_mmap和smem_*域的互斥鎖*/
struct fb_var_screeninfo var; /* Current var 當前可變參數*/
struct fb_fix_screeninfo fix; /* Current fix 當前固定參數*/
struct fb_monspecs monspecs; /* Current Monitor specs 當前顯示器特性*/
struct work_struct queue; /* Framebuffer event queue 幀緩衝時間隊列*/
struct fb_pixmap pixmap; /* Image hardware mapper 圖像硬件映射*/
struct fb_pixmap sprite; /* Cursor hardware mapper 光標硬件映射*/
struct fb_cmap cmap; /* Current cmap 當前調色板*/
struct list_head modelist; /* mode list 當前模式列表*/
struct fb_videomode *mode; /* current mode 當前視頻模式*/
#ifdef CONFIG_FB_BACKLIGHT /*如果LCD支持背光*/
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev; /*背光設備*/
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops; /*幀緩衝操作函數集*/
struct device *device; /* This is the parent父設備 */
struct device *dev; /* This is this fb device 當前fb設備*/
int class_flag; /* private sysfs flags 私有sysfs標誌*/
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address 虛擬內存基地址(屏幕顯存)*/
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 虛擬內存大小(屏幕顯存大小)*/
void *pseudo_palette; /* Fake palette of 16 colors 僞16位調色板*/
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
bool skip_vt_switch; /* no VT switch on suspend/resume required */
};
fb_info結構體的成員變量很多,我們重點關注 var、 fix、 fbops、 screen_base、 screen_size和 pseudo_palette
mxsfb_probe函數的主要工作內容爲:
1、申請 fb_info。
2、初始化 fb_info結構體中的各個成員變量。
3、初始化 eLCDIF控制器。
4、使用 register_framebuffer函數向 Linux內核註冊初始化好的 fb_info
register_framebuffer函數原型如下
int register_framebuffer(struct fb_info *fb_info);
驅動程序的編寫
6ULL的 eLCDIF接口驅動程序 NXP已經編寫好了,因此 LCD驅動部分我們不需要去修改。我們需要做的就是按照所使用的 LCD來修改設備樹。重點要注意三個地方
1、LCD所使用的IO配置
這個部分NXP已經寫好了
pinctrl_lcdif_dat: lcdifdatgrp {
fsl,pins = <
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79
MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79
MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79
MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79
MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79
MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79
MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79
MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79
MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79
MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79
MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79
MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79
MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79
MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79
MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79
MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79
>;
};
pinctrl_lcdif_ctrl: lcdifctrlgrp {
fsl,pins = <
MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79
MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79
MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79
MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79
>;
};
2、LCD屏幕節點修改,修改相應的屬性值,換成我們所使用的LCD屏幕參數
&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat
&pinctrl_lcdif_ctrl>;
display = <&display0>;
status = "okay";
display0: display {
bits-per-pixel = <24>;
bus-width = <24>;
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <9000000>;
hactive = <480>;
vactive = <272>;
hfront-porch = <5>;
hback-porch = <40>;
hsync-len = <20>;
vback-porch = <8>;
vfront-porch = <8>;
vsync-len = <3>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <0>;
};
};
};
};
3、LCD背光節點信息修改,要根據實際所使用的背光IO來修改相應的設備節點信息
pinctrl_pwm1: pwm1grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0
>;
};
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
status = "okay";
};
設置LCD作爲終端
1、在Uboot中修改bootargs
之前的bootargs爲
bootargs=console=ttymxc0,115200 root=/dev/nfs rw \
nfsroot=192.168.1.111:/home/gyy/linux/nfs/buildrootfs \
ip=192.168.1.144:192.168.1.111:192.168.1.1:255.255.255.0::eth0:off
我們修改爲
bootargs=console=tty1 console=ttymxc0,115200 root=/dev/nfs rw \
nfsroot=192.168.1.111:/home/gyy/linux/nfs/buildrootfs \
ip=192.168.1.144:192.168.1.111:192.168.1.1:255.255.255.0::eth0:off
重點就是加了一句console=tty1
2、修改/etc/inittab文件
在裏面加一句
tty1::askfirst:-/bin/sh
打開tty1,將LCD設置爲終端
這時我們連接上LCD屏幕啓動板子後就可以在LCD上看到Linux的啓動畫面了
LCD背光調節
前面在設備樹的backlight節點設置了七個亮度等級(其實就是七個不同佔空比)
在命令行界面使用命令
cd /sys/devices/platform/backlight/backlight/backlight
echo 7 > brightness
這樣就設置了最大亮度