欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MSM8909开机logo显示(1)---LCD背光的控制

程序员文章站 2022-07-14 09:38:26
...

在MSM8909平台上,PWM信号由PMU的MPP2输出,MPP的全称是Multi Purpose Pin,即多用途管脚。在文档80-NN174-64中,说明了MPP2的性质:
MSM8909开机logo显示(1)---LCD背光的控制

PMU输出PWM非常地简单,MSM8909可参考80-NP409,只需配置10个寄存器。对于PWM的输出频率和占空比,高通提供了一个公式来计算:
MSM8909开机logo显示(1)---LCD背光的控制
PWM_FREQ_CLK_SELECTPWM_SIZE由寄存器0x1BC41配置:
MSM8909开机logo显示(1)---LCD背光的控制
PWM_FREQ_EXPONENTPWM_FREQ_PRE_DIVIDE由寄存器0x1BC42配置:

MSM8909开机logo显示(1)---LCD背光的控制

0x1BC41和0x1BC42用来配置PWM的输出频率,占空比由寄存器0x1BC44和0x1BC45控制:
                           0x1BC44寄存器
MSM8909开机logo显示(1)---LCD背光的控制
                           0x1BC45寄存器
MSM8909开机logo显示(1)---LCD背光的控制
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即可。
MSM8909开机logo显示(1)---LCD背光的控制
上述5个寄存器配置好后,PWM的使能只需配置0x1BC46寄存器的bit7为1即可,同时配置0x1BC47寄存器的bit0为1来更新PWM的计数值,该位置1后会由硬件自动清零。
                          0x1BC46寄存器
MSM8909开机logo显示(1)---LCD背光的控制
                           0x1BC47寄存器
MSM8909开机logo显示(1)---LCD背光的控制
PWM配置好后,还需配置MPP寄存器,需要配置的寄存器有3个。
1.  
PM8909的MPP2起始地址位于0xA100。配置0xA140寄存器的bit6-4配置为001,让MPP2为数字输出模式,bit3-0配置为10000,让EN_AND_SOURCE_SEL为DTEST1。
MSM8909开机logo显示(1)---LCD背光的控制
2.   MPP2的输出电压可以通过寄存器0xA141的bit2-0控制。VIN0表示MPP的输出电压等于VPH-PWR,VIN2表示MPP的输出电压和GPIO一样等于1.8V,一般配置为VIN2。
MSM8909开机logo显示(1)---LCD背光的控制
3.   配置0xA146的bit7为1来使能MPP2。
MSM8909开机logo显示(1)---LCD背光的控制
LK中PWM的调用流程如下:
MSM8909开机logo显示(1)---LCD背光的控制

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