devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block
@ 2014-12-09  0:22 Bjorn Andersson
  2014-12-09  0:22 ` [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver Bjorn Andersson
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Bjorn Andersson @ 2014-12-09  0:22 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bryan Wu, Richard Purdie, Grant Likely
  Cc: Courtney Cavin, devicetree, linux-kernel, linux-leds,
	linux-arm-msm

From: Courtney Cavin <courtney.cavin@sonymobile.com>

This adds device tree binding documentation for the WLED ('White' LED)
block on Qualcomm's PM8941 PMICs.

Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 .../devicetree/bindings/leds/leds-pm8941-wled.txt  | 43 ++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
new file mode 100644
index 0000000..a85a964
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
@@ -0,0 +1,43 @@
+Binding for Qualcomm PM8941 WLED driver
+
+Required properties:
+- compatible: should be "qcom,pm8941-wled"
+- reg: slave address
+
+Optional properties:
+- label: The label for this led
+  See Documentation/devicetree/bindings/leds/common.txt
+- linux,default-trigger: Default trigger assigned to the LED
+  See Documentation/devicetree/bindings/leds/common.txt
+- qcom,cs-out: bool; enable current sink output
+- qcom,cabc: bool; enable content adaptive backlight control
+- qcom,ext-gen: bool; use externally generated modulator signal to dim
+- qcom,current-limit: mA; per-string current limit; value from 0 to 25
+	default: 20mA
+- qcom,current-boost-limit: mA; boost current limit; one of:
+	105, 385, 525, 805, 980, 1260, 1400, 1680
+	default: 805mA
+- qcom,switching-freq: kHz; switching frequency; one of:
+	600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371,
+	1600, 1920, 2400, 3200, 4800, 9600,
+	default: 1600kHz
+- qcom,ovp: V; Over-voltage protection limit; one of:
+	27, 29, 32, 35
+	default: 29V
+- qcom,num-strings: #; number of led strings attached; value from 1 to 3
+	default: 2
+
+Example:
+
+pm8941-wled@d800 {
+	compatible = "qcom,pm8941-wled";
+	reg = <0xd800>;
+	label = "backlight";
+
+	qcom,cs-out;
+	qcom,current-limit = <20>;
+	qcom,current-boost-limit = <805>;
+	qcom,switching-freq = <1600>;
+	qcom,ovp = <29>;
+	qcom,num-strings = <2>;
+};
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver
  2014-12-09  0:22 [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bjorn Andersson
@ 2014-12-09  0:22 ` Bjorn Andersson
  2014-12-17  0:54   ` Bryan Wu
  2014-12-17  1:15   ` Stephen Boyd
  2014-12-10  1:19 ` [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bryan Wu
  2014-12-17  1:15 ` Stephen Boyd
  2 siblings, 2 replies; 11+ messages in thread
From: Bjorn Andersson @ 2014-12-09  0:22 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bryan Wu, Richard Purdie, Grant Likely
  Cc: Courtney Cavin, devicetree, linux-kernel, linux-leds,
	linux-arm-msm

From: Courtney Cavin <courtney.cavin@sonymobile.com>

This adds support for the WLED ('White' LED) block on Qualcomm's
PM8941 PMICs.

Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 drivers/leds/Kconfig            |   8 +
 drivers/leds/Makefile           |   1 +
 drivers/leds/leds-pm8941-wled.c | 466 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
 create mode 100644 drivers/leds/leds-pm8941-wled.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index a210338..9091cf4 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -505,6 +505,14 @@ config LEDS_VERSATILE
 	  This option enabled support for the LEDs on the ARM Versatile
 	  and RealView boards. Say Y to enabled these.
 
+config LEDS_PM8941_WLED
+	tristate "LED support for the Qualcomm PM8941 WLED block"
+	depends on LEDS_CLASS
+	depends on REGMAP
+	help
+	  This option enables support for the 'White' LED block
+	  on Qualcomm PM8941 PMICs.
+
 comment "LED Triggers"
 source "drivers/leds/trigger/Kconfig"
 
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a2b1647..5474afc 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
 obj-$(CONFIG_LEDS_SYSCON)		+= leds-syscon.o
 obj-$(CONFIG_LEDS_VERSATILE)		+= leds-versatile.o
 obj-$(CONFIG_LEDS_MENF21BMC)		+= leds-menf21bmc.o
