imx6 framebuffer 分析

分析imx6 framebuffer设备和驱动的注册过程。

                          Tony Liu, 2016-8-31, Shenzhen

相关文件:

  arch/arm/mach-mx6/board-mx6q_sabresd.c

  kernel/video/mxc/mxc_ipuv3_fb.c

  mm/memblock.c |

  drivers/video/fbmem.c |

设备注册

MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
    /* Maintainer: Freescale Semiconductor, Inc. */
    .boot_params = MX6_PHYS_OFFSET + 0x100,
    .fixup = fixup_mxc_board,                    --------------+        //读取uboot bootargs中关于framebufer的参数
    .map_io = mx6_map_io,                                      |
    .init_irq = mx6_init_irq,                                  |
    .init_machine = mx6_sabresd_board_init,      ---------------------------------------------+     //设备初始化
    .timer = &mx6_sabresd_timer,                               |                              |
    .reserve = mx6q_sabresd_reserve,             -----------------------------------+         |     //framebuffer内存保留
MACHINE_END                                                    |                    |         |
                                                               |                    |         |
//读取bootargs中的参数                                         V                    |         |
static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,     |         |
                   char **cmdline, struct meminfo *mi)                              |         |
{                                                                                   |         |
    char *str;                                                                      |         |
    struct tag *t;                                                                  |         |
    int i = 0;                                                                      |         |
    struct ipuv3_fb_platform_data *pdata_fb = sabresd_fb_data;                      |         |
                                                                                    |         |
    for_each_tag(t, tags) {                                                         |         |
        if (t->hdr.tag == ATAG_CMDLINE) {                                           |         |
            str = t->u.cmdline.cmdline;                                             |         |
            str = strstr(str, "fbmem=");                                            |         |
            if (str != NULL) {                                                      |         |
                str += 6;                                                           |         |
                pdata_fb[i++].res_size[0] = memparse(str, &str);                    |         |
                while (*str == ',' &&                                               |         |
                    i < ARRAY_SIZE(sabresd_fb_data)) {                              |         |
                    str++;                                                          |         |
                    pdata_fb[i++].res_size[0] = memparse(str, &str);                |         |
                }                                                                   |         |
            }                                                                       |         |
            /* ION reserved memory */                                               |         |
            str = t->u.cmdline.cmdline;                                             |         |
            str = strstr(str, "ionmem=");                                           |         |
            if (str != NULL) {                                                      |         |
                str += 7;                                                           |         |
                imx_ion_data.heaps[0].size = memparse(str, &str);                   |         |
            }                                                                       |         |
            /* Primary framebuffer base address */                                  |         |
            str = t->u.cmdline.cmdline;                                             |         |
            str = strstr(str, "fb0base=");                                          |         |
            if (str != NULL) {                                                      |         |
                str += 8;                                                           |         |
                pdata_fb[0].res_base[0] =                                           |         |
                        simple_strtol(str, &str, 16);                               |         |
            }                                                                       |         |
            /* GPU reserved memory */                                               |         |
            str = t->u.cmdline.cmdline;                                             |         |
            str = strstr(str, "gpumem=");                                           |         |
            if (str != NULL) {                                                      |         |
                str += 7;                                                           |         |
                imx6q_gpu_pdata.reserved_mem_size = memparse(str, &str);            |         |
            }                                                                       |         |
            break;                                                                  |         |
        }                                                                           |         |
    }                                                                               |         |
}                                                                                   |         |
                                                                                    |         |
