Linux Kernel LCD屏驱动流程

为了解决24位色渐变效果,来从头梳理一次LCD屏的驱动,看下问题出在哪里。

------------------LCD Device Tree --------------------------

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";
};
----------------- LCD driver ------------------------------
#define DRIVER_NAME "mxsfb"

static const struct of_device_id mxsfb_dt_ids[] = {
    
    
	{
    
     .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
	{
    
     .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
	{
    
     /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);

// LCD驱动还提供了很多电源管理相关的函数一同注册到内核。
static const struct dev_pm_ops mxsfb_pm_ops = {
    
    
	SET_RUNTIME_PM_OPS(mxsfb_runtime_suspend, mxsfb_runtime_resume, NULL)
	SET_SYSTEM_SLEEP_PM_OPS(mxsfb_suspend, mxsfb_resume)
};

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,
	},
};

这里可以看到LCD屏的驱动是以platform平台设备方式注册到内核。这里的匹配方式有二种

  • DRIVER_NAME 与 设备资源中的DEVICE NAME来匹配。
  • 使用 mxsfb_dt_ids本匹配在设备树中定义的LCD controller。

这里可以看到设备树中的 compatible = “fsl,imx6ul-lcdif”, “fsl,imx28-lcdif”; 与驱动中的 { .compatible = “fsl,imx28-lcdif”, .data = &mxsfb_devtype[1], }, 是可以匹配到,从而驱动可以获取到LCD控制器相资源。

匹配完成后调用mxsfb_probe函数进行LCD设备的初始化。

LCD driver Probe函数主要流程及数据

在probe函数的主要数据结构初始化,完成数据的填充及通过这个填充来初始化LCD register。

	struct resource *res;
	struct mxsfb_info *host; //drver数据结构体
	struct fb_info *fb_info; //LCD控制器及framebuf设置相关数据
	struct pinctrl *pinctrl;
	int irq = platform_get_irq(pdev, 0);

几个主要函数:

  1. giants gpio inode from device tree and request gpio port to used LCD power control.
	gpio = of_get_named_gpio(pdev->dev.of_node, "enable-gpio", 0);
	if (gpio == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	if (gpio_is_valid(gpio)) {
    
    
		ret = devm_gpio_request_one(&pdev->dev, gpio, GPIOF_OUT_INIT_LOW, "lcd_pwr_en");
		if (ret) {
    
    
			dev_err(&pdev->dev, "faild to request gpio %d, ret = %d\n", gpio, ret);
			return ret;
		}
	}

  1. giants LCD registers from device tree.
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
    
    
		dev_err(&pdev->dev, "Cannot get memory IO resource\n");
		return -ENODEV;
	}
	// to ioremap io register to host base pointer(host->base).
	host->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(host->base)) {
    
    
		dev_err(&pdev->dev, "ioremap failed\n");
		ret = PTR_ERR(host->base);
		goto fb_release;
	}

  1. Allocates memory for LCD driver data of mxsfb_info structure.
	host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);
	if (!host) {
    
    
		dev_err(&pdev->dev, "Failed to allocate IO resource\n");
		return -ENOMEM;
	}
  1. Allocates memory for LCD framebuffer that describe by fb_info structure.
fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);
	if (!fb_info) {
    
    
		dev_err(&pdev->dev, "Failed to allocate fbdev\n");
		devm_kfree(&pdev->dev, host);
		return -ENOMEM;
	}
	host->fb_info = fb_info;
	fb_info->par = host;
  1. requests LCD controller IRQ and bonding the IRQ handler function.
	ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,
			  dev_name(&pdev->dev), host);
	if (ret) {
    
    
		dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
				irq, ret);
		ret = -ENODEV;
		goto fb_release;
	}
  1. Init fb_info structure and setting the var info, memory allocation for framebuffer
mxsfb_init_fbinfo
	mxsfb_init_fbinfo_dt() // parse device tree for LCD parameters.
	fb_videomode_to_var() // setting framebuffer var.
	mxsfb_check_var() // check input paramerters for var.
	mxsfb_map_videomem() //memory allocation for framebuffer.	
  1. Write var parameters to LCD controller register.
		writel(0, host->base + LCDC_CTRL); //clear register
		mxsfb_set_par(fb_info); //set lcd controller register
		mxsfb_enable_controller(fb_info);
  1. register framebuffer to kernel layer/fbmem.c.
	ret = register_framebuffer(fb_info);
	if (ret != 0) {
    
    
		dev_err(&pdev->dev, "Failed to register framebuffer\n");
		goto fb_destroy;
	}
  1. add LCD operations function and ioctl interface to user.
static struct fb_ops mxsfb_ops = {
    
    
	.owner = THIS_MODULE,
	.fb_check_var = mxsfb_check_var,
	.fb_set_par = mxsfb_set_par,
	.fb_setcolreg = mxsfb_setcolreg,
	.fb_ioctl = mxsfb_ioctl,
	.fb_blank = mxsfb_blank,
	.fb_pan_display = mxsfb_pan_display,
	.fb_mmap = mxsfb_mmap,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

.fb_mmap = mxsfb_mmap, LCD的mmap接口给到用户层API,使应用与内核共用framebuffer内存。

LCD存驱动与硬件相关部分是这么多,另外还有LCD设备驱动框架部分没有分析。待续。。。

猜你喜欢

转载自blog.csdn.net/zj646268653/article/details/112703973