+obj-$(CONFIG_LEDS_PM8941_WLED)		+= leds-pm8941-wled.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/leds-pm8941-wled.c b/drivers/leds/leds-pm8941-wled.c
new file mode 100644
index 0000000..aa06d0b
--- /dev/null
+++ b/drivers/leds/leds-pm8941-wled.c
@@ -0,0 +1,466 @@
+/* Copyright (c) 2013, Sony Mobile Communications, AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+enum pm8941_wled_reg {
+	PM8941_WLED_REG_VAL_BASE		= 0x40,
+#define PM8941_WLED_REG_VAL_MAX			0xFFF
+
+	PM8941_WLED_REG_MOD_EN			= 0x46,
+#define PM8941_WLED_REG_MOD_EN_BIT		BIT(7)
+#define PM8941_WLED_REG_MOD_EN_MASK		BIT(7)
+
+	PM8941_WLED_REG_SYNC			= 0x47,
+#define PM8941_WLED_REG_SYNC_MASK		0x07
+#define PM8941_WLED_REG_SYNC_LED1		BIT(0)
+#define PM8941_WLED_REG_SYNC_LED2		BIT(1)
+#define PM8941_WLED_REG_SYNC_LED3		BIT(2)
+#define PM8941_WLED_REG_SYNC_ALL		0x07
+#define PM8941_WLED_REG_SYNC_CLEAR		0x00
+
+	PM8941_WLED_REG_FREQ			= 0x4c,
+#define PM8941_WLED_REG_FREQ_MASK		0x0f
+
+	PM8941_WLED_REG_OVP			= 0x4d,
+#define PM8941_WLED_REG_OVP_MASK		0x03
+
+	PM8941_WLED_REG_BOOST			= 0x4e,
+#define PM8941_WLED_REG_BOOST_MASK		0x07
+
+	PM8941_WLED_REG_SINK			= 0x4f,
+#define PM8941_WLED_REG_SINK_MASK		0xe0
+#define PM8941_WLED_REG_SINK_SHFT		0x05
+
+/* Per-'string' registers below */
+#define PM8941_WLED_REG_STR_OFFSET		0x10
+
+	PM8941_WLED_REG_STR_MOD_EN_BASE		= 0x60,
+#define PM8941_WLED_REG_STR_MOD_EN		BIT(7)
+#define PM8941_WLED_REG_STR_MOD_MASK		BIT(7)
+
+	PM8941_WLED_REG_STR_SCALE_BASE		= 0x62,
+#define PM8941_WLED_REG_STR_SCALE_MASK		0x1f
+
+	PM8941_WLED_REG_STR_MOD_SRC_BASE	= 0x63,
+#define PM8941_WLED_REG_STR_MOD_SRC_MASK	0x01
+#define PM8941_WLED_REG_STR_MOD_SRC_INT		0x00
+#define PM8941_WLED_REG_STR_MOD_SRC_EXT		0x01
+
+	PM8941_WLED_REG_STR_CABC_BASE		= 0x66,
+#define PM8941_WLED_REG_STR_CABC_MASK		BIT(7)
+#define PM8941_WLED_REG_STR_CABC_EN		BIT(7)
+};
+
+struct pm8941_wled_config {
+	u32 i_boost_limit;
+	u32 ovp;
+	u32 switch_freq;
+	u32 num_strings;
+	u32 i_limit;
+	bool cs_out_en;
+	bool ext_gen;
+	bool cabc_en;
+};
+
+struct pm8941_wled {
+	struct led_classdev cdev;
+	struct pm8941_wled_config cfg;
+	u16 addr;
+};
+
+struct pm8941_wled_context {
+	struct pm8941_wled wled;
+	struct regmap *regmap;
+};
+
+static int pm8941_wled_set(struct led_classdev *cdev,
+			   enum led_brightness value)
+{
+	struct pm8941_wled_context *ctx;
+	struct pm8941_wled *wled;
+	u8 ctrl = 0;
+	u16 val;
+	int rc;
+	int i;
+
+	wled = container_of(cdev, struct pm8941_wled, cdev);
+	ctx = container_of(wled, struct pm8941_wled_context, wled);
+
+	if (value != 0)
+		ctrl = PM8941_WLED_REG_MOD_EN_BIT;
+
+	val = (value * PM8941_WLED_REG_VAL_MAX) / LED_FULL;
+
+	rc = regmap_update_bits(ctx->regmap,
+			wled->addr + PM8941_WLED_REG_MOD_EN,
+			PM8941_WLED_REG_MOD_EN_MASK, ctrl);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < wled->cfg.num_strings; ++i) {
+		u8 v[2] = { val & 0xff, (val >> 8) & 0xf };
+
+		rc = regmap_bulk_write(ctx->regmap,
+				wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i,
+				v, 2);
+		if (rc)
+			return rc;
+	}
+
+	rc = regmap_update_bits(ctx->regmap,
+			wled->addr + PM8941_WLED_REG_SYNC,
+			PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL);
+	if (rc)
+		return rc;
+
+	rc = regmap_update_bits(ctx->regmap,
+			wled->addr + PM8941_WLED_REG_SYNC,
+			PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR);
+	return rc;
+}
+
+static void pm8941_wled_set_brightness(struct led_classdev *cdev,
+				       enum led_brightness value)
+{
+	if (pm8941_wled_set(cdev, value)) {
+		dev_err(cdev->dev, "Unable to set brightness\n");
+		return;
+	}
+	cdev->brightness = value;
+}
+
+static int pm8941_wled_setup(struct pm8941_wled *wled)
+{
+	struct pm8941_wled_context *ctx;
+	int rc;
+	int i;
+
+	ctx = container_of(wled, struct pm8941_wled_context, wled);
+	rc = regmap_update_bits(ctx->regmap,
+			wled->addr + PM8941_WLED_REG_OVP,
+			PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp);
+	if (rc)
+		return rc;
+
+	rc = regmap_update_bits(ctx->regmap,
+			wled->addr + PM8941_WLED_REG_BOOST,
+			PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit);
+	if (rc)
+		return rc;
+
+	rc = regmap_update_bits(ctx->regmap,
+			wled->addr + PM8941_WLED_REG_FREQ,
+			PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq);
+	if (rc)
+		return rc;
+
+	if (wled->cfg.cs_out_en) {
+		u8 all = (BIT(wled->cfg.num_strings) - 1)
+				<< PM8941_WLED_REG_SINK_SHFT;
+
+		rc = regmap_update_bits(ctx->regmap,
+				wled->addr + PM8941_WLED_REG_SINK,
+				PM8941_WLED_REG_SINK_MASK, all);
+		if (rc)
+			return rc;
+	}
+
+	for (i = 0; i < wled->cfg.num_strings; ++i) {
+		u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i;
+
+		rc = regmap_update_bits(ctx->regmap,
+				addr + PM8941_WLED_REG_STR_MOD_EN_BASE,
+				PM8941_WLED_REG_STR_MOD_MASK,
+				PM8941_WLED_REG_STR_MOD_EN);
+		if (rc)
+			return rc;
+
+		if (wled->cfg.ext_gen) {
+			rc = regmap_update_bits(ctx->regmap,
+					addr + PM8941_WLED_REG_STR_MOD_SRC_BASE,
+					PM8941_WLED_REG_STR_MOD_SRC_MASK,
+					PM8941_WLED_REG_STR_MOD_SRC_EXT);
+			if (rc)
+				return rc;
+		}
+
+		rc = regmap_update_bits(ctx->regmap,
+				addr + PM8941_WLED_REG_STR_SCALE_BASE,
+				PM8941_WLED_REG_STR_SCALE_MASK,
+				wled->cfg.i_limit);
+		if (rc)
+			return rc;
+
+		rc = regmap_update_bits(ctx->regmap,
+				addr + PM8941_WLED_REG_STR_CABC_BASE,
+				PM8941_WLED_REG_STR_CABC_MASK,
+				wled->cfg.cabc_en ?
+					PM8941_WLED_REG_STR_CABC_EN : 0);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+static const struct pm8941_wled_config pm8941_wled_config_defaults = {
+	.i_boost_limit = 3,
+	.i_limit = 20,
+	.ovp = 2,
+	.switch_freq = 5,
+	.num_strings = 0,
+	.cs_out_en = false,
+	.ext_gen = false,
+	.cabc_en = false,
+};
+
+struct pm8941_wled_var_cfg {
+	const u32 *values;
+	u32 (*fn)(u32);
+	int size;
+};
+
+static const u32 pm8941_wled_i_boost_limit_values[] = {
+	105, 385, 525, 805, 980, 1260, 1400, 1680,
+};
+
+static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = {
+	.values = pm8941_wled_i_boost_limit_values,
+	.size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values),
+};
+
+static const u32 pm8941_wled_ovp_values[] = {
+	35, 32, 29, 27,
+};
+
+static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = {
+	.values = pm8941_wled_ovp_values,
+	.size = ARRAY_SIZE(pm8941_wled_ovp_values),
+};
+
+static u32 pm8941_wled_num_strings_values_fn(u32 idx)
+{
+	return idx + 1;
+}
+
+static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = {
+	.fn = pm8941_wled_num_strings_values_fn,
+	.size = 3,
+};
+
+static u32 pm8941_wled_switch_freq_values_fn(u32 idx)
+{
+	return 19200 / (2 * (1 + idx));
+}
+
+static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = {
+	.fn = pm8941_wled_switch_freq_values_fn,
+	.size = 16,
+};
+
+static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = {
+	.size = 26,
+};
+
+static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx)
+{
+	if (idx >= cfg->size)
+		return UINT_MAX;
+	if (cfg->fn)
+		return cfg->fn(idx);
+	if (cfg->values)
+		return cfg->values[idx];
+	return idx;
+}
+
+static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev)
+{
+	struct pm8941_wled_config *cfg = &wled->cfg;
+	u32 val;
+	int rc;
+	int i;
+
+	const struct {
+		const char *name;
+		u32 *val_ptr;
+		const struct pm8941_wled_var_cfg *cfg;
+	} u32_opts[] = {
+		{
+			"qcom,current-boost-limit", &cfg->i_boost_limit,
+			.cfg = &pm8941_wled_i_boost_limit_cfg,
+		},
+		{
+			"qcom,current-limit", &cfg->i_limit,
+			.cfg = &pm8941_wled_i_limit_cfg,
+		},
+		{
+			"qcom,ovp", &cfg->ovp,
+			.cfg = &pm8941_wled_ovp_cfg,
+		},
+		{
+			"qcom,switching-freq", &cfg->switch_freq,
+			.cfg = &pm8941_wled_switch_freq_cfg,
+		},
+		{
+			"qcom,num-strings", &cfg->num_strings,
+			.cfg = &pm8941_wled_num_strings_cfg,
+		},
+	};
+	const struct {
+		const char *name;
+		bool *val_ptr;
+	} bool_opts[] = {
+		{ "qcom,cs-out", &cfg->cs_out_en, },
+		{ "qcom,ext-gen", &cfg->ext_gen, },
+		{ "qcom,cabc", &cfg->cabc_en, },
+	};
+
+	if (dev->of_node == NULL) {
+		dev_err(dev, "no OF node!\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "reg", &val);
+	if (rc || val > 0xffff) {
+		dev_err(dev, "invalid IO resources\n");
+		return rc ? rc : -EINVAL;
+	}
+	wled->addr = val;
+
+	rc = of_property_read_string(dev->of_node, "label", &wled->cdev.name);
+	if (rc)
+		wled->cdev.name = dev->of_node->name;
+
+	wled->cdev.default_trigger = of_get_property(dev->of_node,
+			"linux,default-trigger", NULL);
+
+	*cfg = pm8941_wled_config_defaults;
+	for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) {
+		u32 sel, c;
+		int j, rj;
+
+		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
+		if (rc) {
+			if (rc != -EINVAL) {
+				dev_err(dev, "error reading '%s'\n",
+						u32_opts[i].name);
+				return rc;
+			}
+			continue;
+		}
+
+		sel = UINT_MAX;
+		rj = -1;
+		c = pm8941_wled_values(u32_opts[i].cfg, 0);
+		for (j = 0; c != UINT_MAX; ++j) {
+			if (c <= val && (sel == UINT_MAX || c >= sel)) {
+				sel = c;
+				rj = j;
+			}
+			c = pm8941_wled_values(u32_opts[i].cfg, j + 1);
+		}
+		if (sel == UINT_MAX) {
+			dev_err(dev, "invalid value for '%s'\n",
+					u32_opts[i].name);
+			return rc;
+		}
+		if (sel != val) {
+			dev_warn(dev, "rounding '%s' down to %u\n",
+					u32_opts[i].name, sel);
+		}
+		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, sel);
+		*u32_opts[i].val_ptr = rj;
+	};
+
+	for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
+		if (of_property_read_bool(dev->of_node, bool_opts[i].name))
+			*bool_opts[i].val_ptr = true;
+	}
+
+	cfg->num_strings = cfg->num_strings + 1;
+
+	return 0;
+}
+
+static int pm8941_wled_probe(struct platform_device *pdev)
+{
+	struct pm8941_wled_context *ctx;
+	struct regmap *regmap;
+	int rc;
+
+	regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!regmap) {
+		dev_err(&pdev->dev, "Unable to get regmap\n");
+		return -EINVAL;
+	}
+
+	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	ctx->regmap = regmap;
+
+	rc = pm8941_wled_configure(&ctx->wled, &pdev->dev);
+	if (rc)
+		return rc;
+
+	rc = pm8941_wled_setup(&ctx->wled);
+	if (rc)
+		return rc;
+
+	ctx->wled.cdev.brightness_set = pm8941_wled_set_brightness;
+
+	rc = led_classdev_register(&pdev->dev, &ctx->wled.cdev);
+	if (rc)
+		return rc;
+
+	platform_set_drvdata(pdev, ctx);
+	dev_info(&pdev->dev, "registered led \"%s\"\n", ctx->wled.cdev.name);
+
+	return 0;
+};
+
+static int pm8941_wled_remove(struct platform_device *pdev)
+{
+	struct pm8941_wled_context *ctx;
+
+	ctx = platform_get_drvdata(pdev);
+	led_classdev_unregister(&ctx->wled.cdev);
+
+	return 0;
+}
+
+static const struct of_device_id pm8941_wled_match_table[] = {
+	{ .compatible = "qcom,pm8941-wled" },
+	{}
+};
+
+static struct platform_driver pm8941_wled_driver = {
+	.probe	= pm8941_wled_probe,
+	.remove = pm8941_wled_remove,
+	.driver	= {
+		.name		= "pm8941-wled",
+		.owner		= THIS_MODULE,
+		.of_match_table	= pm8941_wled_match_table,
+	},
+};
+
+module_platform_driver(pm8941_wled_driver);
+
+MODULE_DESCRIPTION("pm8941 wled driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: pm8941-wled");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block
  2014-12-09  0:22 [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bjorn Andersson
  2014-12-09  0:22 ` [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver Bjorn Andersson
@ 2014-12-10  1:19 ` Bryan Wu
  2014-12-17  0:54   ` Bryan Wu
  2014-12-17  1:15 ` Stephen Boyd
  2 siblings, 1 reply; 11+ messages in thread
From: Bryan Wu @ 2014-12-10  1:19 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Richard Purdie, Grant Likely, Courtney Cavin,
	devicetree@vger.kernel.org, lkml, Linux LED Subsystem,
	linux-arm-msm

On Mon, Dec 8, 2014 at 4:22 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:
> From: Courtney Cavin <courtney.cavin@sonymobile.com>
>
> This adds device tree binding documentation for the WLED ('White' LED)
> block on Qualcomm's PM8941 PMICs.
>

Looks good to me, I need device tree reviewer's Ack to merge it.

Thanks,
-Bryan

> Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> ---
>  .../devicetree/bindings/leds/leds-pm8941-wled.txt  | 43 ++++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
>
> diff --git a/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
> new file mode 100644
> index 0000000..a85a964
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
> @@ -0,0 +1,43 @@
> +Binding for Qualcomm PM8941 WLED driver
> +
> +Required properties:
> +- compatible: should be "qcom,pm8941-wled"
> +- reg: slave address
> +
> +Optional properties:
> +- label: The label for this led
> +  See Documentation/devicetree/bindings/leds/common.txt
> +- linux,default-trigger: Default trigger assigned to the LED
> +  See Documentation/devicetree/bindings/leds/common.txt
> +- qcom,cs-out: bool; enable current sink output
> +- qcom,cabc: bool; enable content adaptive backlight control
> +- qcom,ext-gen: bool; use externally generated modulator signal to dim
> +- qcom,current-limit: mA; per-string current limit; value from 0 to 25
> +       default: 20mA
> +- qcom,current-boost-limit: mA; boost current limit; one of:
> +       105, 385, 525, 805, 980, 1260, 1400, 1680
> +       default: 805mA
> +- qcom,switching-freq: kHz; switching frequency; one of:
> +       600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371,
> +       1600, 1920, 2400, 3200, 4800, 9600,
> +       default: 1600kHz
> +- qcom,ovp: V; Over-voltage protection limit; one of:
> +       27, 29, 32, 35
> +       default: 29V
> +- qcom,num-strings: #; number of led strings attached; value from 1 to 3
> +       default: 2
> +
> +Example:
> +
> +pm8941-wled@d800 {
> +       compatible = "qcom,pm8941-wled";
> +       reg = <0xd800>;
> +       label = "backlight";
> +
> +       qcom,cs-out;
> +       qcom,current-limit = <20>;
> +       qcom,current-boost-limit = <805>;
> +       qcom,switching-freq = <1600>;
> +       qcom,ovp = <29>;
> +       qcom,num-strings = <2>;
> +};
> --
> 1.8.2.2
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver
  2014-12-09  0:22 ` [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver Bjorn Andersson
@ 2014-12-17  0:54   ` Bryan Wu
  2014-12-18 23:43     ` Bjorn
  2014-12-17  1:15   ` Stephen Boyd
  1 sibling, 1 reply; 11+ messages in thread
From: Bryan Wu @ 2014-12-17  0:54 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Richard Purdie, Grant Likely, Courtney Cavin,
	devicetree@vger.kernel.org, lkml, Linux LED Subsystem,
	linux-arm-msm

On Mon, Dec 8, 2014 at 4:22 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:
> From: Courtney Cavin <courtney.cavin@sonymobile.com>
>
> This adds support for the WLED ('White' LED) block on Qualcomm's
> PM8941 PMICs.
>
> Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> ---
>  drivers/leds/Kconfig            |   8 +
>  drivers/leds/Makefile           |   1 +
>  drivers/leds/leds-pm8941-wled.c | 466 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 475 insertions(+)
>  create mode 100644 drivers/leds/leds-pm8941-wled.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index a210338..9091cf4 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -505,6 +505,14 @@ config LEDS_VERSATILE
>           This option enabled support for the LEDs on the ARM Versatile
>           and RealView boards. Say Y to enabled these.
>
> +config LEDS_PM8941_WLED
> +       tristate "LED support for the Qualcomm PM8941 WLED block"
> +       depends on LEDS_CLASS
> +       depends on REGMAP
Here should be "select REGMAP"

> +       help
> +         This option enables support for the 'White' LED block
> +         on Qualcomm PM8941 PMICs.
> +
>  comment "LED Triggers"
>  source "drivers/leds/trigger/Kconfig"
>
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index a2b1647..5474afc 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_LEDS_BLINKM)             += leds-blinkm.o
>  obj-$(CONFIG_LEDS_SYSCON)              += leds-syscon.o
>  obj-$(CONFIG_LEDS_VERSATILE)           += leds-versatile.o
>  obj-$(CONFIG_LEDS_MENF21BMC)           += leds-menf21bmc.o
> +obj-$(CONFIG_LEDS_PM8941_WLED)         += leds-pm8941-wled.o
>
>  # LED SPI Drivers
>  obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
> diff --git a/drivers/leds/leds-pm8941-wled.c b/drivers/leds/leds-pm8941-wled.c
> new file mode 100644
> index 0000000..aa06d0b
> --- /dev/null
> +++ b/drivers/leds/leds-pm8941-wled.c
> @@ -0,0 +1,466 @@
> +/* Copyright (c) 2013, Sony Mobile Communications, AB.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +
> +enum pm8941_wled_reg {
> +       PM8941_WLED_REG_VAL_BASE                = 0x40,
> +#define PM8941_WLED_REG_VAL_MAX                        0xFFF
> +
> +       PM8941_WLED_REG_MOD_EN                  = 0x46,
> +#define PM8941_WLED_REG_MOD_EN_BIT             BIT(7)
> +#define PM8941_WLED_REG_MOD_EN_MASK            BIT(7)
> +
> +       PM8941_WLED_REG_SYNC                    = 0x47,
> +#define PM8941_WLED_REG_SYNC_MASK              0x07
> +#define PM8941_WLED_REG_SYNC_LED1              BIT(0)
> +#define PM8941_WLED_REG_SYNC_LED2              BIT(1)
> +#define PM8941_WLED_REG_SYNC_LED3              BIT(2)
> +#define PM8941_WLED_REG_SYNC_ALL               0x07
> +#define PM8941_WLED_REG_SYNC_CLEAR             0x00
> +
> +       PM8941_WLED_REG_FREQ                    = 0x4c,
> +#define PM8941_WLED_REG_FREQ_MASK              0x0f
> +
> +       PM8941_WLED_REG_OVP                     = 0x4d,
> +#define PM8941_WLED_REG_OVP_MASK               0x03
> +
> +       PM8941_WLED_REG_BOOST                   = 0x4e,
> +#define PM8941_WLED_REG_BOOST_MASK             0x07
> +
> +       PM8941_WLED_REG_SINK                    = 0x4f,
> +#define PM8941_WLED_REG_SINK_MASK              0xe0
> +#define PM8941_WLED_REG_SINK_SHFT              0x05
> +
> +/* Per-'string' registers below */
> +#define PM8941_WLED_REG_STR_OFFSET             0x10
> +
> +       PM8941_WLED_REG_STR_MOD_EN_BASE         = 0x60,
> +#define PM8941_WLED_REG_STR_MOD_EN             BIT(7)
> +#define PM8941_WLED_REG_STR_MOD_MASK           BIT(7)
> +
> +       PM8941_WLED_REG_STR_SCALE_BASE          = 0x62,
> +#define PM8941_WLED_REG_STR_SCALE_MASK         0x1f
> +
> +       PM8941_WLED_REG_STR_MOD_SRC_BASE        = 0x63,
> +#define PM8941_WLED_REG_STR_MOD_SRC_MASK       0x01
> +#define PM8941_WLED_REG_STR_MOD_SRC_INT                0x00
> +#define PM8941_WLED_REG_STR_MOD_SRC_EXT                0x01
> +
> +       PM8941_WLED_REG_STR_CABC_BASE           = 0x66,
> +#define PM8941_WLED_REG_STR_CABC_MASK          BIT(7)
> +#define PM8941_WLED_REG_STR_CABC_EN            BIT(7)
> +};
> +
> +struct pm8941_wled_config {
> +       u32 i_boost_limit;
> +       u32 ovp;
> +       u32 switch_freq;
> +       u32 num_strings;
> +       u32 i_limit;
> +       bool cs_out_en;
> +       bool ext_gen;
> +       bool cabc_en;
> +};
> +
> +struct pm8941_wled {
> +       struct led_classdev cdev;
> +       struct pm8941_wled_config cfg;
> +       u16 addr;
> +};
> +
> +struct pm8941_wled_context {
> +       struct pm8941_wled wled;
> +       struct regmap *regmap;
> +};
> +