static void __init mx6q_sabresd_reserve(void)                        <------------- +         |
{                                                                                             |
    phys_addr_t phys;                                                                         |
    int i, fb0_reserved = 0, fb_array_size;                                                   |
                                                                                              |
    /*                                                                                        |
     * Reserve primary framebuffer memory if its base address                                 |
     * is set by kernel command line.                                                         |
     */                                                                                       |
    // fb0                                                                                    |
    fb_array_size = ARRAY_SIZE(sabresd_fb_data);                                              |
    if (fb_array_size > 0 && sabresd_fb_data[0].res_base[0] &&                                |
        sabresd_fb_data[0].res_size[0]) {                                                     |
        //将framebuffer的数据保留,放在memblock.reverse中
        memblock_reserve(sabresd_fb_data[0].res_base[0],                                      |
                 sabresd_fb_data[0].res_size[0]);                                             |
        //将原来的内存中memblock.memory中删除
        memblock_remove(sabresd_fb_data[0].res_base[0],                                       |
                sabresd_fb_data[0].res_size[0]);                                              |
        sabresd_fb_data[0].late_init = true;                                                  |
        ipu_data[ldb_data.ipu_id].bypass_reset = true;                                        |
        fb0_reserved = 1;                                                                     |
    }                                                                                         |
    // 对于fb_arrary剩下的元素
    for (i = fb0_reserved; i < fb_array_size; i++)                                            |
        if (sabresd_fb_data[i].res_size[0]) {                                                 |
            /* Reserve for other background buffer. */                                        |
            //申请空间,保留到memblock.reverse中
            phys = memblock_alloc(sabresd_fb_data[i].res_size[0],                             |
                        SZ_4K);                                                               |
            //将memblock.memory中一块大小的空间删除,因为已经申请,并放入memblock.reverse中
            memblock_remove(phys, sabresd_fb_data[i].res_size[0]);                            |
            sabresd_fb_data[i].res_base[0] = phys;                      --------------+       |
        }                                                                             |       |
                                                                                      |       |
#ifdef CONFIG_ANDROID_RAM_CONSOLE                                                     |       |
    //保留128K的内存到memblock.reserve,4K对齐                                        |       |
    phys = memblock_alloc_base(SZ_128K, SZ_4K, SZ_1G);                                |       |
    memblock_remove(phys, SZ_128K);                                                   |       |
    memblock_free(phys, SZ_128K);                                                     |       |
    ram_console_resource.start = phys;                                                |       |
    ram_console_resource.end   = phys + SZ_128K - 1;                                  |       |
#endif                                                                                |       |
                                                                                      |       |
#if defined(CONFIG_MXC_GPU_VIV) || defined(CONFIG_MXC_GPU_VIV_MODULE)                 |       |
    //保留  内存到memblock.reserve,4K对齐
    if (imx6q_gpu_pdata.reserved_mem_size) {                                          |       |
        phys = memblock_alloc_base(imx6q_gpu_pdata.reserved_mem_size,                 |       |
                       SZ_4K, SZ_1G);                                                 |       |
        memblock_remove(phys, imx6q_gpu_pdata.reserved_mem_size);                     |       |
        imx6q_gpu_pdata.reserved_mem_base = phys;                                     |       |
    }                                                                                 |       |
#endif                                                                                |       |
                                                                                      |       |
#if defined(CONFIG_ION)                                                               |       |
    if (imx_ion_data.heaps[0].size) {                                                 |       |
        phys = memblock_alloc(imx_ion_data.heaps[0].size, SZ_4K);                     |       |
        memblock_remove(phys, imx_ion_data.heaps[0].size);                            |       |
        imx_ion_data.heaps[0].base = phys;                                            |       |
    }                                                                                 |       |
#endif                                                                                |       |
}                                                                                     |       |
                                                                                      |       |
//#define IPU_PIX_FMT_BIT24                                                           |       |
static struct ipuv3_fb_platform_data sabresd_fb_data[] = {                   <--------+       |
    { /*fb0*/                                                                                 |
    .disp_dev = "ldb",                                                                        |
#ifdef IPU_PIX_FMT_BIT24                                                                      |
    .interface_pix_fmt = IPU_PIX_FMT_RGB24,                                                   |
    .default_bpp = 24,                                                                        |
#else                                                                                         |
    .interface_pix_fmt = IPU_PIX_FMT_RGB666,                                                  |
    .default_bpp = 16,                                                                        |
#endif                                                                                        |
    .mode_str = "LDB-XGA",                                                                    |
    .int_clk = false,                                                                         |
    .late_init = false,                                                                       |
    },                                                                                        |
    {                                                                                         |
    .disp_dev = "hdmi",                                                                       |
    .interface_pix_fmt = IPU_PIX_FMT_RGB24,                                                   |
    .mode_str = "1920x1080M@60",                                                              |
    .default_bpp = 32,                                                                        |
    .int_clk = false,                                                                         |
    .late_init = false,                                                                       |
    },                                                                                        |
    {                                                                                         |
    .disp_dev = "ldb",                                                                        |
#ifdef IPU_PIX_FMT_BIT24                                                                      |
    .interface_pix_fmt = IPU_PIX_FMT_RGB24,                                                   |
    .default_bpp = 24,                                                                        |
#else                                                                                         |
    .interface_pix_fmt = IPU_PIX_FMT_RGB666,                                                  |
    .default_bpp = 16,                                                                        |
#endif                                                                                        |
    .mode_str = "LDB-XGA",                                                                    |
    .int_clk = false,                                                                         |
    .late_init = false,                                                                       |
    },                                                                                        |
};                                                                                            |
                                                                                              |
mm/memblock.c                                                                                 |
long __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)                     |
{                                                                                             |
    struct memblock_type *_rgn = &memblock.reserved;                                          |
                                                                                              |
    BUG_ON(0 == size);                                                                        |
                                                                                              |
    return memblock_add_region(_rgn, base, size);                                             |
}                                                                                             |
                                                                                              |
long __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size)                      |
{                                                                                             |
    return __memblock_remove(&memblock.memory, base, size);                                   |
}                                                                                             |
                                                                                              |
phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align)                        |
{                                                                                             |
    return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);                       |
}                                                                                             |
                                                                                              |
