All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
To: <pavel@ucw.cz>, <krzk+dt@kernel.org>, <andy.shevchenko@gmail.com>
Cc: <robh+dt@kernel.org>, <linux-leds@vger.kernel.org>,
	<devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	Jean-Jacques Hiblot <jjhiblot@traphandler.com>
Subject: [PATCH v5 2/3] leds: Add driver for the TLC5925 LED controller
Date: Tue, 14 Jun 2022 17:42:44 +0200	[thread overview]
Message-ID: <20220614154245.354167-3-jjhiblot@traphandler.com> (raw)
In-Reply-To: <20220614154245.354167-1-jjhiblot@traphandler.com>

The TLC5925 is a 16-channels constant-current LED sink driver.
It is controlled via SPI but doesn't offer a register-based interface.
Instead it contains a shift register and latches that convert the
serial input into a parallel output.

Datasheet: https://www.ti.com/lit/ds/symlink/tlc5925.pdf
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
 drivers/leds/Kconfig        |   6 ++
 drivers/leds/Makefile       |   1 +
 drivers/leds/leds-tlc5925.c | 147 ++++++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+)
 create mode 100644 drivers/leds/leds-tlc5925.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index a49979f41eee..b17eb01210ba 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -658,6 +658,12 @@ config LEDS_TLC591XX
 	  This option enables support for Texas Instruments TLC59108
 	  and TLC59116 LED controllers.
 
+config LEDS_TLC5925
+	tristate "LED driver for TLC5925 controller"
+	depends on LEDS_CLASS && SPI
+	help
+	  This option enables support for Texas Instruments TLC5925.
+
 config LEDS_MAX77650
 	tristate "LED support for Maxim MAX77650 PMIC"
 	depends on LEDS_CLASS && MFD_MAX77650
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4fd2f92cd198..9d15b88d482f 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_LEDS_SYSCON)		+= leds-syscon.o
 obj-$(CONFIG_LEDS_TCA6507)		+= leds-tca6507.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= leds-ti-lmu-common.o
 obj-$(CONFIG_LEDS_TLC591XX)		+= leds-tlc591xx.o
+obj-$(CONFIG_LEDS_TLC5925)		+= leds-tlc5925.o
 obj-$(CONFIG_LEDS_TPS6105X)		+= leds-tps6105x.o
 obj-$(CONFIG_LEDS_TURRIS_OMNIA)		+= leds-turris-omnia.o
 obj-$(CONFIG_LEDS_WM831X_STATUS)	+= leds-wm831x-status.o