I think you can just add *regmap into pm8941_wled struct and
pm8941_wled_context is redundant to me.

> +static int pm8941_wled_set(struct led_classdev *cdev,
> +                          enum led_brightness value)
> +{
> +       struct pm8941_wled_context *ctx;
> +       struct pm8941_wled *wled;
> +       u8 ctrl = 0;
> +       u16 val;
> +       int rc;
> +       int i;
> +
> +       wled = container_of(cdev, struct pm8941_wled, cdev);
> +       ctx = container_of(wled, struct pm8941_wled_context, wled);
> +
> +       if (value != 0)
> +               ctrl = PM8941_WLED_REG_MOD_EN_BIT;
> +
> +       val = (value * PM8941_WLED_REG_VAL_MAX) / LED_FULL;
> +
> +       rc = regmap_update_bits(ctx->regmap,
> +                       wled->addr + PM8941_WLED_REG_MOD_EN,
> +                       PM8941_WLED_REG_MOD_EN_MASK, ctrl);
> +       if (rc)
> +               return rc;
> +
> +       for (i = 0; i < wled->cfg.num_strings; ++i) {
> +               u8 v[2] = { val & 0xff, (val >> 8) & 0xf };
> +
> +               rc = regmap_bulk_write(ctx->regmap,
> +                               wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i,
> +                               v, 2);
> +               if (rc)
> +                       return rc;
> +       }
> +
> +       rc = regmap_update_bits(ctx->regmap,
> +                       wled->addr + PM8941_WLED_REG_SYNC,
> +                       PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL);
> +       if (rc)
> +               return rc;
> +
> +       rc = regmap_update_bits(ctx->regmap,
> +                       wled->addr + PM8941_WLED_REG_SYNC,
> +                       PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR);
> +       return rc;
> +}
> +
> +static void pm8941_wled_set_brightness(struct led_classdev *cdev,
> +                                      enum led_brightness value)
> +{
> +       if (pm8941_wled_set(cdev, value)) {
> +               dev_err(cdev->dev, "Unable to set brightness\n");
> +               return;
> +       }
> +       cdev->brightness = value;
> +}
> +
> +static int pm8941_wled_setup(struct pm8941_wled *wled)
> +{
> +       struct pm8941_wled_context *ctx;
> +       int rc;
> +       int i;
> +
> +       ctx = container_of(wled, struct pm8941_wled_context, wled);
> +       rc = regmap_update_bits(ctx->regmap,
> +                       wled->addr + PM8941_WLED_REG_OVP,
> +                       PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp);
> +       if (rc)
> +               return rc;
> +
> +       rc = regmap_update_bits(ctx->regmap,
> +                       wled->addr + PM8941_WLED_REG_BOOST,
> +                       PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit);
> +       if (rc)
> +               return rc;
> +
> +       rc = regmap_update_bits(ctx->regmap,
> +                       wled->addr + PM8941_WLED_REG_FREQ,
> +                       PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq);
> +       if (rc)
> +               return rc;
> +
> +       if (wled->cfg.cs_out_en) {
> +               u8 all = (BIT(wled->cfg.num_strings) - 1)
> +                               << PM8941_WLED_REG_SINK_SHFT;
> +
> +               rc = regmap_update_bits(ctx->regmap,
> +                               wled->addr + PM8941_WLED_REG_SINK,
> +                               PM8941_WLED_REG_SINK_MASK, all);
> +               if (rc)
> +                       return rc;
> +       }
> +
> +       for (i = 0; i < wled->cfg.num_strings; ++i) {
> +               u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i;
> +
> +               rc = regmap_update_bits(ctx->regmap,
> +                               addr + PM8941_WLED_REG_STR_MOD_EN_BASE,
> +                               PM8941_WLED_REG_STR_MOD_MASK,
> +                               PM8941_WLED_REG_STR_MOD_EN);
> +               if (rc)
> +                       return rc;
> +
> +               if (wled->cfg.ext_gen) {
> +                       rc = regmap_update_bits(ctx->regmap,
> +                                       addr + PM8941_WLED_REG_STR_MOD_SRC_BASE,
> +                                       PM8941_WLED_REG_STR_MOD_SRC_MASK,
> +                                       PM8941_WLED_REG_STR_MOD_SRC_EXT);
> +                       if (rc)
> +                               return rc;
> +               }
> +
> +               rc = regmap_update_bits(ctx->regmap,
> +                               addr + PM8941_WLED_REG_STR_SCALE_BASE,
> +                               PM8941_WLED_REG_STR_SCALE_MASK,
> +                               wled->cfg.i_limit);
> +               if (rc)
> +                       return rc;
> +
> +               rc = regmap_update_bits(ctx->regmap,
> +                               addr + PM8941_WLED_REG_STR_CABC_BASE,
> +                               PM8941_WLED_REG_STR_CABC_MASK,
> +                               wled->cfg.cabc_en ?
> +                                       PM8941_WLED_REG_STR_CABC_EN : 0);
> +               if (rc)
> +                       return rc;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct pm8941_wled_config pm8941_wled_config_defaults = {
> +       .i_boost_limit = 3,
> +       .i_limit = 20,
> +       .ovp = 2,
> +       .switch_freq = 5,
> +       .num_strings = 0,
> +       .cs_out_en = false,
> +       .ext_gen = false,
> +       .cabc_en = false,
> +};
> +
> +struct pm8941_wled_var_cfg {
> +       const u32 *values;
> +       u32 (*fn)(u32);
> +       int size;
> +};
> +
> +static const u32 pm8941_wled_i_boost_limit_values[] = {
> +       105, 385, 525, 805, 980, 1260, 1400, 1680,
> +};
> +
> +static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = {
> +       .values = pm8941_wled_i_boost_limit_values,
> +       .size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values),
> +};
> +
> +static const u32 pm8941_wled_ovp_values[] = {
> +       35, 32, 29, 27,
> +};
> +
> +static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = {
> +       .values = pm8941_wled_ovp_values,
> +       .size = ARRAY_SIZE(pm8941_wled_ovp_values),
> +};
> +
> +static u32 pm8941_wled_num_strings_values_fn(u32 idx)
> +{
> +       return idx + 1;
> +}
> +
> +static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = {
> +       .fn = pm8941_wled_num_strings_values_fn,
> +       .size = 3,
> +};
> +
> +static u32 pm8941_wled_switch_freq_values_fn(u32 idx)
> +{
> +       return 19200 / (2 * (1 + idx));
> +}
> +
> +static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = {
> +       .fn = pm8941_wled_switch_freq_values_fn,
> +       .size = 16,
> +};
> +
> +static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = {
> +       .size = 26,
> +};
> +
> +static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx)
> +{
> +       if (idx >= cfg->size)
> +               return UINT_MAX;
> +       if (cfg->fn)
> +               return cfg->fn(idx);
> +       if (cfg->values)
> +               return cfg->values[idx];
> +       return idx;
> +}
> +
> +static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev)
> +{
> +       struct pm8941_wled_config *cfg = &wled->cfg;
> +       u32 val;
> +       int rc;
> +       int i;
> +
> +       const struct {
> +               const char *name;
> +               u32 *val_ptr;
> +               const struct pm8941_wled_var_cfg *cfg;
> +       } u32_opts[] = {
> +               {
> +                       "qcom,current-boost-limit", &cfg->i_boost_limit,
> +                       .cfg = &pm8941_wled_i_boost_limit_cfg,
> +               },
> +               {
> +                       "qcom,current-limit", &cfg->i_limit,
> +                       .cfg = &pm8941_wled_i_limit_cfg,
> +               },
> +               {
> +                       "qcom,ovp", &cfg->ovp,
> +                       .cfg = &pm8941_wled_ovp_cfg,
> +               },
> +               {
> +                       "qcom,switching-freq", &cfg->switch_freq,
> +                       .cfg = &pm8941_wled_switch_freq_cfg,
> +               },
> +               {
> +                       "qcom,num-strings", &cfg->num_strings,
> +                       .cfg = &pm8941_wled_num_strings_cfg,
> +               },
> +       };
> +       const struct {
> +               const char *name;
> +               bool *val_ptr;
> +       } bool_opts[] = {
> +               { "qcom,cs-out", &cfg->cs_out_en, },
> +               { "qcom,ext-gen", &cfg->ext_gen, },
> +               { "qcom,cabc", &cfg->cabc_en, },
> +       };
> +
> +       if (dev->of_node == NULL) {
> +               dev_err(dev, "no OF node!\n");
> +               return -EINVAL;
> +       }
> +
> +       rc = of_property_read_u32(dev->of_node, "reg", &val);
> +       if (rc || val > 0xffff) {
> +               dev_err(dev, "invalid IO resources\n");
> +               return rc ? rc : -EINVAL;
> +       }
> +       wled->addr = val;
> +
> +       rc = of_property_read_string(dev->of_node, "label", &wled->cdev.name);
> +       if (rc)
> +               wled->cdev.name = dev->of_node->name;
> +
> +       wled->cdev.default_trigger = of_get_property(dev->of_node,
> +                       "linux,default-trigger", NULL);
> +
> +       *cfg = pm8941_wled_config_defaults;
> +       for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) {
> +               u32 sel, c;
> +               int j, rj;
> +
> +               rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
> +               if (rc) {
> +                       if (rc != -EINVAL) {
> +                               dev_err(dev, "error reading '%s'\n",
> +                                               u32_opts[i].name);
> +                               return rc;
> +                       }
> +                       continue;
> +               }
> +
> +               sel = UINT_MAX;
> +               rj = -1;
> +               c = pm8941_wled_values(u32_opts[i].cfg, 0);
> +               for (j = 0; c != UINT_MAX; ++j) {
> +                       if (c <= val && (sel == UINT_MAX || c >= sel)) {
> +                               sel = c;
> +                               rj = j;
> +                       }
> +                       c = pm8941_wled_values(u32_opts[i].cfg, j + 1);
> +               }
> +               if (sel == UINT_MAX) {
> +                       dev_err(dev, "invalid value for '%s'\n",
> +                                       u32_opts[i].name);
> +                       return rc;
> +               }
> +               if (sel != val) {
> +                       dev_warn(dev, "rounding '%s' down to %u\n",
> +                                       u32_opts[i].name, sel);
> +               }
> +               dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, sel);
> +               *u32_opts[i].val_ptr = rj;
> +       };
> +
> +       for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
> +               if (of_property_read_bool(dev->of_node, bool_opts[i].name))
> +                       *bool_opts[i].val_ptr = true;
> +       }
> +
> +       cfg->num_strings = cfg->num_strings + 1;
> +
> +       return 0;
> +}
> +
> +static int pm8941_wled_probe(struct platform_device *pdev)
> +{
> +       struct pm8941_wled_context *ctx;
> +       struct regmap *regmap;
> +       int rc;
> +
> +       regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +       if (!regmap) {
> +               dev_err(&pdev->dev, "Unable to get regmap\n");
> +               return -EINVAL;
> +       }
> +

Is this regmap mmio registers or i2c registers? Will the regmap
operation sleep?

> +       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
> +       if (ctx == NULL)
> +               return -ENOMEM;
> +
> +       ctx->regmap = regmap;
> +
> +       rc = pm8941_wled_configure(&ctx->wled, &pdev->dev);
> +       if (rc)
> +               return rc;
> +
> +       rc = pm8941_wled_setup(&ctx->wled);
> +       if (rc)
> +               return rc;
> +
> +       ctx->wled.cdev.brightness_set = pm8941_wled_set_brightness;
> +
> +       rc = led_classdev_register(&pdev->dev, &ctx->wled.cdev);
> +       if (rc)
> +               return rc;
> +
> +       platform_set_drvdata(pdev, ctx);
> +       dev_info(&pdev->dev, "registered led \"%s\"\n", ctx->wled.cdev.name);
> +
> +       return 0;
> +};
> +
> +static int pm8941_wled_remove(struct platform_device *pdev)
> +{
> +       struct pm8941_wled_context *ctx;
> +
> +       ctx = platform_get_drvdata(pdev);
> +       led_classdev_unregister(&ctx->wled.cdev);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id pm8941_wled_match_table[] = {
> +       { .compatible = "qcom,pm8941-wled" },
> +       {}
> +};
> +
> +static struct platform_driver pm8941_wled_driver = {
> +       .probe  = pm8941_wled_probe,
> +       .remove = pm8941_wled_remove,
> +       .driver = {
> +               .name           = "pm8941-wled",
> +               .owner          = THIS_MODULE,
> +               .of_match_table = pm8941_wled_match_table,
> +       },
> +};
> +
> +module_platform_driver(pm8941_wled_driver);
> +
> +MODULE_DESCRIPTION("pm8941 wled driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform: pm8941-wled");
No space after ":"

> --
> 1.8.2.2
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block
  2014-12-10  1:19 ` [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bryan Wu
@ 2014-12-17  0:54   ` Bryan Wu
  0 siblings, 0 replies; 11+ messages in thread
From: Bryan Wu @ 2014-12-17  0:54 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Richard Purdie, Grant Likely, Courtney Cavin,
	devicetree@vger.kernel.org, lkml, Linux LED Subsystem,
	linux-arm-msm

On Tue, Dec 9, 2014 at 5:19 PM, Bryan Wu <cooloney@gmail.com> wrote:
> On Mon, Dec 8, 2014 at 4:22 PM, Bjorn Andersson
> <bjorn.andersson@sonymobile.com> wrote:
>> From: Courtney Cavin <courtney.cavin@sonymobile.com>
>>
>> This adds device tree binding documentation for the WLED ('White' LED)
>> block on Qualcomm's PM8941 PMICs.
>>
>
> Looks good to me, I need device tree reviewer's Ack to merge it.
>

Hi Rob, can you help to review it?

-Bryan

> Thanks,
> -Bryan
>
>> Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
>> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
>> ---
>>  .../devicetree/bindings/leds/leds-pm8941-wled.txt  | 43 ++++++++++++++++++++++
>>  1 file changed, 43 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
>>
>> diff --git a/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
>> new file mode 100644
>> index 0000000..a85a964
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt
>> @@ -0,0 +1,43 @@
>> +Binding for Qualcomm PM8941 WLED driver
>> +
>> +Required properties:
>> +- compatible: should be "qcom,pm8941-wled"
>> +- reg: slave address
>> +
>> +Optional properties:
>> +- label: The label for this led
>> +  See Documentation/devicetree/bindings/leds/common.txt
>> +- linux,default-trigger: Default trigger assigned to the LED
>> +  See Documentation/devicetree/bindings/leds/common.txt
>> +- qcom,cs-out: bool; enable current sink output
>> +- qcom,cabc: bool; enable content adaptive backlight control
>> +- qcom,ext-gen: bool; use externally generated modulator signal to dim
>> +- qcom,current-limit: mA; per-string current limit; value from 0 to 25
>> +       default: 20mA
>> +- qcom,current-boost-limit: mA; boost current limit; one of:
>> +       105, 385, 525, 805, 980, 1260, 1400, 1680
>> +       default: 805mA
>> +- qcom,switching-freq: kHz; switching frequency; one of:
>> +       600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371,
>> +       1600, 1920, 2400, 3200, 4800, 9600,
>> +       default: 1600kHz
>> +- qcom,ovp: V; Over-voltage protection limit; one of:
>> +       27, 29, 32, 35
>> +       default: 29V
>> +- qcom,num-strings: #; number of led strings attached; value from 1 to 3
>> +       default: 2
>> +
>> +Example:
>> +
>> +pm8941-wled@d800 {
>> +       compatible = "qcom,pm8941-wled";
>> +       reg = <0xd800>;
>> +       label = "backlight";
>> +
>> +       qcom,cs-out;
>> +       qcom,current-limit = <20>;
>> +       qcom,current-boost-limit = <805>;
>> +       qcom,switching-freq = <1600>;
>> +       qcom,ovp = <29>;
>> +       qcom,num-strings = <2>;
>> +};
>> --
>> 1.8.2.2
>>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver
  2014-12-09  0:22 ` [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver Bjorn Andersson
  2014-12-17  0:54   ` Bryan Wu
@ 2014-12-17  1:15   ` Stephen Boyd
  2014-12-18 23:57     ` Bjorn
  1 sibling, 1 reply; 11+ messages in thread
From: Stephen Boyd @ 2014-12-17  1:15 UTC (permalink / raw)
  To: Bjorn Andersson, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Bryan Wu, Richard Purdie, Grant Likely
  Cc: Courtney Cavin, devicetree, linux-kernel, linux-leds,
	linux-arm-msm

On 12/08/2014 04:22 PM, Bjorn Andersson wrote:
> diff --git a/drivers/leds/leds-pm8941-wled.c b/drivers/leds/leds-pm8941-wled.c
> new file mode 100644
> index 0000000..aa06d0b
> --- /dev/null
> +++ b/drivers/leds/leds-pm8941-wled.c
> @@ -0,0 +1,466 @@
> +/* Copyright (c) 2013, Sony Mobile Communications, AB.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>

Missing <linux/kernel.h> for container_of() macro.

> +
> +enum pm8941_wled_reg {
> +	PM8941_WLED_REG_VAL_BASE		= 0x40,
> +#define PM8941_WLED_REG_VAL_MAX			0xFFF
> +
> +	PM8941_WLED_REG_MOD_EN			= 0x46,
> +#define PM8941_WLED_REG_MOD_EN_BIT		BIT(7)
> +#define PM8941_WLED_REG_MOD_EN_MASK		BIT(7)
> +
> +	PM8941_WLED_REG_SYNC			= 0x47,
> +#define PM8941_WLED_REG_SYNC_MASK		0x07
> +#define PM8941_WLED_REG_SYNC_LED1		BIT(0)
> +#define PM8941_WLED_REG_SYNC_LED2		BIT(1)
> +#define PM8941_WLED_REG_SYNC_LED3		BIT(2)
> +#define PM8941_WLED_REG_SYNC_ALL		0x07
> +#define PM8941_WLED_REG_SYNC_CLEAR		0x00
> +
> +	PM8941_WLED_REG_FREQ			= 0x4c,
> +#define PM8941_WLED_REG_FREQ_MASK		0x0f
> +
> +	PM8941_WLED_REG_OVP			= 0x4d,
> +#define PM8941_WLED_REG_OVP_MASK		0x03
> +
> +	PM8941_WLED_REG_BOOST			= 0x4e,
> +#define PM8941_WLED_REG_BOOST_MASK		0x07
> +
> +	PM8941_WLED_REG_SINK			= 0x4f,
> +#define PM8941_WLED_REG_SINK_MASK		0xe0
> +#define PM8941_WLED_REG_SINK_SHFT		0x05
> +
> +/* Per-'string' registers below */
> +#define PM8941_WLED_REG_STR_OFFSET		0x10
> +
> +	PM8941_WLED_REG_STR_MOD_EN_BASE		= 0x60,
> +#define PM8941_WLED_REG_STR_MOD_EN		BIT(7)
> +#define PM8941_WLED_REG_STR_MOD_MASK		BIT(7)
> +
> +	PM8941_WLED_REG_STR_SCALE_BASE		= 0x62,
> +#define PM8941_WLED_REG_STR_SCALE_MASK		0x1f
> +
> +	PM8941_WLED_REG_STR_MOD_SRC_BASE	= 0x63,
> +#define PM8941_WLED_REG_STR_MOD_SRC_MASK	0x01
> +#define PM8941_WLED_REG_STR_MOD_SRC_INT		0x00
> +#define PM8941_WLED_REG_STR_MOD_SRC_EXT		0x01
> +
> +	PM8941_WLED_REG_STR_CABC_BASE		= 0x66,
> +#define PM8941_WLED_REG_STR_CABC_MASK		BIT(7)
> +#define PM8941_WLED_REG_STR_CABC_EN		BIT(7)
> +};