static void __init mx6_sabresd_board_init(void)                              <----------------+
{
    .....
    imx6q_add_ipuv3(0, &ipu_data[0]);
    if (cpu_is_mx6q()) {
        imx6q_add_ipuv3(1, &ipu_data[1]);                                              <------+
        for (i = 0; i < 4 && i < ARRAY_SIZE(sabresd_fb_data); i++)                            |
            imx6q_add_ipuv3fb(i, &sabresd_fb_data[i]);                                        |
    } else                                                                                    |
        for (i = 0; i < 2 && i < ARRAY_SIZE(sabresd_fb_data); i++)                            |
            imx6q_add_ipuv3fb(i, &sabresd_fb_data[i]);                                        |
    ......                                                                                    |
}                                                                                             |
                                                                                              |
extern const struct imx_ipuv3_data imx6q_ipuv3_data[] __initconst;                     <------+
#define imx6q_add_ipuv3(id, pdata)    imx_add_ipuv3(id, &imx6q_ipuv3_data[id], pdata)         |
#define imx6q_add_ipuv3fb(id, pdata)    imx_add_ipuv3_fb(id, pdata)                           |
                                                                                              |
struct platform_device *__init imx_add_ipuv3_fb(                                <-------------+
        const int id,
        const struct ipuv3_fb_platform_data *pdata)
{
    if (pdata->res_size[0] > 0) {
        struct resource res[] = {
            {
                .start = pdata->res_base[0],
                .end = pdata->res_base[0] + pdata->res_size[0] - 1,
                .flags = IORESOURCE_MEM,
            }, {
                .start = 0,
                .end = 0,
                .flags = IORESOURCE_MEM,
            },
        };

        if (pdata->res_size[1] > 0) {
            res[1].start = pdata->res_base[1];
            res[1].end = pdata->res_base[1] +
                    pdata->res_size[1] - 1;
        }

        return imx_add_platform_device_dmamask("mxc_sdc_fb",
                id, res, ARRAY_SIZE(res), pdata,
                sizeof(*pdata), DMA_BIT_MASK(32));
    } else
        return imx_add_platform_device_dmamask("mxc_sdc_fb", id,
                NULL, 0, pdata, sizeof(*pdata),
                DMA_BIT_MASK(32));
}

驱动注册

kernel/video/mxc/mxc_ipuv3_fb.c

int __init mxcfb_init(void)
{
    return platform_driver_register(&mxcfb_driver);       ------+
}                                                               |
                                                                |
static struct platform_driver mxcfb_driver = {             <----+
    .driver = {
           .name = MXCFB_NAME,
           },
    .probe = mxcfb_probe,             -------------------+
    .remove = mxcfb_remove,                              |
    .suspend = mxcfb_suspend,                            |
    .resume = mxcfb_resume,                              |
};                                                       |
#define MXCFB_NAME      "mxc_sdc_fb"                     |
                                                         |
                                                         |
