程序员人生 网站导航

漫谈android系统(5)点亮LED

栏目:综合技术时间:2016-07-14 08:12:00

LED indicator 简介

所谓的LED indicator就是手机上面充电发短信的灯,有时会亮绿灯,有时会亮红灯,有时1起亮(橙灯),主要用于提示用户电量、短信、电话。

bring up LED

要想点亮LED,就要了解其电路。
以下是我工作中案子的led。

VPH_PWR是系统默许电。处于高电平状态。

这里可以看到2个LED遭到pmi8953的mpp2\mmp4控制。下面重要的是如何将mmp2\mpp4置起来。

bring up LED的步骤

现在我们基本上是采取pwm来控制LED。那末我们是如何来控制的呢?

通过soc->pmi8952(内部pwm)->mpp2/mpp4的方式去控制,那末咋么控制,首先你得晓得如何通过它的协议对这2pin做操作的。

根据pmi8952的寄存器描写文件,可以知道pmi8952这颗ic是挂在spi总线上的。

以下是我大致总结出来的步骤

  • 查看spec肯定其如何IC如何挂载,mpp2/mpp4的地址与描写、
  • 肯定led是如何初始化的
  • debug方式点亮LED,保证硬件的可靠性
  • led是如何控制mpp2/mpp4的
  • pmi8952的内部寄存器是如何控制pwm的
  • 修改dtsi
dtsi修改

具体的dtsi在这里展现。以供后面解说为什么要这样做!

