MSM8909开机logo显示(1)---LCD背光的控制
在MSM8909平台上,PWM信号由PMU的MPP2输出,MPP的全称是Multi Purpose Pin,即多用途管脚。在文档80-NN174-64中,说明了MPP2的性质:
PMU输出PWM非常地简单,MSM8909可参考80-NP409,只需配置10个寄存器。对于PWM的输出频率和占空比,高通提供了一个公式来计算:
PWM_FREQ_CLK_SELECT和PWM_SIZE由寄存器0x1BC41配置:
PWM_FREQ_EXPONENT和PWM_FREQ_PRE_DIVIDE由寄存器0x1BC42配置:
0x1BC41和0x1BC42用来配置PWM的输出频率,占空比由寄存器0x1BC44和0x1BC45控制:
0x1BC44寄存器
0x1BC45寄存器
0x1BC41寄存器的bit2配置为0,即PWM_SIZE = 6,那么0x1BC44寄存器只能配置最低6位。占空比为100%时,0x1BC44寄存器配置为0011 1111,即0x3F。占空比为50%时,0x1BC44寄存器配置为0001 1111,即0x1F。如果0x1BC41寄存器的bit2配置为1,即PWM_SIZE = 9,那么0x1BC45的bit0和0x1BC45的bit0-7组合成9位,占空比的计算方法和6位的计算方法一样。
除了上述4个寄存器配置需要外,还有一个寄存器需要配置,该寄存器位于0x1BC43,配置PWM的输出类型,一般配置该寄存器的bit5为1即可。
上述5个寄存器配置好后,PWM的使能只需配置0x1BC46寄存器的bit7为1即可,同时配置0x1BC47寄存器的bit0为1来更新PWM的计数值,该位置1后会由硬件自动清零。
0x1BC46寄存器
0x1BC47寄存器
PWM配置好后,还需配置MPP寄存器,需要配置的寄存器有3个。
1. PM8909的MPP2起始地址位于0xA100。配置0xA140寄存器的bit6-4配置为001,让MPP2为数字输出模式,bit3-0配置为10000,让EN_AND_SOURCE_SEL为DTEST1。
2. MPP2的输出电压可以通过寄存器0xA141的bit2-0控制。VIN0表示MPP的输出电压等于VPH-PWR,VIN2表示MPP的输出电压和GPIO一样等于1.8V,一般配置为VIN2。
3. 配置0xA146的bit7为1来使能MPP2。
LK中PWM的调用流程如下:
aboot_init的调用
在bootable/bootloader/lk/app/aboot/aboot.c中,有如下定义:
APP_START(aboot)
.init = aboot_init,
APP_END
APPS_START和APPS_END是宏,在bootable/bootloader/lk/include/app.h中定义:#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };
宏展开后,如下:struct app_descriptor _app_aboot __SECTION(".apps") = {
.name = "aboot",
.init = aboot_init,
};
在bootable/bootloader/lk/kernel/main.c中kmain中,新建了一个线程来调用bootstrap2函数。
/* called from crt0.S */
void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
void kmain(void)
{
......
// create a thread to complete system initialization
dprintf(SPEW, "creating bootstrap completion thread\n");
thread_resume(thread_create("bootstrap2", &bootstrap2,
NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
bootstrap2调用apps_init,apps_init依次取出apps段中所有的init函数,逐一调用。static int bootstrap2(void *arg)
{
......
apps_init();
return 0;
}
/* one time setup */
void apps_init(void)
{
const struct app_descriptor *app;
/* call all the init routines */
for (app = &__apps_start; app != &__apps_end; app++) {
if (app->init)
app->init(app);
}
......
}
apps->init(app)调用的就是bootable/bootloader/lk/app/aboot/aboot.c中的aboot_init函数。mdss_dsi_bl_enable的调用
在gcdb_display_init函数中,将panel结构体的bl_func成员赋值为mdss_dsi_bl_enable:
int gcdb_display_init(const char *panel_name, uint32_t rev, void *base)
{
......
if (pan_type == PANEL_TYPE_DSI) {
......
panel.panel_info.mipi.mdss_dsi_phy_db = &dsi_video_mode_phy_db;
panel.pll_clk_func = mdss_dsi_panel_clock;
panel.power_func = mdss_dsi_panel_power;
panel.pre_init_func = mdss_dsi_panel_pre_init;
panel.bl_func = mdss_dsi_bl_enable; // 背光控制函数被赋值
panel.fb.base = base;
panel.fb.width = panel.panel_info.xres;
panel.fb.height = panel.panel_info.yres;
panel.fb.stride = panel.panel_info.xres;
panel.fb.bpp = panel.panel_info.bpp;
panel.fb.format = panel.panel_info.mipi.dst_format;
} else if (pan_type == PANEL_TYPE_EDP) {
......
} else {
......
}
......
ret = msm_display_init(&panel);
......
}
接下来在msm_display_init函数中,通过pdata->bl_func(1)来调用mdss_dsi_bl_enable函数。int msm_display_init(struct msm_fb_panel_data *pdata)
{
......
/* Turn on backlight */
if (pdata->bl_func)
ret = pdata->bl_func(1);
......
}
背光控制的核心在target_backlight_ctrl函数,该函数在bootable/bootloader/lk/target/msm8909/target_display.c中:int target_backlight_ctrl(struct backlight *bl, uint8_t enable)
{
struct pm8x41_mpp mpp;
uint32_t hw_id = board_hardware_id();
struct board_pmic_data pmic_info;
int rc;
if (bl->bl_interface_type == BL_DCS)
return 0;
board_pmic_info(&pmic_info, 1);
if (pmic_info.pmic_version == PM8916_VER)
mpp.base = PM8x41_MMP4_BASE;
else
mpp.base = PM8x41_MMP2_BASE; // PM8x41_MMP2_BASE = 0xA100
mpp.vin = MPP_VIN0; // MPP_VIN0 = 0
if (enable) {
pm_pwm_enable(false);
rc = pm_pwm_config(PWM_DUTY_US, PWM_PERIOD_US);
if (rc < 0)
mpp.mode = MPP_HIGH;
else {
mpp.mode = MPP_DTEST1; // MPP_DTEST1 = 8
pm_pwm_enable(true);
}
pm8x41_config_output_mpp(&mpp);
pm8x41_enable_mpp(&mpp, MPP_ENABLE);
} else {
pm_pwm_enable(false);
pm8x41_enable_mpp(&mpp, MPP_DISABLE);
}
mdelay(20);
if (enable) {
if (hw_id == HW_PLATFORM_SURF || (hw_id == HW_PLATFORM_MTP)) {
/* configure backlight gpio for CDP and MTP */
gpio_tlmm_config(bkl_gpio.pin_id, 0,
bkl_gpio.pin_direction, bkl_gpio.pin_pull,
bkl_gpio.pin_strength, bkl_gpio.pin_state);
gpio_set(bkl_gpio.pin_id, 2);
}
}
return 0;
}
pm_pwm_enable的分析bootabl/bootloader/lk/dev/pmic/pm8x41/pm_pwm.c
void pm_pwm_enable(bool enable)
{
uint8_t reg;
// PM_PWM_ENABLE_CTL_SHIFT = 7
// PM_PWM_ENABLE_CTL_MASK = 0x80
// reg = enable << 7 & 0x80;
reg = enable << PM_PWM_ENABLE_CTL_SHIFT
& PM_PWM_ENABLE_CTL_MASK;
// PM_PWM_ENABLE_CTL_REG_OFFSET = 0x46
pm_pwm_reg_write(PM_PWM_ENABLE_CTL_REG_OFFSET, reg);
}
static void pm_pwm_reg_write(uint8_t off, uint8_t val)
{
REG_WRITE(PM_PWM_BASE(off), val);
}
bootable/bootloader/lk/dev/pmic/pm8x41/include/pm8x41_hw.h#define REG_WRITE(_a, _v) pm8x41_reg_write(_a, _v)
bootable/bootloader/lk/dev/pmic/pm8x41/include/pm_pwm.h#define PM_PWM_BASE(x) (0x1BC00 + (x))
从上面的代码可以看出,pm_pwm_enable函数就是控制PM8909的0x1BC46寄存器的bit7。pm_pwm_config的分析
bootabl/bootloader/lk/dev/pmic/pm8x41/pm_pwm.c
int pm_pwm_config(unsigned int duty_us, unsigned int period_us)
{
struct pm_pwm_config pwm_config;
if ((duty_us > period_us) || (period_us > PM_PWM_PERIOD_MAX) ||
(period_us < PM_PWM_PERIOD_MIN)) {
dprintf(CRITICAL, "Error in duty cycle and period\n");
return -1;
}
pm_pwm_calc_period(period_us, &pwm_config);
pm_pwm_calc_pwm_value(&pwm_config, period_us, duty_us);
dprintf(SPEW, "duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
duty_us, period_us, pwm_config.pwm_value, 1 << pwm_config.pwm_size);
pm_pwm_config_regs(&pwm_config);
return 0;
}
函数带两个参数,分别表示PWM的占空比和周期,单位是微妙。PWM_DUTY_US和PWM_PERIOD_US这两个参数在bootable/bootloader/lk/target/msm8909/target_display.c中定义,可以根据自己的需要修改:#define PWM_DUTY_US 13
#define PWM_PERIOD_US 27
在pm_pwm_calc_period函数内部,根据给定的PWM周期和占空比计算出最佳的寄存器配置,即PM8909的0x1BC41和0x1BC42两个寄存器。static void pm_pwm_calc_period(unsigned int period_us,
struct pm_pwm_config *pwm_config)
{
int n, m, clk, div;
int best_m, best_div, best_clk;
unsigned int last_err, cur_err, min_err;
unsigned int tmp_p, period_n;
n = 6;
if (period_us < ((unsigned)(-1) / NSEC_PER_USEC))
period_n = (period_us * NSEC_PER_USEC) >> n;
else
period_n = (period_us >> n) * NSEC_PER_USEC;
if (period_n >= MAX_MPT) {
n = 9;
period_n >>= 3;
}
min_err = last_err = (unsigned)(-1);
best_m = 0;
best_clk = 0;
best_div = 0;
for (clk = 0; clk < NUM_REF_CLOCKS; clk++) {
for (div = 0; div < NUM_PRE_DIVIDE; div++) {
/* period_n = (PWM Period / N) */
/* tmp_p = (Pre-divide * Clock Period) * 2^m */
tmp_p = pt_t[div][clk];
for (m = 0; m <= PM_PWM_M_MAX; m++) {
if (period_n > tmp_p)
cur_err = period_n - tmp_p;
else
cur_err = tmp_p - period_n;
if (cur_err < min_err) {
min_err = cur_err;
best_m = m;
best_clk = clk;
best_div = div;
}
if (m && cur_err > last_err)
/* Break for bigger cur_err */
break;
last_err = cur_err;
tmp_p <<= 1;
}
}
}
pwm_config->pwm_size = n; // 0x1BC41寄存器bit2
pwm_config->clk = best_clk; // 0x1BC41寄存器bit1,bit0
pwm_config->pre_div = best_div; // 0x1BC42寄存器bit6,bit5
pwm_config->pre_div_exp = best_m; // 0x1BC42寄存器bit2,bit1,bi0
}
pm_pwm_calc_pwm_value函数根据占空比计算出0x1BC44和0x1BC45寄存器的配置:static void pm_pwm_calc_pwm_value(struct pm_pwm_config *pwm_config,
unsigned int period_us,
unsigned int duty_us)
{
unsigned int max_pwm_value, tmp;
/* Figure out pwm_value with overflow handling */
tmp = 1 << (sizeof(tmp) * 8 - pwm_config->pwm_size);
if (duty_us < tmp) {
tmp = duty_us << pwm_config->pwm_size;
pwm_config->pwm_value = tmp / period_us;
} else {
tmp = period_us >> pwm_config->pwm_size;
pwm_config->pwm_value = duty_us / tmp;
}
max_pwm_value = (1 << pwm_config->pwm_size) - 1;
if (pwm_config->pwm_value > max_pwm_value)
pwm_config->pwm_value = max_pwm_value;
}
pm_pwm_config_regs函数将6个寄存器的值更新到PM8909的寄存器中:static void pm_pwm_config_regs(struct pm_pwm_config *pwm_config)
{
int i;
uint8_t reg;
reg = ((pwm_config->pwm_size > 6 ? PM_PWM_SIZE_9_BIT : PM_PWM_SIZE_6_BIT)
<< PM_PWM_SIZE_SEL_SHIFT)
& PM_PWM_SIZE_SEL_MASK;
reg |= (pwm_config->clk + 1) & PM_PWM_CLK_SEL_MASK;
pwm_config->pwm_ctl[SIZE_CLK] = reg; // 0x1BC41寄存器
reg = (pwm_config->pre_div << PM_PWM_PREDIVIDE_SHIFT)
& PM_PWM_PREDIVIDE_MASK;
reg |= pwm_config->pre_div_exp & PM_PWM_M_MASK;
pwm_config->pwm_ctl[FREQ_PREDIV_CLK] = reg; // 0x1BC42寄存器
/* Enable glitch removal by default */
reg = 1 << PM_PWM_EN_GLITCH_REMOVAL_SHIFT
& PM_PWM_EN_GLITCH_REMOVAL_MASK;
pwm_config->pwm_ctl[TYPE_CONFIG] = reg; // 0x1BC43寄存器
// 0x1BC44,0x1BC45寄存器
if (pwm_config->pwm_size > 6) {
pwm_config->pwm_ctl[VALUE_LSB] = pwm_config->pwm_value
& PM_PWM_VALUE_BIT7_0;
pwm_config->pwm_ctl[VALUE_MSB] = (pwm_config->pwm_value >> 8)
& PM_PWM_VALUE_BIT8;
} else
pwm_config->pwm_ctl[VALUE_LSB] = pwm_config->pwm_value
& PM_PWM_VALUE_BIT5_0;
// 更新PM8909的0x1BC41-0x1BC45
for (i = 0; i < NUM_PWM_CTL_REGS; i++)
pm_pwm_reg_write(PM_PWM_CTL_REG_OFFSET + i, pwm_config->pwm_ctl[i]);
// 将0x1BC47寄存器的bit0置1
reg = 1 & PM_PWM_SYNC_MASK;
pm_pwm_reg_write(PM_PWM_SYNC_REG_OFFSET, reg);
}
pm8x41_config_output_mpp的分析void pm8x41_config_output_mpp(struct pm8x41_mpp *mpp)
{
ASSERT(mpp);
// 更新PM8909的0xA141寄存器bit2:bit1:bit0=0:0:0
// VOLTAGE_SEL = VIN0
// REG_WRITE(((0xA100 + 0x41) + (0 << 16)), 0);
REG_WRITE(((mpp->base + MPP_DIG_VIN_CTL) + (mpp_slave_id << 16)), mpp->vin);
// 更新PM8909的0xA140寄存器bit6:bit5:bit4:bit3:bit2:bit1:bit0=0:0:1:1:0:0:0
// MODE = DIGITAL_OUTPUT, EN_AND_SOURCE_SEL = MPP_DTEST1
// REG_WRITE(((0xA100 + 0x40) + (0 << 16)), 8 | (1 << 4));
REG_WRITE(((mpp->base + MPP_MODE_CTL) + (mpp_slave_id << 16)), mpp->mode | (MPP_DIGITAL_OUTPUT << MPP_MODE_CTL_MODE_SHIFT));
}
pm8x41_enable_mpp的分析void pm8x41_enable_mpp(struct pm8x41_mpp *mpp, enum mpp_en_ctl enable)
{
ASSERT(mpp);
// 更新PM8909的0xA146寄存去bit7=1
// PERPH_EN = MPP_ENABLED
// REG_WRITE(((0xA100 + 0x46) + (0 << 16)), 1 << 7);
REG_WRITE(((mpp->base + MPP_EN_CTL) + (mpp_slave_id << 16)), enable << MPP_EN_CTL_ENABLE_SHIFT);
}
控制PM8909输出PWM信号,需要配置10个寄存器:0x1BC41 0x1BC42 0x1BC43 0x1BC44 0x1BC45 0x1BC46 0x1BC47 0xA140 0xA141 0xA146
上一篇: Linux驱动分析之LCD驱动架构
下一篇: linux php调用c库