mt8163-kernel-3.18\drivers\misc\mediatek\leds\leds_drv.c
mt8163-kernel-3.18\drivers\misc\mediatek\leds\leds_drv.h
\mt8163-kernel-3.18\drivers\misc\mediatek\leds\mt8163\leds.c
\mt8163-kernel-3.18\drivers\misc\mediatek\leds\mt8163\leds_hal.h
\mt8163-kernel-3.18\drivers\misc\mediatek\leds\mt8163\leds_sw.h
mt8163-kernel-3.18\drivers\misc\mediatek\leds\leds_drv.c
static struct platform_driver mt65xx_leds_driver = {
.driver = {
.name = "leds-mt65xx",
.owner = THIS_MODULE,
},
.probe = mt65xx_leds_probe,
.remove = mt65xx_leds_remove,
/* .suspend = mt65xx_leds_suspend, */
.shutdown = mt65xx_leds_shutdown,
};
mt65xx_leds_probe函数
struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list();//获取客制化的led配置信息,该函数的具体实现后面再来看
for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) {
if (cust_led_list[i].mode == MT65XX_LED_MODE_NONE) {
g_leds_data[i] = NULL;
continue;
}
g_leds_data[i] =
kzalloc(sizeof(struct mt65xx_led_data), GFP_KERNEL);
if (!g_leds_data[i]) {
ret = -ENOMEM;
goto err;
}
//将客制化的led配置信息赋值给全局变量g_leds_data
g_leds_data[i]->cust.mode = cust_led_list[i].mode;
g_leds_data[i]->cust.data = cust_led_list[i].data;
g_leds_data[i]->cust.name = cust_led_list[i].name;
g_leds_data[i]->cdev.name = cust_led_list[i].name;
g_leds_data[i]->cust.config_data = cust_led_list[i].config_data; /* bei add */
g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;//亮度设置函数
g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;//闪灯函数
INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);
ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);//向内核注册led驱动,会在/sys/class/leds下产生相应的节点,当操作节点下的brghtness属性时就会调用到上面的brightness_set对应的函数mt65xx_led_set,后面分析mt65xx_led_set和mt65xx_blink_set具体实现
回过头来看客制化的led配置是如何解析的?
struct cust_mt65xx_led *mt_get_cust_led_list(void)
{
struct cust_mt65xx_led *cust_led_list = get_cust_led_dtsi();//直接从dtsi文件中读取led的配置信息,用户需要什么样的led效果只需要在dtsi中配置就可以了
return cust_led_list;
}
struct cust_mt65xx_led *get_cust_led_dtsi(void)
{
for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++){
char node_name[32] = "mediatek,";
pled_dtsi[i].name = leds_name[i];//指示灯的名字
led_node =
of_find_compatible_node(NULL, NULL,
strcat(node_name,
leds_name[i]));
if (!led_node) {
LEDS_DEBUG("Cannot find LED node from dts\n");
pled_dtsi[i].mode = 0;
pled_dtsi[i].data = -1;
} else {
isSupportDTS = true;
ret =
of_property_read_u32(led_node, "led_mode",
&mode);
if (!ret) {
pled_dtsi[i].mode = mode;//指示灯模式
LEDS_DEBUG
("The %s's led mode is : %d\n",
pled_dtsi[i].name,
pled_dtsi[i].mode);
} else {
LEDS_DEBUG
("led dts can not get led mode");
pled_dtsi[i].mode = 0;
}
ret =
of_property_read_u32(led_node, "data",
&data);
if (!ret) {
pled_dtsi[i].data = data;//指示灯的data字段
LEDS_DEBUG
("The %s's led data is : %ld\n",
pled_dtsi[i].name,
pled_dtsi[i].data);
} else {
LEDS_DEBUG
("led dts can not get led data");
pled_dtsi[i].data = -1;
}
ret =
of_property_read_u32_array(led_node,
"pwm_config",
pwm_config,
ARRAY_SIZE
(pwm_config));
if (!ret) {
LEDS_DEBUG
("The %s's pwm config data is %d %d %d %d %d\n",
pled_dtsi[i].name, pwm_config[0],
pwm_config[1], pwm_config[2],
pwm_config[3], pwm_config[4]);
pled_dtsi[i].config_data.clock_source =
pwm_config[0];
pled_dtsi[i].config_data.div =
pwm_config[1];
pled_dtsi[i].config_data.low_duration =
pwm_config[2];
pled_dtsi[i].config_data.High_duration =
pwm_config[3];
pled_dtsi[i].config_data.pmic_pad =
pwm_config[4];//pwm配置信息
} else
LEDS_DEBUG
("led dts can not get pwm config data.\n");
switch (pled_dtsi[i].mode) {
case MT65XX_LED_MODE_CUST_LCM://如果是CUST_LCM模式设置data字段
pled_dtsi[i].data =
(long)mtkfb_set_backlight_level;
LEDS_DEBUG
("kernel:the backlight hw mode is LCM.\n");
break;
case MT65XX_LED_MODE_CUST_BLS_PWM: //如果是CUST_BLS_PWM模式设置data字段
pled_dtsi[i].data =
(long)disp_bls_set_backlight;
LEDS_DEBUG
("kernel:the backlight hw mode is BLS.\n");
break;
default:
break;
}
}
}
}
}
那么dtsi中又是如何定义这些客制化的led配置信息呢?
/* led part */
led0:led@0 {
compatible = "mediatek,red";//红灯配置信息
led_mode = <0>;
data = < >;
pwm_config = <0 0 0 0 0>;
};
led1:led@1 {
compatible = "mediatek,green";//绿灯配置信息
led_mode = <0>;
data = < >;
pwm_config = <0 0 0 0 0>;
};
led2:led@2 {
compatible = "mediatek,blue";//蓝灯配置信息
led_mode = <0>;
data = < >;
pwm_config = <0 0 0 0 0>;
};
led3:led@3 {
compatible = "mediatek,jogball-backlight";
led_mode = <0>;
data = < >;
pwm_config = <0 0 0 0 0>;
};
led4:led@4 {
compatible = "mediatek,keyboard-backlight";//键盘灯
led_mode = <0>;
data = < >;
pwm_config = <0 0 0 0 0>;
};
led5:led@5 {
compatible = "mediatek,button-backlight";//按键灯
led_mode = <0>;
data = < >;
pwm_config = <0 0 0 0 0>;
};
led6:led@6 {
compatible = "mediatek,lcd-backlight";//lcd背光
led_mode = <5>;
data = < >;
pwm_config = <0 0 0 0 0>;
gpios = <&pio 43 0>;
};
上面的led_mode和data具体含义是什么?下面来看mt65xx_led_set的具体实现
static void mt65xx_led_set(struct led_classdev *led_cdev,
enum led_brightness level)
{
struct mt65xx_led_data *led_data =
container_of(led_cdev, struct mt65xx_led_data, cdev);
#ifdef CONFIG_BACKLIGHT_SUPPORT_LP8557
bool flag = FALSE;
int value = 0;
int retval;
struct device_node *node = NULL;
struct i2c_client *client = g_client;
value = i2c_smbus_read_byte_data(g_client, 0x10);
LEDS_DRV_DEBUG("LEDS:mt65xx_led_set:0x10 = %d\n", value);
node = of_find_compatible_node(NULL, NULL,
"mediatek,lcd-backlight");
if (node) {
I2C_SET_FOR_BACKLIGHT = of_get_named_gpio(node, "gpios", 0);
LEDS_DRV_DEBUG("Led_i2c gpio num for power:%d\n", I2C_SET_FOR_BACKLIGHT);
}
#endif
if (strcmp(led_data->cust.name, "lcd-backlight") == 0) {
#ifdef CONTROL_BL_TEMPERATURE
mutex_lock(&bl_level_limit_mutex);
current_level = level;
/* LEDS_DRV_DEBUG("brightness_set_cust:current_level=%d\n", current_level); */
if (0 == limit_flag) {
last_level = level;
/* LEDS_DRV_DEBUG("brightness_set_cust:last_level=%d\n", last_level); */
} else {
if (limit < current_level) {
level = limit;
LEDS_DRV_DEBUG
("backlight_set_cust: control level=%d\n",
level);
}
}
mutex_unlock(&bl_level_limit_mutex);
#endif
}
#ifdef CONFIG_BACKLIGHT_SUPPORT_LP8557
retval = gpio_request(I2C_SET_FOR_BACKLIGHT, "i2c_set_for_backlight");
if (retval)
LEDS_DRV_DEBUG("LEDS: request I2C gpio149 failed\n");
if (strcmp(led_data->cust.name, "lcd-backlight") == 0) {
if (level == 0) {
LEDS_DRV_DEBUG("LEDS:mt65xx_led_set:close the power\n");
i2c_smbus_write_byte_data(client, 0x00, 0);
gpio_direction_output(I2C_SET_FOR_BACKLIGHT, 0);
}
if (!last_level1 && level) {
LEDS_DRV_DEBUG("LEDS:mt65xx_led_set:open the power\n");
gpio_direction_output(I2C_SET_FOR_BACKLIGHT, 1);
mdelay(100);
i2c_smbus_write_byte_data(client, 0x10, 4);
flag = TRUE;
}
last_level1 = level;
}
gpio_free(I2C_SET_FOR_BACKLIGHT);
#endif
mt_mt65xx_led_set(led_cdev, level);//最终是调用mt_mt65xx_led_set函数
#ifdef CONFIG_BACKLIGHT_SUPPORT_LP8557
if (strcmp(led_data->cust.name, "lcd-backlight") == 0) {
if (flag) {
i2c_smbus_write_byte_data(client, 0x14, 0xdf);
i2c_smbus_write_byte_data(client, 0x04, 0xff);
i2c_smbus_write_byte_data(client, 0x00, 1);
}
}
#endif
}
void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)
{
switch (cust->mode) {
case MT65XX_LED_MODE_PWM:
if (strcmp(cust->name, "lcd-backlight") == 0) {
bl_brightness_hal = level;
if (level == 0) {
mt_pwm_disable(cust->data,
cust->config_data.pmic_pad);
} else {
if (BacklightLevelSupport ==
BACKLIGHT_LEVEL_PWM_256_SUPPORT)
level = brightness_mapping(tmp_level);
else
level = brightness_mapto64(tmp_level);
mt_backlight_set_pwm(cust->data, level,
bl_div_hal,
&cust->config_data);
}
bl_duty_hal = level;
} else {
if (level == 0) {
led_tmp_setting.nled_mode = NLED_OFF;
mt_led_set_pwm(cust->data, &led_tmp_setting);
mt_pwm_disable(cust->data,
cust->config_data.pmic_pad);
} else {
led_tmp_setting.nled_mode = NLED_ON;
mt_led_set_pwm(cust->data, &led_tmp_setting);
}
}
return 1;
case MT65XX_LED_MODE_GPIO:
return ((cust_set_brightness) (cust->data)) (level);
case MT65XX_LED_MODE_PMIC:
return mt_brightness_set_pmic(cust->data, level, bl_div_hal);
case MT65XX_LED_MODE_CUST_LCM:
return ((cust_brightness_set) (cust->data)) (level, bl_div_hal);
case MT65XX_LED_MODE_CUST_BLS_PWM:
return ((cust_set_brightness) (cust->data)) (level);
}
int mt_brightness_set_pmic(enum mt65xx_led_pmic pmic_type, u32 level, u32 div)
{
if (pmic_type == MT65XX_LED_PMIC_BUTTON) {
if (level) {
#ifdef PMIC_MT6325
upmu_set_kpled_dim_duty(0x9);
upmu_set_kpled_en(0x1);
#endif
} else {
#ifdef PMIC_MT6325
upmu_set_kpled_en(0x0);
#endif
}
return 0;
}else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK0) {
if (first_time == true) {
#ifdef PMIC_MT6325
upmu_set_isinks_ch1_en(0x0); /* sw workround for sync leds status */
upmu_set_isink_rsv2_isink1(0x00);
upmu_set_isinks_ch2_en(0x0);
upmu_set_isink_rsv2_isink2(0x00);
#endif
first_time = false;
}
#ifdef PMIC_MT6325
upmu_set_isinks_ch0_mode(ISINK_PWM_MODE);
upmu_set_isinks_ch0_step(0x0); /* 4mA */
upmu_set_isink_dim0_duty(15);
upmu_set_isink_dim0_fsel(11); /* 6320 0.25KHz */
#endif
led_init_flag[0] = true;
if (level) {
#ifdef PMIC_MT6325
upmu_set_rg_bst_drv_1m_ck_pdn(0x0);
upmu_set_isink_rsv2_isink0(0x1);
upmu_set_isinks_ch0_en(0x01);
#endif
} else {
#ifdef PMIC_MT6325
upmu_set_isinks_ch0_en(0x00);
upmu_set_isink_rsv2_isink0(0x00);
#endif
}
return 0;
}else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK1) {
if (first_time == true) {
#ifdef PMIC_MT6325
upmu_set_isinks_ch0_en(0); /* sw workround for sync leds status */
upmu_set_isink_rsv2_isink0(0x00);
upmu_set_isinks_ch2_en(0);
upmu_set_isink_rsv2_isink2(0x00);
#endif
first_time = false;
}
#ifdef PMIC_MT6325
upmu_set_isinks_ch1_mode(ISINK_PWM_MODE);
upmu_set_isinks_ch1_step(0x3); /* 4mA */
upmu_set_isink_dim1_duty(15);
upmu_set_isink_dim1_fsel(11); /* 6320 0.25KHz */
#endif
led_init_flag[1] = true;
if (level) {
#ifdef PMIC_MT6325
upmu_set_rg_bst_drv_1m_ck_pdn(0x0);
upmu_set_isink_rsv2_isink1(0x1);
upmu_set_isinks_ch1_en(0x01);
#endif
} else {
#ifdef PMIC_MT6325
upmu_set_isinks_ch1_en(0x00);
upmu_set_isink_rsv2_isink1(0x00);
#endif
}
return 0;
}else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK2) {
if (first_time == true) {
#ifdef PMIC_MT6325
upmu_set_isinks_ch0_en(0); /* sw workround for sync leds status */
upmu_set_isink_rsv2_isink0(0x00);
upmu_set_isinks_ch1_en(0);
upmu_set_isink_rsv2_isink1(0x00);
#endif
first_time = false;
}
#ifdef PMIC_MT6325
upmu_set_isinks_ch2_mode(ISINK_PWM_MODE);
upmu_set_isinks_ch2_step(0x3); /* 16mA */
upmu_set_isink_dim2_duty(15);
upmu_set_isink_dim2_fsel(11); /* 6320 0.25KHz */
#endif
led_init_flag[2] = true;
if (level) {
#ifdef PMIC_MT6325
upmu_set_rg_bst_drv_1m_ck_pdn(0x0);
upmu_set_isink_rsv2_isink2(0x1);
upmu_set_isinks_ch2_en(0x01);
#endif
} else {
#ifdef PMIC_MT6325
upmu_set_isinks_ch2_en(0x00);
upmu_set_isink_rsv2_isink2(0x00);
#endif
}
return 0;
}else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK01) {
#ifdef PMIC_MT6325
upmu_set_isinks_ch0_mode(ISINK_PWM_MODE);
upmu_set_isinks_ch0_step(0x0); /* 4mA */
upmu_set_isink_dim0_duty(1);
upmu_set_isink_dim0_fsel(1); /* 6320 1.5KHz */
upmu_set_isinks_ch1_mode(ISINK_PWM_MODE);
upmu_set_isinks_ch1_step(0x3); /* 4mA */
upmu_set_isink_dim1_duty(15);
upmu_set_isink_dim1_fsel(11); /* 6320 0.25KHz */
#endif
led_init_flag[0] = true;
led_init_flag[1] = true;
if (level) {
#ifdef PMIC_MT6325
upmu_set_rg_bst_drv_1m_ck_pdn(0x0);
upmu_set_isinks_ch0_en(0x01);
upmu_set_isinks_ch1_en(0x01);
#endif
} else {
#ifdef PMIC_MT6325
upmu_set_isinks_ch0_en(0x00);
upmu_set_isinks_ch1_en(0x00);
#endif
}
return 0;
}
}