qcom,leds@a100 { status = "okay"; compatible = "qcom,leds-qpnp"; reg = <0xa100 0x100>; label = "mpp"; qcom,led_mpp_2{ label = "mpp"; linux,name = "red"; linux,default-trigger = "none"; qcom,default-state = "off"; qcom,max-current = <40>; qcom,current-setting = <5>; qcom,id = <6>; qcom,mode = "pwm"; qcom,source-sel = <8>; qcom,mode-ctrl = <0x60>; qcom,vin-ctrl=<3>; pwms = <&pmi8950_pwm 0 0>; qcom,pwm-us = <100>; //setup 0.0001s }; }; qcom,leds@a300 { status = "okay"; compatible = "qcom,leds-qpnp"; reg = <0xa300 0x100>; label = "mpp"; qcom,led_mpp_2{ label = "mpp"; linux,name = "green"; linux,default-trigger = "none"; qcom,default-state = "off"; qcom,max-current = <40>; qcom,current-setting = <5>; qcom,id = <6>; qcom,mode = "pwm"; qcom,source-sel = <8>; qcom,mode-ctrl = <0x60>; qcom,vin-ctrl=<3>; pwms = <&pmi8950_pwm 0 0>; qcom,pwm-us = <100>; //setup 0.0001s }; };

LED初始化启动

在android源码/kernel/driver/leds/led_qpnp.c就能够看出它是如何做初始化的动作的。

下面是led driver 的devices tree

static struct spmi_driver qpnp_leds_driver = { .driver = { .name = "qcom,leds-qpnp", .of_match_table = spmi_match_table, }, .probe = qpnp_leds_probe, .remove = qpnp_leds_remove, }; static int __init qpnp_led_init(void) { return spmi_driver_register(&qpnp_leds_driver); } module_init(qpnp_led_init);

可以看到其name为qcom,leds-qpnp,那末它将会去找dts中的qcom,leds-qpnp字段做解析动作

probe函数:
//当传进来的值就已是qcom,leds-qpnp了 static int qpnp_leds_probe(struct spmi_device *spmi) { struct qpnp_led_data *led, *led_array; struct resource *led_resource; struct device_node *node, *temp; int rc, i, num_leds = 0, parsed_leds = 0; const char *led_label; bool regulator_probe = false; printk("[LED] qpnp_leds_probe\n"); //获得LEDdevices tree spi节点并且遍历节点+++++ node = spmi->dev.of_node; if (node == NULL) return -ENODEV; temp = NULL; while ((temp = of_get_next_child(node, temp))) num_leds++; if (!num_leds) return -ECHILD; //为每个led节点分配空间,资源等等 led_array = devm_kzalloc(&spmi->dev, (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL); if (!led_array) { dev_err(&spmi->dev, "Unable to allocate memory\n"); return -ENOMEM; } for_each_child_of_node(node, temp) { led = &led_array[parsed_leds]; led->num_leds = num_leds; led->spmi_dev = spmi; led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0); if (!led_resource) { dev_err(&spmi->dev, "Unable to get LED base address\n"); rc = -ENXIO; goto fail_id_check; } led->base = led_resource->start; rc = of_property_read_string(temp, "label", &led_label);//找到dtsi的label项给变量led_label if (rc < 0) { dev_err(&led->spmi_dev->dev, "Failure reading label, rc = %d\n", rc); goto fail_id_check; } rc = of_property_read_string(temp, "linux,name", &led->cdev.name);//找到dtsi的linux,name项给变量led->cdev.name if (rc < 0) { dev_err(&led->spmi_dev->dev, "Failure reading led name, rc = %d\n", rc); goto fail_id_check; } printk("[LED] linux,name : %s\n", led->cdev.name); if(!strcmp(led->cdev.name,"button-backlight"))//判断其是否是虚拟按键的led { printk("[LED] : Copy Led structure\n"); copy_led = led; } rc = of_property_read_u32(temp, "qcom,max-current", &led->max_current);//读取最大电量 if (rc < 0) { dev_err(&led->spmi_dev->dev, "Failure reading max_current, rc = %d\n", rc); goto fail_id_check; } rc = of_property_read_u32(temp, "qcom,id", &led->id);//读取id,如果是led indicator1般为6.具体为什么是6后面分析 if (rc < 0) { dev_err(&led->spmi_dev->dev, "Failure reading led id, rc = %d\n", rc); goto fail_id_check; } rc = qpnp_get_common_configs(led, temp);//这个函数对我们不大 if (rc) { dev_err(&led->spmi_dev->dev, "Failure reading common led configuration," \ " rc = %d\n", rc); goto fail_id_check; } led->cdev.brightness_set = qpnp_led_set; led->cdev.brightness_get = qpnp_led_get; //比对led_label,很明显我们走的是mpp方式,具体他如何实现的,我也将贴出来,但在此不做详解 if (strncmp(led_label, "wled", sizeof("wled")) == 0) { rc = qpnp_get_config_wled(led, temp); if (rc < 0) { dev_err(&led->spmi_dev->dev, "Unable to read wled config data\n"); goto fail_id_check; } } else if (strncmp(led_label, "flash", sizeof("flash")) == 0) { if (!of_find_property(node, "flash-boost-supply", NULL)) regulator_probe = true; rc = qpnp_get_config_flash(led, temp, ®ulator_probe); if (rc < 0) { dev_err(&led->spmi_dev->dev, "Unable to read flash config data\n"); goto fail_id_check; } } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) { rc = qpnp_get_config_rgb(led, temp); if (rc < 0) { dev_err(&led->spmi_dev->dev, "Unable to read rgb config data\n"); goto fail_id_check; } } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) { rc = qpnp_get_config_mpp(led, temp); if (rc < 0) { dev_err(&led->spmi_dev->dev, "Unable to read mpp config data\n"); goto fail_id_check; } } else if (strcmp(led_label, "gpio") == 0) { rc = qpnp_get_config_gpio(led, temp); if (rc < 0) { dev_err(&led->spmi_dev->dev, "Unable to read gpio config data\n"); goto fail_id_check; } } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) { bitmap_zero(kpdbl_leds_in_use, NUM_KPDBL_LEDS); is_kpdbl_master_turn_on = false; rc = qpnp_get_config_kpdbl(led, temp); if (rc < 0) { dev_err(&led->spmi_dev->dev, "Unable to read kpdbl config data\n"); goto fail_id_check; } } else { dev_err(&led->spmi_dev->dev, "No LED matching label\n"); rc = -EINVAL; goto fail_id_check; } //既不是flash led那末在此会新建锁,并作work queue的动作 if (led->id != QPNP_ID_FLASH1_LED0 && led->id != QPNP_ID_FLASH1_LED1) mutex_init(&led->lock); led->in_order_command_processing = of_property_read_bool (temp, "qcom,in-order-command-processing"); if (led->in_order_command_processing) { /* * the command order from user space needs to be * maintained use ordered workqueue to prevent * concurrency */ led->workqueue = alloc_ordered_workqueue ("led_workqueue", 0); if (!led->workqueue) { rc = -ENOMEM; goto fail_id_check; } } INIT_WORK(&led->work, qpnp_led_work); //可以初始化led rc = qpnp_led_initialize(led); if (rc < 0) goto fail_id_check; rc = qpnp_led_set_max_brightness(led); if (rc < 0) goto fail_id_check; rc = led_classdev_register(&spmi->dev, &led->cdev); if (rc) { dev_err(&spmi->dev, "unable to register led %d,rc=%d\n", led->id, rc); goto fail_id_check; } if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) { rc = sysfs_create_group(&led->cdev.dev->kobj, &led_attr_group); if (rc) goto fail_id_check; } //看mpp进入哪一种mode,明显选择的是pwm if (led->id == QPNP_ID_LED_MPP) { if (!led->mpp_cfg->pwm_cfg) break; if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &pwm_attr_group); if (rc) goto fail_id_check; } if (led->mpp_cfg->pwm_cfg->use_blink) { rc = sysfs_create_group(&led->cdev.dev->kobj, &blink_attr_group); if (rc) goto fail_id_check; rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); if (rc) goto fail_id_check; } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); if (rc) goto fail_id_check; } } else if ((led->id == QPNP_ID_RGB_RED) || (led->id == QPNP_ID_RGB_GREEN) || (led->id == QPNP_ID_RGB_BLUE)) { if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &pwm_attr_group); if (rc) goto fail_id_check; } if (led->rgb_cfg->pwm_cfg->use_blink) { rc = sysfs_create_group(&led->cdev.dev->kobj, &blink_attr_group); if (rc) goto fail_id_check; rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); if (rc) goto fail_id_check; } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); if (rc) goto fail_id_check; } } else if (led->id == QPNP_ID_KPDBL) { if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &pwm_attr_group); if (rc) goto fail_id_check; } if (led->kpdbl_cfg->pwm_cfg->use_blink) { rc = sysfs_create_group(&led->cdev.dev->kobj, &blink_attr_group); if (rc) goto fail_id_check; rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); if (rc) goto fail_id_check; } else if (led->kpdbl_cfg->pwm_cfg->mode == LPG_MODE) { rc = sysfs_create_group(&led->cdev.dev->kobj, &lpg_attr_group); if (rc) goto fail_id_check; } } /* configure default state */ if (led->default_on) { led->cdev.brightness = led->cdev.max_brightness; __qpnp_led_work(led, led->cdev.brightness); if (led->turn_off_delay_ms > 0) qpnp_led_turn_off(led); } else led->cdev.brightness = LED_OFF; parsed_leds++; } dev_set_drvdata(&spmi->dev, led_array); return 0; fail_id_check: for (i = 0; i < parsed_leds; i++) { if (led_array[i].id != QPNP_ID_FLASH1_LED0 && led_array[i].id != QPNP_ID_FLASH1_LED1) mutex_destroy(&led_array[i].lock); if (led_array[i].in_order_command_processing) destroy_workqueue(led_array[i].workqueue); led_classdev_unregister(&led_array[i].cdev); } return rc; }

以下是led_label选择为mpp的函数配置,其道理还是和前面的1致。

static int qpnp_get_config_mpp(struct qpnp_led_data *led, struct device_node *node) { int rc; u32 val; u8 led_mode; const char *mode; led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev, sizeof(struct mpp_config_data), GFP_KERNEL); if (!led->mpp_cfg) { dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n"); return -ENOMEM; } if (of_find_property(of_get_parent(node), "mpp-power-supply", NULL)) { led->mpp_cfg->mpp_reg = regulator_get(&led->spmi_dev->dev, "mpp-power"); if (IS_ERR(led->mpp_cfg->mpp_reg)) { rc = PTR_ERR(led->mpp_cfg->mpp_reg); dev_err(&led->spmi_dev->dev, "MPP regulator get failed(%d)\n", rc); return rc; } } if (led->mpp_cfg->mpp_reg) { rc = of_property_read_u32(of_get_parent(node), "qcom,mpp-power-max-voltage", &val); if (!rc) led->mpp_cfg->max_uV = val; else goto err_config_mpp; rc = of_property_read_u32(of_get_parent(node), "qcom,mpp-power-min-voltage", &val); if (!rc) led->mpp_cfg->min_uV = val; else goto err_config_mpp; } else { rc = of_property_read_u32(of_get_parent(node), "qcom,mpp-power-max-voltage", &val); if (!rc) dev_warn(&led->spmi_dev->dev, "No regulator specified\n"); rc = of_property_read_u32(of_get_parent(node), "qcom,mpp-power-min-voltage", &val); if (!rc) dev_warn(&led->spmi_dev->dev, "No regulator specified\n"); } led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN; rc = of_property_read_u32(node, "qcom,current-setting", &val); if (!rc) { if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN) led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN; else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX) led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX; else led->mpp_cfg->current_setting = (u8) val; } else if (rc != -EINVAL) goto err_config_mpp; led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT; rc = of_property_read_u32(node, "qcom,source-sel", &val); if (!rc) led->mpp_cfg->source_sel = (u8) val; else if (rc != -EINVAL) goto err_config_mpp; led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK; rc = of_property_read_u32(node, "qcom,mode-ctrl", &val); if (!rc) led->mpp_cfg->mode_ctrl = (u8) val; else if (rc != -EINVAL) goto err_config_mpp; led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT; rc = of_property_read_u32(node, "qcom,vin-ctrl", &val); if (!rc) led->mpp_cfg->vin_ctrl = (u8) val; else if (rc != -EINVAL) goto err_config_mpp; led->mpp_cfg->min_brightness = 0; rc = of_property_read_u32(node, "qcom,min-brightness", &val); if (!rc) led->mpp_cfg->min_brightness = (u8) val; else if (rc != -EINVAL) goto err_config_mpp; rc = of_property_read_string(node, "qcom,mode", &mode); if (!rc) { led_mode = qpnp_led_get_mode(mode); led->mpp_cfg->pwm_mode = led_mode; if (led_mode == MANUAL_MODE) return MANUAL_MODE; else if (led_mode == -EINVAL) { dev_err(&led->spmi_dev->dev, "Selected mode not " \ "supported for mpp.\n"); rc = -EINVAL; goto err_config_mpp; } led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev, sizeof(struct pwm_config_data), GFP_KERNEL); if (!led->mpp_cfg->pwm_cfg) { dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n"); rc = -ENOMEM; goto err_config_mpp; } led->mpp_cfg->pwm_cfg->mode = led_mode; led->mpp_cfg->pwm_cfg->default_mode = led_mode; } else return rc; rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node); if (rc < 0) goto err_config_mpp; return 0; err_config_mpp: if (led->mpp_cfg->mpp_reg) regulator_put(led->mpp_cfg->mpp_reg); return rc; }
remove函数

