在裸机开发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屏幕区别在于设置屏幕参数,这部分要放到设备树中
打开 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驱动
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";
};
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的启动画面了
前面在设备树的backlight节点设置了七个亮度等级(其实就是七个不同占空比)
在命令行界面使用命令
cd /sys/devices/platform/backlight/backlight/backlight
echo 7 > brightness
这样就设置了最大亮度