static int mxcfb_probe(struct platform_device *pdev)  <--+
{
    struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
    struct fb_info *fbi;
    struct mxcfb_info *mxcfbi;
    struct resource *res;
    struct device *disp_dev;
    char buf[32];
    int ret = 0;
                                             +--------------------------------+
    /* Initialize FB structures */           |                                |
    fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);                          |
    if (!fbi) {                                                               |
        ret = -ENOMEM;                                                        |
        goto init_fbinfo_failed;                                              |
    }                                                                         |
                                                                              |
    ret = mxcfb_option_setup(pdev, fbi);               ----------------------------+
    if (ret)                                                                  |    |
        goto get_fb_option_failed;                                            |    |
                                                                              |    |
    mxcfbi = (struct mxcfb_info *)fbi->par;                                   |    |
    spin_lock_init(&mxcfbi->lock);                                            |    |
    mxcfbi->fbi = fbi;                                                        |    |
    mxcfbi->ipu_int_clk = plat_data->int_clk;                                 |    |
    mxcfbi->late_init = plat_data->late_init;                                 |    |
    mxcfbi->first_set_par = true;                                             |    |
    mxcfbi->panel_width_mm = plat_data->panel_width_mm;                       |    |
    mxcfbi->panel_height_mm = plat_data->panel_height_mm;                     |    |
                                                                              |    |
    ret = mxcfb_dispdrv_init(pdev, fbi);             -----------------------------------+
    if (ret < 0)                                                              |    |    |
        goto init_dispdrv_failed;                                             |    |    |
                                                                              |    |    |
    ret = ipu_test_set_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);       -------------------------+
    if (ret < 0) {                                                            |    |    |    |
        dev_err(&pdev->dev, "ipu%d-di%d already in use
",                    |    |    |    |
                mxcfbi->ipu_id, mxcfbi->ipu_di);                              |    |    |    |
        goto ipu_in_busy;                                                     |    |    |    |
    }                                                                         |    |    |    |
                                                                              |    |    |    |
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                     |    |    |    |
    if (res && res->start && res->end) {                                      |    |    |    |
        fbi->fix.smem_len = res->end - res->start + 1;                        |    |    |    |
        fbi->fix.smem_start = res->start;                                     |    |    |    |
        fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len);   |    |    |    |
        /* 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);                                |    |    |    |
    if (IS_ERR(mxcfbi->ipu)) {                                                |    |    |    |
        ret = -ENODEV;                                                        |    |    |    |
        goto get_ipu_failed;                                                  |    |    |    |
    }                                                                         |    |    |    |
                                                                              |    |    |    |
    /* 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_vsync_pre_irq = mxcfbi->ipu_di ?                          |    |    |    |
                        IPU_IRQ_VSYNC_PRE_1 :                                 |    |    |    |
                        IPU_IRQ_VSYNC_PRE_0;                                  |    |    |    |
        mxcfbi->ipu_ch = MEM_BG_SYNC;                                         |    |    |    |
        /* Unblank the primary fb only by default */                          |    |    |    |
        if (pdev->id == 0)                                                    |    |    |    |
            mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;        |    |    |    |
        else                                                                  |    |    |    |
            mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;      |    |    |    |
                                                                              |    |    |    |
        ret = mxcfb_register(fbi);                 ------------------------------------------------+
        if (ret < 0)                                                          |    |    |    |     |
            goto mxcfb_register_failed;                                       |    |    |    |     |
                                                                              |    |    |    |     |
        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);                            |    |    |    |     |
                                                                              |    |    |    |     |
        if (ret < 0) {                                                        |    |    |    |     |
            mxcfb_unregister(fbi);                                            |    |    |    |     |
            goto mxcfb_setupoverlay_failed;                                   |    |    |    |     |
        }                                                                     |    |    |    |     |
                                                                              |    |    |    |     |
        g_dp_in_use[mxcfbi->ipu_id] = true;                                   |    |    |    |     |
                                                                              |    |    |    |     |
        ret = device_create_file(mxcfbi->ovfbi->dev,                          |    |    |    |     |
                     &dev_attr_fsl_disp_property);                            |    |    |    |     |
        if (ret)                                                              |    |    |    |     |
            dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "               |    |    |    |     |
                            "file for disp property
",                       |    |    |    |     |
                            ret);                                             |    |    |    |     |
                                                                              |    |    |    |     |
        ret = device_create_file(mxcfbi->ovfbi->dev,                          |    |    |    |     |
                     &dev_attr_fsl_disp_dev_property);                        |    |    |    |     |
        if (ret)                                                              |    |    |    |     |
            dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "               |    |    |    |     |
                            "file for disp device "                           |    |    |    |     |
                            "propety