I don't see any value in this enum. It's never used in a switch
statement so why not just have #defines for the register offsets. It
would make reading this a lot easier too.


> +
> +static int pm8941_wled_set(struct led_classdev *cdev,
> +			   enum led_brightness value)
> +{
> +	struct pm8941_wled_context *ctx;
> +	struct pm8941_wled *wled;
> +	u8 ctrl = 0;
> +	u16 val;
> +	int rc;
> +	int i;
> +
> +	wled = container_of(cdev, struct pm8941_wled, cdev);
> +	ctx = container_of(wled, struct pm8941_wled_context, wled);
> +
> +	if (value != 0)
> +		ctrl = PM8941_WLED_REG_MOD_EN_BIT;
> +
> +	val = (value * PM8941_WLED_REG_VAL_MAX) / LED_FULL;

Unnecessary parens here.

> +
> +	rc = regmap_update_bits(ctx->regmap,
>
> +
> +static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev)
> +{
[...]
> +
> +	if (dev->of_node == NULL) {
> +		dev_err(dev, "no OF node!\n");
> +		return -EINVAL;
> +	}

Seems like an unnecessary check. Everything's DT so far.

> +
> +	rc = of_property_read_u32(dev->of_node, "reg", &val);
> +	if (rc || val > 0xffff) {
> +		dev_err(dev, "invalid IO resources\n");
> +		return rc ? rc : -EINVAL;
> +	}
> +	wled->addr = val;
> +
> +	rc = of_property_read_string(dev->of_node, "label", &wled->cdev.name);
> +	if (rc)
> +		wled->cdev.name = dev->of_node->name;
> +
> +	wled->cdev.default_trigger = of_get_property(dev->of_node,
> +			"linux,default-trigger", NULL);
> +
> +	*cfg = pm8941_wled_config_defaults;
> +	for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) {
> +		u32 sel, c;
> +		int j, rj;
> +
> +		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
> +		if (rc) {
> +			if (rc != -EINVAL) {
> +				dev_err(dev, "error reading '%s'\n",
> +						u32_opts[i].name);
> +				return rc;
> +			}
> +			continue;
> +		}
> +
> +		sel = UINT_MAX;
> +		rj = -1;
> +		c = pm8941_wled_values(u32_opts[i].cfg, 0);
> +		for (j = 0; c != UINT_MAX; ++j) {
> +			if (c <= val && (sel == UINT_MAX || c >= sel)) {
> +				sel = c;
> +				rj = j;
> +			}
> +			c = pm8941_wled_values(u32_opts[i].cfg, j + 1);
> +		}
> +		if (sel == UINT_MAX) {
> +			dev_err(dev, "invalid value for '%s'\n",
> +					u32_opts[i].name);
> +			return rc;
> +		}
> +		if (sel != val) {
> +			dev_warn(dev, "rounding '%s' down to %u\n",
> +					u32_opts[i].name, sel);
> +		}

Do we really want to do all this complicated rounding and checking?
Can't we just assume the DT is correct?

> +		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, sel);
> +		*u32_opts[i].val_ptr = rj;
> +	};
> +
> +	for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
> +		if (of_property_read_bool(dev->of_node, bool_opts[i].name))
> +			*bool_opts[i].val_ptr = true;
> +	}
> +
> +	cfg->num_strings = cfg->num_strings + 1;
> +
> +	return 0;
> +}
> +
> +static int pm8941_wled_probe(struct platform_device *pdev)
> +{
> +	struct pm8941_wled_context *ctx;
> +	struct regmap *regmap;
> +	int rc;
> +
> +	regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!regmap) {
> +		dev_err(&pdev->dev, "Unable to get regmap\n");
> +		return -EINVAL;
> +	}
> +
> +	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
> +	if (ctx == NULL)