该函数的主要工作就是去掉work queue的动作释放内存,在此便不在详细解释了。

static int qpnp_leds_remove(struct spmi_device *spmi) { struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev); int i, parsed_leds = led_array->num_leds; for (i = 0; i < parsed_leds; i++) { cancel_work_sync(&led_array[i].work); if (led_array[i].id != QPNP_ID_FLASH1_LED0 && led_array[i].id != QPNP_ID_FLASH1_LED1) mutex_destroy(&led_array[i].lock); if (led_array[i].in_order_command_processing) destroy_workqueue(led_array[i].workqueue); led_classdev_unregister(&led_array[i].cdev); switch (led_array[i].id) { case QPNP_ID_WLED: break; case QPNP_ID_FLASH1_LED0: case QPNP_ID_FLASH1_LED1: if (led_array[i].flash_cfg->flash_reg_get) regulator_put(led_array[i].flash_cfg-> \ flash_boost_reg); if (led_array[i].flash_cfg->torch_enable) if (!led_array[i].flash_cfg->no_smbb_support) regulator_put(led_array[i]. flash_cfg->torch_boost_reg); sysfs_remove_group(&led_array[i].cdev.dev->kobj, &led_attr_group); break; case QPNP_ID_RGB_RED: case QPNP_ID_RGB_GREEN: case QPNP_ID_RGB_BLUE: if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE) sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &pwm_attr_group); if (led_array[i].rgb_cfg->pwm_cfg->use_blink) { sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &blink_attr_group); sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &lpg_attr_group); } else if (led_array[i].rgb_cfg->pwm_cfg->mode\ == LPG_MODE) sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &lpg_attr_group); break; case QPNP_ID_LED_MPP: if (!led_array[i].mpp_cfg->pwm_cfg) break; if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE) sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &pwm_attr_group); if (led_array[i].mpp_cfg->pwm_cfg->use_blink) { sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &blink_attr_group); sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &lpg_attr_group); } else if (led_array[i].mpp_cfg->pwm_cfg->mode\ == LPG_MODE) sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &lpg_attr_group); if (led_array[i].mpp_cfg->mpp_reg) regulator_put(led_array[i].mpp_cfg->mpp_reg); break; case QPNP_ID_KPDBL: if (led_array[i].kpdbl_cfg->pwm_cfg->mode == PWM_MODE) sysfs_remove_group(&led_array[i].cdev.dev-> kobj, &pwm_attr_group); if (led_array[i].kpdbl_cfg->pwm_cfg->use_blink) { sysfs_remove_group(&led_array[i].cdev.dev-> kobj, &blink_attr_group); sysfs_remove_group(&led_array[i].cdev.dev-> kobj, &lpg_attr_group); } else if (led_array[i].kpdbl_cfg->pwm_cfg->mode == LPG_MODE) sysfs_remove_group(&led_array[i].cdev.dev-> kobj, &lpg_attr_group); break; default: dev_err(&led_array[i].spmi_dev->dev, "Invalid LED(%d)\n", led_array[i].id); return -EINVAL; } } return 0; }
小结