", ret);                                |    |    |    |     |
    } else {                                                                  |    |    |    |     |
        mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;                             |    |    |    |     |
        mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK;                        |    |    |    |     |
        mxcfbi->ipu_alp_ch_irq = -1;                                          |    |    |    |     |
        mxcfbi->ipu_vsync_pre_irq = mxcfbi->ipu_di ?                          |    |    |    |     |
                        IPU_IRQ_VSYNC_PRE_1 :                                 |    |    |    |     |
                        IPU_IRQ_VSYNC_PRE_0;                                  |    |    |    |     |
        mxcfbi->ipu_ch = MEM_DC_SYNC;                                         |    |    |    |     |
        mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;          |    |    |    |     |
                                                                              |    |    |    |     |
        ret = mxcfb_register(fbi);                                            |    |    |    |     |
        if (ret < 0)                                                          |    |    |    |     |
            goto mxcfb_register_failed;                                       |    |    |    |     |
    }                                                                         |    |    |    |     |
                                                                              |    |    |    |     |
    platform_set_drvdata(pdev, fbi);                                          |    |    |    |     |
                                                                              |    |    |    |     |
    ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_property);          |    |    |    |     |
    if (ret)                                                                  |    |    |    |     |
        dev_err(&pdev->dev, "Error %d on creating file for disp "             |    |    |    |     |
                    "property
", ret);                                       |    |    |    |     |
                                                                              |    |    |    |     |
    ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_dev_property);      |    |    |    |     |
    if (ret)                                                                  |    |    |    |     |
        dev_err(&pdev->dev, "Error %d on creating file for disp "             |    |    |    |     |
                    " device propety
", ret);                                |    |    |    |     |
                                                                              |    |    |    |     |
    disp_dev = mxc_dispdrv_getdev(mxcfbi->dispdrv);                           |    |    |    |     |
    if (disp_dev) {                                                           |    |    |    |     |
        ret = sysfs_create_link(&fbi->dev->kobj,                              |    |    |    |     |
                &disp_dev->kobj, "disp_dev");                                 |    |    |    |     |
        if (ret)                                                              |    |    |    |     |
            dev_err(&pdev->dev,                                               |    |    |    |     |
                "Error %d on creating file
", ret);                          |    |    |    |     |
    }                                                                         |    |    |    |     |
                                                                              |    |    |    |     |
    INIT_WORK(&mxcfbi->vsync_pre_work, mxcfb_vsync_pre_work);                 |    |    |    |     |
                                                                              |    |    |    |     |
    snprintf(buf, sizeof(buf), "mxcfb%d-vsync-pre", fbi->node);               |    |    |    |     |
    mxcfbi->vsync_pre_queue = create_singlethread_workqueue(buf);             |    |    |    |     |
    if (mxcfbi->vsync_pre_queue == NULL) {                                    |    |    |    |     |
        dev_err(fbi->device,                                                  |    |    |    |     |
            "Failed to alloc vsync-pre workqueue
");                         |    |    |    |     |
        ret = -ENOMEM;                                                        |    |    |    |     |
        goto workqueue_alloc_failed;                                          |    |    |    |     |
    }                                                                         |    |    |    |     |
                                                                              |    |    |    |     |
#ifdef CONFIG_HAS_EARLYSUSPEND                                                |    |    |    |     |
    mxcfbi->fbdrv_earlysuspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;        |    |    |    |     |
    mxcfbi->fbdrv_earlysuspend.suspend = mxcfb_early_suspend;                 |    |    |    |     |
    mxcfbi->fbdrv_earlysuspend.resume = mxcfb_later_resume;                   |    |    |    |     |
    mxcfbi->fbdrv_earlysuspend.data = pdev;                                   |    |    |    |     |
    register_early_suspend(&mxcfbi->fbdrv_earlysuspend);                      |    |    |    |     |
#endif                                                                        |    |    |    |     |
                                                                              |    |    |    |     |
#ifdef CONFIG_LOGO                                                            |    |    |    |     |
    fb_prepare_logo(fbi, 0);                                                  |    |    |    |     |
    fb_show_logo(fbi, 0);                                                     |    |    |    |     |
#endif                                                                        |    |    |    |     |
    /*                                                                        |    |    |    |     |
     * Disable HannStar touch panel CABC function,                            |    |    |    |     |
     * this function turns the panel's backlight automatically                |    |    |    |     |
     * according to the content shown on the panel which                      |    |    |    |     |
     * may cause annoying unstable backlight issue.                           |    |    |    |     |
     *                                                                        |    |    |    |     |
     * zengjf 2015-10-8 this also has down in uboot                           |    |    |    |     |
     */                                                                       |    |    |    |     |
    /*                                                                        |    |    |    |     |
    gpio_request(SABRESD_CABC_EN1, "cabc-en1");                               |    |    |    |     |
    gpio_direction_output(SABRESD_CABC_EN1, 1);                               |    |    |    |     |
    gpio_request(SABRESD_CABC_EN0, "cabc-en0");                               |    |    |    |     |
    gpio_direction_output(SABRESD_CABC_EN0, 1);                               |    |    |    |     |
    */                                                                        |    |    |    |     |
                                                                              |    |    |    |     |
    return 0;                                                                 |    |    |    |     |
                                                                              |    |    |    |     |
workqueue_alloc_failed:                                                       |    |    |    |     |
mxcfb_setupoverlay_failed:                                                    |    |    |    |     |
mxcfb_register_failed:                                                        |    |    |    |     |
get_ipu_failed:                                                               |    |    |    |     |
    ipu_clear_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);                          |    |    |    |     |
ipu_in_busy:                                                                  |    |    |    |     |
init_dispdrv_failed:                                                          |    |    |    |     |
    fb_dealloc_cmap(&fbi->cmap);                                              |    |    |    |     |
    framebuffer_release(fbi);                                                 |    |    |    |     |
get_fb_option_failed:                                                         |    |    |    |     |
init_fbinfo_failed:                                                           |    |    |    |     |
    return ret;                                                               |    |    |    |     |
}                                                                             |    |    |    |     |
                                                                              |    |    |    |     |
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,                                                       |    |    |     |
};                                                                                 |    |    |     |
                                                                                   |    |    |     |
                                                                                   |    |    |     |