Nitpick only because it doesn't match the !regmap style above. Please
use the !ctx style here as well.

> +		return -ENOMEM;
> +
> +	ctx->regmap = regmap;
> +
> +	rc = pm8941_wled_configure(&ctx->wled, &pdev->dev);
> +	if (rc)
> +		return rc;
> +
> +	rc = pm8941_wled_setup(&ctx->wled);
> +	if (rc)
> +		return rc;
> +
> +	ctx->wled.cdev.brightness_set = pm8941_wled_set_brightness;
> +
> +	rc = led_classdev_register(&pdev->dev, &ctx->wled.cdev);
> +	if (rc)
> +		return rc;
> +
> +	platform_set_drvdata(pdev, ctx);
> +	dev_info(&pdev->dev, "registered led \"%s\"\n", ctx->wled.cdev.name);

Please don't spam the log with "I'm alive!" messages.

> +
> +	return 0;
> +};
> +
> +static int pm8941_wled_remove(struct platform_device *pdev)
> +{
> +	struct pm8941_wled_context *ctx;
> +
> +	ctx = platform_get_drvdata(pdev);
> +	led_classdev_unregister(&ctx->wled.cdev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id pm8941_wled_match_table[] = {
> +	{ .compatible = "qcom,pm8941-wled" },
> +	{}
> +};

MODULE_DEVICE_TABLE?

> +
> +static struct platform_driver pm8941_wled_driver = {
> +	.probe	= pm8941_wled_probe,
> +	.remove = pm8941_wled_remove,
> +	.driver	= {
> +		.name		= "pm8941-wled",
> +		.owner		= THIS_MODULE,
> +		.of_match_table	= pm8941_wled_match_table,
> +	},
> +};
> +
> +module_platform_driver(pm8941_wled_driver);
> +
> +MODULE_DESCRIPTION("pm8941 wled driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform: pm8941-wled");

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block
  2014-12-09  0:22 [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bjorn Andersson
  2014-12-09  0:22 ` [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver Bjorn Andersson
  2014-12-10  1:19 ` [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bryan Wu
@ 2014-12-17  1:15 ` Stephen Boyd
  2014-12-18 23:36   ` Bjorn
  2 siblings, 1 reply; 11+ messages in thread
From: Stephen Boyd @ 2014-12-17  1:15 UTC (permalink / raw)
  To: Bjorn Andersson, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Bryan Wu, Richard Purdie, Grant Likely
  Cc: Courtney Cavin, devicetree, linux-kernel, linux-leds,
	linux-arm-msm

On 12/08/2014 04:22 PM, Bjorn Andersson wrote:

> +
> +Optional properties:
> +- label: The label for this led
> +  See Documentation/devicetree/bindings/leds/common.txt
> +- linux,default-trigger: Default trigger assigned to the LED
> +  See Documentation/devicetree/bindings/leds/common.txt
> +- qcom,cs-out: bool; enable current sink output
> +- qcom,cabc: bool; enable content adaptive backlight control
> +- qcom,ext-gen: bool; use externally generated modulator signal to dim
> +- qcom,current-limit: mA; per-string current limit; value from 0 to 25
> +	default: 20mA
> +- qcom,current-boost-limit: mA; boost current limit; one of:
> +	105, 385, 525, 805, 980, 1260, 1400, 1680
> +	default: 805mA
> +- qcom,switching-freq: kHz; switching frequency; one of:
> +	600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371,
> +	1600, 1920, 2400, 3200, 4800, 9600,
> +	default: 1600kHz
> +- qcom,ovp: V; Over-voltage protection limit; one of:
> +	27, 29, 32, 35
> +	default: 29V

It would be nice if the units were part of the property name, i.e.
qcom,ovp-V, qcom,current-boost-limit-mA, etc. That way the units are
known without having to refer to the documentation.

> +- qcom,num-strings: #; number of led strings attached; value from 1 to 3
> +	default: 2

I wonder if qcom,#strings is more appropriate?

> +
> +Example:
> +
> +pm8941-wled@d800 {
> +	compatible = "qcom,pm8941-wled";
> +	reg = <0xd800>;
> +	label = "backlight";
> +
> +	qcom,cs-out;
> +	qcom,current-limit = <20>;
> +	qcom,current-boost-limit = <805>;
> +	qcom,switching-freq = <1600>;
> +	qcom,ovp = <29>;
> +	qcom,num-strings = <2>;
> +};


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block
  2014-12-17  1:15 ` Stephen Boyd
@ 2014-12-18 23:36   ` Bjorn
  0 siblings, 0 replies; 11+ messages in thread
From: Bjorn @ 2014-12-18 23:36 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bryan Wu, Richard Purdie, Grant Likely, Cavin, Courtney,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-leds@vger.kernel.org, linux-arm-msm@vger.kernel.org

On Tue 16 Dec 17:15 PST 2014, Stephen Boyd wrote:

> On 12/08/2014 04:22 PM, Bjorn Andersson wrote:
> 
[..]
> > +- qcom,current-limit: mA; per-string current limit; value from 0 to 25
> > +	default: 20mA
> > +- qcom,current-boost-limit: mA; boost current limit; one of:
> > +	105, 385, 525, 805, 980, 1260, 1400, 1680
> > +	default: 805mA
[..]
> > +- qcom,ovp: V; Over-voltage protection limit; one of:
> > +	27, 29, 32, 35
> > +	default: 29V
> 
> It would be nice if the units were part of the property name, i.e.
> qcom,ovp-V, qcom,current-boost-limit-mA, etc. That way the units are
> known without having to refer to the documentation.
> 

That might be useful, on the other hand one probably need to look inte bindings
docs to figure out valid values anyways.

> > +- qcom,num-strings: #; number of led strings attached; value from 1 to 3
> > +	default: 2
> 
> I wonder if qcom,#strings is more appropriate?
> 

Checking the other docs indicate that it's far more common to use num- than #
here, but I haven't found anything written about it - it's more common to name
this just "qcom,strings" than "qcom,#strings" at least...

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver
  2014-12-17  0:54   ` Bryan Wu
@ 2014-12-18 23:43     ` Bjorn
  2014-12-19  8:15       ` Ivan T. Ivanov
  0 siblings, 1 reply; 11+ messages in thread
From: Bjorn @ 2014-12-18 23:43 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Richard Purdie, Grant Likely, Cavin, Courtney,
	devicetree@vger.kernel.org, lkml, Linux LED Subsystem,
	linux-arm-msm@vger.kernel.org

On Tue 16 Dec 16:54 PST 2014, Bryan Wu wrote:

> On Mon, Dec 8, 2014 at 4:22 PM, Bjorn Andersson

[..]

> > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig

[..]

> > +config LEDS_PM8941_WLED
> > +       tristate "LED support for the Qualcomm PM8941 WLED block"
> > +       depends on LEDS_CLASS
> > +       depends on REGMAP
> Here should be "select REGMAP"
> 

Thanks
> > +       help
> > +         This option enables support for the 'White' LED block
> > +         on Qualcomm PM8941 PMICs.
> > +

[..]

> > diff --git a/drivers/leds/leds-pm8941-wled.c b/drivers/leds/leds-pm8941-wled.c

[..]

> > +
> > +struct pm8941_wled_context {
> > +       struct pm8941_wled wled;
> > +       struct regmap *regmap;
> > +};
> > +
> 
> I think you can just add *regmap into pm8941_wled struct and
> pm8941_wled_context is redundant to me.
> 

Looks like a development leftover, I will fold it in.

[..]

> > +
> > +static int pm8941_wled_probe(struct platform_device *pdev)
> > +{
> > +       struct pm8941_wled_context *ctx;
> > +       struct regmap *regmap;
> > +       int rc;
> > +
> > +       regmap = dev_get_regmap(pdev->dev.parent, NULL);
> > +       if (!regmap) {
> > +               dev_err(&pdev->dev, "Unable to get regmap\n");
> > +               return -EINVAL;
> > +       }
> > +
> 
> Is this regmap mmio registers or i2c registers? Will the regmap
> operation sleep?
> 

It's a spmi regmap, in it's current configuration it's marked as "fast" i.e. it
will not sleep.

I discussed this with Courtney before sending the patch out and as this is used
for backlight we don't see a reason calling set_brightness() in atomic context.
If this changes I would send out a follow up patch.

> > +       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
> > +       if (ctx == NULL)
> > +               return -ENOMEM;
> > +
> > +       ctx->regmap = regmap;
> > +
> > +       rc = pm8941_wled_configure(&ctx->wled, &pdev->dev);
> > +       if (rc)
> > +               return rc;
> > +
> > +       rc = pm8941_wled_setup(&ctx->wled);
> > +       if (rc)
> > +               return rc;
> > +
> > +       ctx->wled.cdev.brightness_set = pm8941_wled_set_brightness;
> > +
> > +       rc = led_classdev_register(&pdev->dev, &ctx->wled.cdev);
> > +       if (rc)
> > +               return rc;
> > +
> > +       platform_set_drvdata(pdev, ctx);
> > +       dev_info(&pdev->dev, "registered led \"%s\"\n", ctx->wled.cdev.name);
> > +
> > +       return 0;
> > +};
> > +

[..]

> > +MODULE_DESCRIPTION("pm8941 wled driver");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform: pm8941-wled");
> No space after ":"
> 

Oops.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver
  2014-12-17  1:15   ` Stephen Boyd
@ 2014-12-18 23:57     ` Bjorn
  0 siblings, 0 replies; 11+ messages in thread
From: Bjorn @ 2014-12-18 23:57 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bryan Wu, Richard Purdie, Grant Likely, Cavin, Courtney,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-leds@vger.kernel.org, linux-arm-msm@vger.kernel.org

On Tue 16 Dec 17:15 PST 2014, Stephen Boyd wrote:

> On 12/08/2014 04:22 PM, Bjorn Andersson wrote:
> > diff --git a/drivers/leds/leds-pm8941-wled.c b/drivers/leds/leds-pm8941-wled.c

[..]

> > +#include <linux/leds.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/regmap.h>
> 
> Missing <linux/kernel.h> for container_of() macro.
> 

Thanks

> > +
> > +enum pm8941_wled_reg {
> > +	PM8941_WLED_REG_VAL_BASE		= 0x40,
> > +#define PM8941_WLED_REG_VAL_MAX			0xFFF
> > +
> > +	PM8941_WLED_REG_MOD_EN			= 0x46,
> > +#define PM8941_WLED_REG_MOD_EN_BIT		BIT(7)
> > +#define PM8941_WLED_REG_MOD_EN_MASK		BIT(7)
> > +
> > +	PM8941_WLED_REG_SYNC			= 0x47,
> > +#define PM8941_WLED_REG_SYNC_MASK		0x07
> > +#define PM8941_WLED_REG_SYNC_LED1		BIT(0)
> > +#define PM8941_WLED_REG_SYNC_LED2		BIT(1)
> > +#define PM8941_WLED_REG_SYNC_LED3		BIT(2)
> > +#define PM8941_WLED_REG_SYNC_ALL		0x07
> > +#define PM8941_WLED_REG_SYNC_CLEAR		0x00
> > +
> > +	PM8941_WLED_REG_FREQ			= 0x4c,
> > +#define PM8941_WLED_REG_FREQ_MASK		0x0f
> > +
> > +	PM8941_WLED_REG_OVP			= 0x4d,
> > +#define PM8941_WLED_REG_OVP_MASK		0x03
> > +
> > +	PM8941_WLED_REG_BOOST			= 0x4e,
> > +#define PM8941_WLED_REG_BOOST_MASK		0x07
> > +
> > +	PM8941_WLED_REG_SINK			= 0x4f,
> > +#define PM8941_WLED_REG_SINK_MASK		0xe0
> > +#define PM8941_WLED_REG_SINK_SHFT		0x05
> > +
> > +/* Per-'string' registers below */
> > +#define PM8941_WLED_REG_STR_OFFSET		0x10
> > +
> > +	PM8941_WLED_REG_STR_MOD_EN_BASE		= 0x60,
> > +#define PM8941_WLED_REG_STR_MOD_EN		BIT(7)
> > +#define PM8941_WLED_REG_STR_MOD_MASK		BIT(7)
> > +
> > +	PM8941_WLED_REG_STR_SCALE_BASE		= 0x62,
> > +#define PM8941_WLED_REG_STR_SCALE_MASK		0x1f
> > +
> > +	PM8941_WLED_REG_STR_MOD_SRC_BASE	= 0x63,
> > +#define PM8941_WLED_REG_STR_MOD_SRC_MASK	0x01
> > +#define PM8941_WLED_REG_STR_MOD_SRC_INT		0x00
> > +#define PM8941_WLED_REG_STR_MOD_SRC_EXT		0x01
> > +
> > +	PM8941_WLED_REG_STR_CABC_BASE		= 0x66,
> > +#define PM8941_WLED_REG_STR_CABC_MASK		BIT(7)
> > +#define PM8941_WLED_REG_STR_CABC_EN		BIT(7)
> > +};
> 
> I don't see any value in this enum. It's never used in a switch
> statement so why not just have #defines for the register offsets. It
> would make reading this a lot easier too.
> 

Make sense, I'll rework it.

> > +
> > +static int pm8941_wled_set(struct led_classdev *cdev,
> > +			   enum led_brightness value)
> > +{
> > +	struct pm8941_wled_context *ctx;
> > +	struct pm8941_wled *wled;
> > +	u8 ctrl = 0;
> > +	u16 val;
> > +	int rc;
> > +	int i;
> > +
> > +	wled = container_of(cdev, struct pm8941_wled, cdev);
> > +	ctx = container_of(wled, struct pm8941_wled_context, wled);
> > +
> > +	if (value != 0)
> > +		ctrl = PM8941_WLED_REG_MOD_EN_BIT;
> > +
> > +	val = (value * PM8941_WLED_REG_VAL_MAX) / LED_FULL;
> 
> Unnecessary parens here.
> 

Agreed

> > +
> > +	rc = regmap_update_bits(ctx->regmap,
> >
> > +
> > +static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev)
> > +{
> [...]
> > +
> > +	if (dev->of_node == NULL) {
> > +		dev_err(dev, "no OF node!\n");
> > +		return -EINVAL;
> > +	}
> 
> Seems like an unnecessary check. Everything's DT so far.
> 

Agreed.

> > +
> > +	rc = of_property_read_u32(dev->of_node, "reg", &val);
> > +	if (rc || val > 0xffff) {
> > +		dev_err(dev, "invalid IO resources\n");
> > +		return rc ? rc : -EINVAL;
> > +	}
> > +	wled->addr = val;
> > +
> > +	rc = of_property_read_string(dev->of_node, "label", &wled->cdev.name);
> > +	if (rc)
> > +		wled->cdev.name = dev->of_node->name;
> > +
> > +	wled->cdev.default_trigger = of_get_property(dev->of_node,
> > +			"linux,default-trigger", NULL);
> > +
> > +	*cfg = pm8941_wled_config_defaults;
> > +	for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) {
> > +		u32 sel, c;
> > +		int j, rj;
> > +
> > +		rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val);
> > +		if (rc) {
> > +			if (rc != -EINVAL) {
> > +				dev_err(dev, "error reading '%s'\n",
> > +						u32_opts[i].name);
> > +				return rc;
> > +			}
> > +			continue;
> > +		}
> > +
> > +		sel = UINT_MAX;
> > +		rj = -1;
> > +		c = pm8941_wled_values(u32_opts[i].cfg, 0);
> > +		for (j = 0; c != UINT_MAX; ++j) {
> > +			if (c <= val && (sel == UINT_MAX || c >= sel)) {
> > +				sel = c;
> > +				rj = j;
> > +			}
> > +			c = pm8941_wled_values(u32_opts[i].cfg, j + 1);
> > +		}
> > +		if (sel == UINT_MAX) {
> > +			dev_err(dev, "invalid value for '%s'\n",
> > +					u32_opts[i].name);
> > +			return rc;
> > +		}
> > +		if (sel != val) {
> > +			dev_warn(dev, "rounding '%s' down to %u\n",
> > +					u32_opts[i].name, sel);
> > +		}
> 
> Do we really want to do all this complicated rounding and checking?
> Can't we just assume the DT is correct?
> 

We could remove the printout about rounding, but the logic turns human-readable
values into register values (indices), so we need something for that.

> > +		dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, sel);
> > +		*u32_opts[i].val_ptr = rj;
> > +	};
> > +
> > +	for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) {
> > +		if (of_property_read_bool(dev->of_node, bool_opts[i].name))
> > +			*bool_opts[i].val_ptr = true;
> > +	}
> > +
> > +	cfg->num_strings = cfg->num_strings + 1;
> > +
> > +	return 0;
> > +}
> > +
> > +static int pm8941_wled_probe(struct platform_device *pdev)
> > +{
> > +	struct pm8941_wled_context *ctx;
> > +	struct regmap *regmap;
> > +	int rc;
> > +
> > +	regmap = dev_get_regmap(pdev->dev.parent, NULL);
> > +	if (!regmap) {
> > +		dev_err(&pdev->dev, "Unable to get regmap\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
> > +	if (ctx == NULL)
> 
> Nitpick only because it doesn't match the !regmap style above. Please
> use the !ctx style here as well.
> 

Agreed

> > +		return -ENOMEM;
> > +
> > +	ctx->regmap = regmap;
> > +
> > +	rc = pm8941_wled_configure(&ctx->wled, &pdev->dev);
> > +	if (rc)
> > +		return rc;
> > +
> > +	rc = pm8941_wled_setup(&ctx->wled);
> > +	if (rc)
> > +		return rc;
> > +
> > +	ctx->wled.cdev.brightness_set = pm8941_wled_set_brightness;
> > +
> > +	rc = led_classdev_register(&pdev->dev, &ctx->wled.cdev);
> > +	if (rc)
> > +		return rc;
> > +
> > +	platform_set_drvdata(pdev, ctx);
> > +	dev_info(&pdev->dev, "registered led \"%s\"\n", ctx->wled.cdev.name);
> 
> Please don't spam the log with "I'm alive!" messages.
> 

Ok

> > +
> > +	return 0;
> > +};
> > +

[..]

> > +
> > +static const struct of_device_id pm8941_wled_match_table[] = {
> > +	{ .compatible = "qcom,pm8941-wled" },
> > +	{}
> > +};
> 
> MODULE_DEVICE_TABLE?
> 

Indeed

Thanks for the review Stephen.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver
  2014-12-18 23:43     ` Bjorn
@ 2014-12-19  8:15       ` Ivan T. Ivanov
  0 siblings, 0 replies; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-12-19  8:15 UTC (permalink / raw)
  To: Bjorn
  Cc: Bryan Wu, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Richard Purdie, Grant Likely, Cavin, Courtney,
	devicetree@vger.kernel.org, lkml, Linux LED Subsystem,
	linux-arm-msm@vger.kernel.org


On Thu, 2014-12-18 at 15:43 -0800, Bjorn wrote:
> On Tue 16 Dec 16:54 PST 2014, Bryan Wu wrote:
> 
> > On Mon, Dec 8, 2014 at 4:22 PM, Bjorn Andersson
> 
> [..]
> 
> > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> 
> [..]
> 
> > > +config LEDS_PM8941_WLED
> > > +       tristate "LED support for the Qualcomm PM8941 WLED block"
> > > +       depends on LEDS_CLASS
> > > +       depends on REGMAP
> > Here should be "select REGMAP"
> > 
> 
> Thanks
> > > 


If not mistaken the correct way was:

        depends on SPMI
        select REGMAP_SPMI

or
        depends on MFD_SPMI_PMIC

Regards,
Ivan

https://patchwork.kernel.org/patch/4566591/

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2014-12-19  8:15 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-09  0:22 [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bjorn Andersson
2014-12-09  0:22 ` [PATCH 2/2] leds: add Qualcomm PM8941 WLED driver Bjorn Andersson
2014-12-17  0:54   ` Bryan Wu
2014-12-18 23:43     ` Bjorn
2014-12-19  8:15       ` Ivan T. Ivanov
2014-12-17  1:15   ` Stephen Boyd
2014-12-18 23:57     ` Bjorn
2014-12-10  1:19 ` [PATCH 1/2] leds: add DT binding for Qualcomm PM8941 WLED block Bryan Wu
2014-12-17  0:54   ` Bryan Wu
2014-12-17  1:15 ` Stephen Boyd
2014-12-18 23:36   ` Bjorn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).