• imx6 lvds 代码分析


    查看imx6 kernel中lvds设备和驱动的初始化过程。

    相关文档:
      arm/arm/mach-mx6/board-mx6q_sabresd.c

      kernel/drivers/video/mxc/ldb.c

    设备初始化

    arm/arm/mach-mx6/board-mx6q_sabresd.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,
    	.map_io = mx6_map_io,
    	.init_irq = mx6_init_irq,
    	.init_machine = mx6_sabresd_board_init,    ----------+
    	.timer = &mx6_sabresd_timer,                         |
    	.reserve = mx6q_sabresd_reserve,                     |
    MACHINE_END                                              |
                                                             |
    // board-mx6q_sabresd.c                                  |
    static void __init mx6_sabresd_board_init(void)      <---+
    {                            ------------------------------+
        ......                  /                              |
        imx6q_add_ipuv3(0, &ipu_data[0]);          ---+        |   // ipu
                                                      |        |
        imx6q_add_ldb(&ldb_data);            ----------------------------+   //ldb
        ......                                        |        |         |
    }                                                 |        |         |
                                                      V        |         |
    #define imx6q_add_ipuv3(id, pdata)                        |         |
        imx_add_ipuv3(id, &imx6q_ipuv3_data[id], pdata)        |         |
                                                               |         |
    struct platform_device *__init imx_add_ipuv3(              |         |
            const int id,                                      |         |
            const struct imx_ipuv3_data *data,                 |         |
            struct imx_ipuv3_platform_data *pdata)             |         |
    {                                                          |         |
        struct resource res[] = {                              |         |
            {                                                  |         |
                .start = data->iobase,                         |         |
                .end = data->iobase + data->iosize - 1,        |         |
                .flags = IORESOURCE_MEM,                       |         |
            }, {                                               |         |
                .start = data->irq_err,                        |         |
                .end = data->irq_err,                          |         |
                .flags = IORESOURCE_IRQ,                       |         |
            }, {                                               |         |
                .start = data->irq,                            |         |
                .end = data->irq,                              |         |
                .flags = IORESOURCE_IRQ,                       |         |
            },                                                 |         |
        };                                                     |         |
                                                               |         |
        pdata->init = data->init;                              |         |
        pdata->pg = data->pg;                                  |         |
                                                               |         |
        return imx_add_platform_device_dmamask("imx-ipuv3", id,|         |
                res, ARRAY_SIZE(res), pdata, sizeof(*pdata),   |         |
                DMA_BIT_MASK(32));                             |         |
    }                                                          |         |
                                                               |         |
    static struct imx_ipuv3_platform_data ipu_data[] = {    <--+         |
        {                                                                |
        .rev = 4,                                                        |
        .csi_clk[0] = "clko_clk",                                        |
        .bypass_reset = false,                                           |
        }, {                                                             |
        .rev = 4,                                                        |
        .csi_clk[0] = "clko_clk",                                        |
        .bypass_reset = false,                                           |
        },                                                               |
    };                                                                   |
                                                                         |
    #define imx6q_add_ldb(pdata)                          <-------------+
        imx_add_ldb(&imx6q_ldb_data, pdata);                             |
                                                                         |
    struct platform_device *__init imx_add_ldb(                          |
            const struct imx_ldb_data *data,                             |
            struct fsl_mxc_ldb_platform_data *pdata)                     |
    {                                                                    |
        struct resource res[] = {                                        |
            {                                                            |
                .start = data->iobase,                                   |
                .end = data->iobase + data->iosize - 1,                  |
                .flags = IORESOURCE_MEM,                                 |
            },                                                           |
        };                                                               |
                                                                         |
        return imx_add_platform_device("mxc_ldb", -1,                    |
                res, ARRAY_SIZE(res), pdata, sizeof(*pdata));            |
    }                                                                    |
                                                                         |
    static struct fsl_mxc_ldb_platform_data ldb_data = {          <------+
        .ipu_id = 0,
        .disp_id = 1,
        .ext_ref = 1,
        .mode = LDB_SEP1,
        .sec_ipu_id = 0,
        .sec_disp_id = 0,
    };
    

    驱动初始化

    kernel/drivers/video/mxc/ldb.c
    
    static int __init ldb_init(void)
    {
        return platform_driver_register(&mxcldb_driver);      -----+
    }                                                              |
                                                                   |
    static struct platform_driver mxcldb_driver = {            <---+
        .driver = {
               .name = "mxc_ldb",
               },
        .probe = ldb_probe,                              -----+
        .remove = ldb_remove,                                 |
        .suspend = ldb_suspend,                               |
        .resume = ldb_resume,                                 |
    };                                                        |
                                                              |
    static int ldb_probe(struct platform_device *pdev)    <---+
    {
        int ret = 0;
        struct ldb_data *ldb;
    
        ldb = kzalloc(sizeof(struct ldb_data), GFP_KERNEL);
        if (!ldb) {
            ret = -ENOMEM;
            goto alloc_failed;
        }
                                                  +------------------+
        ldb->pdev = pdev;                         |                  |
        ldb->disp_ldb = mxc_dispdrv_register(&ldb_drv);              |
        mxc_dispdrv_setdata(ldb->disp_ldb, ldb);                     |
                                                                     |
        dev_set_drvdata(&pdev->dev, ldb);                            |
                                                                     |
        /*                                                           |
         * 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.              |
         *                                                           |
         */                                                          |
                                                                     |
    alloc_failed:                                                    |
        return ret;                                                  |
    }                                                                |
                                                                     |
                                                                     |
    static struct mxc_dispdrv_driver ldb_drv = {             <-------+
        .name     = DISPDRV_LDB,
        .init     = ldb_disp_init,           ----------------------+
        .deinit    = ldb_disp_deinit,                              |
        .setup = ldb_disp_setup,             ----------------------------------------------+
    };                                                             |                       |
                                                                   |                       |
    static int ldb_disp_init(struct mxc_dispdrv_handle *disp,   <--+                       |
        struct mxc_dispdrv_setting *setting)                                               |
    {                                                                                      |
        int ret = 0, i;                                                                    |
        struct ldb_data *ldb = mxc_dispdrv_getdata(disp);                                  |
        struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data;        |
        struct i2c_client *i2c_dev;                                                        |
        struct resource *res;                                                              |
        uint32_t base_addr;                                                                |
        uint32_t reg, setting_idx;                                                         |
        uint32_t ch_mask = 0, ch_val = 0;                                                  |
        int lvds_channel = 0;                                                              |
        uint32_t ipu_id, disp_id;                                                          |
                                                                                           |
        /* if input format not valid, make RGB666 as default*/                             |
        if (!valid_mode(setting->if_fmt)) {                                                |
            dev_warn(&ldb->pdev->dev, "Input pixel format not valid"                       |
                        " use default RGB666
    ");                                          |
            setting->if_fmt = IPU_PIX_FMT_RGB666;                                          |
        }                                                                                  |
        //初始化                                                                           |
        if (!ldb->inited) {                                                                |
            char di_clk[] = "ipu1_di0_clk";                                                |
            char ldb_clk[] = "ldb_di0_clk";                                                |
                                                                                           |
            setting_idx = 0;                                                               |
            res = platform_get_resource(ldb->pdev, IORESOURCE_MEM, 0);                     |
            if (IS_ERR(res))                                                               |
                return -ENOMEM;                                                            |
                                                                                           |
            base_addr = res->start;                                                        |
            ldb->reg = ioremap(base_addr, res->end - res->start + 1);                      |
            ldb->control_reg = ldb->reg + 2;                                               |
            ldb->gpr3_reg = ldb->reg + 3;                                                  |
                                                                                           |
            ldb->lvds_bg_reg = regulator_get(&ldb->pdev->dev, plat_data->lvds_bg_reg);     |
            if (!IS_ERR(ldb->lvds_bg_reg)) {                                               |
                regulator_set_voltage(ldb->lvds_bg_reg, 2500000, 2500000);                 |
                regulator_enable(ldb->lvds_bg_reg);                                        |
            }                                                                              |
                                                                                           |
            /* ipu selected by platform data setting */                                    |
            setting->dev_id = plat_data->ipu_id;                                           |
                                                                                           |
            reg = readl(ldb->control_reg);                                                 |
            //ldb参考电阻选择                                                              |
            /* refrence resistor select */                                                 |
            reg &= ~LDB_BGREF_RMODE_MASK;                                                  |
            if (plat_data->ext_ref)                                                        |
                reg |= LDB_BGREF_RMODE_EXT;                                                |
            else                                                                           |
                reg |= LDB_BGREF_RMODE_INT;                                                |
            //使用SPWG标准对数据进行映射                                                   |
            /* TODO: now only use SPWG data mapping for both channel */                    |
            reg &= ~(LDB_BIT_MAP_CH0_MASK | LDB_BIT_MAP_CH1_MASK);                         |
            reg |= LDB_BIT_MAP_CH0_SPWG | LDB_BIT_MAP_CH1_SPWG;                            |
                                                                                           |
            /* channel mode setting */                                                     |
            reg &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);                               |
            reg &= ~(LDB_DATA_WIDTH_CH0_MASK | LDB_DATA_WIDTH_CH1_MASK);                   |
            //指定数据宽度,24bit或者18bit                                                 |
            if (bits_per_pixel(setting->if_fmt) == 24)                                     |
                reg |= LDB_DATA_WIDTH_CH0_24 | LDB_DATA_WIDTH_CH1_24;                      |
            else                                                                           |
                reg |= LDB_DATA_WIDTH_CH0_18 | LDB_DATA_WIDTH_CH1_18;                      |
                                                                                           |
            if (g_ldb_mode)                                                                |
                ldb->mode = g_ldb_mode;                                                    |
            else                                                                           |
                ldb->mode = plat_data->mode;                                               |
            //single mode                                                                  |
            if ((ldb->mode == LDB_SIN0) || (ldb->mode == LDB_SIN1)) {                      |
                ret = ldb->mode - LDB_SIN0;                                                |
                if (plat_data->disp_id != ret) {                                           |
                    dev_warn(&ldb->pdev->dev,                                              |
                        "change IPU DI%d to IPU DI%d for LDB "                             |
                        "channel%d.
    ",                                                    |
                        plat_data->disp_id, ret, ret);                                     |
                    plat_data->disp_id = ret;                                              |
                }                                                                          |
            // separate mode                                                               |
            } else if (((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))                |
                    && (cpu_is_mx6q() || cpu_is_mx6dl())) {                                |
                if (plat_data->disp_id == plat_data->sec_disp_id) {                        |
                    dev_err(&ldb->pdev->dev,                                               |
                        "For LVDS separate mode,"                                          |
                        "two DIs should be different!
    ");                                 |
                    return -EINVAL;                                                        |
                }                                                                          |
                                                                                           |
                if (((!plat_data->disp_id) && (ldb->mode == LDB_SEP1))                     |
                    || ((plat_data->disp_id) &&                                            |
                        (ldb->mode == LDB_SEP0))) {                                        |
                    dev_dbg(&ldb->pdev->dev,                                               |
                        "LVDS separate mode:"                                              |
                        "swap DI configuration!
    ");                                       |
                    ipu_id = plat_data->ipu_id;                                            |
                    disp_id = plat_data->disp_id;                                          |
                    plat_data->ipu_id = plat_data->sec_ipu_id;                             |
                    plat_data->disp_id = plat_data->sec_disp_id;                           |
                    plat_data->sec_ipu_id = ipu_id;                                        |
                    plat_data->sec_disp_id = disp_id;                                      |
                }                                                                          |
            }                                                                              |
            // split mode                                                                  |
            if (ldb->mode == LDB_SPL_DI0) {                                                |
                reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI0                          |
                    | LDB_CH1_MODE_EN_TO_DI0;                                              |
                setting->disp_id = 0;                                                      |
            } else if (ldb->mode == LDB_SPL_DI1) {                                         |
                reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI1                          |
                    | LDB_CH1_MODE_EN_TO_DI1;                                              |
                setting->disp_id = 1;                                                      |
            } else if (ldb->mode == LDB_DUL_DI0) {                                         |
                reg &= ~LDB_SPLIT_MODE_EN;                                                 |
                reg |= LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0;                    |
                setting->disp_id = 0;                                                      |
            } else if (ldb->mode == LDB_DUL_DI1) {                                         |
                reg &= ~LDB_SPLIT_MODE_EN;                                                 |
                reg |= LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1;                    |
                setting->disp_id = 1;                                                      |
            } else if (ldb->mode == LDB_SIN0) {                                            |
                reg &= ~LDB_SPLIT_MODE_EN;                                                 |
                setting->disp_id = plat_data->disp_id;                                     |
                if (setting->disp_id == 0)                                                 |
                    reg |= LDB_CH0_MODE_EN_TO_DI0;                                         |
                else                                                                       |
                    reg |= LDB_CH0_MODE_EN_TO_DI1;                                         |
                ch_mask = LDB_CH0_MODE_MASK;                                               |
                ch_val = reg & LDB_CH0_MODE_MASK;                                          |
            } else if (ldb->mode == LDB_SIN1) {                                            |
                reg &= ~LDB_SPLIT_MODE_EN;                                                 |
                setting->disp_id = plat_data->disp_id;                                     |
                if (setting->disp_id == 0)                                                 |
                    reg |= LDB_CH1_MODE_EN_TO_DI0;                                         |
                else                                                                       |
                    reg |= LDB_CH1_MODE_EN_TO_DI1;                                         |
                ch_mask = LDB_CH1_MODE_MASK;                                               |
                ch_val = reg & LDB_CH1_MODE_MASK;                                          |
            } else { /* separate mode*/                                                    |
            // dual mode                                                                   |
                setting->disp_id = plat_data->disp_id;                                     |
                                                                                           |
                /* first output is LVDS0 or LVDS1 */                                       |
                if (ldb->mode == LDB_SEP0)                                                 |
                    lvds_channel = 0;                                                      |
                else                                                                       |
                    lvds_channel = 1;                                                      |
                                                                                           |
                reg &= ~LDB_SPLIT_MODE_EN;                                                 |
                                                                                           |
                if ((lvds_channel == 0) && (setting->disp_id == 0))                        |
                    reg |= LDB_CH0_MODE_EN_TO_DI0;                                         |
                else if ((lvds_channel == 0) && (setting->disp_id == 1))                   |
                    reg |= LDB_CH0_MODE_EN_TO_DI1;                                         |
                else if ((lvds_channel == 1) && (setting->disp_id == 0))                   |
                    reg |= LDB_CH1_MODE_EN_TO_DI0;                                         |
                else                                                                       |
                    reg |= LDB_CH1_MODE_EN_TO_DI1;                                         |
                ch_mask = lvds_channel ? LDB_CH1_MODE_MASK :                               |
                        LDB_CH0_MODE_MASK;                                                 |
                ch_val = reg & ch_mask;                                                    |
                                                                                           |
                if (bits_per_pixel(setting->if_fmt) == 24) {                               |
                    if (lvds_channel == 0)                                                 |
                        reg &= ~LDB_DATA_WIDTH_CH1_24;                                     |
                    else                                                                   |
                        reg &= ~LDB_DATA_WIDTH_CH0_24;                                     |
                } else {                                                                   |
                    if (lvds_channel == 0)                                                 |
                        reg &= ~LDB_DATA_WIDTH_CH1_18;                                     |
                    else                                                                   |
                        reg &= ~LDB_DATA_WIDTH_CH0_18;                                     |
                }                                                                          |
            }                                                                              |
                                                                                           |
            writel(reg, ldb->control_reg);                                                 |
            if (ldb->mode <  LDB_SIN0) {                                                   |
                ch_mask = LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK;                           |
                ch_val = reg & (LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);                    |
            }                                                                              |
                                                                                           |
            /* clock setting */                                                            |
            if ((cpu_is_mx6q() || cpu_is_mx6dl()) &&                                       |
                ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1)))                      |
                ldb_clk[6] += lvds_channel;                                                |
            else                                                                           |
                ldb_clk[6] += setting->disp_id;                                            |
            ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev,                |
                                    ldb_clk);                                              |
            if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) {                            |
                dev_err(&ldb->pdev->dev, "get ldb clk0 failed
    ");                         |
                iounmap(ldb->reg);                                                         |
                return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk);                      |
            }                                                                              |
            di_clk[3] += setting->dev_id;                                                  |
            di_clk[7] += setting->disp_id;                                                 |
            ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev,                    |
                                    di_clk);                                               |
            if (IS_ERR(ldb->setting[setting_idx].di_clk)) {                                |
                dev_err(&ldb->pdev->dev, "get di clk0 failed
    ");                          |
                iounmap(ldb->reg);                                                         |
                return PTR_ERR(ldb->setting[setting_idx].di_clk);                          |
            }                                                                              |
                                                                                           |
            dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s
    ", ldb_clk, di_clk);    |
                                                                                           |
            /* fb notifier for clk setting */                                              |
            ldb->nb.notifier_call = ldb_fb_event,                                          |
            ret = fb_register_client(&ldb->nb);                                            |
            if (ret < 0) {                                                                 |
                iounmap(ldb->reg);                                                         |
                return ret;                                                                |
            }                                                                              |
                                                                                           |
            ldb->inited = true;                                                            |
                                                                                           |
            i2c_dev = ldb_i2c_client[lvds_channel];                                        |
                                                                                           |
        } else { /* second time for separate mode */                                       |
            char di_clk[] = "ipu1_di0_clk";                                                |
            char ldb_clk[] = "ldb_di0_clk";                                                |
                                                                                           |
            if ((ldb->mode == LDB_SPL_DI0) ||                                              |
                (ldb->mode == LDB_SPL_DI1) ||                                              |
                (ldb->mode == LDB_DUL_DI0) ||                                              |
                (ldb->mode == LDB_DUL_DI1) ||                                              |
                (ldb->mode == LDB_SIN0) ||                                                 |
                (ldb->mode == LDB_SIN1)) {                                                 |
                dev_err(&ldb->pdev->dev, "for second ldb disp"                             |
                        "ldb mode should in separate mode
    ");                             |
                return -EINVAL;                                                            |
            }                                                                              |
                                                                                           |
            setting_idx = 1;                                                               |
            if (cpu_is_mx6q() || cpu_is_mx6dl()) {                                         |
                setting->dev_id = plat_data->sec_ipu_id;                                   |
                setting->disp_id = plat_data->sec_disp_id;                                 |
            } else {                                                                       |
                setting->dev_id = plat_data->ipu_id;                                       |
                setting->disp_id = !plat_data->disp_id;                                    |
            }                                                                              |
            if (setting->disp_id == ldb->setting[0].di) {                                  |
                dev_err(&ldb->pdev->dev, "Err: for second ldb disp in"                     |
                    "separate mode, DI should be different!
    ");                           |
                return -EINVAL;                                                            |
            }                                                                              |
                                                                                           |
            /* second output is LVDS0 or LVDS1 */                                          |
            if (ldb->mode == LDB_SEP0)                                                     |
                lvds_channel = 1;                                                          |
            else                                                                           |
                lvds_channel = 0;                                                          |
                                                                                           |
            reg = readl(ldb->control_reg);                                                 |
            if ((lvds_channel == 0) && (setting->disp_id == 0))                            |
                reg |= LDB_CH0_MODE_EN_TO_DI0;                                             |
            else if ((lvds_channel == 0) && (setting->disp_id == 1))                       |
                reg |= LDB_CH0_MODE_EN_TO_DI1;                                             |
            else if ((lvds_channel == 1) && (setting->disp_id == 0))                       |
                reg |= LDB_CH1_MODE_EN_TO_DI0;                                             |
            else                                                                           |
                reg |= LDB_CH1_MODE_EN_TO_DI1;                                             |
            ch_mask = lvds_channel ?  LDB_CH1_MODE_MASK :                                  |
                    LDB_CH0_MODE_MASK;                                                     |
            ch_val = reg & ch_mask;                                                        |
                                                                                           |
            if (bits_per_pixel(setting->if_fmt) == 24) {                                   |
                if (lvds_channel == 0)                                                     |
                    reg |= LDB_DATA_WIDTH_CH0_24;                                          |
                else                                                                       |
                    reg |= LDB_DATA_WIDTH_CH1_24;                                          |
            } else {                                                                       |
                if (lvds_channel == 0)                                                     |
                    reg |= LDB_DATA_WIDTH_CH0_18;                                          |
                else                                                                       |
                    reg |= LDB_DATA_WIDTH_CH1_18;                                          |
            }                                                                              |
            writel(reg, ldb->control_reg);                                                 |
                                                                                           |
            /* clock setting */                                                            |
            if (cpu_is_mx6q() || cpu_is_mx6dl())                                           |
                ldb_clk[6] += lvds_channel;                                                |
            else                                                                           |
                ldb_clk[6] += setting->disp_id;                                            |
            ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev,                |
                                    ldb_clk);                                              |
            if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) {                            |
                dev_err(&ldb->pdev->dev, "get ldb clk1 failed
    ");                         |
                return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk);                      |
            }                                                                              |
            di_clk[3] += setting->dev_id;                                                  |
            di_clk[7] += setting->disp_id;                                                 |
            ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev,                    |
                                    di_clk);                                               |
            if (IS_ERR(ldb->setting[setting_idx].di_clk)) {                                |
                dev_err(&ldb->pdev->dev, "get di clk1 failed
    ");                          |
                return PTR_ERR(ldb->setting[setting_idx].di_clk);                          |
            }                                                                              |
                                                                                           |
            i2c_dev = ldb_i2c_client[lvds_channel];                                        |
                                                                                           |
            dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s
    ", ldb_clk, di_clk);    |
        }                                                                                  |
                                                                                           |
        if (i2c_dev)                                                                       |
            mxc_dispdrv_setdev(ldb->disp_ldb, &i2c_dev->dev);                              |
        else                                                                               |
            mxc_dispdrv_setdev(ldb->disp_ldb, NULL);                                       |
                                                                                           |
        ldb->setting[setting_idx].ch_mask = ch_mask;                                       |
        ldb->setting[setting_idx].ch_val = ch_val;                                         |
                                                                                           |
        if (cpu_is_mx6q() || cpu_is_mx6dl())                                               |
            ldb_ipu_ldb_route(setting->dev_id, setting->disp_id, ldb);          -----------------+
                                                                                           |     |
        /*                                                                                 |     |
         * ldb_di0_clk -> ipux_di0_clk                                                     |     |
         * ldb_di1_clk -> ipux_di1_clk                                                     |     |
         */                                                                                |     |
        clk_set_parent(ldb->setting[setting_idx].di_clk,                                   |     |
                ldb->setting[setting_idx].ldb_di_clk);                                     |     |
                                                                                           |     |
        /* must use spec video mode defined by driver */                                   |     |
        ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,        |     |
                    ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp);                |     |
        if (ret != 1)                                                                      |     |
            fb_videomode_to_var(&setting->fbi->var, &ldb_modedb[0]);                       |     |
                                                                                           |     |
        INIT_LIST_HEAD(&setting->fbi->modelist);                                           |     |
        for (i = 0; i < ldb_modedb_sz; i++) {                                              |     |
            struct fb_videomode m;                                                         |     |
            fb_var_to_videomode(&m, &setting->fbi->var);                                   |     |
            if (fb_mode_is_equal(&m, &ldb_modedb[i])) {                                    |     |
                fb_add_videomode(&ldb_modedb[i],                                           |     |
                        &setting->fbi->modelist);                                          |     |
                break;                                                                     |     |
            }                                                                              |     |
        }                                                                                  |     |
                                                                                           |     |
        /* get screen size in edid */                                                      |     |
        if (i2c_dev) {                                                                     |     |
            ret = mxc_ldb_edidread(i2c_dev);                                               |     |
            if (ret > 0) {                                                                 |     |
                fb_edid_to_monspecs(&g_edid[lvds_channel][0],                              |     |
                            &setting->fbi->monspecs);                                      |     |
                /* centimeter to millimeter */                                             |     |
                setting->fbi->var.width =                                                  |     |
                        setting->fbi->monspecs.max_x * 10;                                 |     |
                setting->fbi->var.height =                                                 |     |
                        setting->fbi->monspecs.max_y * 10;                                 |     |
            } else {                                                                       |     |
                /* ignore i2c access failure */                                            |     |
                ret = 0;                                                                   |     |
            }                                                                              |     |
        }                                                                                  |     |
                                                                                           |     |
        /* save current ldb setting for fb notifier */                                     |     |
        ldb->setting[setting_idx].active = true;                                           |     |
        ldb->setting[setting_idx].ipu = setting->dev_id;                                   |     |
        ldb->setting[setting_idx].di = setting->disp_id;                                   |     |
        return ret;                                                                        |     |
    }                                                                                      |     |
                                                                                           |     |
    static int ldb_disp_setup(struct mxc_dispdrv_handle *disp, struct fb_info *fbi)   <----+     |
    {                                                                                            |
        uint32_t reg, val;                                                                       |
        uint32_t pixel_clk, rounded_pixel_clk;                                                   |
        struct clk *ldb_clk_parent;                                                              |
        struct ldb_data *ldb = mxc_dispdrv_getdata(disp);                                        |
        int setting_idx, di;                                                                     |
                                                                                                 |
        setting_idx = find_ldb_setting(ldb, fbi);                                                |
        if (setting_idx < 0)                                                                     |
            return setting_idx;                                                                  |
                                                                                                 |
        di = ldb->setting[setting_idx].di;                                                       |
                                                                                                 |
        /* restore channel mode setting */                                                       |
        val = readl(ldb->control_reg);                                                           |
        val |= ldb->setting[setting_idx].ch_val;                                                 |
        writel(val, ldb->control_reg);                                                           |
        dev_dbg(&ldb->pdev->dev, "LDB setup, control reg:0x%x
    ",                                |
                readl(ldb->control_reg));                                                        |
        //设置vsync信号的极性
        /* vsync setup */                                                                        |
        reg = readl(ldb->control_reg);                                                           |
        if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {                                             |
            if (di == 0)                                                                         |
                reg = (reg & ~LDB_DI0_VS_POL_MASK)                                               |
                    | LDB_DI0_VS_POL_ACT_HIGH;                                                   |
            else                                                                                 |
                reg = (reg & ~LDB_DI1_VS_POL_MASK)                                               |
                    | LDB_DI1_VS_POL_ACT_HIGH;                                                   |
        } else {                                                                                 |
            if (di == 0)                                                                         |
                reg = (reg & ~LDB_DI0_VS_POL_MASK)                                               |
                    | LDB_DI0_VS_POL_ACT_LOW;                                                    |
            else                                                                                 |
                reg = (reg & ~LDB_DI1_VS_POL_MASK)                                               |
                    | LDB_DI1_VS_POL_ACT_LOW;                                                    |
        }                                                                                        |
        writel(reg, ldb->control_reg);                                                           |
                                                                                                 |
        /* clk setup */                                                                          |
        //时钟关闭                                                                               |
        if (ldb->setting[setting_idx].clk_en)                                                    |
            clk_disable(ldb->setting[setting_idx].ldb_di_clk);                                   |
        pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL;                                     |
        ldb_clk_parent = clk_get_parent(ldb->setting[setting_idx].ldb_di_clk);                   |
        if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1))                            |
            clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2);                                     |
        else                                                                                     |
            clk_set_rate(ldb_clk_parent, pixel_clk * 7);                                         |
        rounded_pixel_clk = clk_round_rate(ldb->setting[setting_idx].ldb_di_clk,                 |
                pixel_clk);                                                                      |
        clk_set_rate(ldb->setting[setting_idx].ldb_di_clk, rounded_pixel_clk);                   |
        //时钟使能                                                                               |
        clk_enable(ldb->setting[setting_idx].ldb_di_clk);                                        |
        if (!ldb->setting[setting_idx].clk_en)                                                   |
            ldb->setting[setting_idx].clk_en = true;                                             |
                                                                                                 |
        return 0;                                                                                |
    }                                                                                            |
                                                                                                 |
    #define LVDS_MUX_CTL_WIDTH    2                                                              |
    #define LVDS_MUX_CTL_MASK    3                                                               |
    #define LVDS0_MUX_CTL_OFFS    6                                                              |
    #define LVDS1_MUX_CTL_OFFS    8                                                              |
    #define LVDS0_MUX_CTL_MASK    (LVDS_MUX_CTL_MASK << 6)                                       |
    #define LVDS1_MUX_CTL_MASK    (LVDS_MUX_CTL_MASK << 8)                                       |
    #define ROUTE_IPU_DI(ipu, di)    (((ipu << 1) | di) & LVDS_MUX_CTL_MASK)                     |
    static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb)              <-----------+
    {
        uint32_t reg;
        int channel;
        int shift;
        int mode = ldb->mode;
        //IOMUXC_GPR3
        reg = readl(ldb->gpr3_reg);
        if (mode < LDB_SIN0) {
            reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
            reg |= (ROUTE_IPU_DI(ipu, di) << LVDS0_MUX_CTL_OFFS) |
                (ROUTE_IPU_DI(ipu, di) << LVDS1_MUX_CTL_OFFS);
            dev_dbg(&ldb->pdev->dev,
                "Dual/Split mode both channels route to IPU%d-DI%d
    ",
                ipu, di);
        } else if ((mode == LDB_SIN0) || (mode == LDB_SIN1)) {
            reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);      //选择lvds源
            channel = mode - LDB_SIN0;
            shift = LVDS0_MUX_CTL_OFFS + channel * LVDS_MUX_CTL_WIDTH;
            reg |= ROUTE_IPU_DI(ipu, di) << shift;
            dev_dbg(&ldb->pdev->dev,
                "Single mode channel %d route to IPU%d-DI%d
    ",
                    channel, ipu, di);
        } else {
            static bool first = true;
    
            if (first) {
                if (mode == LDB_SEP0) {
                    reg &= ~LVDS0_MUX_CTL_MASK;
                    channel = 0;
                } else {
                    reg &= ~LVDS1_MUX_CTL_MASK;
                    channel = 1;
                }
                first = false;
            } else {
                if (mode == LDB_SEP0) {
                    reg &= ~LVDS1_MUX_CTL_MASK;
                    channel = 1;
                } else {
                    reg &= ~LVDS0_MUX_CTL_MASK;
                    channel = 0;
                }
            }
    
            shift = LVDS0_MUX_CTL_OFFS + channel * LVDS_MUX_CTL_WIDTH;
            reg |= ROUTE_IPU_DI(ipu, di) << shift;
    
            dev_dbg(&ldb->pdev->dev,
                "Separate mode channel %d route to IPU%d-DI%d
    ",
                channel, ipu, di);
        }
        writel(reg, ldb->gpr3_reg);
    
        return 0;
    }
    

    Author

    Tony Liu

    2016-8-31, Shenzhen

  • 相关阅读:
    二叉树进阶之寻找一棵二叉树中的最大二叉搜索子树
    二叉树进阶之求一棵二叉树中结点间最大距离
    软件工程各阶段的UML图
    软件工程各阶段的开发文档
    二叉树应用进阶之折纸(二叉树的右根左遍历)
    二叉树进阶应用之查找结点的后继结点
    二叉树进阶之满二叉树和完全二叉树
    二叉树进阶之搜索二叉树的判断与找出搜索二叉树中出错的结点
    二叉树进阶之平衡二叉树的判断
    二叉树基础之序列化和反序列化二叉树
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/5824691.html
Copyright © 2020-2023  润新知