diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt index 8078abf1923..8c91db01af2 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt @@ -19,7 +19,7 @@ Required properties for each child node, WLED, Flash and RGB: - qcom,max-current : maximum current that the LED can sustain in mA - linux,name : name of the led that is used in led framework -Optional properties for each child node, WLED, Flash, MPP and RGB: +Optional properties for each child node, WLED, Flash, MPP, RGB and KPDBL: - qcom,in-order-command-processing : specify if user space requests leds in order WLED is primarily used as display backlight. Display subsystem uses @@ -121,6 +121,7 @@ Optional properties for keypad backlight: for vph_pwr. - qcom,row-src-en: specify to enable row source - qcom,always-on: specify if the module has to be always on +- 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. Required properties for PWM mode only: - qcom,pwm-channel: pwm channel the led will operate on diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index ce559ca0642..059e4883609 100755 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -434,6 +434,7 @@ struct flash_config_data { * @always_on - always on row * @lut_params - lut parameters to be used by pwm driver * @duty_cycles - duty cycles for lut + * @pwm_mode - pwm mode in use */ struct kpdbl_config_data { struct pwm_config_data *pwm_cfg; @@ -443,6 +444,7 @@ struct kpdbl_config_data { bool always_on; struct pwm_duty_cycles *duty_cycles; struct lut_params lut_params; + u8 pwm_mode; }; /** @@ -1835,7 +1837,7 @@ static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg, struct spmi_device *spmi_dev, const char *name) { - int rc, start_idx, idx_len; + int rc, start_idx, idx_len, lut_max_size; if (pwm_cfg->pwm_channel != -1) { pwm_cfg->pwm_dev = @@ -1857,14 +1859,18 @@ static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg, idx_len = pwm_cfg->duty_cycles->num_duty_pcts; - if (idx_len >= PWM_LUT_MAX_SIZE && - start_idx) { + if (strnstr(name, "kpdbl", sizeof("kpdbl"))) + lut_max_size = PWM_GPLED_LUT_MAX_SIZE; + else + lut_max_size = PWM_LUT_MAX_SIZE; + + if (idx_len >= lut_max_size && start_idx) { dev_err(&spmi_dev->dev, "Wrong LUT size or index\n"); return -EINVAL; } - if ((start_idx + idx_len) > - PWM_LUT_MAX_SIZE) { + + if ((start_idx + idx_len) > lut_max_size) { dev_err(&spmi_dev->dev, "Exceed LUT limit\n"); return -EINVAL; @@ -1914,6 +1920,9 @@ static ssize_t pwm_us_store(struct device *dev, case QPNP_ID_RGB_BLUE: pwm_cfg = led->rgb_cfg->pwm_cfg; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for pwm_us\n"); @@ -1966,6 +1975,9 @@ static ssize_t pause_lo_store(struct device *dev, case QPNP_ID_RGB_BLUE: pwm_cfg = led->rgb_cfg->pwm_cfg; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for pause lo\n"); @@ -2018,6 +2030,9 @@ static ssize_t pause_hi_store(struct device *dev, case QPNP_ID_RGB_BLUE: pwm_cfg = led->rgb_cfg->pwm_cfg; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for pause hi\n"); @@ -2070,6 +2085,9 @@ static ssize_t start_idx_store(struct device *dev, case QPNP_ID_RGB_BLUE: pwm_cfg = led->rgb_cfg->pwm_cfg; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for start idx\n"); @@ -2123,6 +2141,9 @@ static ssize_t ramp_step_ms_store(struct device *dev, case QPNP_ID_RGB_BLUE: pwm_cfg = led->rgb_cfg->pwm_cfg; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for ramp step\n"); @@ -2175,6 +2196,9 @@ static ssize_t lut_flags_store(struct device *dev, case QPNP_ID_RGB_BLUE: pwm_cfg = led->rgb_cfg->pwm_cfg; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for lut flags\n"); @@ -2231,6 +2255,10 @@ static ssize_t duty_pcts_store(struct device *dev, pwm_cfg = led->rgb_cfg->pwm_cfg; max_duty_pcts = PWM_LUT_MAX_SIZE; break; + case QPNP_ID_KPDBL: + pwm_cfg = led->kpdbl_cfg->pwm_cfg; + max_duty_pcts = PWM_GPLED_LUT_MAX_SIZE; + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for duty pcts\n"); @@ -2300,12 +2328,17 @@ static void led_blink(struct qpnp_led_data *led, pwm_cfg->blinking = true; if (led->id == QPNP_ID_LED_MPP) led->mpp_cfg->pwm_mode = LPG_MODE; + else if (led->id == QPNP_ID_KPDBL) + led->kpdbl_cfg->pwm_mode = LPG_MODE; pwm_cfg->mode = LPG_MODE; } else { pwm_cfg->blinking = false; pwm_cfg->mode = pwm_cfg->default_mode; if (led->id == QPNP_ID_LED_MPP) led->mpp_cfg->pwm_mode = pwm_cfg->default_mode; + else if (led->id == QPNP_ID_KPDBL) + led->kpdbl_cfg->pwm_mode = + pwm_cfg->default_mode; } pwm_free(pwm_cfg->pwm_dev); qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name); @@ -2320,6 +2353,11 @@ static void led_blink(struct qpnp_led_data *led, if (rc < 0) dev_err(&led->spmi_dev->dev, "MPP set brightness failed (%d)\n", rc); + } else if (led->id == QPNP_ID_KPDBL) { + rc = qpnp_kpdbl_set(led); + if (rc < 0) + dev_err(&led->spmi_dev->dev, + "KPDBL set brightness failed (%d)\n", rc); } } mutex_unlock(&led->lock); @@ -2349,6 +2387,9 @@ static ssize_t blink_store(struct device *dev, case QPNP_ID_RGB_BLUE: led_blink(led, led->rgb_cfg->pwm_cfg); break; + case QPNP_ID_KPDBL: + led_blink(led, led->kpdbl_cfg->pwm_cfg); + break; default: dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n"); return -EINVAL; @@ -3003,9 +3044,10 @@ static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg, struct device_node *node) { struct property *prop; - int rc, i; + int rc, i, lut_max_size; u32 val; u8 *temp_cfg; + const char *led_label; rc = of_property_read_u32(node, "qcom,pwm-channel", &val); if (!rc) @@ -3049,9 +3091,22 @@ static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg, goto bad_lpg_params; } + rc = of_property_read_string(node, "label", &led_label); + + if (rc < 0) { + dev_err(&spmi_dev->dev, + "Failure reading label, rc = %d\n", rc); + return rc; + } + + if (strcmp(led_label, "kpdbl") == 0) + lut_max_size = PWM_GPLED_LUT_MAX_SIZE; + else + lut_max_size = PWM_LUT_MAX_SIZE; + pwm_cfg->duty_cycles->duty_pcts = devm_kzalloc(&spmi_dev->dev, - sizeof(int) * PWM_LUT_MAX_SIZE, + sizeof(int) * lut_max_size, GFP_KERNEL); if (!pwm_cfg->duty_cycles->duty_pcts) { dev_err(&spmi_dev->dev, @@ -3062,7 +3117,7 @@ static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg, pwm_cfg->old_duty_pcts = devm_kzalloc(&spmi_dev->dev, - sizeof(int) * PWM_LUT_MAX_SIZE, + sizeof(int) * lut_max_size, GFP_KERNEL); if (!pwm_cfg->old_duty_pcts) { dev_err(&spmi_dev->dev, @@ -3570,6 +3625,29 @@ static int __devinit qpnp_leds_probe(struct spmi_device *spmi) 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 */ @@ -3659,6 +3737,20 @@ static int __devexit qpnp_leds_remove(struct spmi_device *spmi) sysfs_remove_group(&led_array[i].cdev.dev->\ kobj, &lpg_attr_group); 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",