Linux驱动学习笔记:基于framebuffer的LCD驱动编程

一、Framebuffer概述

       Frame 是帧的意思,buffer 是缓冲的意思,在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。LCD由一个一个像素组成,假设他的分辨率为xres*yres,那么就是每行有xres个像素,总共有yres行。假设每个像素的颜色用16位来表示,一个LCD的所有像素点有xres * y res个,那么需要的内存为:xres * yres * 16 / 8,这块内存就被称为framebuffer。

        framebuffer存放在内存里面,LCD控制器会按照一定频率把framebuffer上的数据刷到LCD硬件上面去,从而点亮LCD。

二、Framebuffer驱动程序编写

        分为上下两层:

        上层: fbmem.c (Linux内核自带)

        承上启下,实现、注册file_operations结构体,把APP的调用向下转发到具体的硬件驱动程序 

        下层: xxx_fb.c(硬件相关的驱动程序)
         分配、设置、注册fb_info结构体,LCD硬件操作(引脚设置、 时钟设置、LCD控制器设置)

2.1、xxx_fb.c硬件驱动框架

        xxx_fb.c驱动框架采用平台总线设备驱动框架,驱动程序+设备树的形式,设备树描述硬件信息,驱动程序根据设备树的硬件信息设置fb_info结构体信息,初始化硬件,注册fb_info结构体。

        平台总线驱动框架代码大致如下:

static int lcd_probe(struct platform_device *pdev)
{
	return 0;
}

static int lcd_remove(struct platform_device *pdev)
{
	return 0;
}

static const struct of_device_id lcd_of_match[] = {
	{ .compatible = "wyq,lcd", },
	{ },
};

static struct platform_driver lcd_driver = {
	.driver = {
		.name = "lcd",
		.of_match_table = lcd_of_match,
	},
	.probe = lcd_probe,
	.remove = lcd_remove,
};

static int __init lcd_drv_init(void)
{
	int ret;
	ret = platform_driver_register(&lcd_driver);
	if (ret)
		return ret;
	return 0;
}

static void __exit lcd_drv_exit(void)
{
	platform_driver_unregister(&lcd_driver);
}

module_init(lcd_drv_init);
module_exit(lcd_drv_exit);
MODULE_LICENSE("GPL");

         首先创建一个平台总线驱动lcd_driver,在入口函数里面去注册它,在出口函数里面去注销,如果lcd_driver里面的of_match_table里面的compatible属性与设备树里面的compatible属性一致,那么内核就会自动调用lcd_driver里面的probe函数。