/*                                                                                 |    |    |     |
 * Parse user specified options (`video=trident:')                                 |    |    |     |
 * example:                                                                        |    |    |     |
 *     video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,bpp=16,noaccel                |    |    |     |
 *    video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,fbpix=RGB565                   |    |    |     |
 */                                                                                |    |    |     |
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)) {                              -------------+   |    |     |
        dev_err(&pdev->dev, "Can't get fb option for %s!
", name);                 |   |    |     |
        return -ENODEV;                                                             |   |    |     |
    }                                                                               |   |    |     |
                                                                                    |   |    |     |
    if (!options || !*options)                                                      |   |    |     |
        return 0;                                                                   |   |    |     |
                                                                                    |   |    |     |
    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] = '';                                |   |    |     |
        } 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, "BGR24", 5))                                   |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_BGR24;                       |   |    |     |
            else if (!strncmp(opt+3, "GBR24", 5))                                   |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_GBR24;                       |   |    |     |
            else if (!strncmp(opt+3, "RGB565", 6))                                  |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_RGB565;                      |   |    |     |
            else if (!strncmp(opt+3, "RGB666", 6))                                  |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_RGB666;                      |   |    |     |
            else if (!strncmp(opt+3, "YUV444", 6))                                  |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_YUV444;                      |   |    |     |
            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+3, "UYVY16", 6))                                  |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_UYVY;                        |   |    |     |
            else if (!strncmp(opt+3, "YVYU16", 6))                                  |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_YVYU;                        |   |    |     |
            else if (!strncmp(opt+3, "VYUY16", 6))                                  |   |    |     |
                pdata->interface_pix_fmt = IPU_PIX_FMT_VYUY;                        |   |    |     |
        } else if (!strncmp(opt, "fbpix=", 6)) {                                    |   |    |     |
            if (!strncmp(opt+6, "RGB24", 5))                                        |   |    |     |
                fb_pix_fmt = IPU_PIX_FMT_RGB24;                                     |   |    |     |
            else if (!strncmp(opt+6, "BGR24", 5))                                   |   |    |     |
                fb_pix_fmt = IPU_PIX_FMT_BGR24;                                     |   |    |     |
            else if (!strncmp(opt+6, "RGB32", 5))                                   |   |    |     |
                fb_pix_fmt = IPU_PIX_FMT_RGB32;                                     |   |    |     |
            else if (!strncmp(opt+6, "BGR32", 5))                                   |   |    |     |
                fb_pix_fmt = IPU_PIX_FMT_BGR32;                                     |   |    |     |
            else if (!strncmp(opt+6, "ABGR32", 6))                                  |   |    |     |
                fb_pix_fmt = IPU_PIX_FMT_ABGR32;                                    |   |    |     |
            else if (!strncmp(opt+6, "RGB565", 6))                                  |   |    |     |
                fb_pix_fmt = IPU_PIX_FMT_RGB565;                                    |   |    |     |
                                                                                    |   |    |     |
            if (fb_pix_fmt) {                                                       |   |    |     |
                pixfmt_to_var(fb_pix_fmt, &fbi->var);                               |   |    |     |
                pdata->default_bpp =                                                |   |    |     |
                    fbi->var.bits_per_pixel;                                        |   |    |     |
            }                                                                       |   |    |     |
        } else if (!strncmp(opt, "int_clk", 7)) {                                   |   |    |     |
            pdata->int_clk = true;                                                  |   |    |     |
            continue;                                                               |   |    |     |
        } else if (!strncmp(opt, "bpp=", 4)) {                                      |   |    |     |
            /* bpp setting cannot overwirte fbpix setting */                        |   |    |     |
            if (fb_pix_fmt)                                                         |   |    |     |
                continue;                                                           |   |    |     |
                                                                                    |   |    |     |
            pdata->default_bpp =                                                    |   |    |     |
                simple_strtoul(opt + 4, NULL, 0);                                   |   |    |     |
                                                                                    |   |    |     |
            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;                                                                       |   |    |     |
}                                                                                   |   |    |     |
                                                                                    |   |    |     |
drivers/video/fbmem.c                                                               |   |    |     |
/**                                                                                 |   |    |     |
 * fb_get_options - get kernel boot parameters                                      |   |    |     |
 * @name:   framebuffer name as it would appear in                                  |   |    |     |
 *          the boot parameter line                                                 |   |    |     |
 *          (video=<name>:<options>)                                                |   |    |     |
 * @option: the option will be stored here                                          |   |    |     |
 *                                                                                  |   |    |     |
 * NOTE: Needed to maintain backwards compatibility                                 |   |    |     |
 */                                                                                 |   |    |     |