diff --git a/drivers/leds/leds-tlc5925.c b/drivers/leds/leds-tlc5925.c
new file mode 100644
index 000000000000..2fb91d9767aa
--- /dev/null
+++ b/drivers/leds/leds-tlc5925.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The driver supports controllers with a very simple SPI protocol:
+ * - the data is deserialized in a shift-register when CS is asserted
+ * - the data is latched when CS is de-asserted
+ * - the LED are either on or off (no control of the brightness)
+ *
+ * Supported devices:
+ * - "ti,tlc5925":  Low-Power 16-Channel Constant-Current LED Sink Driver
+ *                  https://www.ti.com/lit/ds/symlink/tlc5925.pdf
+ */
+
+#include <linux/container_of.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/types.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+struct single_led_priv {
+	struct led_classdev cdev;
+	int idx;
+};
+
+struct tlc5925_leds_priv {
+	int max_num_leds;
+	unsigned long *state;
+	struct single_led_priv leds[];
+};
+
+static int tlc5925_brightness_set_blocking(struct led_classdev *cdev,
+					    enum led_brightness brightness)
+{
+	struct spi_device *spi = to_spi_device(cdev->dev->parent);
+	struct tlc5925_leds_priv *priv = spi_get_drvdata(spi);
+	struct single_led_priv *led =
+		container_of(cdev, struct single_led_priv, cdev);
+	int index = led->idx;
+
+	assign_bit(index, priv->state, !!brightness);
+
+	return spi_write(spi, priv->state, BITS_TO_BYTES(priv->max_num_leds));
+}
+
+static int tlc5925_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct fwnode_handle *child;
+	struct tlc5925_leds_priv *priv;
+	int ret;
+	int max_num_leds, count;
+	struct gpio_descs *gpios;
+
+	/* Assert all the OE/ lines */
+	gpios = devm_gpiod_get_array(dev, "output-enable-b", GPIOD_OUT_LOW);
+	if (IS_ERR(gpios))
+		return dev_err_probe(dev, PTR_ERR(gpios),
+			      "Unable to get the 'output-enable-b' gpios\n");
+
+	count = device_get_child_node_count(dev);
+	if (!count)
+		return dev_err_probe(dev, -ENODEV, "no led defined.\n");
+
+	ret = device_property_read_u32(dev, "ti,shift-register-length",
+					     &max_num_leds);
+	if (ret)
+		return dev_err_probe(dev, -EINVAL,
+				     "'ti,shift-register-length' property is required.\n");
+	if (max_num_leds % 8)
+		return dev_err_probe(dev, -EINVAL,
+				     "'ti,shift-register-length' must be a multiple of 8\n");
+	if (max_num_leds == 0)
+		return dev_err_probe(dev, -EINVAL,
+				     "'ti,shift-register-length' must be greater than 0\n");
+
+	priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->state = devm_bitmap_zalloc(dev, max_num_leds, GFP_KERNEL);
+	if (!priv->state)
+		return -ENOMEM;
+
+	priv->max_num_leds = max_num_leds;
+
+	device_for_each_child_node(dev, child) {
+		struct led_init_data init_data = { .fwnode = child };
+		struct led_classdev *cdev;
+		u32 idx;
+
+		ret = fwnode_property_read_u32(child, "reg", &idx);
+		if (ret || idx >= max_num_leds) {
+			dev_warn(dev, "%pfwP: invalid reg value. Ignoring.\n",
+				 child);
+			fwnode_handle_put(child);
+			continue;
+		}
+
+		count--;
+		priv->leds[count].idx = idx;
+		cdev = &(priv->leds[count].cdev);
+		cdev->brightness = LED_OFF;
+		cdev->max_brightness = 1;
+		cdev->brightness_set_blocking = tlc5925_brightness_set_blocking;
+
+		ret = devm_led_classdev_register_ext(dev, cdev, &init_data);
+		if (ret) {
+			dev_warn(dev, "%pfwP: cannot create LED device.\n",
+				child);
+			fwnode_handle_put(child);
+			continue;
+		}
+	}
+
+	spi_set_drvdata(spi, priv);
+
+	return 0;
+}
+
+static const struct of_device_id tlc5925_dt_ids[] = {
+	{ .compatible = "ti,tlc5925", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, tlc5925_dt_ids);
+
+static const struct spi_device_id tlc5925_id[] = {
+	{"tlc5925", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, tlc5925_id);
+
+static struct spi_driver tlc5925_driver = {
+	.driver = {
+		.name		= KBUILD_MODNAME,
+		.of_match_table	= tlc5925_dt_ids,
+	},
+	.id_table = tlc5925_id,
+	.probe = tlc5925_probe,
+};
+module_spi_driver(tlc5925_driver);
+
+MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
+MODULE_DESCRIPTION("TLC5925 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tlc5925");
-- 
2.25.1


  parent reply	other threads:[~2022-06-14 15:43 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-14 15:42 [PATCH v5 0/3] Add support for the TLC5925 Jean-Jacques Hiblot
2022-06-14 15:42 ` [PATCH v5 1/3] dt-bindings: leds: Add bindings for the TLC5925 controller Jean-Jacques Hiblot
2022-06-17 23:19   ` Rob Herring
2022-06-27  7:57     ` Jean-Jacques Hiblot
2022-06-14 15:42 ` Jean-Jacques Hiblot [this message]
2022-06-14 15:42 ` [PATCH v5 3/3] leds: tlc5925: Add support for non blocking operations Jean-Jacques Hiblot
2022-06-14 15:59 ` [PATCH v5 0/3] Add support for the TLC5925 Andy Shevchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220614154245.354167-3-jjhiblot@traphandler.com \
    --to=jjhiblot@traphandler.com \
    --cc=andy.shevchenko@gmail.com \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=pavel@ucw.cz \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.