显示屏驱动
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了显示屏驱动相关的知识,希望对你有一定的参考价值。
继上篇继续分析。驱动第二层: mxc_ipuv3_fb.c
module_init(mxcfb_init);
int __init mxcfb_init(void)
{
return platform_driver_register(&mxcfb_driver);
}
static struct platform_driver mxcfb_driver = {
.driver = {
.name = MXCFB_NAME,
.of_match_table = imx_mxcfb_dt_ids,
},
.probe = mxcfb_probe,
.remove = mxcfb_remove,
.suspend = mxcfb_suspend,
.resume = mxcfb_resume,
};
static int mxcfb_probe(struct platform_device *pdev)
{
struct ipuv3_fb_platform_data *plat_data;
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
struct device *disp_dev;
struct resource *res;
int ret = 0;
dev_dbg(&pdev->dev, "%s enter\\n", __func__);
pdev->id = of_alias_get_id(pdev->dev.of_node, "mxcfb"); //获取fb 的id 号
plat_data = devm_kzalloc(&pdev->dev, sizeof(struct ipuv3_fb_platform_data), GFP_KERNEL); //申请plat_data内存
pdev->dev.platform_data = plat_data; //将申请到的plat_data 内存地址赋值给pdev
ret = mxcfb_get_of_property(pdev, plat_data); //获取设备树参数 --->2-1
/* Initialize FB structures */
fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops); //初始化一个fb_info结构体并并填充ops --->2-2
ret = mxcfb_option_setup(pdev, fbi); //获取kernel cmdline 中的显示参数并覆盖设备树中设置的pdata->disp_dev --->2-3
mxcfbi = (struct mxcfb_info *)fbi->par;
mxcfbi->ipu_int_clk = plat_data->int_clk;
mxcfbi->late_init = plat_data->late_init;
mxcfbi->first_set_par = true;
ret = mxcfb_dispdrv_init(pdev, fbi); //初始化具体的显示屏控制器 --->2-4
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取framebuffer的资源
if (res && res->start && res->end) {
fbi->fix.smem_len = res->end - res->start + 1;
fbi->fix.smem_start = res->start; //将从设备树中获取到的frame buffer缓冲区的物理地址填充到fbi->fix.smem_start
fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len); //将frame buffer缓冲区的物理地址转化为虚拟地址(即成为可操控的显存)
/* Do not clear the fb content drawn in bootloader. */
if (!mxcfbi->late_init)
memset(fbi->screen_base, 0, fbi->fix.smem_len);
}
mxcfbi->ipu = ipu_get_soc(mxcfbi->ipu_id);
/* first user uses DP with alpha feature */
if (!g_dp_in_use[mxcfbi->ipu_id]) {
mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK;
mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
mxcfbi->ipu_ch = MEM_BG_SYNC;
ret = mxcfb_register(fbi); //注册fb --->2-5
ipu_disp_set_global_alpha(mxcfbi->ipu, mxcfbi->ipu_ch, true, 0x80);
ipu_disp_set_color_key(mxcfbi->ipu, mxcfbi->ipu_ch, false, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ret = mxcfb_setup_overlay(pdev, fbi, res);
g_dp_in_use[mxcfbi->ipu_id] = true;
ret = device_create_file(mxcfbi->ovfbi->dev, &dev_attr_fsl_disp_property); //创建属性文件
ret = device_create_file(mxcfbi->ovfbi->dev, &dev_attr_fsl_disp_dev_property);
}
platform_set_drvdata(pdev, fbi);
return 0;
}
2-1代码分析
static int mxcfb_get_of_property(struct platform_device *pdev, struct ipuv3_fb_platform_data *plat_data)
{
struct device_node *np = pdev->dev.of_node;
const char *disp_dev;
const char *mode_str;
const char *pixfmt;
int err;
int len;
u32 bpp, int_clk;
u32 late_init;
err = of_property_read_string(np, "disp_dev", &disp_dev); //获取设备树中默认设置的各类参数
err = of_property_read_string(np, "mode_str", &mode_str);
err = of_property_read_string(np, "interface_pix_fmt", &pixfmt);
err = of_property_read_u32(np, "default_bpp", &bpp);
err = of_property_read_u32(np, "int_clk", &int_clk);
err = of_property_read_u32(np, "late_init", &late_init);
if (!strncmp(pixfmt, "RGB24", 5))
plat_data->interface_pix_fmt = IPU_PIX_FMT_RGB24;
else if (!strncmp(pixfmt, "LVDS666", 7))
plat_data->interface_pix_fmt = IPU_PIX_FMT_LVDS666;
······
else if (!strncmp(pixfmt, "YUYV16", 6))
plat_data->interface_pix_fmt = IPU_PIX_FMT_YUYV;
else {
dev_err(&pdev->dev, "err interface_pix_fmt!\\n");
return -ENOENT;
}
len = min(sizeof(plat_data->disp_dev) - 1, strlen(disp_dev));
memcpy(plat_data->disp_dev, disp_dev, len); //把前面从设备树中获取的各类参数设置到plat_data中
plat_data->disp_dev[len] = '\\0';
plat_data->mode_str = (char *)mode_str;
plat_data->default_bpp = bpp;
plat_data->int_clk = (bool)int_clk;
plat_data->late_init = (bool)late_init;
return err;
}
2-2代码分析
//传入的ops是mxcfb_ops结构体
static struct fb_ops mxcfb_ops = {
.owner = THIS_MODULE,
.fb_set_par = mxcfb_set_par,
.fb_check_var = mxcfb_check_var,
.fb_setcolreg = mxcfb_setcolreg,
.fb_pan_display = mxcfb_pan_display,
.fb_ioctl = mxcfb_ioctl,
.fb_mmap = mxcfb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_blank = mxcfb_blank,
};
static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
{
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
mxcfbi = (struct mxcfb_info *)fbi->par;
fbi->var.activate = FB_ACTIVATE_NOW;
fbi->fbops = ops; //填充ops
return fbi;
}
2-3代码分析
static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi)
{
struct ipuv3_fb_platform_data *pdata = pdev->dev.platform_data;
char *options, *opt, *fb_mode_str = NULL;
char name[] = "mxcfb0";
uint32_t fb_pix_fmt = 0;
name[5] += pdev->id;
if (fb_get_options(name, &options)) { //获取kernel cmdline 中的显示参数
dev_err(&pdev->dev, "Can't get fb option for %s!\\n", name);
return -ENODEV;
}
while ((opt = strsep(&options, ",")) != NULL) {
if (!*opt)
continue;
if (!strncmp(opt, "dev=", 4)) {
memcpy(pdata->disp_dev, opt + 4, strlen(opt) - 4);
pdata->disp_dev[strlen(opt) - 4] = '\\0'; //如果cmdline中指定了显示设备,则覆盖设备树中设置的pdata->disp_dev
} else if (!strncmp(opt, "if=", 3)) {
if (!strncmp(opt+3, "RGB24", 5))
pdata->interface_pix_fmt = IPU_PIX_FMT_RGB24;
else if (!strncmp(opt+3, "LVDS666", 7))
pdata->interface_pix_fmt = IPU_PIX_FMT_LVDS666;
else if (!strncmp(opt+3, "YUYV16", 6))
pdata->interface_pix_fmt = IPU_PIX_FMT_YUYV;
} else if (!strncmp(opt, "fbpix=", 6)) {
if (!strncmp(opt+6, "RGB24", 5))
fb_pix_fmt = IPU_PIX_FMT_RGB24;
else if (!strncmp(opt+6, "RGB565", 6))
fb_pix_fmt = IPU_PIX_FMT_RGB565;
}
} else if (!strncmp(opt, "int_clk", 7)) {
pdata->int_clk = true;
continue;
} else if (!strncmp(opt, "bpp=", 4)) {
fb_pix_fmt = bpp_to_pixfmt(pdata->default_bpp);
if (fb_pix_fmt)
pixfmt_to_var(fb_pix_fmt, &fbi->var);
} else
fb_mode_str = opt;
}
if (fb_mode_str)
pdata->mode_str = fb_mode_str;
return 0;
}
2-4代码分析
static int mxcfb_dispdrv_init(struct platform_device *pdev, struct fb_info *fbi)
{
struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
struct mxc_dispdrv_setting setting;
char disp_dev[32], *default_dev = "lcd";
int ret = 0;
if (!strlen(plat_data->disp_dev)) {
memcpy(disp_dev, default_dev, strlen(default_dev));
disp_dev[strlen(default_dev)] = '\\0';
} else {
memcpy(disp_dev, plat_data->disp_dev, strlen(plat_data->disp_dev)); //由pdata->disp_dev构建 disp_dev 字符串
disp_dev[strlen(plat_data->disp_dev)] = '\\0';
}
mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting); //找到对应的drv,并调用drv->init --->
dev_info(&pdev->dev, "registered mxc display driver %s\\n", disp_dev);
return ret;
}
struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name, struct mxc_dispdrv_setting *setting)
{
int ret, found = 0;
struct mxc_dispdrv_entry *entry;
mutex_lock(&dispdrv_lock);
list_for_each_entry(entry, &dispdrv_list, list) { //遍历dispdrv_list列表
if (!strcmp(entry->drv->name, name) && (entry->drv->init)) { //对比drv->name与name,找到对应的drv
ret = entry->drv->init((struct mxc_dispdrv_handle *) //调用drv->init
entry, setting);
if (ret >= 0) {
entry->active = true;
found = 1;
break;
}
}
}
mutex_unlock(&dispdrv_lock);
return found ? (struct mxc_dispdrv_handle *)entry:ERR_PTR(-ENODEV);
}
2-5代码分析
static int mxcfb_register(struct fb_info *fbi)
{
struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
struct fb_videomode m;
mxcfb_check_var(&fbi->var, fbi);
mxcfb_set_fix(fbi);
if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq,
mxcfb_irq_handler, IPU_IRQF_ONESHOT, MXCFB_NAME, fbi) != 0) {
dev_err(fbi->device, "Error registering EOF irq handler.\\n");
ret = -EBUSY;
goto err0;
}
ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq);
······
ret = register_framebuffer(fbi); //调用第一层frame buffer框架层提供的接口注册fb_info结构体
return ret;
}
以上是关于显示屏驱动的主要内容,如果未能解决你的问题,请参考以下文章