多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > 漫談android系統(5)點亮LED

漫談android系統(5)點亮LED

來源:程序員人生   發布時間:2016-07-14 08:12:00 閱讀次數:5965次

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
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 亚洲 欧美 激情 另类 校园 | 高清视频在线观看+免费 | 色综合久久中文 | 亚洲成 人a影院青久在线观看 | 国产欧美又粗又猛又爽老 | 免费叼嘿视频 | 国产成人亚洲综合欧美一部 | 看一级特黄a大片日本片 | 中日韩欧美在线观看 | www免费视频com | 99综合网 | 亚洲 自拍 另类 制服在线 | 欧美一级特黄毛片视频 | 国产成人香蕉在线视频fuz | 欧美中文日韩 | 一级在线观看视频 | 国产v国产v片大片线观看网站 | 亚洲欧美一区二区三区另类 | 久久精品视频观看 | 美国一级免费 | 国产性一交一乱一伦一色一情 | 欧美videofree高清杂交 | 亚欧洲精品bb | 国产日韩精品欧美一区喷 | 影视精品网站入口 | 午夜免费视频网站 | 最近中文字幕大全高清在线 | 久草精品视频在线播放 | 久久色国产 | 亚洲男女一区二区三区出奶水了 | 欧美三级大片在线观看 | 国产欧美另类久久精品91 | 色吊丝永久在线观看最新 | 爱爱小视频日本 | 日本三级午夜理伦三级三 | 国内自拍网 | 美女午夜影院 | 欧美天天性影院 | 精品国产一区二区三区国产馆 | 国产一级淫片视频免费看 | 成 人国产在线观看高清不卡 |