从代码中可以看出led的初始化就是建立1个device tree然后做各种资源配置,而这些资源配置全部来自于dtsi,最后建立1个work queue进行工作。

debug模式下点亮led

其实高通不可能让你直接很好地完成1些事情,当你看完spec后,做的第1件事,就是通太高通给的debug方式进行分析,确保你的硬件没问题的情况下,做配置才是最安全的。

找bus

其实我们很容易找到高通将led挂在spi的哪一个bus上了。

比如:

&spmi_bus { qcom.pmi8950@2

很明显可以看到应当是在bus2上

debug
cd /sys/kernel/debug/spmi/spmi-0/ /*1、mpp2*/ echo 0x2a100 > address echo 0x100 > count cat data /*以上可以得到从地址0x2a100开始得到0x100个字节的数据*/ //enable mpp2 echo 0x2a146 > address echo 0x80 > data //查看地址0x2a108的状态是否是0x80?如果是 表示被置起来了 //set mpp2 output and set output 1 echo 0x2a140 > address echo 0x11 > data /*此时是将red led点亮*/

这个debug就是看spec轻易就可以做到的。

mpp2/mpp4控制led

qpnp_led_initialize函数将唤起mpp的配置。

static int qpnp_led_initialize(struct qpnp_led_data *led) { int rc = 0; switch (led->id) { case QPNP_ID_WLED: rc = qpnp_wled_init(led); if (rc) dev_err(&led->spmi_dev->dev, "WLED initialize failed(%d)\n", rc); break; case QPNP_ID_FLASH1_LED0: case QPNP_ID_FLASH1_LED1: rc = qpnp_flash_init(led); if (rc) dev_err(&led->spmi_dev->dev, "FLASH initialize failed(%d)\n", rc); break; case QPNP_ID_RGB_RED: case QPNP_ID_RGB_GREEN: case QPNP_ID_RGB_BLUE: rc = qpnp_rgb_init(led); if (rc) dev_err(&led->spmi_dev->dev, "RGB initialize failed(%d)\n", rc); break; case QPNP_ID_LED_MPP: rc = qpnp_mpp_init(led); if (rc) dev_err(&led->spmi_dev->dev, "MPP initialize failed(%d)\n", rc); break; case QPNP_ID_LED_GPIO: rc = qpnp_gpio_init(led); if (rc) dev_err(&led->spmi_dev->dev, "GPIO initialize failed(%d)\n", rc); break; case QPNP_ID_KPDBL: rc = qpnp_kpdbl_init(led); if (rc) dev_err(&led->spmi_dev->dev, "KPDBL initialize failed(%d)\n", rc); break; default: dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id); return -EINVAL; } return rc; } static int qpnp_get_common_configs(struct qpnp_led_data *led, struct device_node *node) { int rc; u32 val; const char *temp_string; led->cdev.default_trigger = LED_TRIGGER_DEFAULT; rc = of_property_read_string(node, "linux,default-trigger", &temp_string); if (!rc) led->cdev.default_trigger = temp_string; else if (rc != -EINVAL) return rc; led->default_on = false; rc = of_property_read_string(node, "qcom,default-state", &temp_string); if (!rc) { if (strncmp(temp_string, "on", sizeof("on")) == 0) led->default_on = true; } else if (rc != -EINVAL) return rc; led->turn_off_delay_ms = 0; rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val); if (!rc) led->turn_off_delay_ms = val; else if (rc != -EINVAL) return rc; return 0; }
那末mpp会如何初始化呢?

