Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 漫談android系統5

漫談android系統5

編輯:關於Android編程

LED indicator 簡介

所謂的LED indicator就是手機上面充電發短信的燈,有時會亮綠燈,有時會亮紅燈,有時一起亮(橙燈),主要用於提醒用戶電量、短信、電話。

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 indicator一般為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的函數配置,其道理還是和前面的一致。

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的初始化就是建立一個device tree然後做各種資源配置,而這些資源配置全部來自於dtsi,最後建立一個work queue進行工作。

debug模式下點亮led

其實高通不可能讓你直接很好地完成一些事情,當你看完spec後,做的第一件事,就是通過高通給的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->cdev.brightness;
                rc = pwm_config(
                    led->mpp_cfg->pwm_cfg->pwm_dev,
                    duty_ns,
                    period_us * NSEC_PER_USEC);
            }
            if (rc < 0) {
                dev_err(&led->spmi_dev->dev, "Failed to " \
                    "configure pwm for new values\n");
                goto err_mpp_reg_write;
            }
        }

        if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
            pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
        else {
            if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
                led->cdev.brightness = LED_MPP_CURRENT_MIN;
            else {
                /*
                 * PMIC supports LED intensity from 5mA - 40mA
                 * in steps of 5mA. Brightness is rounded to
                 * 5mA or nearest lower supported values
                 */
                led->cdev.brightness /= LED_MPP_CURRENT_MIN;
                led->cdev.brightness *= LED_MPP_CURRENT_MIN;
            }

            val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;

            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");
                goto err_mpp_reg_write;
            }
        }

        val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
            (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);

        rc = qpnp_led_masked_write(led,
            LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
            val);
        if (rc) {
            dev_err(&led->spmi_dev->dev,
                    "Failed to write led mode reg\n");
            goto err_mpp_reg_write;
        }

        rc = qpnp_led_masked_write(led,
                LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
                LED_MPP_EN_ENABLE);
        if (rc) {
            dev_err(&led->spmi_dev->dev,
                    "Failed to write led enable " \
                    "reg\n");
            goto err_mpp_reg_write;
        }
    } else {
        if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
            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;
            pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
        }
        rc = qpnp_led_masked_write(led,
                    LED_MPP_MODE_CTRL(led->base),
                    LED_MPP_MODE_MASK,
                    LED_MPP_MODE_DISABLE);
        if (rc) {
            dev_err(&led->spmi_dev->dev,
                    "Failed to write led mode reg\n");
            goto err_mpp_reg_write;
        }

        rc = qpnp_led_masked_write(led,
                    LED_MPP_EN_CTRL(led->base),
                    LED_MPP_EN_MASK,
                    LED_MPP_EN_DISABLE);
        if (rc) {
            dev_err(&led->spmi_dev->dev,
                    "Failed to write led enable reg\n");
            goto err_mpp_reg_write;
        }

        if (led->mpp_cfg->mpp_reg && led->mpp_cfg->enable) {
            rc = regulator_disable(led->mpp_cfg->mpp_reg);
            if (rc) {
                dev_err(&led->spmi_dev->dev,
                    "MPP regulator disable failed(%d)\n",
                    rc);
                return rc;
            }

            rc = regulator_set_voltage(led->mpp_cfg->mpp_reg,
                        0, led->mpp_cfg->max_uV);
            if (rc) {
                dev_err(&led->spmi_dev->dev,
                    "MPP regulator voltage set failed(%d)\n",
                    rc);
                return rc;
            }
        }

        led->mpp_cfg->enable = false;
    }

    if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
        led->mpp_cfg->pwm_cfg->blinking = false;
    qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));

    return 0;

err_mpp_reg_write:
    if (led->mpp_cfg->mpp_reg)
        regulator_disable(led->mpp_cfg->mpp_reg);
err_reg_enable:
    if (led->mpp_cfg->mpp_reg)
        regulator_set_voltage(led->mpp_cfg->mpp_reg, 0,
                            led->mpp_cfg->max_uV);
    led->mpp_cfg->enable = false;

    return rc;
}

它會在workqueue不停地去設置LED的狀態。

pwm的設定

pmi8953內部只有一個pwm。因此需要公用一個pwm。pwm是如何設定,產生的將會在以後解釋。

將DTEST1連接到pwm上

在kernel/driver/pwm/pwm-qpnp.c

它的probe函數中加入

