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\n");                                          |
        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.\n",                                                    |
                    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!\n");                                 |
                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!\n");                                       |
                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\n");                         |
            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\n");                          |
            iounmap(ldb->reg);                                                         |
            return PTR_ERR(ldb->setting[setting_idx].di_clk);                          |
        }                                                                              |
                                                                                       |
        dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", 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\n");                             |
            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!\n");                           |
            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\n");                         |
            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\n");                          |
            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\n", 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\n",                                |
            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\n",
            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\n",
                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\n",
            channel, ipu, di);
    }
    writel(reg, ldb->gpr3_reg);

    return 0;
}

Author

Tony Liu

2016-8-31, Shenzhen

posted @ 2016-08-31 09:41  SuperTao1024  阅读(3025)  评论(0编辑  收藏  举报