又是根据dtsi进行配置。

static int qpnp_mpp_init(struct qpnp_led_data *led) { int rc; u8 val; //判断配置没有超过限定的最大电流40与最小电流5 if (led->max_current < LED_MPP_CURRENT_MIN || led->max_current > LED_MPP_CURRENT_MAX) { dev_err(&led->spmi_dev->dev, "max current for mpp is not valid\n"); return -EINVAL; } val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1; if (val < 0) val = 0; //偏移量0x41的地方写入vin ctrl spec上有写明 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base), LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl); if (rc) { dev_err(&led->spmi_dev->dev, "Failed to write led vin control reg\n"); return rc; } //偏移量0x4c的地方写上sink ctrl rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base), LED_MPP_SINK_MASK, val); if (rc) { dev_err(&led->spmi_dev->dev, "Failed to write sink control reg\n"); return rc; } //初始化pwm if (led->mpp_cfg->pwm_mode != MANUAL_MODE) { rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev, led->cdev.name); if (rc) { dev_err(&led->spmi_dev->dev, "Failed to initialize pwm\n"); return rc; } } return 0;

相应的work queue也将会对mpp进行相干的设置

static void __qpnp_led_work(struct qpnp_led_data *led, enum led_brightness value) { int rc; if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) mutex_lock(&flash_lock); else mutex_lock(&led->lock); switch (led->id) { case QPNP_ID_WLED: rc = qpnp_wled_set(led); if (rc < 0) dev_err(&led->spmi_dev->dev, "WLED set brightness failed (%d)\n", rc); break; case QPNP_ID_FLASH1_LED0: case QPNP_ID_FLASH1_LED1: rc = qpnp_flash_set(led); if (rc < 0) dev_err(&led->spmi_dev->dev, "FLASH set brightness failed (%d)\n", rc); break; case QPNP_ID_RGB_RED: case QPNP_ID_RGB_GREEN: case QPNP_ID_RGB_BLUE: rc = qpnp_rgb_set(led); if (rc < 0) dev_err(&led->spmi_dev->dev, "RGB set brightness failed (%d)\n", rc); break; case QPNP_ID_LED_MPP: rc = qpnp_mpp_set(led); if (rc < 0) dev_err(&led->spmi_dev->dev, "MPP set brightness failed (%d)\n", rc); break; case QPNP_ID_LED_GPIO: rc = qpnp_gpio_set(led); if (rc < 0) dev_err(&led->spmi_dev->dev, "GPIO set brightness failed (%d)\n", rc); break; case QPNP_ID_KPDBL: rc = qpnp_kpdbl_set(led); if (rc < 0) dev_err(&led->spmi_dev->dev, "KPDBL set brightness failed (%d)\n", rc); break; default: dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id); break; } if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) mutex_unlock(&flash_lock); else mutex_unlock(&led->lock); }
} static int qpnp_mpp_set(struct qpnp_led_data *led) { int rc; u8 val; int duty_us, duty_ns, period_us; //设置背光 if (led->cdev.brightness) { if (led->mpp_cfg->mpp_reg && !led->mpp_cfg->enable) { rc = regulator_set_voltage(led->mpp_cfg->mpp_reg, led->mpp_cfg->min_uV, led->mpp_cfg->max_uV); if (rc) { dev_err(&led->spmi_dev->dev, "Regulator voltage set failed rc=%d\n", rc); return rc; } rc = regulator_enable(led->mpp_cfg->mpp_reg); if (rc) { dev_err(&led->spmi_dev->dev, "Regulator enable failed(%d)\n", rc); goto err_reg_enable; } } led->mpp_cfg->enable = true; if (led->cdev.brightness < led->mpp_cfg->min_brightness) { dev_warn(&led->spmi_dev->dev, "brightness is less than supported..." \ "set to minimum supported\n"); led->cdev.brightness = led->mpp_cfg->min_brightness; } //设置pwm if (led->mpp_cfg->pwm_mode != MANUAL_MODE) { if (!led->mpp_cfg->pwm_cfg->blinking) { led->mpp_cfg->pwm_cfg->mode = led->mpp_cfg->pwm_cfg->default_mode; led->mpp_cfg->pwm_mode = led->mpp_cfg->pwm_cfg->default_mode; } } if (led->mpp_cfg->pwm_mode == PWM_MODE) { /*config pwm for brightness scaling*/ period_us = led->mpp_cfg->pwm_cfg->pwm_period_us; if (period_us > INT_MAX / NSEC_PER_USEC) { duty_us = (period_us * led->cdev.brightness) / LED_FULL; rc = pwm_config_us( led->mpp_cfg->pwm_cfg->pwm_dev, duty_us, period_us); } else { duty_ns = ((period_us * NSEC_PER_USEC) / LED_FULL) * led
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