From 88b7e9ffe59473afffecef71a937a7b12ccefb69 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 25 Feb 2019 14:23:41 +0200 Subject: leds: as3645a: Switch to fwnode property API Switch the as3645a from OF to the fwnode property API. Also add ACPI support. Signed-off-by: Sakari Ailus Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-as3645a.c | 93 +++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 41 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-as3645a.c b/drivers/leds/leds-as3645a.c index 98a69b1a43f9..b0df514992e1 100644 --- a/drivers/leds/leds-as3645a.c +++ b/drivers/leds/leds-as3645a.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -148,8 +148,8 @@ struct as3645a { struct v4l2_flash *vf; struct v4l2_flash *vfind; - struct device_node *flash_node; - struct device_node *indicator_node; + struct fwnode_handle *flash_node; + struct fwnode_handle *indicator_node; struct as3645a_config cfg; @@ -493,30 +493,31 @@ static int as3645a_detect(struct as3645a *flash) static int as3645a_parse_node(struct as3645a *flash, struct as3645a_names *names, - struct device_node *node) + struct fwnode_handle *fwnode) { struct as3645a_config *cfg = &flash->cfg; - struct device_node *child; + struct fwnode_handle *child; const char *name; int rval; - for_each_child_of_node(node, child) { + fwnode_for_each_child_node(fwnode, child) { u32 id = 0; - of_property_read_u32(child, "reg", &id); + fwnode_property_read_u32(child, "reg", &id); switch (id) { case AS_LED_FLASH: - flash->flash_node = of_node_get(child); + flash->flash_node = child; break; case AS_LED_INDICATOR: - flash->indicator_node = of_node_get(child); + flash->indicator_node = child; break; default: dev_warn(&flash->client->dev, "unknown LED %u encountered, ignoring\n", id); break; } + fwnode_handle_get(child); } if (!flash->flash_node) { @@ -524,42 +525,46 @@ static int as3645a_parse_node(struct as3645a *flash, return -ENODEV; } - rval = of_property_read_string(flash->flash_node, "label", &name); - if (!rval) + rval = fwnode_property_read_string(flash->flash_node, "label", &name); + if (!rval) { strlcpy(names->flash, name, sizeof(names->flash)); - else + } else if (is_of_node(fwnode)) { snprintf(names->flash, sizeof(names->flash), - "%pOFn:flash", node); + "%pOFn:flash", to_of_node(fwnode)); + } else { + dev_err(&flash->client->dev, "flash node has no label!\n"); + return -EINVAL; + } - rval = of_property_read_u32(flash->flash_node, "flash-timeout-us", - &cfg->flash_timeout_us); + rval = fwnode_property_read_u32(flash->flash_node, "flash-timeout-us", + &cfg->flash_timeout_us); if (rval < 0) { dev_err(&flash->client->dev, "can't read flash-timeout-us property for flash\n"); goto out_err; } - rval = of_property_read_u32(flash->flash_node, "flash-max-microamp", - &cfg->flash_max_ua); + rval = fwnode_property_read_u32(flash->flash_node, "flash-max-microamp", + &cfg->flash_max_ua); if (rval < 0) { dev_err(&flash->client->dev, "can't read flash-max-microamp property for flash\n"); goto out_err; } - rval = of_property_read_u32(flash->flash_node, "led-max-microamp", - &cfg->assist_max_ua); + rval = fwnode_property_read_u32(flash->flash_node, "led-max-microamp", + &cfg->assist_max_ua); if (rval < 0) { dev_err(&flash->client->dev, "can't read led-max-microamp property for flash\n"); goto out_err; } - of_property_read_u32(flash->flash_node, "voltage-reference", - &cfg->voltage_reference); + fwnode_property_read_u32(flash->flash_node, "voltage-reference", + &cfg->voltage_reference); - of_property_read_u32(flash->flash_node, "ams,input-max-microamp", - &cfg->peak); + fwnode_property_read_u32(flash->flash_node, "ams,input-max-microamp", + &cfg->peak); cfg->peak = AS_PEAK_mA_TO_REG(cfg->peak); if (!flash->indicator_node) { @@ -568,15 +573,21 @@ static int as3645a_parse_node(struct as3645a *flash, goto out_err; } - rval = of_property_read_string(flash->indicator_node, "label", &name); - if (!rval) + rval = fwnode_property_read_string(flash->indicator_node, "label", + &name); + if (!rval) { strlcpy(names->indicator, name, sizeof(names->indicator)); - else + } else if (is_of_node(fwnode)) { snprintf(names->indicator, sizeof(names->indicator), - "%pOFn:indicator", node); + "%pOFn:indicator", to_of_node(fwnode)); + } else { + dev_err(&flash->client->dev, "indicator node has no label!\n"); + return -EINVAL; + } - rval = of_property_read_u32(flash->indicator_node, "led-max-microamp", - &cfg->indicator_max_ua); + rval = fwnode_property_read_u32(flash->indicator_node, + "led-max-microamp", + &cfg->indicator_max_ua); if (rval < 0) { dev_err(&flash->client->dev, "can't read led-max-microamp property for indicator\n"); @@ -586,8 +597,8 @@ static int as3645a_parse_node(struct as3645a *flash, return 0; out_err: - of_node_put(flash->flash_node); - of_node_put(flash->indicator_node); + fwnode_handle_put(flash->flash_node); + fwnode_handle_put(flash->indicator_node); return rval; } @@ -668,14 +679,14 @@ static int as3645a_v4l2_setup(struct as3645a *flash) strlcpy(cfgind.dev_name, flash->iled_cdev.name, sizeof(cfg.dev_name)); flash->vf = v4l2_flash_init( - &flash->client->dev, of_fwnode_handle(flash->flash_node), - &flash->fled, NULL, &cfg); + &flash->client->dev, flash->flash_node, &flash->fled, NULL, + &cfg); if (IS_ERR(flash->vf)) return PTR_ERR(flash->vf); flash->vfind = v4l2_flash_indicator_init( - &flash->client->dev, of_fwnode_handle(flash->indicator_node), - &flash->iled_cdev, &cfgind); + &flash->client->dev, flash->indicator_node, &flash->iled_cdev, + &cfgind); if (IS_ERR(flash->vfind)) { v4l2_flash_release(flash->vf); return PTR_ERR(flash->vfind); @@ -690,7 +701,7 @@ static int as3645a_probe(struct i2c_client *client) struct as3645a *flash; int rval; - if (client->dev.of_node == NULL) + if (!dev_fwnode(&client->dev)) return -ENODEV; flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); @@ -699,7 +710,7 @@ static int as3645a_probe(struct i2c_client *client) flash->client = client; - rval = as3645a_parse_node(flash, &names, client->dev.of_node); + rval = as3645a_parse_node(flash, &names, dev_fwnode(&client->dev)); if (rval < 0) return rval; @@ -731,8 +742,8 @@ out_mutex_destroy: mutex_destroy(&flash->mutex); out_put_nodes: - of_node_put(flash->flash_node); - of_node_put(flash->indicator_node); + fwnode_handle_put(flash->flash_node); + fwnode_handle_put(flash->indicator_node); return rval; } @@ -751,8 +762,8 @@ static int as3645a_remove(struct i2c_client *client) mutex_destroy(&flash->mutex); - of_node_put(flash->flash_node); - of_node_put(flash->indicator_node); + fwnode_handle_put(flash->flash_node); + fwnode_handle_put(flash->indicator_node); return 0; } -- cgit v1.2.3-59-g8ed1b From 0eae60583f3229dc67dab952d2be76936c5f56c1 Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Wed, 6 Mar 2019 19:15:46 +0100 Subject: drivers: leds: Kconfig: pedantic cleanups Clean up spaces before tabs. Signed-off-by: Enrico Weigelt, metux IT consult Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/Kconfig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a72f97fca57b..2b5ae50f8c9a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -56,7 +56,7 @@ config LEDS_AAT1290 depends on OF depends on PINCTRL help - This option enables support for the LEDs on the AAT1290. + This option enables support for the LEDs on the AAT1290. config LEDS_AN30259A tristate "LED support for Panasonic AN30259A" @@ -413,13 +413,13 @@ config LEDS_CLEVO_MAIL This module can drive the mail LED for the following notebooks: - Clevo D400P - Clevo D410J - Clevo D410V - Clevo D400V/D470V (not tested, but might work) - Clevo M540N - Clevo M5x0N (not tested, but might work) - Positivo Mobile (Clevo M5x0V) + Clevo D400P + Clevo D410J + Clevo D410V + Clevo D400V/D470V (not tested, but might work) + Clevo M540N + Clevo M5x0N (not tested, but might work) + Positivo Mobile (Clevo M5x0V) If your model is not listed here you can try the "nodetect" module parameter. @@ -462,7 +462,7 @@ config LEDS_WM831X_STATUS depends on MFD_WM831X help This option enables support for the status LEDs of the WM831x - series of PMICs. + series of PMICs. config LEDS_WM8350 tristate "LED Support for WM8350 AudioPlus PMIC" -- cgit v1.2.3-59-g8ed1b From c90fbae3612d2effdfc9797e3a48063cea67182d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 15 Mar 2019 21:13:41 +0200 Subject: leds: pca963x: Revert "Add ACPI support" There is no evidence of officially registered ACPI IDs for these devices. Thus, revert commit f26dab9fc745540f7e30aa8840be6e49b9671080. Signed-off-by: Andy Shevchenko Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca963x.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 5c0908113e38..7780edff06b8 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -25,7 +25,6 @@ * or by adding the 'nxp,hw-blink' property to the DTS. */ -#include #include #include #include @@ -97,15 +96,6 @@ static const struct i2c_device_id pca963x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pca963x_id); -static const struct acpi_device_id pca963x_acpi_ids[] = { - { "PCA9632", pca9633 }, - { "PCA9633", pca9633 }, - { "PCA9634", pca9634 }, - { "PCA9635", pca9635 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, pca963x_acpi_ids); - struct pca963x_led; struct pca963x { @@ -376,16 +366,7 @@ static int pca963x_probe(struct i2c_client *client, struct pca963x_chipdef *chip; int i, err; - if (id) { - chip = &pca963x_chipdefs[id->driver_data]; - } else { - const struct acpi_device_id *acpi_id; - - acpi_id = acpi_match_device(pca963x_acpi_ids, &client->dev); - if (!acpi_id) - return -ENODEV; - chip = &pca963x_chipdefs[acpi_id->driver_data]; - } + chip = &pca963x_chipdefs[id->driver_data]; pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -496,7 +477,6 @@ static struct i2c_driver pca963x_driver = { .driver = { .name = "leds-pca963x", .of_match_table = of_match_ptr(of_pca963x_match), - .acpi_match_table = ACPI_PTR(pca963x_acpi_ids), }, .probe = pca963x_probe, .remove = pca963x_remove, -- cgit v1.2.3-59-g8ed1b From 5b6cd445dbef2e59174504e06f7918627d0ae8fd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 15 Mar 2019 21:13:42 +0200 Subject: leds: pca955x: Revert "Add ACPI support" There is no evidence of officially registered ACPI IDs for these devices. Thus, revert commit 44b3e31d540e917a4d2292b902ade63fa1748d9a. Signed-off-by: Andy Shevchenko Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca955x.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index f51b356d4426..f023729353b5 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -40,7 +40,6 @@ * bits the chip supports. */ -#include #include #include #include @@ -110,15 +109,6 @@ static const struct i2c_device_id pca955x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pca955x_id); -static const struct acpi_device_id pca955x_acpi_ids[] = { - { "PCA9550", pca9550 }, - { "PCA9551", pca9551 }, - { "PCA9552", pca9552 }, - { "PCA9553", pca9553 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, pca955x_acpi_ids); - struct pca955x { struct mutex lock; struct pca955x_led *leds; @@ -450,16 +440,7 @@ static int pca955x_probe(struct i2c_client *client, struct pca955x_platform_data *pdata; int ngpios = 0; - if (id) { - chip = &pca955x_chipdefs[id->driver_data]; - } else { - const struct acpi_device_id *acpi_id; - - acpi_id = acpi_match_device(pca955x_acpi_ids, &client->dev); - if (!acpi_id) - return -ENODEV; - chip = &pca955x_chipdefs[acpi_id->driver_data]; - } + chip = &pca955x_chipdefs[id->driver_data]; adapter = to_i2c_adapter(client->dev.parent); pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -602,7 +583,6 @@ static int pca955x_probe(struct i2c_client *client, static struct i2c_driver pca955x_driver = { .driver = { .name = "leds-pca955x", - .acpi_match_table = ACPI_PTR(pca955x_acpi_ids), .of_match_table = of_match_ptr(of_pca955x_match), }, .probe = pca955x_probe, -- cgit v1.2.3-59-g8ed1b From dd08e136f77bfbd1cc2917bafbd690ad5f40a61b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 20 Mar 2019 22:52:27 +0100 Subject: leds: lt3593: drop pdata handling code The only user of this driver in mainline has now moved to DTS, so the pdata code is no longer in use by anyone. Let's drop some dead code, and make the driver depend on CONFIG_OF. Signed-off-by: Daniel Mack Signed-off-by: Jacek Anaszewski --- drivers/leds/Kconfig | 1 + drivers/leds/leds-lt3593.c | 56 ---------------------------------------------- 2 files changed, 1 insertion(+), 56 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 2b5ae50f8c9a..0414adebb177 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -533,6 +533,7 @@ config LEDS_LT3593 tristate "LED driver for LT3593 controllers" depends on LEDS_CLASS depends on GPIOLIB || COMPILE_TEST + depends on OF help This option enables support for LEDs driven by a Linear Technology LT3593 controller. This controller uses a special one-wire pulse diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index de3623e0d094..54f0e5dbdbd0 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -60,50 +60,6 @@ static int lt3593_led_set(struct led_classdev *led_cdev, return 0; } -static struct lt3593_led_data *lt3593_led_probe_pdata(struct device *dev) -{ - struct gpio_led_platform_data *pdata = dev_get_platdata(dev); - const struct gpio_led *template = &pdata->leds[0]; - struct lt3593_led_data *led_data; - int ret, state; - - if (pdata->num_leds != 1) - return ERR_PTR(-EINVAL); - - led_data = devm_kzalloc(dev, sizeof(*led_data), GFP_KERNEL); - if (!led_data) - return ERR_PTR(-ENOMEM); - - led_data->cdev.name = template->name; - led_data->cdev.default_trigger = template->default_trigger; - led_data->cdev.brightness_set_blocking = lt3593_led_set; - - state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); - led_data->cdev.brightness = state ? LED_FULL : LED_OFF; - - if (!template->retain_state_suspended) - led_data->cdev.flags |= LED_CORE_SUSPENDRESUME; - - ret = devm_gpio_request_one(dev, template->gpio, state ? - GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, - template->name); - if (ret < 0) - return ERR_PTR(ret); - - led_data->gpiod = gpio_to_desc(template->gpio); - if (!led_data->gpiod) - return ERR_PTR(-EPROBE_DEFER); - - ret = devm_led_classdev_register(dev, &led_data->cdev); - if (ret < 0) - return ERR_PTR(ret); - - dev_info(dev, "registered LT3593 LED '%s' at GPIO %d\n", - template->name, template->gpio); - - return led_data; -} - static int lt3593_led_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -113,14 +69,6 @@ static int lt3593_led_probe(struct platform_device *pdev) enum gpiod_flags flags = GPIOD_OUT_LOW; const char *tmp; - if (dev_get_platdata(dev)) { - led_data = lt3593_led_probe_pdata(dev); - if (IS_ERR(led_data)) - return PTR_ERR(led_data); - - goto out; - } - if (!dev->of_node) return -ENODEV; @@ -171,20 +119,16 @@ static int lt3593_led_probe(struct platform_device *pdev) } led_data->cdev.dev->of_node = dev->of_node; - -out: platform_set_drvdata(pdev, led_data); return 0; } -#ifdef CONFIG_OF static const struct of_device_id of_lt3593_leds_match[] = { { .compatible = "lltc,lt3593", }, {}, }; MODULE_DEVICE_TABLE(of, of_lt3593_leds_match); -#endif static struct platform_driver lt3593_led_driver = { .probe = lt3593_led_probe, -- cgit v1.2.3-59-g8ed1b From 1c310074ad6cf5197d47b83cc64c272c538d7e1d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 23 Mar 2019 11:26:59 +0000 Subject: leds: lt3593: Remove unneeded assignment in lt3593_led_probe As the chip can't be queried for it's current state, the "keep" option doesn't make sense. The only option in DT should be turn the LED on or off at probe time. Also 'flags' is never used since introduction. Signed-off-by: YueHaibing Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-lt3593.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 54f0e5dbdbd0..83e8e58d81cb 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -66,7 +66,6 @@ static int lt3593_led_probe(struct platform_device *pdev) struct lt3593_led_data *led_data; struct fwnode_handle *child; int ret, state = LEDS_GPIO_DEFSTATE_OFF; - enum gpiod_flags flags = GPIOD_OUT_LOW; const char *tmp; if (!dev->of_node) @@ -99,13 +98,8 @@ static int lt3593_led_probe(struct platform_device *pdev) &led_data->cdev.default_trigger); if (!fwnode_property_read_string(child, "default-state", &tmp)) { - if (!strcmp(tmp, "keep")) { - state = LEDS_GPIO_DEFSTATE_KEEP; - flags = GPIOD_ASIS; - } else if (!strcmp(tmp, "on")) { + if (!strcmp(tmp, "on")) state = LEDS_GPIO_DEFSTATE_ON; - flags = GPIOD_OUT_HIGH; - } } led_data->cdev.name = led_data->name; -- cgit v1.2.3-59-g8ed1b From 967f69de81478c491f49681d10c71c66b6e7fb96 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2019 16:05:00 +0200 Subject: leds: pca955x: Make use of device property API Make use of device property API in this driver so that both OF based system and ACPI based system can use this driver. Signed-off-by: Andy Shevchenko Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca955x.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index f023729353b5..a9f5dad55956 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -47,8 +47,8 @@ #include #include #include -#include #include +#include #include #include @@ -363,16 +363,14 @@ static int pca955x_gpio_direction_output(struct gpio_chip *gc, } #endif /* CONFIG_LEDS_PCA955X_GPIO */ -#if IS_ENABLED(CONFIG_OF) static struct pca955x_platform_data * -pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) +pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) { - struct device_node *np = client->dev.of_node; - struct device_node *child; struct pca955x_platform_data *pdata; + struct fwnode_handle *child; int count; - count = of_get_child_count(np); + count = device_get_child_node_count(&client->dev); if (!count || count > chip->bits) return ERR_PTR(-ENODEV); @@ -386,24 +384,25 @@ pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) if (!pdata->leds) return ERR_PTR(-ENOMEM); - for_each_child_of_node(np, child) { + device_for_each_child_node(&client->dev, child) { const char *name; u32 reg; int res; - res = of_property_read_u32(child, "reg", ®); + res = fwnode_property_read_u32(child, "reg", ®); if ((res != 0) || (reg >= chip->bits)) continue; - if (of_property_read_string(child, "label", &name)) - name = child->name; + res = fwnode_property_read_string(child, "label", &name); + if ((res != 0) && is_of_node(child)) + name = to_of_node(child)->name; snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), "%s", name); pdata->leds[reg].type = PCA955X_TYPE_LED; - of_property_read_u32(child, "type", &pdata->leds[reg].type); - of_property_read_string(child, "linux,default-trigger", + fwnode_property_read_u32(child, "type", &pdata->leds[reg].type); + fwnode_property_read_string(child, "linux,default-trigger", &pdata->leds[reg].default_trigger); } @@ -419,15 +418,7 @@ static const struct of_device_id of_pca955x_match[] = { { .compatible = "nxp,pca9553", .data = (void *)pca9553 }, {}, }; - MODULE_DEVICE_TABLE(of, of_pca955x_match); -#else -static struct pca955x_platform_data * -pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) -{ - return ERR_PTR(-ENODEV); -} -#endif static int pca955x_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -444,7 +435,7 @@ static int pca955x_probe(struct i2c_client *client, adapter = to_i2c_adapter(client->dev.parent); pdata = dev_get_platdata(&client->dev); if (!pdata) { - pdata = pca955x_pdata_of_init(client, chip); + pdata = pca955x_get_pdata(client, chip); if (IS_ERR(pdata)) return PTR_ERR(pdata); } @@ -583,7 +574,7 @@ static int pca955x_probe(struct i2c_client *client, static struct i2c_driver pca955x_driver = { .driver = { .name = "leds-pca955x", - .of_match_table = of_match_ptr(of_pca955x_match), + .of_match_table = of_pca955x_match, }, .probe = pca955x_probe, .id_table = pca955x_id, -- cgit v1.2.3-59-g8ed1b From 0b6034d84b0077e60038163e9dd683d43135ba8b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2019 16:02:07 +0200 Subject: leds: pca963x: Make use of device property API Make use of device property API in this driver so that both OF based system and ACPI based system can use this driver. Signed-off-by: Andy Shevchenko Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-pca963x.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 7780edff06b8..9b4ef070d956 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -277,16 +278,15 @@ static int pca963x_blink_set(struct led_classdev *led_cdev, return 0; } -#if IS_ENABLED(CONFIG_OF) static struct pca963x_platform_data * -pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) +pca963x_get_pdata(struct i2c_client *client, struct pca963x_chipdef *chip) { - struct device_node *np = client->dev.of_node, *child; struct pca963x_platform_data *pdata; struct led_info *pca963x_leds; + struct fwnode_handle *child; int count; - count = of_get_child_count(np); + count = device_get_child_node_count(&client->dev); if (!count || count > chip->n_leds) return ERR_PTR(-ENODEV); @@ -295,18 +295,22 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) if (!pca963x_leds) return ERR_PTR(-ENOMEM); - for_each_child_of_node(np, child) { + device_for_each_child_node(&client->dev, child) { struct led_info led = {}; u32 reg; int res; - res = of_property_read_u32(child, "reg", ®); + res = fwnode_property_read_u32(child, "reg", ®); if ((res != 0) || (reg >= chip->n_leds)) continue; - led.name = - of_get_property(child, "label", NULL) ? : child->name; - led.default_trigger = - of_get_property(child, "linux,default-trigger", NULL); + + res = fwnode_property_read_string(child, "label", &led.name); + if ((res != 0) && is_of_node(child)) + led.name = to_of_node(child)->name; + + fwnode_property_read_string(child, "linux,default-trigger", + &led.default_trigger); + pca963x_leds[reg] = led; } pdata = devm_kzalloc(&client->dev, @@ -318,22 +322,23 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) pdata->leds.num_leds = chip->n_leds; /* default to open-drain unless totem pole (push-pull) is specified */ - if (of_property_read_bool(np, "nxp,totem-pole")) + if (device_property_read_bool(&client->dev, "nxp,totem-pole")) pdata->outdrv = PCA963X_TOTEM_POLE; else pdata->outdrv = PCA963X_OPEN_DRAIN; /* default to software blinking unless hardware blinking is specified */ - if (of_property_read_bool(np, "nxp,hw-blink")) + if (device_property_read_bool(&client->dev, "nxp,hw-blink")) pdata->blink_type = PCA963X_HW_BLINK; else pdata->blink_type = PCA963X_SW_BLINK; - if (of_property_read_u32(np, "nxp,period-scale", &chip->scaling)) + if (device_property_read_u32(&client->dev, "nxp,period-scale", + &chip->scaling)) chip->scaling = 1000; /* default to non-inverted output, unless inverted is specified */ - if (of_property_read_bool(np, "nxp,inverted-out")) + if (device_property_read_bool(&client->dev, "nxp,inverted-out")) pdata->dir = PCA963X_INVERTED; else pdata->dir = PCA963X_NORMAL; @@ -349,13 +354,6 @@ static const struct of_device_id of_pca963x_match[] = { {}, }; MODULE_DEVICE_TABLE(of, of_pca963x_match); -#else -static struct pca963x_platform_data * -pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) -{ - return ERR_PTR(-ENODEV); -} -#endif static int pca963x_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -370,7 +368,7 @@ static int pca963x_probe(struct i2c_client *client, pdata = dev_get_platdata(&client->dev); if (!pdata) { - pdata = pca963x_dt_init(client, chip); + pdata = pca963x_get_pdata(client, chip); if (IS_ERR(pdata)) { dev_warn(&client->dev, "could not parse configuration\n"); pdata = NULL; @@ -476,7 +474,7 @@ static int pca963x_remove(struct i2c_client *client) static struct i2c_driver pca963x_driver = { .driver = { .name = "leds-pca963x", - .of_match_table = of_match_ptr(of_pca963x_match), + .of_match_table = of_pca963x_match, }, .probe = pca963x_probe, .remove = pca963x_remove, -- cgit v1.2.3-59-g8ed1b From 857b079b1a43e9a1556902d8a0a1d3a6b0747d96 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 30 Mar 2019 20:37:57 +0000 Subject: leds: blinkm: clean up double assignment to data->i2c_addr Currently data->i2c_addr is being assigned twice, first with the value 0x09 and then immediately afterwards with the value 0x08. The first assignment is redundant and can be removed. Signed-off-by: Colin Ian King Reviewed-by: Mukesh Ojha Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/leds-blinkm.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 851c1920b63c..11b771fb933b 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -594,7 +594,6 @@ static int blinkm_probe(struct i2c_client *client, goto exit; } - data->i2c_addr = 0x09; data->i2c_addr = 0x08; /* i2c addr - use fake addr of 0x08 initially (real is 0x09) */ data->fw_ver = 0xfe; -- cgit v1.2.3-59-g8ed1b From 6e6c214f16e418ac8583e3dc8ea967a8cc59d9f0 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 3 Apr 2019 13:19:22 -0500 Subject: leds: Small fixes for Flash class description Fix misspelling and capitalization of LED in the Kconfig. Reported-by: Randy Dunlap Signed-off-by: Dan Murphy Acked-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 0414adebb177..6b016526b111 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -23,8 +23,8 @@ config LEDS_CLASS_FLASH tristate "LED Flash Class Support" depends on LEDS_CLASS help - This option enables the flash led sysfs class in /sys/class/leds. - It wrapps LED Class and adds flash LEDs specific sysfs attributes + This option enables the flash LED sysfs class in /sys/class/leds. + It wraps LED Class and adds flash LEDs specific sysfs attributes and kernel internal API to it. You'll need this to provide support for the flash related features of a LED device. It can be built as a module. -- cgit v1.2.3-59-g8ed1b From bc1b8492c764fea940fc66206047e37a7f8d77e1 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 21 Mar 2019 09:28:38 -0500 Subject: leds: lm3532: Introduce the lm3532 LED driver Introduce the Texas Instruments LM3532 White LED driver. The driver supports ALS configurability or manual brightness control. The driver also supports associating LED strings with specific control banks in a group or as individually controlled strings. Signed-off-by: Dan Murphy Tested-by: Tony Lindgren Signed-off-by: Jacek Anaszewski --- drivers/leds/Kconfig | 10 + drivers/leds/Makefile | 1 + drivers/leds/leds-lm3532.c | 683 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 694 insertions(+) create mode 100644 drivers/leds/leds-lm3532.c (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a72f97fca57b..da00b9ed5a5c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -138,6 +138,16 @@ config LEDS_LM3530 controlled manually or using PWM input or using ambient light automatically. +config LEDS_LM3532 + tristate "LCD Backlight driver for LM3532" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for the LCD backlight using + LM3532 ambient light sensor chip. This ALS chip can be + controlled manually or using PWM input or using ambient + light automatically. + config LEDS_LM3533 tristate "LED support for LM3533" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 4c1b0054f379..7a8b1f55d459 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o +obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c new file mode 100644 index 000000000000..180895b83b88 --- /dev/null +++ b/drivers/leds/leds-lm3532.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0 +// TI LM3532 LED driver +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LM3532_NAME "lm3532-led" +#define LM3532_BL_MODE_MANUAL 0x00 +#define LM3532_BL_MODE_ALS 0x01 + +#define LM3532_REG_OUTPUT_CFG 0x10 +#define LM3532_REG_STARTSHUT_RAMP 0x11 +#define LM3532_REG_RT_RAMP 0x12 +#define LM3532_REG_PWM_A_CFG 0x13 +#define LM3532_REG_PWM_B_CFG 0x14 +#define LM3532_REG_PWM_C_CFG 0x15 +#define LM3532_REG_ZONE_CFG_A 0x16 +#define LM3532_REG_CTRL_A_BRT 0x17 +#define LM3532_REG_ZONE_CFG_B 0x18 +#define LM3532_REG_CTRL_B_BRT 0x19 +#define LM3532_REG_ZONE_CFG_C 0x1a +#define LM3532_REG_CTRL_C_BRT 0x1b +#define LM3532_REG_ENABLE 0x1d +#define LM3532_ALS_CONFIG 0x23 +#define LM3532_REG_ZN_0_HI 0x60 +#define LM3532_REG_ZN_0_LO 0x61 +#define LM3532_REG_ZN_1_HI 0x62 +#define LM3532_REG_ZN_1_LO 0x63 +#define LM3532_REG_ZN_2_HI 0x64 +#define LM3532_REG_ZN_2_LO 0x65 +#define LM3532_REG_ZN_3_HI 0x66 +#define LM3532_REG_ZN_3_LO 0x67 +#define LM3532_REG_MAX 0x7e + +/* Contorl Enable */ +#define LM3532_CTRL_A_ENABLE BIT(0) +#define LM3532_CTRL_B_ENABLE BIT(1) +#define LM3532_CTRL_C_ENABLE BIT(2) + +/* PWM Zone Control */ +#define LM3532_PWM_ZONE_MASK 0x7c +#define LM3532_PWM_ZONE_0_EN BIT(2) +#define LM3532_PWM_ZONE_1_EN BIT(3) +#define LM3532_PWM_ZONE_2_EN BIT(4) +#define LM3532_PWM_ZONE_3_EN BIT(5) +#define LM3532_PWM_ZONE_4_EN BIT(6) + +/* Brightness Configuration */ +#define LM3532_I2C_CTRL BIT(0) +#define LM3532_ALS_CTRL 0 +#define LM3532_LINEAR_MAP BIT(1) +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) +#define LM3532_ZONE_0 0 +#define LM3532_ZONE_1 BIT(2) +#define LM3532_ZONE_2 BIT(3) +#define LM3532_ZONE_3 (BIT(2) | BIT(3)) +#define LM3532_ZONE_4 BIT(4) + +#define LM3532_ENABLE_ALS BIT(3) +#define LM3532_ALS_SEL_SHIFT 6 + +/* Zone Boundary Register */ +#define LM3532_ALS_WINDOW_mV 2000 +#define LM3532_ALS_ZB_MAX 4 +#define LM3532_ALS_OFFSET_mV 2 + +#define LM3532_CONTROL_A 0 +#define LM3532_CONTROL_B 1 +#define LM3532_CONTROL_C 2 +#define LM3532_MAX_CONTROL_BANKS 3 +#define LM3532_MAX_LED_STRINGS 3 + +#define LM3532_OUTPUT_CFG_MASK 0x3 +#define LM3532_BRT_VAL_ADJUST 8 +#define LM3532_RAMP_DOWN_SHIFT 3 + +#define LM3532_NUM_RAMP_VALS 8 +#define LM3532_NUM_AVG_VALS 8 +#define LM3532_NUM_IMP_VALS 32 + +/* + * struct lm3532_als_data + * @config - value of ALS configuration register + * @als1_imp_sel - value of ALS1 resistor select register + * @als2_imp_sel - value of ALS2 resistor select register + * @als_avrg_time - ALS averaging time + * @als_input_mode - ALS input mode for brightness control + * @als_vmin - Minimum ALS voltage + * @als_vmax - Maximum ALS voltage + * @zone_lo - values of ALS lo ZB(Zone Boundary) registers + * @zone_hi - values of ALS hi ZB(Zone Boundary) registers + */ +struct lm3532_als_data { + u8 config; + u8 als1_imp_sel; + u8 als2_imp_sel; + u8 als_avrg_time; + u8 als_input_mode; + u32 als_vmin; + u32 als_vmax; + u8 zones_lo[LM3532_ALS_ZB_MAX]; + u8 zones_hi[LM3532_ALS_ZB_MAX]; +}; + +/** + * struct lm3532_led + * @led_dev: led class device + * @priv - Pointer the device data structure + * @control_bank - Control bank the LED is associated to + * @mode - Mode of the LED string + * @num_leds - Number of LED strings are supported in this array + * @led_strings - The LED strings supported in this array + * @label - LED label + */ +struct lm3532_led { + struct led_classdev led_dev; + struct lm3532_data *priv; + + int control_bank; + int mode; + int num_leds; + u32 led_strings[LM3532_MAX_CONTROL_BANKS]; + char label[LED_MAX_NAME_SIZE]; +}; + +/** + * struct lm3532_data + * @enable_gpio - Hardware enable gpio + * @regulator: regulator + * @client: i2c client + * @regmap - Devices register map + * @dev - Pointer to the devices device struct + * @lock - Lock for reading/writing the device + * @als_data - Pointer to the als data struct + * @runtime_ramp_up - Runtime ramp up setting + * @runtime_ramp_down - Runtime ramp down setting + * @leds - Array of LED strings + */ +struct lm3532_data { + struct gpio_desc *enable_gpio; + struct regulator *regulator; + struct i2c_client *client; + struct regmap *regmap; + struct device *dev; + struct mutex lock; + + struct lm3532_als_data *als_data; + + u32 runtime_ramp_up; + u32 runtime_ramp_down; + + struct lm3532_led leds[]; +}; + +static const struct reg_default lm3532_reg_defs[] = { + {LM3532_REG_OUTPUT_CFG, 0xe4}, + {LM3532_REG_STARTSHUT_RAMP, 0xc0}, + {LM3532_REG_RT_RAMP, 0xc0}, + {LM3532_REG_PWM_A_CFG, 0x82}, + {LM3532_REG_PWM_B_CFG, 0x82}, + {LM3532_REG_PWM_C_CFG, 0x82}, + {LM3532_REG_ZONE_CFG_A, 0xf1}, + {LM3532_REG_CTRL_A_BRT, 0xf3}, + {LM3532_REG_ZONE_CFG_B, 0xf1}, + {LM3532_REG_CTRL_B_BRT, 0xf3}, + {LM3532_REG_ZONE_CFG_C, 0xf1}, + {LM3532_REG_CTRL_C_BRT, 0xf3}, + {LM3532_REG_ENABLE, 0xf8}, + {LM3532_ALS_CONFIG, 0x44}, + {LM3532_REG_ZN_0_HI, 0x35}, + {LM3532_REG_ZN_0_LO, 0x33}, + {LM3532_REG_ZN_1_HI, 0x6a}, + {LM3532_REG_ZN_1_LO, 0x66}, + {LM3532_REG_ZN_2_HI, 0xa1}, + {LM3532_REG_ZN_2_LO, 0x99}, + {LM3532_REG_ZN_3_HI, 0xdc}, + {LM3532_REG_ZN_3_LO, 0xcc}, +}; + +static const struct regmap_config lm3532_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = LM3532_REG_MAX, + .reg_defaults = lm3532_reg_defs, + .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs), + .cache_type = REGCACHE_FLAT, +}; + +const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330, + 92500, 7400, 6170, 5290, + 4630, 4110, 3700, 3360, + 3080, 2850, 2640, 2440, + 2310, 2180, 2060, 1950, + 1850, 1760, 1680, 1610, + 1540, 1480, 1420, 1370, + 1320, 1280, 1230, 1190}; +static int lm3532_get_als_imp_index(int als_imped) +{ + int i; + + if (als_imped > als_imp_table[1]) + return 0; + + if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1]) + return LM3532_NUM_IMP_VALS - 1; + + for (i = 1; i < LM3532_NUM_IMP_VALS; i++) { + if (als_imped == als_imp_table[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (als_imped < als_imp_table[i - 1] && + als_imped > als_imp_table[i]) { + if (als_imped - als_imp_table[i - 1] < + als_imp_table[i] - als_imped) + return i + 1; + else + return i; + } + } + + return -EINVAL; +} + +static int lm3532_get_index(const int table[], int size, int value) +{ + int i; + + for (i = 1; i < size; i++) { + if (value == table[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (value > table[i - 1] && + value < table[i]) { + if (value - table[i - 1] < table[i] - value) + return i - 1; + else + return i; + } + } + + return -EINVAL; +} + +const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680, + 1433360, 286720, 573440, + 1146880, 2293760}; +static int lm3532_get_als_avg_index(int avg_time) +{ + if (avg_time <= als_avrg_table[0]) + return 0; + + if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1]) + return LM3532_NUM_AVG_VALS - 1; + + return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS, + avg_time); +} + +const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192, + 16384, 32768, 65536}; +static int lm3532_get_ramp_index(int ramp_time) +{ + if (ramp_time <= ramp_table[0]) + return 0; + + if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1]) + return LM3532_NUM_RAMP_VALS - 1; + + return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS, + ramp_time); +} + +static int lm3532_led_enable(struct lm3532_led *led_data) +{ + int ctrl_en_val = BIT(led_data->control_bank); + int ret; + + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, + ctrl_en_val, ctrl_en_val); + if (ret) { + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); + return ret; + } + + return regulator_enable(led_data->priv->regulator); +} + +static int lm3532_led_disable(struct lm3532_led *led_data) +{ + int ctrl_en_val = BIT(led_data->control_bank); + int ret; + + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, + ctrl_en_val, ~ctrl_en_val); + if (ret) { + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); + return ret; + } + + return regulator_disable(led_data->priv->regulator); +} + +static int lm3532_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brt_val) +{ + struct lm3532_led *led = + container_of(led_cdev, struct lm3532_led, led_dev); + u8 brightness_reg; + int ret; + + mutex_lock(&led->priv->lock); + + if (led->mode == LM3532_BL_MODE_ALS) { + if (brt_val > LED_OFF) + ret = lm3532_led_enable(led); + else + ret = lm3532_led_disable(led); + + goto unlock; + } + + if (brt_val == LED_OFF) { + ret = lm3532_led_disable(led); + goto unlock; + } + + ret = lm3532_led_enable(led); + if (ret) + goto unlock; + + brightness_reg = LM3532_REG_CTRL_A_BRT + led->control_bank * 2; + brt_val = brt_val / LM3532_BRT_VAL_ADJUST; + + ret = regmap_write(led->priv->regmap, brightness_reg, brt_val); + +unlock: + mutex_unlock(&led->priv->lock); + return ret; +} + +static int lm3532_init_registers(struct lm3532_led *led) +{ + struct lm3532_data *drvdata = led->priv; + unsigned int runtime_ramp_val; + unsigned int output_cfg_val = 0; + unsigned int output_cfg_shift = 0; + unsigned int output_cfg_mask = 0; + int ret, i; + + for (i = 0; i < led->num_leds; i++) { + output_cfg_shift = led->led_strings[i] * 2; + output_cfg_val |= (led->control_bank << output_cfg_shift); + output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift; + } + + ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG, + output_cfg_mask, output_cfg_val); + if (ret) + return ret; + + runtime_ramp_val = drvdata->runtime_ramp_up | + (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT); + + return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP, + runtime_ramp_val); +} + +static int lm3532_als_configure(struct lm3532_data *priv, + struct lm3532_led *led) +{ + struct lm3532_als_data *als = priv->als_data; + u32 als_vmin, als_vmax, als_vstep; + int zone_reg = LM3532_REG_ZN_0_HI; + int brightnes_config_reg; + int ret; + int i; + + als_vmin = als->als_vmin; + als_vmax = als->als_vmax; + + als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2); + + for (i = 0; i < LM3532_ALS_ZB_MAX; i++) { + als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) * + LED_FULL) / 1000; + als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV + + als_vstep + (i * als_vstep)) * LED_FULL) / 1000; + + zone_reg = LM3532_REG_ZN_0_HI + i * 2; + ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]); + if (ret) + return ret; + + zone_reg += 1; + ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]); + if (ret) + return ret; + } + + als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) | + (als->als_input_mode << LM3532_ALS_SEL_SHIFT)); + + ret = regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config); + if (ret) + return ret; + + brightnes_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2; + + return regmap_update_bits(priv->regmap, brightnes_config_reg, + LM3532_I2C_CTRL, LM3532_ALS_CTRL); +} + +static int lm3532_parse_als(struct lm3532_data *priv) +{ + struct lm3532_als_data *als; + int als_avg_time; + int als_impedance; + int ret; + + als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL); + if (als == NULL) + return -ENOMEM; + + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin", + &als->als_vmin); + if (ret) + als->als_vmin = 0; + + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax", + &als->als_vmax); + if (ret) + als->als_vmax = LM3532_ALS_WINDOW_mV; + + if (als->als_vmax > LM3532_ALS_WINDOW_mV) { + ret = -EINVAL; + return ret; + } + + ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel", + &als_impedance); + if (ret) + als->als1_imp_sel = 0; + else + als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance); + + ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel", + &als_impedance); + if (ret) + als->als2_imp_sel = 0; + else + als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance); + + ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us", + &als_avg_time); + if (ret) + als->als_avrg_time = 0; + else + als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time); + + ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode", + &als->als_input_mode); + if (ret) + als->als_input_mode = 0; + + if (als->als_input_mode > LM3532_BL_MODE_ALS) { + ret = -EINVAL; + return ret; + } + + priv->als_data = als; + + return ret; +} + +static int lm3532_parse_node(struct lm3532_data *priv) +{ + struct fwnode_handle *child = NULL; + struct lm3532_led *led; + const char *name; + int control_bank; + u32 ramp_time; + size_t i = 0; + int ret; + + priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev, + "enable", GPIOD_OUT_LOW); + if (IS_ERR(priv->enable_gpio)) + priv->enable_gpio = NULL; + + priv->regulator = devm_regulator_get(&priv->client->dev, "vin"); + if (IS_ERR(priv->regulator)) + priv->regulator = NULL; + + ret = device_property_read_u32(&priv->client->dev, "ramp-up-us", + &ramp_time); + if (ret) + dev_info(&priv->client->dev, "ramp-up-ms property missing\n"); + else + priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time); + + ret = device_property_read_u32(&priv->client->dev, "ramp-down-us", + &ramp_time); + if (ret) + dev_info(&priv->client->dev, "ramp-down-ms property missing\n"); + else + priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); + + device_for_each_child_node(priv->dev, child) { + led = &priv->leds[i]; + + ret = fwnode_property_read_u32(child, "reg", &control_bank); + if (ret) { + dev_err(&priv->client->dev, "reg property missing\n"); + fwnode_handle_put(child); + goto child_out; + } + + if (control_bank > LM3532_CONTROL_C) { + dev_err(&priv->client->dev, "Control bank invalid\n"); + continue; + } + + led->control_bank = control_bank; + + ret = fwnode_property_read_u32(child, "ti,led-mode", + &led->mode); + if (ret) { + dev_err(&priv->client->dev, "ti,led-mode property missing\n"); + fwnode_handle_put(child); + goto child_out; + } + + if (led->mode == LM3532_BL_MODE_ALS) { + ret = lm3532_parse_als(priv); + if (ret) + dev_err(&priv->client->dev, "Failed to parse als\n"); + else + lm3532_als_configure(priv, led); + } + + led->num_leds = fwnode_property_read_u32_array(child, + "led-sources", + NULL, 0); + + if (led->num_leds > LM3532_MAX_LED_STRINGS) { + dev_err(&priv->client->dev, "To many LED string defined\n"); + continue; + } + + ret = fwnode_property_read_u32_array(child, "led-sources", + led->led_strings, + led->num_leds); + if (ret) { + dev_err(&priv->client->dev, "led-sources property missing\n"); + fwnode_handle_put(child); + goto child_out; + } + + fwnode_property_read_string(child, "linux,default-trigger", + &led->led_dev.default_trigger); + + ret = fwnode_property_read_string(child, "label", &name); + if (ret) + snprintf(led->label, sizeof(led->label), + "%s::", priv->client->name); + else + snprintf(led->label, sizeof(led->label), + "%s:%s", priv->client->name, name); + + led->priv = priv; + led->led_dev.name = led->label; + led->led_dev.brightness_set_blocking = lm3532_brightness_set; + + ret = devm_led_classdev_register(priv->dev, &led->led_dev); + if (ret) { + dev_err(&priv->client->dev, "led register err: %d\n", + ret); + fwnode_handle_put(child); + goto child_out; + } + + lm3532_init_registers(led); + + i++; + } + +child_out: + return ret; +} + +static int lm3532_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm3532_data *drvdata; + int ret = 0; + int count; + + count = device_get_child_node_count(&client->dev); + if (!count) { + dev_err(&client->dev, "LEDs are not defined in device tree!"); + return -ENODEV; + } + + drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count), + GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + + drvdata->client = client; + drvdata->dev = &client->dev; + + drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config); + if (IS_ERR(drvdata->regmap)) { + ret = PTR_ERR(drvdata->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + mutex_init(&drvdata->lock); + i2c_set_clientdata(client, drvdata); + + ret = lm3532_parse_node(drvdata); + if (ret) { + dev_err(&client->dev, "Failed to parse node\n"); + return ret; + } + + if (drvdata->enable_gpio) + gpiod_direction_output(drvdata->enable_gpio, 1); + + return ret; +} + +static int lm3532_remove(struct i2c_client *client) +{ + struct lm3532_data *drvdata = i2c_get_clientdata(client); + + mutex_destroy(&drvdata->lock); + + if (drvdata->enable_gpio) + gpiod_direction_output(drvdata->enable_gpio, 0); + + return 0; +} + +static const struct of_device_id of_lm3532_leds_match[] = { + { .compatible = "ti,lm3532", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_lm3532_leds_match); + +static const struct i2c_device_id lm3532_id[] = { + {LM3532_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, lm3532_id); + +static struct i2c_driver lm3532_i2c_driver = { + .probe = lm3532_probe, + .remove = lm3532_remove, + .id_table = lm3532_id, + .driver = { + .name = LM3532_NAME, + .of_match_table = of_lm3532_leds_match, + }, +}; +module_i2c_driver(lm3532_i2c_driver); + +MODULE_DESCRIPTION("Back Light driver for LM3532"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Dan Murphy "); -- cgit v1.2.3-59-g8ed1b From 0db37915d912e8dc6588f25da76d3ed36718d92f Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 29 Apr 2019 17:22:59 +0200 Subject: leds: avoid races with workqueue There are races between "main" thread and workqueue. They manifest themselves on Thinkpad X60: This should result in LED blinking, but it turns it off instead: root@amd:/data/pavel# cd /sys/class/leds/tpacpi\:\:power root@amd:/sys/class/leds/tpacpi::power# echo timer > trigger root@amd:/sys/class/leds/tpacpi::power# echo timer > trigger It should be possible to transition from blinking to solid on by echo 0 > brightness; echo 1 > brightness... but that does not work, either, if done too quickly. Synchronization of the workqueue fixes both. Fixes: 1afcadfcd184 ("leds: core: Use set_brightness_work for the blocking op") Signed-off-by: Pavel Machek Signed-off-by: Jacek Anaszewski --- drivers/leds/led-class.c | 1 + drivers/leds/led-core.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers/leds') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 3c7e3487b373..85848c5da705 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -57,6 +57,7 @@ static ssize_t brightness_store(struct device *dev, if (state == LED_OFF) led_trigger_remove(led_cdev); led_set_brightness(led_cdev, state); + flush_work(&led_cdev->set_brightness_work); ret = size; unlock: diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index e3da7c03da1b..e9ae7f87ab90 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -164,6 +164,11 @@ static void led_blink_setup(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { + /* + * If "set brightness to 0" is pending in workqueue, we don't + * want that to be reordered after blink_set() + */ + flush_work(&led_cdev->set_brightness_work); if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) && led_cdev->blink_set && !led_cdev->blink_set(led_cdev, delay_on, delay_off)) -- cgit v1.2.3-59-g8ed1b