2.2、编写设备树

        我这里使用的LCD为RGB888接口的LCD显示屏(24根数据传输线,红绿蓝各8根,垂直同步信号VSYNC,行同步信号线HSYNC,时钟CLK,DE线,背光backlight,还有其他引脚,但是根据原理图不需要设置),开发板使用的是stm32mp157,开发板支持pinctrl系统,所以引脚设置使用ST官方提供的STM32CubeMX,然后根据开发板原理图设置LCD引脚,最后在pinctrl节点设置如下。

 ltdc_pins_a: ltdc-a-0 {
                pins {
                        pinmux = <STM32_PINMUX('G',  7, AF14)>, /* LCD_CLK */时钟
                                 <STM32_PINMUX('I', 10, AF14)>, /* LCD_HSYNC */垂直信号
                                 <STM32_PINMUX('I',  9, AF14)>, /* LCD_VSYNC */水平信号
                                 <STM32_PINMUX('F', 10, AF14)>, /* LCD_DE */数据使能
                                 <STM32_PINMUX('H',  2, AF14)>, /* LCD_R0 */
                                 <STM32_PINMUX('H',  3, AF14)>, /* LCD_R1 */
                                 <STM32_PINMUX('H',  8, AF14)>, /* LCD_R2 */
                                 <STM32_PINMUX('H',  9, AF14)>, /* LCD_R3 */
                                 <STM32_PINMUX('H', 10, AF14)>, /* LCD_R4 */
                                 <STM32_PINMUX('C',  0, AF14)>, /* LCD_R5 */
                                 <STM32_PINMUX('H', 12, AF14)>, /* LCD_R6 */
                                 <STM32_PINMUX('E', 15, AF14)>, /* LCD_R7 */R0-R7表示红色
                                 <STM32_PINMUX('E',  5, AF14)>, /* LCD_G0 */
                                 <STM32_PINMUX('E',  6, AF14)>, /* LCD_G1 */
                                 <STM32_PINMUX('H', 13, AF14)>, /* LCD_G2 */
                                 <STM32_PINMUX('H', 14, AF14)>, /* LCD_G3 */
                                 <STM32_PINMUX('H', 15, AF14)>, /* LCD_G4 */
                                 <STM32_PINMUX('I',  0, AF14)>, /* LCD_G5 */
                                 <STM32_PINMUX('I',  1, AF14)>, /* LCD_G6 */
                                 <STM32_PINMUX('I',  2, AF14)>, /* LCD_G7 */G0-G7表示绿色
                                 <STM32_PINMUX('D',  9, AF14)>, /* LCD_B0 */
                                 <STM32_PINMUX('G', 12, AF14)>, /* LCD_B1 */
                                 <STM32_PINMUX('G', 10, AF14)>, /* LCD_B2 */
                                 <STM32_PINMUX('D', 10, AF14)>, /* LCD_B3 */
                                 <STM32_PINMUX('I',  4, AF14)>, /* LCD_B4 */
                                 <STM32_PINMUX('A',  3, AF14)>, /* LCD_B5 */
                                 <STM32_PINMUX('B',  8, AF14)>, /* LCD_B6 */
                                 <STM32_PINMUX('D',  8, AF14)>; /* LCD_B7 */B0-B7表示蓝色
                        bias-disable;
                        drive-push-pull;
                        slew-rate = <1>;
                };
        };

   对应原理图

        背光引脚需要复用为GPIO功能,但是该开发板不需要使用pinctrl系统把该引脚复用为GPIO功能,所以不需要在pinctrl系统里面设置。然后把这个节点添加到stm32mp15-pinctrl.dtsi中。

        然后还要把LCD的一些硬件信息和时序参数在设备树中描述出来,在具体单板加入的子节点如下:

framebuffer-lcd {
			compatible = "wyq,lcd";
			reg = <0x5a001000 0x400>;
			pinctrl-names = "default";
			pinctrl-0 = <&ltdc_pins_a>;/*pinctrl信息引用刚才上面的节点*/
			backlight-gpios = <&gpioe 11 GPIO_ACTIVE_HIGH>;/*背光引脚*/
			clocks = <&rcc LTDC_PX>;/*时钟引用rcc*/
            clock-names = "lcd";

            display = <&display0>;/*时序节点,驱动程序根据这个节点获取LCD相关参数*/

			display0: display {
				bits-per-pixel = <16>;/*一个像素16位*/
				bus-width = <24>;

				display-timings {
					native-mode = <&timing0>;

					 timing0: timing0_1024x600 {
					 clock-frequency = <50000000>;/*时钟频率*/
					 hactive = <1024>;/*水平方向分辨率*/
					 vactive = <600>;/*垂直方向分辨率*/
					 hfront-porch = <160>;
					 hback-porch = <140>;
					 hsync-len = <20>;
					 vback-porch = <20>;
					 vfront-porch = <12>;
					 vsync-len = <3>;

					 hsync-active = <0>;/*低电平有效*/
					 vsync-active = <0>;/*低电平有效*/
					 de-active = <1>;/*高电平有效*/
					 pixelclk-active = <0>;/*低电平有效*/
					 };

				};
			};            			
	};

2.3、分配、设置、注册fb_info

        首先创建一个fb_info 结构体。

