From: Nicolas Belin <nbelin@baylibre.com>
To: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org,
jacek.anaszewski@gmail.com, pavel@ucw.cz, dmurphy@ti.com,
devicetree@vger.kernel.org
Cc: baylibre-upstreaming@groups.io, Nicolas Belin <nbelin@baylibre.com>
Subject: [PATCH RFC v2 3/3] drivers: leds: add support for apa102c leds
Date: Wed, 26 Feb 2020 15:33:12 +0100 [thread overview]
Message-ID: <1582727592-4510-4-git-send-email-nbelin@baylibre.com> (raw)
In-Reply-To: <1582727592-4510-1-git-send-email-nbelin@baylibre.com>
Initilial commit in order to support the apa102c RGB leds. This
is based on the Multicolor Framework.
Reviewed-by: Corentin Labbe <clabbe@baylibre.com>
Signed-off-by: Nicolas Belin <nbelin@baylibre.com>
---
drivers/leds/Kconfig | 7 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-apa102c.c | 291 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+)
create mode 100644 drivers/leds/leds-apa102c.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 5dc6535a88ef..71e29727c6ec 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -79,6 +79,13 @@ config LEDS_AN30259A
To compile this driver as a module, choose M here: the module
will be called leds-an30259a.
+config LEDS_APA102C
+ tristate "LED Support for Shiji APA102C"
+ depends on SPI
+ depends on LEDS_CLASS_MULTI_COLOR
+ help
+ This option enables support for APA102C LEDs.
+
config LEDS_APU
tristate "Front panel LED support for PC Engines APU/APU2/APU3 boards"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index b5305b7d43fb..8334cb6dc7e8 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -90,6 +90,7 @@ obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o
obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o
# LED SPI Drivers
+obj-$(CONFIG_LEDS_APA102C) += leds-apa102c.o
obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
obj-$(CONFIG_LEDS_EL15203000) += leds-el15203000.o
diff --git a/drivers/leds/leds-apa102c.c b/drivers/leds/leds-apa102c.c
new file mode 100644
index 000000000000..b46db0db7501
--- /dev/null
+++ b/drivers/leds/leds-apa102c.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/led-class-multicolor.h>
+
+/*
+ * Copyright (C) 2020 BayLibre, SAS
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+/*
+ * APA102C SPI protocol description:
+ * +------+----------------------------------------+------+
+ * |START | DATA FIELD: | END |
+ * |FRAME | N LED FRAMES |FRAME |
+ * +------+------+------+------+------+-----+------+------+
+ * | 0*32 | LED1 | LED2 | LED3 | LED4 | --- | LEDN | 1*32 |
+ * +------+------+------+------+------+-----+------+------+
+ *
+ * +-----------------------------------+
+ * |START FRAME 32bits |
+ * +--------+--------+--------+--------+
+ * |00000000|00000000|00000000|00000000|
+ * +--------+--------+--------+--------+
+ *
+ * +------------------------------------+
+ * |LED FRAME 32bits |
+ * +---+-----+--------+--------+--------+
+ * |111|LUMA | BLUE | GREEN | RED |
+ * +---+-----+--------+--------+--------+
+ * |3b |5bits| 8bits | 8bits | 8bits |
+ * +---+-----+--------+--------+--------+
+ * |MSB LSB|MSB LSB|MSB LSB|MSB LSB|
+ * +---+-----+--------+--------+--------+
+ *
+ * +-----------------------------------+
+ * |END FRAME 32bits |
+ * +--------+--------+--------+--------+
+ * |11111111|11111111|11111111|11111111|
+ * +--------+--------+--------+--------+
+ */
+
+/* apa102c default settings */
+#define MAX_BRIGHTNESS GENMASK(4, 0)
+#define CH_NUM 4
+#define START_BYTE 0
+#define END_BYTE GENMASK(7, 0)
+#define LED_FRAME_HEADER GENMASK(7, 5)
+
+#define IS_RGB (1<<LED_COLOR_ID_RED \
+ | 1<<LED_COLOR_ID_GREEN \
+ | 1<<LED_COLOR_ID_BLUE)
+
+#define APA_DEV_NAME "apa102c"
+
+struct apa102c_led {
+ struct apa102c *priv;
+ struct led_classdev ldev;
+ struct led_classdev_mc mc_cdev;
+};
+
+struct apa102c {
+ size_t led_count;
+ struct device *dev;
+ struct mutex lock;
+ struct spi_device *spi;
+ u8 *buf;
+ struct apa102c_led leds[];
+};
+
+static void apa102c_set_rgb_bytes(u8 *bgr_buf, struct list_head *color_list)
+{
+ struct led_mc_color_entry *color_data;
+
+ /* Looking for the RGB values and putting them in the buffer in
+ * BGR order
+ */
+ list_for_each_entry(color_data, color_list, list) {
+ switch (color_data->led_color_id) {
+ case LED_COLOR_ID_RED:
+ bgr_buf[2] = color_data->intensity;
+ break;
+ case LED_COLOR_ID_GREEN:
+ bgr_buf[1] = color_data->intensity;
+ break;
+ case LED_COLOR_ID_BLUE:
+ bgr_buf[0] = color_data->intensity;
+ break;
+ }
+ }
+}
+
+static int apa102c_sync(struct apa102c *priv)
+{
+ int ret;
+ size_t i;
+ struct apa102c_led *leds = priv->leds;
+ u8 *buf = priv->buf;
+
+ /* creating the data that will be sent to all the leds at once */
+ for (i = 0; i < 4; i++)
+ *buf++ = START_BYTE;
+
+ /* looping on each multicolor led getting the luma and the RGB values */
+ for (i = 0; i < priv->led_count; i++) {
+ *buf++ = LED_FRAME_HEADER | leds[i].ldev.brightness;
+ apa102c_set_rgb_bytes(buf, &leds[i].mc_cdev.color_list);
+ buf += 3;
+ }
+
+ for (i = 0; i < 4; i++)
+ *buf++ = END_BYTE;
+
+ ret = spi_write(priv->spi, priv->buf, priv->led_count*4 + 8);
+
+ return ret;
+}
+
+static int apa102c_brightness_set(struct led_classdev *ldev,
+ enum led_brightness brightness)
+{
+ int ret;
+ struct apa102c_led *led = container_of(ldev,
+ struct apa102c_led,
+ ldev);
+
+ mutex_lock(&led->priv->lock);
+ /* updating the brightness then synching all the leds */
+ ldev->brightness = brightness;
+ ret = apa102c_sync(led->priv);
+ mutex_unlock(&led->priv->lock);
+
+ return ret;
+}
+
+static int apa102c_probe_dt(struct apa102c *priv)
+{
+ int ret = 0;
+ int num_colors;
+ u32 color_id, i;
+ struct apa102c_led *led;
+ struct fwnode_handle *child, *grandchild;
+ struct led_init_data init_data;
+
+ device_for_each_child_node(priv->dev, child) {
+
+ ret = fwnode_property_read_u32(child, "reg", &i);
+ if (ret) {
+ dev_err(priv->dev, "coudld not read reg %d\n", ret);
+ goto child_out;
+ }
+
+ if (i >= priv->led_count) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "reg value too big\n");
+ goto child_out;
+ }
+
+ led = &priv->leds[i];
+ /* checking if this led config is already used */
+ if (led->mc_cdev.led_cdev) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "using the same reg value twice\n");
+ goto child_out;
+ }
+
+ led->priv = priv;
+ led->ldev.max_brightness = MAX_BRIGHTNESS;
+ fwnode_property_read_string(child, "linux,default-trigger",
+ &led->ldev.default_trigger);
+
+ init_data.fwnode = child;
+ init_data.devicename = APA_DEV_NAME;
+ init_data.default_label = ":";
+
+ num_colors = 0;
+ fwnode_for_each_child_node(child, grandchild) {
+ ret = fwnode_property_read_u32(grandchild, "color",
+ &color_id);
+ if (ret) {
+ dev_err(priv->dev, "Cannot read color\n");
+ goto child_out;
+ }
+
+ set_bit(color_id, &led->mc_cdev.available_colors);
+ num_colors++;
+ }
+
+ if (num_colors != 3) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "There should be 3 colors\n");
+ goto child_out;
+ }
+
+ if (led->mc_cdev.available_colors != IS_RGB) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "The led is expected to be RGB\n");
+ goto child_out;
+ }
+
+ led->mc_cdev.num_leds = num_colors;
+ led->mc_cdev.led_cdev = &led->ldev;
+ led->ldev.brightness_set_blocking = apa102c_brightness_set;
+ ret = devm_led_classdev_multicolor_register_ext(priv->dev,
+ &led->mc_cdev,
+ &init_data);
+ if (ret) {
+ dev_err(priv->dev, "led register err: %d\n", ret);
+ fwnode_handle_put(child);
+ goto child_out;
+ }
+ }
+
+child_out:
+ return ret;
+}
+
+static int apa102c_probe(struct spi_device *spi)
+{
+ struct apa102c *priv;
+ size_t led_count;
+ int ret;
+
+ led_count = device_get_child_node_count(&spi->dev);
+ if (!led_count) {
+ dev_err(&spi->dev, "No LEDs defined in device tree!");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(&spi->dev,
+ struct_size(priv, leds, led_count),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->buf = devm_kzalloc(&spi->dev, (led_count + 2) * 4, GFP_KERNEL);
+ if (!priv->buf)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ priv->led_count = led_count;
+ priv->dev = &spi->dev;
+ priv->spi = spi;
+
+ ret = apa102c_probe_dt(priv);
+ if (ret)
+ return ret;
+
+ /* Set the LEDs with default values at start */
+ apa102c_sync(priv);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, priv);
+
+ return 0;
+}
+
+static int apa102c_remove(struct spi_device *spi)
+{
+ struct apa102c *priv = spi_get_drvdata(spi);
+
+ mutex_destroy(&priv->lock);
+
+ return 0;
+}
+
+static const struct of_device_id apa102c_dt_ids[] = {
+ { .compatible = "shiji,apa102c", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, apa102c_dt_ids);
+
+static struct spi_driver apa102c_driver = {
+ .probe = apa102c_probe,
+ .remove = apa102c_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = apa102c_dt_ids,
+ },
+};
+
+module_spi_driver(apa102c_driver);
+
+MODULE_AUTHOR("Nicolas Belin <nbelin@baylibre.com>");
+MODULE_DESCRIPTION("apa102c LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:apa102c");
--
2.7.4
next prev parent reply other threads:[~2020-02-26 14:33 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-26 14:33 [PATCH RFC v2 0/3] leds: add support for apa102c leds Nicolas Belin
2020-02-26 14:33 ` [PATCH RFC v2 1/3] dt-bindings: Document shiji vendor-prefix Nicolas Belin
2020-03-03 14:29 ` Rob Herring
2020-02-26 14:33 ` [PATCH RFC v2 2/3] dt-bindings: leds: Shiji Lighting APA102C LED driver Nicolas Belin
2020-02-26 21:56 ` Rob Herring
2020-02-26 14:33 ` Nicolas Belin [this message]
2020-02-26 20:13 ` [PATCH RFC v2 3/3] drivers: leds: add support for apa102c leds Jacek Anaszewski
2020-03-03 10:30 ` Nicolas Belin
2020-03-03 19:37 ` Jacek Anaszewski
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=1582727592-4510-4-git-send-email-nbelin@baylibre.com \
--to=nbelin@baylibre.com \
--cc=baylibre-upstreaming@groups.io \
--cc=devicetree@vger.kernel.org \
--cc=dmurphy@ti.com \
--cc=jacek.anaszewski@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-leds@vger.kernel.org \
--cc=pavel@ucw.cz \
/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.