//                                                                                  |   |    |     |
int fb_get_options(char *name, char **option)                              <--------+   |    |     |
{                                                                                       |    |     |
    char *opt, *options = NULL;                                                         |    |     |
    int retval = 0;                                                                     |    |     |
    int name_len = strlen(name), i;                                                     |    |     |
                                                                                        |    |     |
    if (name_len && ofonly && strncmp(name, "offb", 4))                                 |    |     |
        retval = 1;                                                                     |    |     |
                                                                                        |    |     |
    if (name_len && !retval) {                                                          |    |     |
        for (i = 0; i < FB_MAX; i++) {                                                  |    |     |
            if (video_options[i] == NULL)                                               |    |     |
                continue;                                                               |    |     |
            if (!video_options[i][0])                                                   |    |     |
                continue;                                                               |    |     |
            opt = video_options[i];                                                     |    |     |
            if (!strncmp(name, opt, name_len) &&                                        |    |     |
                opt[name_len] == ':')                                                   |    |     |
                options = opt + name_len + 1;                                           |    |     |
        }                                                                               |    |     |
    }                                                                                   |    |     |
    if (options && !strncmp(options, "off", 3))                                         |    |     |
        retval = 1;                                                                     |    |     |
                                                                                        |    |     |
    if (option)                                                                         |    |     |
        *option = options;                                                              |    |     |
                                                                                        |    |     |
    return retval;                                                                      |    |     |
}                                                                                       |    |     |
                                                                                        |    |     |
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;                                                                             |     |
                                                                                             |     |
    setting.if_fmt = plat_data->interface_pix_fmt;                                           |     |
    setting.dft_mode_str = plat_data->mode_str;                                              |     |
    setting.default_bpp = plat_data->default_bpp;                                            |     |
    if (!setting.default_bpp)                                                                |     |
        setting.default_bpp = 16;                                                            |     |
    setting.fbi = fbi;                                                                       |     |
    if (!strlen(plat_data->disp_dev)) {                                                      |     |
        memcpy(disp_dev, default_dev, strlen(default_dev));                                  |     |
        disp_dev[strlen(default_dev)] = '';                                                |     |
    } else {                                                                                 |     |
        memcpy(disp_dev, plat_data->disp_dev,                                                |     |
                strlen(plat_data->disp_dev));                                                |     |
        disp_dev[strlen(plat_data->disp_dev)] = '';                                        |     |
    }                                                                                        |     |
                                                                                             |     |
    dev_info(&pdev->dev, "register mxc display driver %s
", disp_dev);                      |     |
                                                                                             |     |
    mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting);                             |     |
    if (IS_ERR(mxcfbi->dispdrv)) {                                                           |     |
        ret = PTR_ERR(mxcfbi->dispdrv);                                                      |     |
        dev_err(&pdev->dev, "NO mxc display driver found!
");                               |     |
        return ret;                                                                          |     |
    } else {                                                                                 |     |
        /* fix-up  */                                                                        |     |
        mxcfbi->ipu_di_pix_fmt = setting.if_fmt;                                             |     |
        mxcfbi->default_bpp = setting.default_bpp;                                           |     |
                                                                                             |     |
        /* setting */                                                                        |     |
        mxcfbi->ipu_id = setting.dev_id;                                                     |     |
        mxcfbi->ipu_di = setting.disp_id;                                                    |     |
    }                                                                                        |     |
                                                                                             |     |
    return ret;                                                                              |     |
}                                                                                            |     |
                                                                                             |     |
static bool ipu_usage[2][2];                                                                 |     |
static int ipu_test_set_usage(int ipu, int di)                    <--------------------------+     |
{                                                                                                  |
    if (ipu_usage[ipu][di])                                                                        |
        return -EBUSY;                                                                             |
    else                                                                                           |
        ipu_usage[ipu][di] = true;                                                                 |
    return 0;                                                                                      |
}                                                                                                  |
                                                                                                   |
static int mxcfb_register(struct fb_info *fbi)                       <-----------------------------+
{
    struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
    struct fb_videomode m;
    int ret = 0;
    char bg0_id[] = "DISP3 BG";
    char bg1_id[] = "DISP3 BG - DI1";
    char fg_id[] = "DISP3 FG";

    if (mxcfbi->ipu_di == 0) {
        bg0_id[4] += mxcfbi->ipu_id;
        strcpy(fbi->fix.id, bg0_id);
    } else if (mxcfbi->ipu_di == 1) {
        bg1_id[4] += mxcfbi->ipu_id;
        strcpy(fbi->fix.id, bg1_id);
    } else { /* Overlay */
        fg_id[4] += mxcfbi->ipu_id;
        strcpy(fbi->fix.id, fg_id);
    }

    mxcfb_check_var(&fbi->var, fbi);

    mxcfb_set_fix(fbi);

    /* Added first mode to fbi modelist. */
    if (!fbi->modelist.next || !fbi->modelist.prev)
        INIT_LIST_HEAD(&fbi->modelist);
    fb_var_to_videomode(&m, &fbi->var);
    fb_add_videomode(&m, &fbi->modelist);

    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.
");
        ret = -EBUSY;
        goto err0;
    }
    ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq);
    if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq,
        mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT, MXCFB_NAME, fbi) != 0) {
        dev_err(fbi->device, "Error registering NFACK irq handler.
");
        ret = -EBUSY;
        goto err1;
    }
    ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq);

    if (mxcfbi->ipu_vsync_pre_irq != -1) {
        if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_vsync_pre_irq,
                    mxcfb_vsync_pre_irq_handler, 0,
                    MXCFB_NAME, fbi) != 0) {
            dev_err(fbi->device, "Error registering VSYNC irq "
                         "handler.
");
            ret = -EBUSY;
            goto err2;
        }
        ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_vsync_pre_irq);
    }

    if (mxcfbi->ipu_alp_ch_irq != -1)
        if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq,
                mxcfb_alpha_irq_handler, IPU_IRQF_ONESHOT,
                    MXCFB_NAME, fbi) != 0) {
            dev_err(fbi->device, "Error registering alpha irq "
                    "handler.
");
            ret = -EBUSY;
            goto err3;
        }

    if (!mxcfbi->late_init) {
        fbi->var.activate |= FB_ACTIVATE_FORCE;
        console_lock();
        fbi->flags |= FBINFO_MISC_USEREVENT;
        ret = fb_set_var(fbi, &fbi->var);
        fbi->flags &= ~FBINFO_MISC_USEREVENT;
        console_unlock();
        if (ret < 0) {
            dev_err(fbi->device, "Error fb_set_var ret:%d
", ret);
            goto err4;
        }

        if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
            console_lock();
            ret = fb_blank(fbi, FB_BLANK_UNBLANK);
            console_unlock();
            if (ret < 0) {
                dev_err(fbi->device,
                    "Error fb_blank ret:%d
", ret);
                goto err5;
            }
        }
    } else {
        /*
         * Setup the channel again though bootloader
         * has done this, then set_par() can stop the
         * channel neatly and re-initialize it .
         */
        if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
            console_lock();
            _setup_disp_channel1(fbi);
            ipu_enable_channel(mxcfbi->ipu, mxcfbi->ipu_ch);
            console_unlock();
        }
    }

    ret = register_framebuffer(fbi);                             -----------------+
    if (ret < 0)                                                                  |
        goto err6;                                                                |
                                                                                  |
    return ret;                                                                   |
err6:                                                                             |
    if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {                                 |
        console_lock();                                                           |
        if (!mxcfbi->late_init)                                                   |
            fb_blank(fbi, FB_BLANK_POWERDOWN);                                    |
        else {                                                                    |
            ipu_disable_channel(mxcfbi->ipu, mxcfbi->ipu_ch,                      |
                        true);                                                    |
            ipu_uninit_channel(mxcfbi->ipu, mxcfbi->ipu_ch);                      |
        }                                                                         |
        console_unlock();                                                         |
    }                                                                             |
err5:                                                                             |
err4:                                                                             |
    if (mxcfbi->ipu_alp_ch_irq != -1)                                             |
        ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);                   |