static int qpnp_pwm_probe(struct spmi_device *spmi)
{
    struct qpnp_pwm_chip    *pwm_chip;
    int         rc;
    u8 value;

    printk("%s\n",__FUNCTION__);

    pwm_chip = kzalloc(sizeof(*pwm_chip), GFP_KERNEL);
    if (pwm_chip == NULL) {
        pr_err("kzalloc() failed.\n");
        return -ENOMEM;
    }

    spin_lock_init(&pwm_chip->lpg_lock);

    pwm_chip->spmi_dev = spmi;
    dev_set_drvdata(&spmi->dev, pwm_chip);

    rc = qpnp_parse_dt_config(spmi, pwm_chip);

    if (rc)
        goto failed_config;

    pwm_chip->chip.dev = &spmi->dev;
    pwm_chip->chip.ops = &qpnp_pwm_ops;
    pwm_chip->chip.base = -1;
    pwm_chip->chip.npwm = 1;

    rc = pwmchip_add(&pwm_chip->chip);
    if (rc < 0) {
        pr_err("pwmchip_add() failed: %d\n", rc);
        goto failed_insert;
    }

    if (pwm_chip->channel_owner)
        pwm_chip->chip.pwms[0].label = pwm_chip->channel_owner;
//++++++++++++++++++++  
    value=0xA5;
    spmi_ext_register_writel(pwm_chip->spmi_dev->ctrl, 3, 0xB0D0, &value, 1 );
    value=0x01;
    spmi_ext_register_writel(pwm_chip->spmi_dev->ctrl, 3, 0xB0E2, &value, 1 );
//--------------------
    return 0;

failed_insert:
    kfree(pwm_chip->lpg_config.lut_config.duty_pct_list);
failed_config:
    dev_set_drvdata(&spmi->dev, NULL);
    kfree(pwm_chip);
    return rc;
}

寄存器為何要如此寫,這是開了case給高通才得知,這部分的內容高通沒有任何spce。

取消pwm設備檢查,使red與green le都可以使用pwm

在kernel/driver/pwm/core.c

static int pwm_device_request(struct pwm_device *pwm, const char *label)
{
    int err;

//  remove this flag to support led green and red request pwm devices twice
    //if (test_bit(PWMF_REQUESTED, &pwm->flags))
        //return -EBUSY;

    if (!try_module_get(pwm->chip->ops->owner))
        return -ENODEV;

    if (pwm->chip->ops->request) {
        err = pwm->chip->ops->request(pwm->chip, pwm);
        if (err) {
            module_put(pwm->chip->ops->owner);
            return err;
        }
    }

    set_bit(PWMF_REQUESTED, &pwm->flags);
    pwm->label = label;

    return 0;
}

配置dtsi

在源碼中自帶的文檔就對這個配置說明了。

Optional properties for MPP LED:
- linux,default-trigger: trigger the led from external modules such as display
- qcom,default-state: default state of the led, should be "on" or "off"
- qcom,source-sel: select power source, default 1 (enabled)
- qcom,mode-ctrl: select operation mode, default 0x60 = Mode Sink
- qcom,mode: mode the led should operate in, options "pwm", "lpg" and "manual"
- qcom,vin-ctrl: select input source, supported values are 0 to 3
- qcom,use-blink: Use blink sysfs entry for switching into lpg mode.  For optimal use, set default mode to pwm.  All required lpg parameters must be supplied.
- qcom,min-brightness - Lowest possible brightness supported on this LED other than 0.
- qcom,current-setting: default current value for wled used as button backlight in mA
- mpp-power-supply: regulator support for MPP LED
- qcom,mpp-power-max-voltage - maximum voltage for MPP LED regulator. This should not be specified when no regulator is in use.
- qcom,mpp-power-min-voltage - minimum voltage for MPP LED regulator. This should not be specified when no regulator is in use.
Required properties for PWM mode only:
- pwms: Use the phandle of pwm device
- qcom,pwm-us: time the pwm device will modulate at (us) 
驗證led
查看是否red與green的節點是否建立成功
cd /sys/class/leds/
//請查看是否出現了red與green目錄
//可以通過控制目錄中的pwm_us與brightness節點進行驗證
關led 0
echo 100 > /sys/class/leds/red/pwm_us
echo  0    > /sys/class/leds/red/brightness
echo 100 > /sys/class/leds/green/pwm_us
echo  0    > /sys/class/leds/green/brightness 
開紅燈
echo 100 > /sys/class/leds/green/pwm_us
echo  0    > /sys/class/leds/green/brightness
echo 100 > /sys/class/leds/red/pwm_us
echo  255   > /sys/class/leds/red/brightness
開綠燈
echo 100 > /sys/class/leds/red/pwm_us
echo  0  > /sys/class/leds/red/brightness
echo 100 > /sys/class/leds/green/pwm_us
echo  255    > /sys/class/leds/green/brightness
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved