All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module ***
@ 2017-05-31  6:14 fenglinw
  2017-05-31  6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
  2017-05-31  6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
  0 siblings, 2 replies; 15+ messages in thread
From: fenglinw @ 2017-05-31  6:14 UTC (permalink / raw)
  To: linux-arm-msm, linux-kernel; +Cc: subbaram, aghayal, wruan, kgunda, Fenglin Wu

From: Fenglin Wu <fenglinw@codeaurora.org>

QTI TRI_LED module and LPG modules are existing in some Qualcomm PMICs
like PMI8998, PM660, etc. TRI_LED module has 3 LED drivers and each is
controlled by a PWM channel used for LED dimming or blinking. LPG (Light
Pulse Generator) module can be configured to operate in PWM mode to
output PWM signal with a fixed amplitude or in LUT (Look Up Table) mode
to output PWM signal with a modulated amplitude. Normally, each LED driver
in TRI_LED module is controlled by the output of a LPG module.

This patchset adds the support of registering TRI_LED as LED devices and
adding LPGs as PWM devices, but only the LPG PWM mode is supported. LUT mode
is not included because current PWM driver framework doesn't support hardware
autonomously modulating amplitude with vary duties. This is going to be
supported in next patchset with some PWM framework change.

Fenglin Wu (2):
  leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module

 .../devicetree/bindings/leds/leds-qti-rgb.txt      |  66 +++
 .../devicetree/bindings/pwm/pwm-qti-lpg.txt        |  39 ++
 drivers/leds/Kconfig                               |   8 +
 drivers/leds/Makefile                              |   1 +
 drivers/leds/leds-qti-rgb.c                        | 634 +++++++++++++++++++++
 drivers/pwm/Kconfig                                |  10 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-qti-lpg.c                          | 578 +++++++++++++++++++
 8 files changed, 1337 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
 create mode 100644 drivers/leds/leds-qti-rgb.c
 create mode 100644 drivers/pwm/pwm-qti-lpg.c

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

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