err3:                                                                             |
    if (mxcfbi->ipu_vsync_pre_irq != -1)                                          |
        ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_vsync_pre_irq, fbi);                |
err2:                                                                             |
    ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);                        |
err1:                                                                             |
    ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);                           |
err0:                                                                             |
    return ret;                                                                   |
}                                                                                 |
                                                                                  |
drivers/video/fbmem.c                                                             |
register_framebuffer(struct fb_info *fb_info)                       <-------------+
{
    int ret;

    mutex_lock(&registration_lock);
    ret = do_register_framebuffer(fb_info);                         -----+
    mutex_unlock(&registration_lock);                                    |
                                                                         |
    return ret;                                                          |
}                                                                        |
                                                                         |
static int do_register_framebuffer(struct fb_info *fb_info)         <----+
{
    int i;
    struct fb_event event;
    struct fb_videomode mode;

    if (fb_check_foreignness(fb_info))
        return -ENOSYS;

    do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
                     fb_is_primary_device(fb_info));

    if (num_registered_fb == FB_MAX)
        return -ENXIO;

    num_registered_fb++;
    for (i = 0 ; i < FB_MAX; i++)
        if (!registered_fb[i])
            break;
    fb_info->node = i;
    atomic_set(&fb_info->count, 1);
    mutex_init(&fb_info->lock);
    mutex_init(&fb_info->mm_lock);

    fb_info->dev = device_create(fb_class, fb_info->device,
                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);                  --------------------------->> // 设备节点创建  /dev/fb0
    if (IS_ERR(fb_info->dev)) {
        /* Not fatal */
        printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld
", i, PTR_ERR(fb_info->dev));
        fb_info->dev = NULL;
    } else
        fb_init_device(fb_info);

    if (fb_info->pixmap.addr == NULL) {
        fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
        if (fb_info->pixmap.addr) {
            fb_info->pixmap.size = FBPIXMAPSIZE;
            fb_info->pixmap.buf_align = 1;
            fb_info->pixmap.scan_align = 1;
            fb_info->pixmap.access_align = 32;
            fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
        }
    }
    fb_info->pixmap.offset = 0;

    if (!fb_info->pixmap.blit_x)
        fb_info->pixmap.blit_x = ~(u32)0;

    if (!fb_info->pixmap.blit_y)
        fb_info->pixmap.blit_y = ~(u32)0;

    if (!fb_info->modelist.prev || !fb_info->modelist.next)
        INIT_LIST_HEAD(&fb_info->modelist);

    fb_var_to_videomode(&mode, &fb_info->var);
    fb_add_videomode(&mode, &fb_info->modelist);
    registered_fb[i] = fb_info;

    event.info = fb_info;
    if (!lock_fb_info(fb_info))
        return -ENODEV;
    fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    unlock_fb_info(fb_info);
    return 0;
}
原文地址:https://www.cnblogs.com/helloworldtoyou/p/5824797.html