查看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. JS中检测数据类型的四种方式及每个方式的优缺点

    //1.typeof 用来检测数据类型的运算符 //->typeof value //->返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"number". ...

  2. 使用freemarker生成word,步骤详解并奉上源代码

    1.   步骤 1.    用word编辑好模板 1. 普通字符串替换为 ${string} 2. 表格循环用标签 <#list userList as user> 姓名:${user.u ...

  3. JAVA图片处理--缩放,切割,类型转换

    import java.io.*; import java.awt.*; import java.awt.image.*; import java.awt.Graphics; import java. ...

  4. Microsoft.AlphaImageLoader滤镜讲解

    Microsoft.AlphaImageLoader是IE滤镜的一种,其主要作用就是对图片进行透明处理.虽然FireFox和IE7以上的IE浏览器已经支持透明的PNG图片,但是就IE5-IE6而言还是 ...

  5. thinkphp 代码执行

    相关漏洞:http://loudong.360.cn/vul/info/id/2919 ThinkPHP 开启lite模式后,会加载ThinkPHP/Extend/Mode/Lite/Dispache ...

  6. c#是否参入中间变量交换变量的几种方法

    大家很熟悉知道,交换变量经常的使用的一种方法是使用第三个变量,也符合正常人的思维逻辑,但是还有其他的一些方法来实现,但是有点“偏门”,记住就好了.下面就列举这几种方法. 第一种方法,会用到参数的方法再 ...

  7. Linux权限问题

    Linux中各个用户对文件和目录的权限有三种: r: 读 w:写 x:执行 各个权限的意义: 文件 r:可以读取文件的内容 w:编辑文件内容 x:执行该文件 目录 r:列出该目录下的内容,即使用ls命 ...

  8. UVALive 7472

    题意 有n个循环 给出x a b c xi+1=(a*x+b)%c 要求是从这些循环中各取一个数 使加和最大并且给出一个m 满足sum%m!=0 n的范围是4次方 c的范围是3次方 训练赛的时候看了一 ...

  9. HDU 1176 经典dp

    记录最晚时间 从time为2枚举到最晚时间 每个时间段的x轴节点都等于上一个时间段的可触及的最大馅饼数 #include<stdio.h> #include<string.h> ...

  10. NVPerfHUD

    http://www.cnblogs.com/cproom/archive/2006/11/13/559287.html NVPerfHUD是一个很好的3D程序调试工具,它是NVPerfKit的一部分 ...