* [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  2017-05-31  6:14 [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module *** fenglinw
@ 2017-05-31  6:14 ` fenglinw
       [not found]   ` <20170531061541.10808-2-fenglinw-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2017-05-31  6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
  1 sibling, 1 reply; 15+ messages in thread
From: fenglinw @ 2017-05-31  6:14 UTC (permalink / raw)
  To: linux-arm-msm, linux-kernel, Richard Purdie, Jacek Anaszewski,
	Pavel Machek, Rob Herring, Mark Rutland, linux-leds, devicetree
  Cc: subbaram, aghayal, wruan, kgunda, Fenglin Wu

From: Fenglin Wu <fenglinw@codeaurora.org>

QTI TRI_LED module has 3 current sinks for LED driver and each is
controlled by a PWM channel used for LED dimming or blinking. Add
the driver to support it.

Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
---
 .../devicetree/bindings/leds/leds-qti-rgb.txt      |  66 +++
 drivers/leds/Kconfig                               |   8 +
 drivers/leds/Makefile                              |   1 +
 drivers/leds/leds-qti-rgb.c                        | 634 +++++++++++++++++++++
 4 files changed, 709 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
 create mode 100644 drivers/leds/leds-qti-rgb.c

diff --git a/Documentation/devicetree/bindings/leds/leds-qti-rgb.txt b/Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
new file mode 100644
index 0000000..62daf38
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
@@ -0,0 +1,66 @@
+Qualcomm Technologies, Inc. TRI_LED driver specific bindings
+
+This binding document describes the properties of TRI_LED module in
+Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: Must be "qcom,leds-rgb".
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Register base of the TRI_LED module and length.
+
+- pwm-names:
+	Usage: required
+	Value type: <stringlist>
+	Definition: A list of string to label the PWM devices defined in pwms
+		property which are using for controlling LEDs.
+		It must be: "blue", "green", "red".
+
+- pwms:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: A list of the PWM devices (phandles) used for controlling
+		LEDs.
+
+- qcom,support-blink:
+	Usage: optional
+	Value type: <prop-encoded-array>
+	Definition: An array of integer values to indicate if "blue", "green", "red"
+		LEDs support blink control. The values are listed as the fixed
+		order for "blue", "green", "red" LEDs.
+
+- qcom,on-ms:
+	Usage: optional
+	Value type: <prop-encoded-array>
+	Definition: An array of time values (milli-seconds) to represent the
+		on duration for "blue", "green", "red" LEDs. The values are
+		listed as the fixed order for "blue", "green", "red" LEDs.
+		This property has to be defined if "qcom,support-blink" is
+		present.
+
+- qcom,off-ms:
+	Usage: optional
+	Value type: <prop-encoded-array>
+	Definition: An array of time values (milli-seconds) to represent the
+		off duration for "blue", "green", "red" LEDs. The values are
+		listed as the fixed order for "blue", "green", "red" LEDs.
+		This property has to be defined if "qcom,support-blink" is
+		present.
+
+Example:
+
+	pmi8998_rgb: rgb@d000{
+		compatible = "qcom,leds-rgb";
+		reg = <0xd000 0x100>;
+		pwm-names = "blue", "green", "red";
+		pwms =  <&pmi8998_pwm_3 0 1000000>,
+			<&pmi8998_pwm_4 0 1000000>,
+			<&pmi8998_pwm_5 0 1000000>;
+		qcom,support-blink = <1 1 1>;
+		qcom,on-ms = <500 500 500>;
+		qcom,off-ms = <500 500 500>;
+	};
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6c29998..8996bde 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -667,6 +667,14 @@ config LEDS_PM8058
 	  Choose this option if you want to use the LED drivers in
 	  the Qualcomm PM8058 PMIC.
 
+config LEDS_QTI_RGB
+	tristate "Qualcomm Technologies, Inc. TRI_LED driver"
+	depends on LEDS_CLASS && MFD_SPMI_PMIC && PWM && OF
+	help
+	  This driver supports the TRI_LED module found in Qualcomm
+	  Technologies, Inc. PMIC's chips. TRI_LED supports 3 LED drivers and
+	  each is controlled by a PWM channel used for dimming or blinking.
+
 config LEDS_MLXCPLD
 	tristate "LED support for the Mellanox boards"
 	depends on X86_64 && DMI
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 45f1339..d1a967c 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_LEDS_COBALT_QUBE)		+= leds-cobalt-qube.o
 obj-$(CONFIG_LEDS_COBALT_RAQ)		+= leds-cobalt-raq.o
 obj-$(CONFIG_LEDS_SUNFIRE)		+= leds-sunfire.o
 obj-$(CONFIG_LEDS_PCA9532)		+= leds-pca9532.o
+obj-$(CONFIG_LEDS_QTI_RGB)		+= leds-qti-rgb.o
 obj-$(CONFIG_LEDS_GPIO_REGISTER)	+= leds-gpio-register.o
 obj-$(CONFIG_LEDS_GPIO)			+= leds-gpio.o
 obj-$(CONFIG_LEDS_LP3944)		+= leds-lp3944.o
diff --git a/drivers/leds/leds-qti-rgb.c b/drivers/leds/leds-qti-rgb.c
new file mode 100644
index 0000000..0e363d4
--- /dev/null
+++ b/drivers/leds/leds-qti-rgb.c
@@ -0,0 +1,634 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#define REG_LED_SRC_SEL		0x45
+#define REG_LED_EN_CTL		0x46
+#define REG_LED_ATC_EN_CTL	0x47
+
+/* REG_LED_SRC_SEL */
+#define LED_SRC_SEL_MASK	GENMASK(1, 0)
+#define LED_SRC_GND		0x00
+#define LED_SRC_VINRGB_VBOOST	0x01
+#define LED_SRC_VSYS		0x03
+
+/* REG_LED_EN_CTL */
+#define LED_EN_CTL_MASK		GENMASK(7, 5)
+#define LED_EN_CTL_OFFSET	5
+
+/* REG_LED_ATC_EN_CTL */
+#define LED_ATC_EN_MASK		GENMASK(7, 5)
+
+#define NUM_LEDS		3
+const char * const led_names[NUM_LEDS] = {"blue", "green", "red"};
+
+struct pwm_setting {
+	u32	initial_period_ns;
+	u32	period_ns;
+	u32	duty_ns;
+};
+
+struct led_setting {
+	u32	brightness;
+	u32	on_ms;
+	u32	off_ms;
+	bool	blink;
+};
+
+struct qti_rgb_led_dev {
+	struct led_classdev	cdev;
+	struct pwm_device	*pwm_dev;
+	struct pwm_setting	pwm_setting;
+	struct led_setting	led_setting;
+	struct qti_rgb_chip	*chip;
+	struct work_struct	work;
+	struct mutex		lock;
+	bool			support_blink;
+	bool			blinking;
+	u8			idx;
+};
+
+struct qti_rgb_chip {
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct qti_rgb_led_dev	leds[NUM_LEDS];
+	struct mutex		bus_lock;
+	u16			reg_base;
+};
+
+static int qti_rgb_masked_write(struct qti_rgb_chip *chip,
+				u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	mutex_lock(&chip->bus_lock);
+	rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
+	if (rc < 0)
+		dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+					addr, val, mask, rc);
+	mutex_unlock(&chip->bus_lock);
+
+	return rc;
+}
+
+static int __rgb_led_config_pwm(struct qti_rgb_led_dev *led,
+				struct pwm_setting *pwm)
+{
+	int rc;
+
+	if (pwm->duty_ns == 0) {
+		pwm_disable(led->pwm_dev);
+		return 0;
+	}
+
+	rc = pwm_config(led->pwm_dev, pwm->duty_ns, pwm->period_ns);
+	if (rc < 0) {
+		dev_err(led->chip->dev, "Config PWM settings for %s led failed, rc=%d\n",
+					led->cdev.name, rc);
+		return rc;
+	}
+
+	rc = pwm_enable(led->pwm_dev);
+	if (rc < 0)
+		dev_err(led->chip->dev, "Enable PWM for %s led failed, rc=%d\n",
+					led->cdev.name, rc);
+
+	return rc;
+}
+
+static int __rgb_led_set(struct qti_rgb_led_dev *led)
+{
+	int rc = 0;
+	u8 val = 0, mask = 0;
+
+	rc = __rgb_led_config_pwm(led, &led->pwm_setting);
+	if (rc < 0) {
+		dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n",
+					led->cdev.name, rc);
+		return rc;
+	}
+
+	mask |= 1 << (led->idx + LED_EN_CTL_OFFSET);
+
+	if (led->pwm_setting.duty_ns == 0)
+		val = 0;
+	else
+		val = mask;
+
+	rc = qti_rgb_masked_write(led->chip, REG_LED_EN_CTL, mask, val);
+	if (rc < 0)
+		dev_err(led->chip->dev, "Update addr 0x%x failed, rc=%d\n",
+					REG_LED_EN_CTL, rc);
+
+	return rc;
+}
+
+static void rgb_led_set_work(struct work_struct *work)
+{
+	struct qti_rgb_led_dev *led = container_of(work,
+			struct qti_rgb_led_dev, work);
+	u32 brightness = 0, on_ms, off_ms, period_ns, duty_ns;
+	int rc = 0;
+
+	mutex_lock(&led->lock);
+	if (led->led_setting.blink) {
+		on_ms = led->led_setting.on_ms;
+		off_ms = led->led_setting.off_ms;
+
+		if (on_ms > INT_MAX / NSEC_PER_MSEC)
+			duty_ns = INT_MAX - 1;
+		else
+			duty_ns = on_ms * NSEC_PER_MSEC;
+
+		if (on_ms + off_ms > INT_MAX / NSEC_PER_MSEC) {
+			period_ns = INT_MAX;
+			duty_ns = (period_ns / (on_ms + off_ms)) * on_ms;
+		} else {
+			period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
+		}
+
+		if (period_ns < duty_ns && duty_ns != 0)
+			period_ns = duty_ns + 1;
+	} else {
+		brightness = led->led_setting.brightness;
+		period_ns = pwm_get_period(led->pwm_dev);
+		/* Use initial period if no blinking is required */
+		if (period_ns > led->pwm_setting.initial_period_ns)
+			period_ns = led->pwm_setting.initial_period_ns;
+
+		if (period_ns > INT_MAX / brightness)
+			duty_ns = (period_ns / LED_FULL) * brightness;
+		else
+			duty_ns = (period_ns * brightness) / LED_FULL;
+
+		if (period_ns < duty_ns && duty_ns != 0)
+			period_ns = duty_ns + 1;
+	}
+	pr_debug("PWM settings for %s led: period = %dns, duty = %dns\n",
+			led->cdev.name, period_ns, duty_ns);
+
+	led->pwm_setting.duty_ns = duty_ns;
+	led->pwm_setting.period_ns = period_ns;
+
+	rc = __rgb_led_set(led);
+	if (rc < 0) {
+		dev_err(led->chip->dev, "rgb_led_set %s failed, rc=%d\n",
+				led->cdev.name, rc);
+		goto unlock;
+	}
+
+	if (led->led_setting.blink) {
+		led->cdev.brightness = LED_FULL;
+		led->blinking = true;
+	} else {
+		led->cdev.brightness = brightness;
+		led->blinking = false;
+	}
+
+unlock:
+	mutex_unlock(&led->lock);
+}
+
+static void qti_rgb_led_set(struct led_classdev *led_cdev,
+		enum led_brightness brightness)
+{
+	struct qti_rgb_led_dev *led =
+		container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+	mutex_lock(&led->lock);
+	if (brightness > LED_FULL)
+		brightness = LED_FULL;
+
+	if (brightness == led->led_setting.brightness &&
+				!led->blinking) {
+		mutex_unlock(&led->lock);
+		return;
+	}
+	led->led_setting.blink = false;
+	led->led_setting.brightness = brightness;
+	mutex_unlock(&led->lock);
+
+	schedule_work(&led->work);
+}
+
+static enum led_brightness qti_rgb_led_get(struct led_classdev *led_cdev)
+{
+	return led_cdev->brightness;
+}
+
+static int qti_rgb_led_blink(struct led_classdev *led_cdev,
+		unsigned long *on_ms, unsigned long *off_ms)
+{
+	struct qti_rgb_led_dev *led =
+		container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+	if (*on_ms == 0 || *off_ms == 0) {
+		dev_err(led->chip->dev, "Can't set blink for on=%lums off=%lums\n",
+						*on_ms, *off_ms);
+		return -EINVAL;
+	}
+
+	mutex_lock(&led->lock);
+	if (led->blinking && *on_ms == led->led_setting.on_ms &&
+			*off_ms == led->led_setting.off_ms) {
+		pr_debug("Ignore, on/off setting is not changed: on %lums, off %lums\n",
+							*on_ms, *off_ms);
+		mutex_unlock(&led->lock);
+		return 0;
+	}
+
+	led->led_setting.blink = true;
+	led->led_setting.on_ms = (u32)*on_ms;
+	led->led_setting.off_ms = (u32)*off_ms;
+	mutex_unlock(&led->lock);
+
+	schedule_work(&led->work);
+
+	return 0;
+}
+
+static ssize_t blink_store(struct device *dev, struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	int rc;
+	u32 blink;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct qti_rgb_led_dev *led =
+		container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+	rc = kstrtouint(buf, 0, &blink);
+	if (rc)
+		return rc;
+
+	if (!!blink)
+		qti_rgb_led_blink(led_cdev,
+				(unsigned long *)&led->led_setting.on_ms,
+				(unsigned long *)&led->led_setting.off_ms);
+	else
+		qti_rgb_led_set(led_cdev, LED_OFF);
+
+	return count;
+}
+
+static ssize_t blink_show(struct device *dev, struct device_attribute *attr,
+							char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct qti_rgb_led_dev *led =
+		container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+	bool blink;
+
+	blink = led->led_setting.blink &&
+			led->cdev.brightness == LED_FULL;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", blink);
+}
+
+static ssize_t on_off_ms_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	int rc, buff_size;
+	char buff[32];
+	char *token, *buff_ptr;
+	bool blink;
+	u32 on_ms, off_ms, brightness;
+
+	buff_size = min(count, sizeof(buff) - 1);
+	memcpy(buff, buf, buff_size);
+	buff[buff_size] = '\0';
+	buff_ptr = buff;
+
+	token = strsep(&buff_ptr, " ");
+	if (!token)
+		return -EINVAL;
+
+	rc = kstrtouint(token, 0, &on_ms);
+	if (rc < 0)
+		return rc;
+
+	token = strsep(&buff_ptr, " ");
+	if (!token)
+		return -EINVAL;
+
+	rc = kstrtouint(token, 0, &off_ms);
+	if (rc < 0)
+		return rc;
+
+	blink = !(on_ms == 0 || off_ms == 0);
+	if (on_ms == 0)
+		brightness = LED_OFF;
+	else if (off_ms == 0)
+		brightness = LED_FULL;
+
+	if (blink)
+		qti_rgb_led_blink(led_cdev, (unsigned long *)&on_ms,
+					(unsigned long *)&off_ms);
+	else
+		qti_rgb_led_set(led_cdev, brightness);
+
+	return count;
+}
+
+static ssize_t on_off_ms_show(struct device *dev, struct device_attribute *attr,
+							char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct qti_rgb_led_dev *led =
+		container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+	return snprintf(buf, PAGE_SIZE, "on: %dms, off: %dms\n",
+			led->led_setting.on_ms, led->led_setting.off_ms);
+}
+
+static DEVICE_ATTR(blink, 0644, blink_show, blink_store);
+static DEVICE_ATTR(on_off_ms, 0644, on_off_ms_show, on_off_ms_store);
+
+static struct attribute *blink_attrs[] = {
+	&dev_attr_blink.attr,
+	&dev_attr_on_off_ms.attr,
+	NULL
+};
+
+static const struct attribute_group blink_attrs_group = {
+	.attrs = blink_attrs,
+};
+
+static int qti_rgb_leds_register(struct qti_rgb_chip *chip)
+{
+	int rc, i, j;
+
+	for (i = 0; i < NUM_LEDS; i++) {
+		INIT_WORK(&chip->leds[i].work, rgb_led_set_work);
+		mutex_init(&chip->leds[i].lock);
+		chip->leds[i].cdev.name = led_names[i];
+		chip->leds[i].cdev.max_brightness = LED_FULL;
+		chip->leds[i].cdev.brightness = LED_OFF;
+		chip->leds[i].cdev.brightness_set = qti_rgb_led_set;
+		chip->leds[i].cdev.brightness_get = qti_rgb_led_get;
+		if (chip->leds[i].support_blink)
+			chip->leds[i].cdev.blink_set = qti_rgb_led_blink;
+
+		rc = devm_led_classdev_register(chip->dev, &chip->leds[i].cdev);
+		if (rc < 0) {
+			dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
+							led_names[i], rc);
+			goto destroy;
+		}
+
+		if (chip->leds[i].support_blink) {
+			rc = sysfs_create_group(&chip->leds[i].cdev.dev->kobj,
+							&blink_attrs_group);
+			if (rc < 0) {
+				dev_err(chip->dev, "Create blink_attrs for %s led failed, rc=%d\n",
+						led_names[i], rc);
+				goto destroy;
+			}
+		}
+	}
+
+	return 0;
+destroy:
+	for (j = 0; j < i; j++) {
+		mutex_destroy(&chip->leds[i].lock);
+		sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
+				&blink_attrs_group);
+	}
+
+	return rc;
+}
+
+static int qti_rgb_leds_init_pwm_settings(struct qti_rgb_chip *chip)
+{
+	u32 period_ns, duty_ns;
+	bool is_enabled;
+	int i;
+
+	for (i = 0; i < NUM_LEDS; i++) {
+		period_ns = pwm_get_period(chip->leds[i].pwm_dev);
+		duty_ns = pwm_get_duty_cycle(chip->leds[i].pwm_dev);
+		is_enabled = pwm_is_enabled(chip->leds[i].pwm_dev);
+
+		pr_debug("%s led PWM default setting: period = %dns, duty = %dns, is_enabled = %d\n",
+			led_names[i], period_ns, duty_ns, is_enabled);
+		chip->leds[i].pwm_setting.initial_period_ns = period_ns;
+		if (duty_ns > period_ns) {
+			duty_ns = period_ns - 1;
+			pwm_set_duty_cycle(chip->leds[i].pwm_dev, duty_ns);
+		}
+
+		if (is_enabled)
+			pwm_disable(chip->leds[i].pwm_dev);
+	}
+
+	return 0;
+}
+
+static int qti_rgb_leds_hw_init(struct qti_rgb_chip *chip)
+{
+	int rc = 0;
+
+	/* Disable ATC_EN for LEDs */
+	rc = qti_rgb_masked_write(chip, REG_LED_ATC_EN_CTL,
+				LED_ATC_EN_MASK, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Writing ATC_EN_CTL failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Select VINRGB_VBOOST as the source */
+	rc = qti_rgb_masked_write(chip, REG_LED_SRC_SEL, LED_SRC_SEL_MASK,
+				LED_SRC_VINRGB_VBOOST);
+	if (rc < 0) {
+		dev_err(chip->dev, "Writing SRC_SEL failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int qti_rgb_leds_parse_dt(struct qti_rgb_chip *chip)
+{
+	int rc, i, count;
+	const __be32 *addr;
+	u32 support_blink[NUM_LEDS], on_ms[NUM_LEDS], off_ms[NUM_LEDS];
+
+	addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+	if (!addr) {
+		dev_err(chip->dev, "Getting address failed\n");
+		return -EINVAL;
+	}
+	chip->reg_base = be32_to_cpu(addr[0]);
+
+	for (i = 0; i < NUM_LEDS; i++) {
+		chip->leds[i].pwm_dev = devm_pwm_get(chip->dev, led_names[i]);
+		if (IS_ERR(chip->leds[i].pwm_dev)) {
+			rc = PTR_ERR(chip->leds[i].pwm_dev);
+			if (rc != -EPROBE_DEFER)
+				dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n",
+						led_names[i], rc);
+			return rc;
+		}
+		chip->leds[i].chip = chip;
+		chip->leds[i].idx = i;
+	}
+
+	count = of_property_count_elems_of_size(chip->dev->of_node,
+			"qcom,support-blink", sizeof(u32));
+	if (count > 0) {
+		if (count != NUM_LEDS) {
+			dev_err(chip->dev, "qcom,support-blink property expects %d elements, but it has %d\n",
+					NUM_LEDS, count);
+			return -EINVAL;
+		}
+		rc = of_property_read_u32_array(chip->dev->of_node,
+				"qcom,support-blink", support_blink, count);
+		if (rc < 0) {
+			dev_err(chip->dev, "qcom,support-blink property reading failed, rc=%d\n",
+					rc);
+			return rc;
+		}
+		rc = of_property_read_u32_array(chip->dev->of_node,
+				"qcom,on-ms", on_ms, count);
+		if (rc < 0) {
+			dev_err(chip->dev, "qcom,on-ms property reading failed, rc=%d\n",
+					rc);
+			return rc;
+		}
+		rc = of_property_read_u32_array(chip->dev->of_node,
+				"qcom,off-ms", off_ms, count);
+		if (rc < 0) {
+			dev_err(chip->dev, "qcom,off-ms property reading failed, rc=%d\n",
+					rc);
+			return rc;
+		}
+
+		for (i = 0; i < NUM_LEDS; i++) {
+			chip->leds[i].support_blink = !!support_blink[i];
+			chip->leds[i].led_setting.on_ms = on_ms[i];
+			chip->leds[i].led_setting.off_ms = off_ms[i];
+			if (chip->leds[i].support_blink)
+				pr_debug("%s led supports blink, on_ms=%d, off_ms=%d!\n",
+					led_names[i], on_ms[i], off_ms[i]);
+			else
+				pr_debug("%s led doesn't support blink\n",
+					led_names[i]);
+		}
+	}
+
+	return rc;
+}
+
+static int qti_rgb_leds_probe(struct platform_device *pdev)
+{
+	struct qti_rgb_chip *chip;
+	int rc = 0;
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = &pdev->dev;
+	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+	if (!chip->regmap) {
+		dev_err(chip->dev, "Getting regmap failed\n");
+		return -EINVAL;
+	}
+
+	rc = qti_rgb_leds_parse_dt(chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+								rc);
+		return rc;
+	}
+
+	rc = qti_rgb_leds_init_pwm_settings(chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Init PWM setting failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	mutex_init(&chip->bus_lock);
+
+	rc = qti_rgb_leds_hw_init(chip);
+	if (rc) {
+		dev_err(chip->dev, "HW initialization failed, rc=%d\n", rc);
+		goto destroy;
+	}
+
+	dev_set_drvdata(chip->dev, chip);
+	rc = qti_rgb_leds_register(chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n",
+								rc);
+		goto destroy;
+	}
+
+	return 0;
+destroy:
+	mutex_destroy(&chip->bus_lock);
+	dev_set_drvdata(chip->dev, NULL);
+
+	return rc;
+}
+
+static int qti_rgb_leds_remove(struct platform_device *pdev)
+{
+	int i;
+	struct qti_rgb_chip *chip = dev_get_drvdata(&pdev->dev);
+
+	mutex_destroy(&chip->bus_lock);
+	for (i = 0; i < NUM_LEDS; i++) {
+		if (chip->leds[i].support_blink)
+			sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
+						&blink_attrs_group);
+		mutex_destroy(&chip->leds[i].lock);
+	}
+	dev_set_drvdata(chip->dev, NULL);
+	return 0;
+}
+
+static const struct of_device_id qti_rgb_of_match[] = {
+	{ .compatible = "qcom,leds-rgb",},
+	{ },
+};
+
+static struct platform_driver qti_rgb_leds_driver = {
+	.driver		= {
+		.name		= "qcom,leds-rgb",
+		.of_match_table	= qti_rgb_of_match,
+	},
+	.probe		= qti_rgb_leds_probe,
+	.remove		= qti_rgb_leds_remove,
+};
+module_platform_driver(qti_rgb_leds_driver);
+
+MODULE_DESCRIPTION("QTI TRI_LED (RGB) driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qti-rgb");
-- 
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

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

* [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
  2017-05-31  6:14 [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module *** fenglinw
  2017-05-31  6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
@ 2017-05-31  6:14 ` fenglinw
  2017-06-03 23:20     ` kbuild test robot
  2017-06-07 21:19   ` Rob Herring
  1 sibling, 2 replies; 15+ messages in thread
From: fenglinw @ 2017-05-31  6:14 UTC (permalink / raw)
  To: linux-arm-msm, linux-kernel, Thierry Reding, Rob Herring,
	Mark Rutland, linux-pwm, devicetree
  Cc: subbaram, aghayal, wruan, kgunda, Fenglin Wu

From: Fenglin Wu <fenglinw@codeaurora.org>

Add pwm_chip to support QTI LPG module and export LPG channels as
PWM devices for consumer drivers' usage.

Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
---
 .../devicetree/bindings/pwm/pwm-qti-lpg.txt        |  39 ++
 drivers/pwm/Kconfig                                |  10 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-qti-lpg.c                          | 578 +++++++++++++++++++++
 4 files changed, 628 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
 create mode 100644 drivers/pwm/pwm-qti-lpg.c

diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
new file mode 100644
index 0000000..df81f5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -0,0 +1,39 @@
+Qualcomm Technologies, Inc. LPG driver specific bindings
+
+This binding document describes the properties of LPG (Light Pulse Generator)
+device module in Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: Must be "qcom,pwm-lpg".
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Register base and length for LPG modules. The length
+		      varies based on the number of channels available in
+		      the PMIC chips.
+
+- reg-names:
+	Usage: required
+	Value type: <string>
+	Definition: The name of the register defined in the reg property.
+		      It must be "lpg-base".
+
+- #pwm-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: The number of cells in "pwms" property specified in
+		      PWM user nodes. It should be 2. The first cell is
+		      the PWM channel ID indexed from 0, and the second
+		      cell is the PWM default period in nanoseconds.
+
+Example:
+
+	pmi8998_lpg: lpg@b100 {
+		compatible = "qcom,pwm-lpg";
+		reg = <0xb100 0x600>;
+		reg-names = "lpg-base";
+		#pwm-cells = <2>;
+	};
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 313c107..711104f 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -349,6 +349,16 @@ config PWM_PXA
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-pxa.
 
+config PWM_QTI_LPG
+	tristate "Qualcomm Technologies, Inc. LPG driver"
+	depends on  MFD_SPMI_PMIC && OF
+	help
+	  This driver supports the LPG (Light Pulse Generator) module found in
+	  Qualcomm Technologies, Inc. PMIC chips. Each LPG channel can be
+	  configured to operate in PWM mode to output a fixed amplitude with
+	  variable duty cycle or in LUT (Look up table) mode to output PWM
+	  signal with a modulated amplitude.
+
 config PWM_RCAR
 	tristate "Renesas R-Car PWM support"
 	depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 93da1f7..16958be 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_PWM_OMAP_DMTIMER)	+= pwm-omap-dmtimer.o
 obj-$(CONFIG_PWM_PCA9685)	+= pwm-pca9685.o
 obj-$(CONFIG_PWM_PUV3)		+= pwm-puv3.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
+obj-$(CONFIG_PWM_QTI_LPG)	+= pwm-qti-lpg.o
 obj-$(CONFIG_PWM_RCAR)		+= pwm-rcar.o
 obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
 obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
new file mode 100644
index 0000000..929b6a4
--- /dev/null
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -0,0 +1,578 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define REG_SIZE_PER_LPG	0x100
+
+#define REG_LPG_PWM_SIZE_CLK		0x41
+#define REG_LPG_PWM_FREQ_PREDIV_CLK	0x42
+#define REG_LPG_PWM_TYPE_CONFIG		0x43
+#define REG_LPG_PWM_VALUE_LSB		0x44
+#define REG_LPG_PWM_VALUE_MSB		0x45
+#define REG_LPG_ENABLE_CONTROL		0x46
+#define REG_LPG_PWM_SYNC		0x47
+
+/* REG_LPG_PWM_SIZE_CLK */
+#define LPG_PWM_SIZE_MASK		BIT(4)
+#define LPG_PWM_SIZE_SHIFT		4
+#define LPG_PWM_CLK_FREQ_SEL_MASK	GENMASK(1, 0)
+
+/* REG_LPG_PWM_FREQ_PREDIV_CLK */
+#define LPG_PWM_FREQ_PREDIV_MASK	GENMASK(6, 5)
+#define LPG_PWM_FREQ_PREDIV_SHIFT	5
+#define LPG_PWM_FREQ_EXPONENT_MASK	GENMASK(2, 0)
+
+/* REG_LPG_PWM_TYPE_CONFIG */
+#define LPG_PWM_EN_GLITCH_REMOVAL_MASK	BIT(5)
+
+/* REG_LPG_PWM_VALUE_LSB */
+#define LPG_PWM_VALUE_LSB_MASK		GENMASK(7, 0)
+
+/* REG_LPG_PWM_VALUE_MSB */
+#define LPG_PWM_VALUE_MSB_MASK		BIT(0)
+
+/* REG_LPG_ENABLE_CONTROL */
+#define LPG_EN_LPG_OUT_BIT		BIT(7)
+#define LPG_PWM_SRC_SELECT_MASK		BIT(2)
+#define LPG_PWM_SRC_SELECT_SHIFT	2
+#define LPG_EN_RAMP_GEN_MASK		BIT(1)
+#define LPG_EN_RAMP_GEN_SHIFT		1
+
+/* REG_LPG_PWM_SYNC */
+#define LPG_PWM_VALUE_SYNC		BIT(0)
+
+#define NUM_PWM_SIZE			2
+#define NUM_PWM_CLK			3
+#define NUM_CLK_PREDIV			4
+#define NUM_PWM_EXP			8
+
+enum {
+	LUT_PATTERN = 0,
+	PWM_OUTPUT,
+};
+
+static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
+static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000};
+static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
+static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+struct lpg_pwm_config {
+	u32	pwm_size;
+	u32	pwm_clk;
+	u32	prediv;
+	u32	clk_exp;
+	u16	pwm_value;
+	u32	best_period_ns;
+};
+
+struct qti_lpg_channel {
+	struct qti_lpg_chip		*chip;
+	struct lpg_pwm_config		pwm_config;
+	u32				lpg_idx;
+	u32				reg_base;
+	u8				src_sel;
+	int				current_period_ns;
+	int				current_duty_ns;
+};
+
+struct qti_lpg_chip {
+	struct pwm_chip		pwm_chip;
+	struct regmap		*regmap;
+	struct device		*dev;
+	struct qti_lpg_channel	*lpgs;
+	struct mutex		bus_lock;
+	u32			lpg_nums;
+};
+
+static int qti_lpg_write(struct qti_lpg_channel *lpg, u16 addr, u8 val)
+{
+	int rc;
+
+	mutex_lock(&lpg->chip->bus_lock);
+	rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
+	if (rc < 0)
+		dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+				lpg->reg_base + addr, val, rc);
+	mutex_unlock(&lpg->chip->bus_lock);
+
+	return rc;
+}
+
+static int qti_lpg_masked_write(struct qti_lpg_channel *lpg,
+				u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	mutex_lock(&lpg->chip->bus_lock);
+	rc = regmap_update_bits(lpg->chip->regmap, lpg->reg_base + addr,
+							mask, val);
+	if (rc < 0)
+		dev_err(lpg->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+				lpg->reg_base + addr, val, mask, rc);
+	mutex_unlock(&lpg->chip->bus_lock);
+
+	return rc;
+}
+
+static struct qti_lpg_channel *pwm_dev_to_qti_lpg(struct pwm_chip *pwm_chip,
+				struct pwm_device *pwm) {
+
+	struct qti_lpg_chip *chip = container_of(pwm_chip,
+			struct qti_lpg_chip, pwm_chip);
+	u32 hw_idx = pwm->hwpwm;
+
+	if (hw_idx >= chip->lpg_nums) {
+		dev_err(chip->dev, "hw index %d out of range [0-%d]\n",
+				hw_idx, chip->lpg_nums - 1);
+		return NULL;
+	}
+
+	return &chip->lpgs[hw_idx];
+}
+
+static int __find_index_in_array(int member, const int array[], int length)
+{
+	int i;
+
+	for (i = 0; i < length; i++) {
+		if (member == array[i])
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+static int qti_lpg_set_pwm_config(struct qti_lpg_channel *lpg)
+{
+	int rc;
+	u8 val, mask;
+	int pwm_size_idx, pwm_clk_idx, prediv_idx, clk_exp_idx;
+
+	pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size,
+			pwm_size, ARRAY_SIZE(pwm_size));
+	pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk,
+			clk_freq_hz, ARRAY_SIZE(clk_freq_hz));
+	prediv_idx = __find_index_in_array(lpg->pwm_config.prediv,
+			clk_prediv, ARRAY_SIZE(clk_prediv));
+	clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp,
+			pwm_exponent, ARRAY_SIZE(pwm_exponent));
+
+	if (pwm_size_idx < 0 || pwm_clk_idx < 0
+			|| prediv_idx < 0 || clk_exp_idx < 0)
+		return -EINVAL;
+
+	pwm_clk_idx += 1;
+	val = pwm_size_idx << LPG_PWM_SIZE_SHIFT | pwm_clk_idx;
+	mask = LPG_PWM_SIZE_MASK | LPG_PWM_CLK_FREQ_SEL_MASK;
+	rc = qti_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	val = prediv_idx << LPG_PWM_FREQ_PREDIV_SHIFT | clk_exp_idx;
+	mask = LPG_PWM_FREQ_PREDIV_MASK | LPG_PWM_FREQ_EXPONENT_MASK;
+	rc = qti_lpg_masked_write(lpg, REG_LPG_PWM_FREQ_PREDIV_CLK, mask, val);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PWM_FREQ_PREDIV_CLK failed, rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
+	rc = qti_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_LSB failed, rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	val = lpg->pwm_config.pwm_value >> 8;
+	mask = LPG_PWM_VALUE_MSB_MASK;
+	rc = qti_lpg_masked_write(lpg, REG_LPG_PWM_VALUE_MSB, mask, val);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_MSB failed, rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	val = LPG_PWM_VALUE_SYNC;
+	rc = qti_lpg_write(lpg, REG_LPG_PWM_SYNC, val);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PWM_SYNC failed, rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static void __qti_lpg_calc_pwm_period(int period_ns,
+			struct lpg_pwm_config *pwm_config)
+{
+	struct lpg_pwm_config configs[NUM_PWM_SIZE];
+	int i, j, m, n;
+	int tmp1, tmp2;
+	int clk_period_ns = 0, pwm_clk_period_ns;
+	int clk_delta_ns = INT_MAX, min_clk_delta_ns = INT_MAX;
+	int pwm_period_delta = INT_MAX, min_pwm_period_delta = INT_MAX;
+	int pwm_size_step;
+
+	/*
+	 *              (2^pwm_size) * (2^pwm_exp) * prediv * NSEC_PER_SEC
+	 * pwm_period = ---------------------------------------------------
+	 *                               clk_freq_hz
+	 *
+	 * Searching the closest settings for the requested PWM period.
+	 */
+	for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
+		pwm_clk_period_ns = period_ns >> pwm_size[n];
+		for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
+			for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
+				for (m = 0; m < ARRAY_SIZE(pwm_exponent); m++) {
+					tmp1 = 1 << pwm_exponent[m];
+					tmp1 *= clk_prediv[j];
+					tmp2 = NSEC_PER_SEC / clk_freq_hz[i];
+
+					clk_period_ns = tmp1 * tmp2;
+
+					clk_delta_ns = abs(pwm_clk_period_ns
+						- clk_period_ns);
+					/*
+					 * Find the closet setting for
+					 * PWM frequency predivide value
+					 */
+					if (clk_delta_ns < min_clk_delta_ns) {
+						min_clk_delta_ns
+							= clk_delta_ns;
+						configs[n].pwm_clk
+							= clk_freq_hz[i];
+						configs[n].prediv
+							= clk_prediv[j];
+						configs[n].clk_exp
+							= pwm_exponent[m];
+						configs[n].pwm_size
+							= pwm_size[n];
+						configs[n].best_period_ns
+							= clk_period_ns;
+					}
+				}
+			}
+		}
+		configs[n].best_period_ns *= 1 << pwm_size[n];
+		/* Find the closet setting for PWM period */
+		pwm_period_delta = min_clk_delta_ns << pwm_size[n];
+		if (pwm_period_delta < min_pwm_period_delta) {
+			min_pwm_period_delta = pwm_period_delta;
+			memcpy(pwm_config, &configs[n],
+					sizeof(struct lpg_pwm_config));
+		}
+	}
+
+	/* Larger PWM size can achieve better resolution for PWM duty */
+	for (n = ARRAY_SIZE(pwm_size) - 1; n > 0; n--) {
+		if (pwm_config->pwm_size >= pwm_size[n])
+			break;
+		pwm_size_step = pwm_size[n] - pwm_config->pwm_size;
+		if (pwm_config->clk_exp >= pwm_size_step) {
+			pwm_config->pwm_size = pwm_size[n];
+			pwm_config->clk_exp -= pwm_size_step;
+		}
+	}
+	pr_debug("PWM setting for period_ns %d: pwm_clk = %dHZ, prediv = %d, exponent = %d, pwm_size = %d\n",
+			period_ns, pwm_config->pwm_clk, pwm_config->prediv,
+			pwm_config->clk_exp, pwm_config->pwm_size);
+	pr_debug("Actual period: %dns\n", pwm_config->best_period_ns);
+}
+
+static void __qti_lpg_calc_pwm_duty(int period_ns, int duty_ns,
+			struct lpg_pwm_config *pwm_config)
+{
+	u16 pwm_value, max_pwm_value;
+
+	if ((1 << pwm_config->pwm_size) > (INT_MAX / duty_ns))
+		pwm_value = duty_ns / (period_ns >> pwm_config->pwm_size);
+	else
+		pwm_value = (duty_ns << pwm_config->pwm_size) / period_ns;
+
+	max_pwm_value = (1 << pwm_config->pwm_size) - 1;
+	if (pwm_value > max_pwm_value)
+		pwm_value = max_pwm_value;
+	pwm_config->pwm_value = pwm_value;
+}
+
+static int qti_lpg_pwm_config(struct pwm_chip *pwm_chip,
+		struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	struct qti_lpg_channel *lpg;
+	int rc = 0;
+
+	lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+	if (lpg == NULL) {
+		dev_err(pwm_chip->dev, "lpg not found\n");
+		return -ENODEV;
+	}
+
+	if (duty_ns > period_ns) {
+		dev_err(pwm_chip->dev, "Duty %dns is larger than period %dns\n",
+						duty_ns, period_ns);
+		return -EINVAL;
+	}
+
+	if (period_ns != lpg->current_period_ns)
+		__qti_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
+
+	if (period_ns != lpg->current_period_ns ||
+			duty_ns != lpg->current_duty_ns)
+		__qti_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
+
+	rc = qti_lpg_set_pwm_config(lpg);
+	if (rc < 0)
+		dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
+						lpg->lpg_idx, rc);
+
+	return rc;
+}
+
+static int qti_lpg_pwm_enable(struct pwm_chip *pwm_chip,
+				struct pwm_device *pwm)
+{
+	struct qti_lpg_channel *lpg;
+	int rc = 0;
+	u8 mask, val;
+
+	lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+	if (lpg == NULL) {
+		dev_err(pwm_chip->dev, "lpg not found\n");
+		return -ENODEV;
+	}
+
+	mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+
+	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
+
+	rc = qti_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+	if (rc < 0)
+		dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+						lpg->lpg_idx, rc);
+
+	return rc;
+}
+
+static void qti_lpg_pwm_disable(struct pwm_chip *pwm_chip,
+				struct pwm_device *pwm)
+{
+	struct qti_lpg_channel *lpg;
+	int rc;
+	u8 mask, val;
+
+	lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+	if (lpg == NULL) {
+		dev_err(pwm_chip->dev, "lpg not found\n");
+		return;
+	}
+
+	mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+
+	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+	rc = qti_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+	if (rc < 0)
+		dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
+						lpg->lpg_idx, rc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void qti_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
+{
+	struct qti_lpg_channel *lpg;
+	struct lpg_pwm_config *cfg;
+	struct pwm_device *pwm;
+	int i;
+
+	for (i = 0; i < pwm_chip->npwm; i++) {
+		pwm = &pwm_chip->pwms[i];
+
+		lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+		if (lpg == NULL) {
+			dev_err(pwm_chip->dev, "lpg not found\n");
+			return;
+		}
+
+		if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+			seq_printf(s, "LPG %d is requested by %s\n",
+					lpg->lpg_idx + 1, pwm->label);
+		} else {
+			seq_printf(s, "LPG %d is free\n",
+					lpg->lpg_idx + 1);
+			continue;
+		}
+
+		if (pwm_is_enabled(pwm)) {
+			seq_puts(s, "  enabled\n");
+		} else {
+			seq_puts(s, "  disabled\n");
+			continue;
+		}
+
+		cfg = &lpg->pwm_config;
+		seq_printf(s, "     clk = %dHz\n", cfg->pwm_clk);
+		seq_printf(s, "     pwm_size = %d\n", cfg->pwm_size);
+		seq_printf(s, "     prediv = %d\n", cfg->prediv);
+		seq_printf(s, "     exponent = %d\n", cfg->clk_exp);
+		seq_printf(s, "     pwm_value = %d\n", cfg->pwm_value);
+		seq_printf(s, "  Requested period: %dns, best period = %dns\n",
+				pwm_get_period(pwm), cfg->best_period_ns);
+	}
+}
+#endif
+
+static const struct pwm_ops qti_lpg_pwm_ops = {
+	.config = qti_lpg_pwm_config,
+	.enable = qti_lpg_pwm_enable,
+	.disable = qti_lpg_pwm_disable,
+#ifdef CONFIG_DEBUG_FS
+	.dbg_show = qti_lpg_pwm_dbg_show,
+#endif
+	.owner = THIS_MODULE,
+};
+
+static int qti_lpg_add_pwmchip(struct qti_lpg_chip *chip)
+{
+	int rc;
+
+	chip->pwm_chip.dev = chip->dev;
+	chip->pwm_chip.base = -1;
+	chip->pwm_chip.npwm = chip->lpg_nums;
+	chip->pwm_chip.ops = &qti_lpg_pwm_ops;
+
+	rc = pwmchip_add(&chip->pwm_chip);
+	if (rc < 0)
+		dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int qti_lpg_parse_dt(struct qti_lpg_chip *chip)
+{
+	int rc = 0, i;
+	u64 base, length;
+	const __be32 *addr;
+
+	addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+	if (!addr) {
+		dev_err(chip->dev, "Getting address failed\n");
+		return -EINVAL;
+	}
+	base = be32_to_cpu(addr[0]);
+	length = be32_to_cpu(addr[1]);
+
+	chip->lpg_nums = length / REG_SIZE_PER_LPG;
+	chip->lpgs = devm_kcalloc(chip->dev, chip->lpg_nums,
+			sizeof(*chip->lpgs), GFP_KERNEL);
+	if (!chip->lpgs)
+		return -ENOMEM;
+
+	for (i = 0; i < chip->lpg_nums; i++) {
+		chip->lpgs[i].chip = chip;
+		chip->lpgs[i].lpg_idx = i;
+		chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
+		chip->lpgs[i].src_sel = PWM_OUTPUT;
+	}
+
+	return rc;
+}
+
+static int qti_lpg_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct qti_lpg_chip *chip;
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = &pdev->dev;
+	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+	if (!chip->regmap) {
+		dev_err(chip->dev, "Getting regmap failed\n");
+		return -EINVAL;
+	}
+
+	rc = qti_lpg_parse_dt(chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	dev_set_drvdata(chip->dev, chip);
+
+	mutex_init(&chip->bus_lock);
+	rc = qti_lpg_add_pwmchip(chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+		mutex_destroy(&chip->bus_lock);
+	}
+
+	return rc;
+}
+
+static int qti_lpg_remove(struct platform_device *pdev)
+{
+	struct qti_lpg_chip *chip = dev_get_drvdata(&pdev->dev);
+	int rc = 0;
+
+	rc = pwmchip_remove(&chip->pwm_chip);
+	if (rc < 0)
+		dev_err(chip->dev, "Remove pwmchip failed, rc=%d\n", rc);
+
+	mutex_destroy(&chip->bus_lock);
+	dev_set_drvdata(chip->dev, NULL);
+
+	return rc;
+}
+
+static const struct of_device_id qti_lpg_of_match[] = {
+	{ .compatible = "qcom,pwm-lpg",},
+	{ },
+};
+
+static struct platform_driver qti_lpg_driver = {
+	.driver		= {
+		.name		= "qcom,pwm-lpg",
+		.of_match_table	= qti_lpg_of_match,
+	},
+	.probe		= qti_lpg_probe,
+	.remove		= qti_lpg_remove,
+};
+module_platform_driver(qti_lpg_driver);
+
+MODULE_DESCRIPTION("QTI LPG driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("pwm:pwm-lpg");
-- 
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

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

* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  2017-05-31  6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
@ 2017-05-31  7:55       ` Pavel Machek
  0 siblings, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2017-05-31  7:55 UTC (permalink / raw)
  To: fenglinw-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Richard Purdie,
	Jacek Anaszewski, Rob Herring, Mark Rutland,
	linux-leds-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	subbaram-jfJNa2p1gH1BDgjK7y7TUQ, aghayal-Rm6X0d1/PG5y9aJCnZT0Uw,
	wruan-jfJNa2p1gH1BDgjK7y7TUQ, kgunda-Rm6X0d1/PG5y9aJCnZT0Uw

[-- Attachment #1: Type: text/plain, Size: 1249 bytes --]

Hi!

> +- qcom,support-blink:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of integer values to indicate if "blue", "green", "red"
> +		LEDs support blink control. The values are listed as the fixed
> +		order for "blue", "green", "red" LEDs.

Normal order is RGB, and no need for the "s.

> +- qcom,on-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		on duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.
> +
> +- qcom,off-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		off duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.

I don't get it; why is this needed?

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-05-31  7:55       ` Pavel Machek
  0 siblings, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2017-05-31  7:55 UTC (permalink / raw)
  To: fenglinw
  Cc: linux-arm-msm, linux-kernel, Richard Purdie, Jacek Anaszewski,
	Rob Herring, Mark Rutland, linux-leds, devicetree, subbaram,
	aghayal, wruan, kgunda

[-- Attachment #1: Type: text/plain, Size: 1249 bytes --]

Hi!

> +- qcom,support-blink:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of integer values to indicate if "blue", "green", "red"
> +		LEDs support blink control. The values are listed as the fixed
> +		order for "blue", "green", "red" LEDs.

Normal order is RGB, and no need for the "s.

> +- qcom,on-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		on duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.
> +
> +- qcom,off-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		off duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.

I don't get it; why is this needed?

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  2017-05-31  7:55       ` Pavel Machek
@ 2017-05-31  8:23         ` Wu Fenglin
  -1 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-05-31  8:23 UTC (permalink / raw)
  To: 'Pavel Machek'
  Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
	'Jacek Anaszewski', 'Rob Herring',
	'Mark Rutland',
	linux-leds, devicetree, subbaram, aghayal, wruan, kgunda

Hi Pavel,

Thanks for the reviewing.

For the order, the hardware register mapping has this order (blue/green/red)
from bit0/1/2, I can revert it to (red/green/blue) if there is a strong
concern.

For these two properties: qcom,off-ms/ qcom,on-ms, I am using them to assign
the default blinking on/off time, then the LEDs would have a default
blinking pattern if you do "echo 1 > /sys/class/leds/red/blink"

Fenglin Wu

-----Original Message-----
From: Pavel Machek [mailto:pavel@ucw.cz] 
Sent: Wednesday, May 31, 2017 3:56 PM
To: fenglinw@codeaurora.org
Cc: linux-arm-msm@vger.kernel.org; linux-kernel@vger.kernel.org; Richard
Purdie <rpurdie@rpsys.net>; Jacek Anaszewski <jacek.anaszewski@gmail.com>;
Rob Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>;
linux-leds@vger.kernel.org; devicetree@vger.kernel.org;
subbaram@quicinc.com; aghayal@qti.qualcomm.com; wruan@quicinc.com;
kgunda@qti.qualcomm.com
Subject: Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI
TRI_LED module

Hi!

> +- qcom,support-blink:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of integer values to indicate if "blue",
"green", "red"
> +		LEDs support blink control. The values are listed as the
fixed
> +		order for "blue", "green", "red" LEDs.

Normal order is RGB, and no need for the "s.

> +- qcom,on-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		on duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.
> +
> +- qcom,off-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		off duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.

I don't get it; why is this needed?

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures)
http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-05-31  8:23         ` Wu Fenglin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-05-31  8:23 UTC (permalink / raw)
  To: 'Pavel Machek'
  Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
	'Jacek Anaszewski', 'Rob Herring',
	'Mark Rutland',
	linux-leds, devicetree, subbaram, aghayal, wruan, kgunda

Hi Pavel,

Thanks for the reviewing.

For the order, the hardware register mapping has this order (blue/green/red)
from bit0/1/2, I can revert it to (red/green/blue) if there is a strong
concern.

For these two properties: qcom,off-ms/ qcom,on-ms, I am using them to assign
the default blinking on/off time, then the LEDs would have a default
blinking pattern if you do "echo 1 > /sys/class/leds/red/blink"

Fenglin Wu

-----Original Message-----
From: Pavel Machek [mailto:pavel@ucw.cz] 
Sent: Wednesday, May 31, 2017 3:56 PM
To: fenglinw@codeaurora.org
Cc: linux-arm-msm@vger.kernel.org; linux-kernel@vger.kernel.org; Richard
Purdie <rpurdie@rpsys.net>; Jacek Anaszewski <jacek.anaszewski@gmail.com>;
Rob Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>;
linux-leds@vger.kernel.org; devicetree@vger.kernel.org;
subbaram@quicinc.com; aghayal@qti.qualcomm.com; wruan@quicinc.com;
kgunda@qti.qualcomm.com
Subject: Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI
TRI_LED module

Hi!

> +- qcom,support-blink:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of integer values to indicate if "blue",
"green", "red"
> +		LEDs support blink control. The values are listed as the
fixed
> +		order for "blue", "green", "red" LEDs.

Normal order is RGB, and no need for the "s.

> +- qcom,on-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		on duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.
> +
> +- qcom,off-ms:
> +	Usage: optional
> +	Value type: <prop-encoded-array>
> +	Definition: An array of time values (milli-seconds) to represent the
> +		off duration for "blue", "green", "red" LEDs. The values are
> +		listed as the fixed order for "blue", "green", "red" LEDs.
> +		This property has to be defined if "qcom,support-blink" is
> +		present.

I don't get it; why is this needed?

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures)
http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  2017-05-31  8:23         ` Wu Fenglin
  (?)
@ 2017-05-31 16:55         ` Pavel Machek
  2017-06-01  0:30             ` Wu Fenglin
  -1 siblings, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2017-05-31 16:55 UTC (permalink / raw)
  To: Wu Fenglin
  Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
	'Jacek Anaszewski', 'Rob Herring',
	'Mark Rutland',
	linux-leds, devicetree, subbaram, aghayal, wruan, kgunda

[-- Attachment #1: Type: text/plain, Size: 705 bytes --]

Hi!

> Thanks for the reviewing.
> 
> For the order, the hardware register mapping has this order (blue/green/red)
> from bit0/1/2, I can revert it to (red/green/blue) if there is a strong
> concern.

I'd do that.

> For these two properties: qcom,off-ms/ qcom,on-ms, I am using them to assign
> the default blinking on/off time, then the LEDs would have a default
> blinking pattern if you do "echo 1 > /sys/class/leds/red/blink"

Normally, dts describes hardware; this does not seem to be hardware
description, so I'd leave it out.
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  2017-05-31 16:55         ` Pavel Machek
@ 2017-06-01  0:30             ` Wu Fenglin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-06-01  0:30 UTC (permalink / raw)
  To: 'Pavel Machek'
  Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
	'Jacek Anaszewski', 'Rob Herring',
	'Mark Rutland',
	linux-leds, devicetree, subbaram, aghayal, wruan, kgunda

Thanks Pavel.
I will remove these two dts properties and define the default on/off time
values in C code.

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

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

* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-06-01  0:30             ` Wu Fenglin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-06-01  0:30 UTC (permalink / raw)
  To: 'Pavel Machek'
  Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
	'Jacek Anaszewski', 'Rob Herring',
	'Mark Rutland',
	linux-leds, devicetree, subbaram, aghayal, wruan, kgunda

Thanks Pavel.
I will remove these two dts properties and define the default on/off time
values in C code.

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

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

* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
  2017-06-01  0:30             ` Wu Fenglin
@ 2017-06-01 19:31               ` Jacek Anaszewski
  -1 siblings, 0 replies; 15+ messages in thread
From: Jacek Anaszewski @ 2017-06-01 19:31 UTC (permalink / raw)
  To: Wu Fenglin, 'Pavel Machek'
  Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, 'Richard Purdie',
	'Rob Herring', 'Mark Rutland',
	linux-leds-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	subbaram-jfJNa2p1gH1BDgjK7y7TUQ, aghayal-Rm6X0d1/PG5y9aJCnZT0Uw,
	wruan-jfJNa2p1gH1BDgjK7y7TUQ, kgunda-Rm6X0d1/PG5y9aJCnZT0Uw

Hi Wu,

On 06/01/2017 02:30 AM, Wu Fenglin wrote:
> Thanks Pavel.
> I will remove these two dts properties and define the default on/off time
> values in C code.

There are many things to sort out regarding your patch. I'll be able to
give you a detailed feedback probably no sooner than at the weekend.

-- 
Best regards,
Jacek Anaszewski
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-06-01 19:31               ` Jacek Anaszewski
  0 siblings, 0 replies; 15+ messages in thread
From: Jacek Anaszewski @ 2017-06-01 19:31 UTC (permalink / raw)
  To: Wu Fenglin, 'Pavel Machek'
  Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
	'Rob Herring', 'Mark Rutland',
	linux-leds, devicetree, subbaram, aghayal, wruan, kgunda

Hi Wu,

On 06/01/2017 02:30 AM, Wu Fenglin wrote:
> Thanks Pavel.
> I will remove these two dts properties and define the default on/off time
> values in C code.

There are many things to sort out regarding your patch. I'll be able to
give you a detailed feedback probably no sooner than at the weekend.

-- 
Best regards,
Jacek Anaszewski

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

* Re: [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
  2017-05-31  6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
@ 2017-06-03 23:20     ` kbuild test robot
  2017-06-07 21:19   ` Rob Herring
  1 sibling, 0 replies; 15+ messages in thread
From: kbuild test robot @ 2017-06-03 23:20 UTC (permalink / raw)
  Cc: kbuild-all, linux-arm-msm, linux-kernel, Thierry Reding,
	Rob Herring, Mark Rutland, linux-pwm, devicetree, subbaram,
	aghayal, wruan, kgunda, Fenglin Wu

[-- Attachment #1: Type: text/plain, Size: 2055 bytes --]

Hi Fenglin,

[auto build test ERROR on j.anaszewski-leds/for-next]
[also build test ERROR on v4.12-rc3 next-20170602]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/fenglinw-codeaurora-org/leds-leds-qti-rgb-Add-LED-driver-for-QTI-TRI_LED-module/20170531-153634
base:   https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git for-next
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m68k 

All errors (new ones prefixed by >>):

   drivers/pwm/pwm-qti-lpg.c: In function 'qti_lpg_pwm_dbg_show':
>> drivers/pwm/pwm-qti-lpg.c:428:4: error: implicit declaration of function 'seq_printf' [-Werror=implicit-function-declaration]
       seq_printf(s, "LPG %d is requested by %s\n",
       ^
>> drivers/pwm/pwm-qti-lpg.c:437:4: error: implicit declaration of function 'seq_puts' [-Werror=implicit-function-declaration]
       seq_puts(s, "  enabled\n");
       ^
   cc1: some warnings being treated as errors

vim +/seq_printf +428 drivers/pwm/pwm-qti-lpg.c

   422			if (lpg == NULL) {
   423				dev_err(pwm_chip->dev, "lpg not found\n");
   424				return;
   425			}
   426	
   427			if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
 > 428				seq_printf(s, "LPG %d is requested by %s\n",
   429						lpg->lpg_idx + 1, pwm->label);
   430			} else {
   431				seq_printf(s, "LPG %d is free\n",
   432						lpg->lpg_idx + 1);
   433				continue;
   434			}
   435	
   436			if (pwm_is_enabled(pwm)) {
 > 437				seq_puts(s, "  enabled\n");
   438			} else {
   439				seq_puts(s, "  disabled\n");
   440				continue;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 41150 bytes --]

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

* Re: [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
@ 2017-06-03 23:20     ` kbuild test robot
  0 siblings, 0 replies; 15+ messages in thread
From: kbuild test robot @ 2017-06-03 23:20 UTC (permalink / raw)
  To: fenglinw
  Cc: kbuild-all, linux-arm-msm, linux-kernel, Thierry Reding,
	Rob Herring, Mark Rutland, linux-pwm, devicetree, subbaram,
	aghayal, wruan, kgunda, Fenglin Wu

[-- Attachment #1: Type: text/plain, Size: 2055 bytes --]

Hi Fenglin,

[auto build test ERROR on j.anaszewski-leds/for-next]
[also build test ERROR on v4.12-rc3 next-20170602]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/fenglinw-codeaurora-org/leds-leds-qti-rgb-Add-LED-driver-for-QTI-TRI_LED-module/20170531-153634
base:   https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git for-next
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m68k 

All errors (new ones prefixed by >>):

   drivers/pwm/pwm-qti-lpg.c: In function 'qti_lpg_pwm_dbg_show':
>> drivers/pwm/pwm-qti-lpg.c:428:4: error: implicit declaration of function 'seq_printf' [-Werror=implicit-function-declaration]
       seq_printf(s, "LPG %d is requested by %s\n",
       ^
>> drivers/pwm/pwm-qti-lpg.c:437:4: error: implicit declaration of function 'seq_puts' [-Werror=implicit-function-declaration]
       seq_puts(s, "  enabled\n");
       ^
   cc1: some warnings being treated as errors

vim +/seq_printf +428 drivers/pwm/pwm-qti-lpg.c

   422			if (lpg == NULL) {
   423				dev_err(pwm_chip->dev, "lpg not found\n");
   424				return;
   425			}
   426	
   427			if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
 > 428				seq_printf(s, "LPG %d is requested by %s\n",
   429						lpg->lpg_idx + 1, pwm->label);
   430			} else {
   431				seq_printf(s, "LPG %d is free\n",
   432						lpg->lpg_idx + 1);
   433				continue;
   434			}
   435	
   436			if (pwm_is_enabled(pwm)) {
 > 437				seq_puts(s, "  enabled\n");
   438			} else {
   439				seq_puts(s, "  disabled\n");
   440				continue;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 41150 bytes --]

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

* Re: [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
  2017-05-31  6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
  2017-06-03 23:20     ` kbuild test robot
@ 2017-06-07 21:19   ` Rob Herring
  1 sibling, 0 replies; 15+ messages in thread
From: Rob Herring @ 2017-06-07 21:19 UTC (permalink / raw)
  To: fenglinw
  Cc: linux-arm-msm, linux-kernel, Thierry Reding, Mark Rutland,
	linux-pwm, devicetree, subbaram, aghayal, wruan, kgunda

On Wed, May 31, 2017 at 02:14:38PM +0800, fenglinw@codeaurora.org wrote:
> From: Fenglin Wu <fenglinw@codeaurora.org>
> 
> Add pwm_chip to support QTI LPG module and export LPG channels as
> PWM devices for consumer drivers' usage.
> 
> Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
> ---
>  .../devicetree/bindings/pwm/pwm-qti-lpg.txt        |  39 ++

Please put binding in a separate patch.

>  drivers/pwm/Kconfig                                |  10 +
>  drivers/pwm/Makefile                               |   1 +
>  drivers/pwm/pwm-qti-lpg.c                          | 578 +++++++++++++++++++++
>  4 files changed, 628 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
>  create mode 100644 drivers/pwm/pwm-qti-lpg.c
> 
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
> new file mode 100644
> index 0000000..df81f5f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
> @@ -0,0 +1,39 @@
> +Qualcomm Technologies, Inc. LPG driver specific bindings
> +
> +This binding document describes the properties of LPG (Light Pulse Generator)
> +device module in Qualcomm Technologies, Inc. PMIC chips.
> +
> +- compatible:
> +	Usage: required
> +	Value type: <string>
> +	Definition: Must be "qcom,pwm-lpg".

Needs SoC specific compatible strings.

> +
> +- reg:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: Register base and length for LPG modules. The length
> +		      varies based on the number of channels available in
> +		      the PMIC chips.
> +
> +- reg-names:
> +	Usage: required
> +	Value type: <string>
> +	Definition: The name of the register defined in the reg property.
> +		      It must be "lpg-base".

-names is pointless when there is only 1.

> +
> +- #pwm-cells:
> +	Usage: required
> +	Value type: <u32>
> +	Definition: The number of cells in "pwms" property specified in
> +		      PWM user nodes. It should be 2. The first cell is
> +		      the PWM channel ID indexed from 0, and the second
> +		      cell is the PWM default period in nanoseconds.
> +
> +Example:
> +
> +	pmi8998_lpg: lpg@b100 {
> +		compatible = "qcom,pwm-lpg";
> +		reg = <0xb100 0x600>;
> +		reg-names = "lpg-base";
> +		#pwm-cells = <2>;
> +	};

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

end of thread, other threads:[~2017-06-07 21:19 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-31  6:14 [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module *** fenglinw
2017-05-31  6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
     [not found]   ` <20170531061541.10808-2-fenglinw-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2017-05-31  7:55     ` Pavel Machek
2017-05-31  7:55       ` Pavel Machek
2017-05-31  8:23       ` Wu Fenglin
2017-05-31  8:23         ` Wu Fenglin
2017-05-31 16:55         ` Pavel Machek
2017-06-01  0:30           ` Wu Fenglin
2017-06-01  0:30             ` Wu Fenglin
2017-06-01 19:31             ` Jacek Anaszewski
2017-06-01 19:31               ` Jacek Anaszewski
2017-05-31  6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
2017-06-03 23:20   ` kbuild test robot
2017-06-03 23:20     ` kbuild test robot
2017-06-07 21:19   ` Rob Herring

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.