为了解决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);
几个主要函数:
- 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;
}
}
- 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;
}
- 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;
}
- 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;
- 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;
}
- 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.
- 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);
- 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;
}
- 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设备驱动框架部分没有分析。待续。。。