查看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

imx6 lvds 代码分析的更多相关文章

  1. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  2. pmd静态代码分析

    在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...

  3. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  4. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  5. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  6. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

  7. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  8. SonarQube-5.6.3 代码分析平台搭建使用

    python代码分析 官网主页: http://docs.sonarqube.org/display/PLUG/Python+Plugin Windows下安装使用: 快速使用: 1.下载jdk ht ...

  9. angular代码分析之异常日志设计

    angular代码分析之异常日志设计 错误异常是面向对象开发中的记录提示程序执行问题的一种重要机制,在程序执行发生问题的条件下,异常会在中断程序执行,同时会沿着代码的执行路径一步一步的向上抛出异常,最 ...

随机推荐

  1. 【wikioi】1026 逃跑的拉尔夫

    题目链接 算法:BFS 14.01.02 PS: 本人再次脑残,BFS又是写得那么脓肿,突然发现我原来什么搜索都是不会的呀.. //2014-02-05已更新 ******************** ...

  2. 【wikioi】1922 骑士共存问题(网络流/二分图匹配)

    用匈牙利tle啊喂?和网络流不都是n^3的吗(匈牙利O(nm), isap O(n^2m) 但是isap实际复杂度很优的(二分图匹配中,dinic是O(sqrt(V)*E),不知道isap是不是一样. ...

  3. POJ 3335 Rotating Scoreboard(多边形的核)

    题目链接 我看的这里:http://www.cnblogs.com/ka200812/archive/2012/01/20/2328316.html 然后整理一下当做模版.0换成eps,会wa,应该要 ...

  4. Google Code Jam 2010 Round 1C Problem B. Load Testing

    https://code.google.com/codejam/contest/619102/dashboard#s=p1&a=1 Problem Now that you have won ...

  5. edtftpj让Java上传FTP文件支持断点续传

    在用Java实现FTP上传文件功能时,特别是上传大文件的时候,可以需要这样的功能:程序在上传的过程中意外终止了,文件传了一大半,想从断掉了地方继续传:或者想做类似迅雷下载类似的功能,文件太大,今天传一 ...

  6. pod 安装 Masonry 遇到问题

    pod 导入第三方库 Masonry: 在工程masonryTest的文件下新建一个Podfile文件 编辑如下内容: platform :ios, '8.0'xcodeproj 'mansoryTe ...

  7. 交叉报表列头排序时遇到的oracle问题—oracle ORA-12704:字符集不匹配、varchar2转化为nvarchar2字符缺失、case when else后的字符类型要一致

    在做交叉报表列头的排序时,遇到这三个问题,下面具体来说一下. 设计的数据库的表结构如图1所示: 图1 要处出来student_name_,s.grade_,s.subject_name_,这三个属性, ...

  8. maven编译项目理解

    如何运行这个Web项目? 右键项目名称,在弹出的选项中选择 Run As => Maven install,然后 Eclise 会下载并安装相关依赖包.在等待一段时间后,构建成功的结果如下:

  9. typecho插件编写教程1 - 从HelloWorld说起

    typecho插件编写教程1 - 从HelloWorld说起 老高 187 5月25日 发布 推荐 0 推荐 收藏 2 收藏,189 浏览 最近老高正在编写一个关于typecho的插件,由于typec ...

  10. Scrapy入门教程

    关键字:scrapy 入门教程 爬虫 Spider作者:http://www.cnblogs.com/txw1958/出处:http://www.cnblogs.com/txw1958/archive ...