Linux驅動開發(十七):LCD驅動(Framebuffer子系統)

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

這樣就設置了最大亮度

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章