struct fb_info *lcd_info;

        然后去设置填充fb_info,主要填充信息有fb_info->var,主要是一些可变参数,分辨率像素值等。fb_info ->fix中的一些固定参数,还有操作函数fbops;设置填充完后就可以注册了。

    /*从设备树中获取节点*/
	display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);

	/* 依据节点获取像素值/
	ret = of_property_read_u32(display_np, "bits-per-pixel",&bits_per_pixel);

    /* 获取时序 */
	timings = of_get_display_timings(display_np);
	timing  = timings->timings[timings->native_mode];

	/* 分配fb_info */
	lcd_info = framebuffer_alloc(0, NULL);

	/* 设置fb_info */
	/* 设置 LCD分辨率*/
	lcd_info->var.xres = timing->hactive.typ;
	lcd_info->var.yres = timing->vactive.typ;
	lcd_info->var.bits_per_pixel = bits_per_pixel;
  
    /* 设置 LCD分辨率、颜色格式 */
    myfb_info->var.red.offset = 16;
	myfb_info->var.red.length = 8;/*红色*/
	myfb_info->var.green.offset = 8;
	myfb_info->var.green.length = 8;/*绿色*/
	myfb_info->var.blue.offset = 0;
	myfb_info->var.blue.length = 8;、/*蓝色*/
    
    /* 设置fix固定参数*/
   lcd_info->fix.smem_len = lcd_info->var.xres * lcd_info->var.yres * lcd_info-             >var.bits_per_pixel / 8;

	lcd_info->screen_base = dma_alloc_wc(&pdev->dev, lcd_info->fix.smem_len, &phy_addr,
					 GFP_KERNEL);
	lcd_info->fix.smem_start = phy_addr;  /* fb的物理地址 */
	
	lcd_info->fix.type = FB_TYPE_PACKED_PIXELS;
	lcd_info->fix.visual = FB_VISUAL_TRUECOLOR;

	lcd_info->fix.line_length = lcd_info->var.xres * lcd_info->var.bits_per_pixel / 8;
	
	/* 设置 fbops */
	lcd_info->fbops = &myfb_ops;
	lcd_info->pseudo_palette = pseudo_palette;

	/* 最后注册fb_info */
	register_framebuffer(lcd_info);

2.4、硬件相关操作

一、引脚设置 

        引脚相关设置大部分已经使用pinctrl系统在设备树里面设置好了,驱动程序里面无需设置,但还需要设置背光引脚。

        在设备树里面已经指定了背光引脚,在驱动程序里面直接使用gpiod_get就可以获取该引脚,然后在对该引脚进行设置。

    bl_gpio = gpiod_get(&pdev->dev, "backlight", 0);//获得背后引脚

	gpiod_direction_output(bl_gpio, 1);//配置为输出

	gpiod_set_value(bl_gpio, 1);//在lcd寄存器初始化完毕并使能后就可以使能背光引脚了

二、时钟设置

       在设备树中指定了引用哪个时钟源,也指定了时钟频率,但是这两个还无法相互直接进行操作,需要在驱动程序里面根据设备树信息获取时钟,然后根据设备树信息指定的频率设置它,最后使能它。

    pixel_clk = devm_clk_get(&pdev->dev, "lcd");//获取时钟

	clk_set_rate(pixel_clk, timing->pixelclock.typ);//设置频率

	clk_prepare_enable(pixel_clk);//使能时钟

三、LCD控制器设置

        这一块主要是根据芯片手册完成相关寄存器的设置,把寄存器的物理地址映射成虚拟地址。

三、参考资料

驱动源码:
         LCD相关:Linux-5.4\drivers\gpu\drm\panel\panel-myir070tft.c
         LCD控制器相关:Linux-5.4\drivers\gpu\drm\stm\ltdc.c
         GPU相关:Linux-5.4\drivers\gpu\drm\stm\drv.c
设备树:
          Linux-5.4/arch/arm/boot/dts/stm32mp151.dtsi
          Linux-5.4/arch/arm/boot/dts/stm32mp15-pinctrl.dtsi

视频:韦东山《驱动大全》

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值