From: Jacek Anaszewski <j.anaszewski@samsung.com>
To: Stefan Wahren <stefan.wahren@i2se.com>
Cc: Richard Purdie <rpurdie@rpsys.net>,
Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-leds@vger.kernel.org
Subject: Re: [PATCH 3/3] leds: add SN3218 LED driver
Date: Mon, 01 Feb 2016 13:53:59 +0100 [thread overview]
Message-ID: <56AF5567.4090102@samsung.com> (raw)
In-Reply-To: <56AF2C63.6000404@samsung.com>
On 02/01/2016 10:58 AM, Jacek Anaszewski wrote:
> Hi Stefan,
>
> Thanks for the update. A few more comments below.
>
> On 01/31/2016 01:59 PM, Stefan Wahren wrote:
>> This patch adds support for Si-En SN3218 18 Channel LED Driver.
>>
>> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
>> ---
>> drivers/leds/Kconfig | 12 ++
>> drivers/leds/Makefile | 1 +
>> drivers/leds/leds-sn3218.c | 297
>> ++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 310 insertions(+)
>> create mode 100644 drivers/leds/leds-sn3218.c
>>
>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>> index 7f940c2..920c2da 100644
>> --- a/drivers/leds/Kconfig
>> +++ b/drivers/leds/Kconfig
>> @@ -568,6 +568,18 @@ config LEDS_SEAD3
>> This driver can also be built as a module. If so the module
>> will be called leds-sead3.
>>
>> +config LEDS_SN3218
>> + tristate "LED support for Si-En SN3218 I2C chip"
>> + depends on LEDS_CLASS && I2C
>> + depends on OF
>> + select REGMAP_I2C
>> + help
>> + This option enables support for the Si-EN SN3218 LED driver
>> + connected through I2C. Say Y to enable support for the SN3218 LED.
>> +
>> + This driver can also be built as a module. If so the module
>> + will be called leds-sn3218.
>> +
>> comment "LED driver for blink(1) USB RGB LED is under Special HID
>> drivers (HID_THINGM)"
>>
>> config LEDS_BLINKM
>> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
>> index e9d5309..89c9b6f 100644
>> --- a/drivers/leds/Makefile
>> +++ b/drivers/leds/Makefile
>> @@ -66,6 +66,7 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
>> obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
>> obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
>> obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o
>> +obj-$(CONFIG_LEDS_SN3218) += leds-sn3218.o
>>
>> # LED SPI Drivers
>> obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
>> diff --git a/drivers/leds/leds-sn3218.c b/drivers/leds/leds-sn3218.c
>> new file mode 100644
>> index 0000000..7a96323
>> --- /dev/null
>> +++ b/drivers/leds/leds-sn3218.c
>> @@ -0,0 +1,297 @@
>> +/*
>> + * Si-En SN3218 18 Channel LED Driver
>> + *
>> + * Copyright (C) 2016 Stefan Wahren <stefan.wahren@i2se.com>
>> + *
>> + * Based on leds-pca963x.c
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * version 2 as published by the Free Software Foundation.
>> + *
>> + * Datasheet: http://www.si-en.com/uploadpdf/s2011517171720.pdf
>> + *
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/i2c.h>
>> +#include <linux/leds.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#define SN3218_MODE 0x00
>> +#define SN3218_PWM_1 0x01
>> +#define SN3218_PWM_2 0x02
>> +#define SN3218_PWM_3 0x03
>> +#define SN3218_PWM_4 0x04
>> +#define SN3218_PWM_5 0x05
>> +#define SN3218_PWM_6 0x06
>> +#define SN3218_PWM_7 0x07
>> +#define SN3218_PWM_8 0x08
>> +#define SN3218_PWM_9 0x09
>> +#define SN3218_PWM_10 0x0a
>> +#define SN3218_PWM_11 0x0b
>> +#define SN3218_PWM_12 0x0c
>> +#define SN3218_PWM_13 0x0d
>> +#define SN3218_PWM_14 0x0e
>> +#define SN3218_PWM_15 0x0f
>> +#define SN3218_PWM_16 0x10
>> +#define SN3218_PWM_17 0x11
>> +#define SN3218_PWM_18 0x12
>> +#define SN3218_LED_1_6 0x13
>> +#define SN3218_LED_7_12 0x14
>> +#define SN3218_LED_13_18 0x15
>> +#define SN3218_UPDATE 0x16 /* applies to reg 0x01 .. 0x15 */
>> +#define SN3218_RESET 0x17
>> +
>> +#define SN3218_LED_MASK 0x3f
>> +#define SN3218_LED_ON 0x01
>> +#define SN3218_LED_OFF 0x00
>> +
>> +#define NUM_LEDS 18
>> +
>> +struct sn3218_led;
>> +
>> +/**
>> + * struct sn3218 -
>> + * @client - Pointer to the I2C client
>> + * @leds - Pointer to the individual LEDs
>> + * @num_leds - Actual number of LEDs
>> +**/
>> +struct sn3218 {
>> + struct i2c_client *client;
>> + struct regmap *regmap;
>> + struct sn3218_led *leds;
>> + int num_leds;
>> +};
>> +
>> +/**
>> + * struct sn3218_led -
>> + * @chip - Pointer to the container
>> + * @led_cdev - led class device pointer
>> + * @led_num - LED index ( 0 .. 17 )
>> +**/
>> +struct sn3218_led {
>> + struct sn3218 *chip;
>
> You don't need this if you have led id here. Please refer to
> drivers/leds/leds-max77693.c, sub_led_to_led() to check how to get
> a pointer to the parent structure in similar case.
Hmm, it would work only if leds was a static array in struct sn3218.
So, let's better leave this "chip" pointer intact.
>> + struct led_classdev led_cdev;
>> + int led_num;
>> +};
>> +
>> +static int sn3218_led_set(struct led_classdev *led_cdev,
>> + enum led_brightness brightness)
>> +{
>> + struct sn3218_led *led =
>> + container_of(led_cdev, struct sn3218_led, led_cdev);
>> + struct regmap *regmap = led->chip->regmap;
>> + u8 bank = led->led_num / 6;
>> + u8 mask = 0x1 << (led->led_num % 6);
>> + u8 val;
>> + int ret;
>> +
>> + if (brightness == LED_OFF)
>> + val = 0;
>> + else
>> + val = mask;
>> +
>> + ret = regmap_update_bits(regmap, SN3218_LED_1_6 + bank, mask, val);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (brightness > LED_OFF) {
>> + ret = regmap_write(regmap, SN3218_PWM_1 + led->led_num,
>> + brightness);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + ret = regmap_write(regmap, SN3218_UPDATE, 0xff);
>> +
>> + return ret;
>> +}
>> +
>> +void sn3218_led_init(struct sn3218 *sn3218, struct device_node *node,
>> u32 reg)
>
> s/void/static void/
>
>> +{
>> + struct sn3218_led *leds = sn3218->leds;
>> + struct led_classdev *cdev = &leds[reg].led_cdev;
>> +
>> + leds[reg].led_num = reg;
>> + leds[reg].chip = sn3218;
>> +
>> + if (of_property_read_string(node, "label", &cdev->name))
>> + cdev->name = node->name;
>> +
>> + of_property_read_string(node, "linux,default-trigger",
>> + &cdev->default_trigger);
>> +
>> + cdev->brightness_set_blocking = sn3218_led_set;
>> + cdev->max_brightness = LED_FULL;
>
> Skip this line. If max_brightness is 0 led_classdev_register()
> automatically initializes it to LED_FULL.
>
>> +}
>> +
>> +static const struct reg_default sn3218_reg_defs[] = {
>> + { SN3218_MODE, 0x00},
>> + { SN3218_PWM_1, 0x00},
>> + { SN3218_PWM_2, 0x00},
>> + { SN3218_PWM_3, 0x00},
>> + { SN3218_PWM_4, 0x00},
>> + { SN3218_PWM_5, 0x00},
>> + { SN3218_PWM_6, 0x00},
>> + { SN3218_PWM_7, 0x00},
>> + { SN3218_PWM_8, 0x00},
>> + { SN3218_PWM_9, 0x00},
>> + { SN3218_PWM_10, 0x00},
>> + { SN3218_PWM_11, 0x00},
>> + { SN3218_PWM_12, 0x00},
>> + { SN3218_PWM_13, 0x00},
>> + { SN3218_PWM_14, 0x00},
>> + { SN3218_PWM_15, 0x00},
>> + { SN3218_PWM_16, 0x00},
>> + { SN3218_PWM_17, 0x00},
>> + { SN3218_PWM_18, 0x00},
>> + { SN3218_LED_1_6, 0x00},
>> + { SN3218_LED_7_12, 0x00},
>> + { SN3218_LED_13_18, 0x00},
>> + { SN3218_UPDATE, 0x00},
>> + { SN3218_RESET, 0x00},
>> +};
>> +
>> +static const struct regmap_config sn3218_regmap_config = {
>> + .reg_bits = 8,
>> + .val_bits = 8,
>> +
>> + .max_register = SN3218_RESET,
>> + .reg_defaults = sn3218_reg_defs,
>> + .num_reg_defaults = ARRAY_SIZE(sn3218_reg_defs),
>> + .cache_type = REGCACHE_RBTREE,
>> +};
>> +
>> +static int sn3218_init(struct i2c_client *client, struct sn3218 *sn3218)
>> +{
>> + struct device_node *np = client->dev.of_node, *child;
>> + struct sn3218_led *leds;
>> + int ret, count;
>> +
>> + count = of_get_child_count(np);
>> + if (!count)
>> + return -ENODEV;
>> +
>> + if (count > NUM_LEDS) {
>> + dev_err(&client->dev, "Invalid LED count %d\n", count);
>> + return -EINVAL;
>> + }
>> +
>> + leds = devm_kzalloc(&client->dev, count * sizeof(*leds),
>> GFP_KERNEL);
>> + if (!leds)
>> + return -ENOMEM;
>> +
>> + sn3218->leds = leds;
>> + sn3218->num_leds = count;
>> + sn3218->client = client;
>> +
>> + sn3218->regmap = devm_regmap_init_i2c(client,
>> &sn3218_regmap_config);
>> + if (IS_ERR(sn3218->regmap)) {
>> + ret = PTR_ERR(sn3218->regmap);
>> + dev_err(&client->dev, "Failed to allocate register map: %d\n",
>> + ret);
>> + return ret;
>> + }
>> +
>> + for_each_child_of_node(np, child) {
>> + u32 reg;
>> +
>> + ret = of_property_read_u32(child, "reg", ®);
>> + if (ret)
>> + goto fail;
>> +
>> + if (reg >= count) {
>> + dev_err(&client->dev, "Invalid LED (%u >= %d)\n", reg,
>> + count);
>> + ret = -EINVAL;
>> + goto fail;
>> + }
>> +
>> + sn3218_led_init(sn3218, child, reg);
>> + }
>> +
>> + return 0;
>> +
>> +fail:
>> + of_node_put(np);
>
> s/np/child/
>
>> + return ret;
>> +}
>> +
>> +static int sn3218_probe(struct i2c_client *client,
>> + const struct i2c_device_id *id)
>> +{
>> + struct sn3218 *sn3218;
>> + struct sn3218_led *leds;
>> + struct device *dev = &client->dev;
>> + int i, ret;
>> +
>> + sn3218 = devm_kzalloc(dev, sizeof(*sn3218), GFP_KERNEL);
>> + if (!sn3218)
>> + return -ENOMEM;
>> +
>> + ret = sn3218_init(client, sn3218);
>> + if (ret)
>> + return ret;
>> +
>> + i2c_set_clientdata(client, sn3218);
>> + leds = sn3218->leds;
>> +
>> + /*
>> + * Since the chip is write-only we need to reset him into
>> + * a defined state (all LEDs off).
>> + */
>> + ret = regmap_write(sn3218->regmap, SN3218_RESET, 0xff);
>> + if (ret)
>> + return ret;
>> +
>> + for (i = 0; i < sn3218->num_leds; i++) {
>> + ret = devm_led_classdev_register(dev, &leds[i].led_cdev);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + /* Set normal mode */
>> + return regmap_write(sn3218->regmap, SN3218_MODE, 0x01);
>> +}
>> +
>> +static int sn3218_remove(struct i2c_client *client)
>> +{
>> + struct sn3218 *sn3218 = i2c_get_clientdata(client);
>> +
>> + /* Set shutdown mode */
>> + regmap_write(sn3218->regmap, SN3218_MODE, 0x00);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct i2c_device_id sn3218_id[] = {
>> + { "sn3218", 0 },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, sn3218_id);
>> +
>> +static const struct of_device_id of_sn3218_match[] = {
>> + { .compatible = "si-en,sn3218", },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, of_sn3218_match);
>> +
>> +static struct i2c_driver sn3218_driver = {
>> + .driver = {
>> + .name = "leds-sn3218",
>> + .of_match_table = of_match_ptr(of_sn3218_match),
>> + },
>> + .probe = sn3218_probe,
>> + .remove = sn3218_remove,
>> + .id_table = sn3218_id,
>> +};
>> +
>> +module_i2c_driver(sn3218_driver);
>> +
>> +MODULE_DESCRIPTION("Si-En SN3218 LED Driver");
>> +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
>> +MODULE_LICENSE("GPL v2");
>>
>
>
--
Best Regards,
Jacek Anaszewski
next prev parent reply other threads:[~2016-02-01 12:54 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-31 12:59 [PATCH 0/3] leds: add SN3218 LED driver Stefan Wahren
2016-01-31 12:59 ` [PATCH 1/3] of: Add vendor prefix for Si-En Technology Stefan Wahren
2016-01-31 12:59 ` [PATCH 2/3] DT: add binding for SN3218 LED driver Stefan Wahren
2016-02-01 9:59 ` Jacek Anaszewski
[not found] ` <1454245156-15747-1-git-send-email-stefan.wahren-eS4NqCHxEME@public.gmane.org>
2016-01-31 12:59 ` [PATCH 3/3] leds: add " Stefan Wahren
2016-01-31 12:59 ` Stefan Wahren
2016-02-01 9:58 ` Jacek Anaszewski
2016-02-01 12:53 ` Jacek Anaszewski [this message]
2016-02-01 13:20 ` Stefan Wahren
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=56AF5567.4090102@samsung.com \
--to=j.anaszewski@samsung.com \
--cc=devicetree@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-leds@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=robh+dt@kernel.org \
--cc=rpurdie@rpsys.net \
--cc=stefan.wahren@i2se.com \
/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.