[PATCH 1/2] leds: Add driver for mp3326

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds a led
 driver for the mp3326 which is from Monolithic Power Systems, Inc.

Signed-off-by: Yuxi Wang <Yuxi.Wang@xxxxxxxxxxxxxxxxxxx>
---
 drivers/leds/Kconfig       |   7 +
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-mp3326.c | 836 +++++++++++++++++++++++++++++++++++++
 3 files changed, 844 insertions(+)
 create mode 100644 drivers/leds/leds-mp3326.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 499d0f215a8b..5369506be7b2 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -244,6 +244,13 @@ config LEDS_MT6323
 	  This option enables support for on-chip LED drivers found on
 	  Mediatek MT6323 PMIC.
 
+config LEDS_MP3326
+	tristate "LED Support for Monolithic power system MP3326"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for on-chip LED drivers found on
+	  Monolithic power system MP3326.
+
 config LEDS_S3C24XX
 	tristate "LED Support for Samsung S3C24XX GPIO LEDs"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4fd2f92cd198..4859959c5c9a 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_LEDS_MIKROTIK_RB532)	+= leds-rb532.o
 obj-$(CONFIG_LEDS_MLXCPLD)		+= leds-mlxcpld.o
 obj-$(CONFIG_LEDS_MLXREG)		+= leds-mlxreg.o
 obj-$(CONFIG_LEDS_MT6323)		+= leds-mt6323.o
+obj-$(CONFIG_LEDS_MP3326)		+= leds-mp3326.o
 obj-$(CONFIG_LEDS_NET48XX)		+= leds-net48xx.o
 obj-$(CONFIG_LEDS_NETXBIG)		+= leds-netxbig.o
 obj-$(CONFIG_LEDS_NIC78BX)		+= leds-nic78bx.o
diff --git a/drivers/leds/leds-mp3326.c b/drivers/leds/leds-mp3326.c
new file mode 100644
index 000000000000..b54e167ea7cf
--- /dev/null
+++ b/drivers/leds/leds-mp3326.c
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * MP3326 Led driver
+ *
+ * Copyright 2023 Monolithic Power Systems, Inc
+ *
+ * Author: Yuxi Wang <Yuxi.Wang@xxxxxxxxxxxxxxxxxxx>
+ */
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/leds.h>
+
+/*reg list*/
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG                 0x00
+#define MP3326_PWM_CTRL                                 0x01
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG                 0x00
+#define MP3326_PWM_CTRL_CHANNEL_9_16                    0x04
+#define MP3326_PWM_CTRL_CHANNEL_1_8                     0x05
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_9_16              0x06
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_1_8               0x07
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_9_16             0x08
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_1_8              0x09
+#define MP3326_PWM_CURRENT_SET_CHANNEL1                 0x0A
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL1                0x0B
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL1                0x0C
+#define MP3326_PWM_CURRENT_SET_CHANNEL2                 0x0D
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL2                0x0E
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL2                0x0F
+#define MP3326_PWM_CURRENT_SET_CHANNEL3                 0x10
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL3                0x11
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL3                0x12
+#define MP3326_PWM_CURRENT_SET_CHANNEL4                 0x13
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL4                0x14
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL4                0x15
+#define MP3326_PWM_CURRENT_SET_CHANNEL5                 0x16
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL5                0x17
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL5                0x18
+#define MP3326_PWM_CURRENT_SET_CHANNEL6                 0x19
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL6                0x1A
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL6                0x1B
+#define MP3326_PWM_CURRENT_SET_CHANNEL7                 0x1C
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL7                0x1D
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL7                0x1E
+#define MP3326_PWM_CURRENT_SET_CHANNEL8                 0x1F
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL8                0x20
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL8                0x21
+#define MP3326_PWM_CURRENT_SET_CHANNEL9                 0x22
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL9                0x23
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL9                0x24
+#define MP3326_PWM_CURRENT_SET_CHANNEL10                0x25
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL10               0x26
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL10               0x27
+#define MP3326_PWM_CURRENT_SET_CHANNEL11                0x28
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL11               0x29
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL11               0x2A
+#define MP3326_PWM_CURRENT_SET_CHANNEL12                0x2B
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL12               0x2C
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL12               0x2D
+#define MP3326_PWM_CURRENT_SET_CHANNEL13                0x2E
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL13               0x2F
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL13               0x30
+#define MP3326_PWM_CURRENT_SET_CHANNEL14                0x31
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL14               0x32
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL14               0x33
+#define MP3326_PWM_CURRENT_SET_CHANNEL15                0x34
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL15               0x35
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL15               0x36
+#define MP3326_PWM_CURRENT_SET_CHANNEL16                0x37
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL16               0x38
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL16               0x39
+
+#define MP3326_REG_FIELD(ref) {		\
+	.regmap_fields = ref##_reg_fields,\
+}
+
+enum MP3326_Channel {
+	Channel1,
+	Channel2,
+	Channel3,
+	Channel4,
+	Channel5,
+	Channel6,
+	Channel7,
+	Channel8,
+	Channel9,
+	Channel10,
+	Channel11,
+	Channel12,
+	Channel13,
+	Channel14,
+	Channel15,
+	Channel16,
+	Max_Channel,
+};
+
+enum RGB_CTRL {
+ENABLE,
+BRIGHTNESS,
+COLOR_H4,
+COLOR_L8,
+OPEN_FAULT,
+SHORT_FAULT,
+Max_CTRL,
+};
+
+struct MP3326_reg_field {
+	struct reg_field  *regmap_fields;
+};
+
+static  struct reg_field channel1_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 0, 0),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL1, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL1, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL1, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 0, 0),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 0, 0),
+};
+
+static  struct reg_field channel2_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 1, 1),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL2, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL2, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL2, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 1, 1),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 1, 1),
+};
+
+static  struct reg_field channel3_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 2, 2),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL3, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL3, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL3, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 2, 2),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 2, 2),
+};
+
+static  struct reg_field channel4_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 3, 3),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL4, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL4, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL4, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 3, 3),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 3, 3),
+};
+
+static  struct reg_field channel5_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 4, 4),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL5, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL5, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL5, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 4, 4),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 4, 4),
+};
+
+static  struct reg_field channel6_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 5, 5),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL6, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL6, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL6, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 5, 5),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 5, 5),
+};
+
+static  struct reg_field channel7_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 6, 6),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL7, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL7, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL7, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 6, 6),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 6, 6),
+};
+
+static  struct reg_field channel8_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_1_8, 7, 7),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL8, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL8, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL8, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_1_8, 7, 7),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_1_8, 7, 7),
+};
+
+static  struct reg_field channel9_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 0, 0),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL9, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL9, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL9, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 0, 0),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 0, 0),
+};
+
+static  struct reg_field channel10_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 1, 1),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL10, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL10, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL10, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 1, 1),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 1, 1),
+};
+
+static  struct reg_field channel11_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 2, 2),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL11, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL11, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL11, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 2, 2),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 2, 2),
+};
+
+static  struct reg_field channel12_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 3, 3),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL12, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL12, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL12, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 3, 3),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 3, 3),
+};
+
+static  struct reg_field channel13_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 4, 4),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL13, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL13, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL13, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 4, 4),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 4, 4),
+};
+
+static  struct reg_field channel14_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 5, 5),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL14, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL14, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL14, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 5, 5),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 5, 5),
+};
+
+static  struct reg_field channel15_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 6, 6),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL15, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL15, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL15, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 6, 6),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 6, 6),
+};
+
+static  struct reg_field channel16_reg_fields[] = {
+	[ENABLE]		= REG_FIELD(MP3326_PWM_CTRL_CHANNEL_9_16, 7, 7),
+	[BRIGHTNESS]	= REG_FIELD(MP3326_PWM_CURRENT_SET_CHANNEL16, 0, 5),
+	[COLOR_H4]		= REG_FIELD(MP3326_PWM_DUTY_LSB_SET_CHANNEL16, 0, 3),
+	[COLOR_L8]		= REG_FIELD(MP3326_PWM_DUTY_MSB_SET_CHANNEL16, 0, 7),
+	[OPEN_FAULT]	= REG_FIELD(MP3326_PWM_OPEN_FAULT_CHANNEL_9_16, 7, 7),
+	[SHORT_FAULT]	= REG_FIELD(MP3326_PWM_SHORT_FAULT_CHANNEL_9_16, 7, 7),
+};
+
+static const struct MP3326_reg_field MP3266_reg_fields[] = {
+	[Channel1] = MP3326_REG_FIELD(channel1),
+	[Channel2] = MP3326_REG_FIELD(channel2),
+	[Channel3] = MP3326_REG_FIELD(channel3),
+	[Channel4] = MP3326_REG_FIELD(channel4),
+	[Channel5] = MP3326_REG_FIELD(channel5),
+	[Channel6] = MP3326_REG_FIELD(channel6),
+	[Channel7] = MP3326_REG_FIELD(channel7),
+	[Channel8] = MP3326_REG_FIELD(channel8),
+	[Channel9] = MP3326_REG_FIELD(channel9),
+	[Channel10] = MP3326_REG_FIELD(channel10),
+	[Channel11] = MP3326_REG_FIELD(channel11),
+	[Channel12] = MP3326_REG_FIELD(channel12),
+	[Channel13] = MP3326_REG_FIELD(channel13),
+	[Channel14] = MP3326_REG_FIELD(channel14),
+	[Channel15] = MP3326_REG_FIELD(channel15),
+	[Channel16] = MP3326_REG_FIELD(channel16),
+};
+
+struct RGB {
+struct led_classdev cdev;
+struct MP3326_Led *chip;
+int red;
+int green;
+int blue;
+int AnalogDim;
+int PWMDim;
+bool IsAlive;
+};
+
+static const struct regmap_config MP3326_regmap_config = {
+.reg_bits = 8,
+.val_bits = 8,
+};
+
+struct ChannelField {
+	struct regmap_field *regmap_fields[Max_CTRL];
+};
+
+struct MP3326_Led {
+struct i2c_client *client;
+struct regmap *regmap;
+struct ChannelField *channel_fields[Max_Channel];
+struct mutex mutex;
+u32 num_rgbs;
+struct  RGB rgbs[];
+};
+
+/*
+ *	Set the brightness.
+ */
+static int MP3326_led_set(struct led_classdev *led_cdev,
+			   enum led_brightness value)
+{
+	struct RGB *led = container_of(led_cdev, struct RGB, cdev);
+	struct MP3326_Led *chip = led->chip;
+	int ret;
+
+	if (value > 63)
+		led->AnalogDim = 63;
+	else
+		led->AnalogDim = value;
+
+	ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+	if (ret)
+		return -EINVAL;
+
+	ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+	if (ret)
+		return -EINVAL;
+
+	ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+	if (ret)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Show the current pwm dim value which is decimal.
+ */
+static ssize_t pwm_Dim_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct MP3326_Led *chip = led->chip;
+	unsigned int val;
+	int rval = 0, gval = 0, bval = 0;
+
+	regmap_field_read(chip->channel_fields[led->red]->regmap_fields[COLOR_L8], &val);
+	rval |= val << 4;
+	regmap_field_read(chip->channel_fields[led->red]->regmap_fields[COLOR_H4], &val);
+	rval |= val;
+	val = 0;
+	regmap_field_read(chip->channel_fields[led->green]->regmap_fields[COLOR_L8], &val);
+	gval |= val << 4;
+	regmap_field_read(chip->channel_fields[led->green]->regmap_fields[COLOR_H4], &val);
+	gval |= val;
+	val = 0;
+	regmap_field_read(chip->channel_fields[led->blue]->regmap_fields[COLOR_L8], &val);
+	bval |= val << 4;
+	regmap_field_read(chip->channel_fields[led->blue]->regmap_fields[COLOR_H4], &val);
+	bval |= val;
+
+	rval = rval * 255 / 4095 + ((rval * 255) % 4095 > 2047 ? 1 : 0);
+	gval = gval * 255 / 4095 + ((gval * 255) % 4095 > 2047 ? 1 : 0);
+	bval = bval * 255 / 4095 + ((bval * 255) % 4095 > 2047 ? 1 : 0);
+
+
+	return  sysfs_emit(buf, "%d %d %d\n", rval, gval, bval);
+}
+
+/*
+ * PWM dimming, here are two input ways.
+ * The first: Input '200 200 200' which sequence is red green blue and decimal.
+ * The Second: Input 'H 0f 0f 0f' which sequence is red green blue and Hex.
+ */
+static ssize_t pwm_Dim_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	ssize_t ret;
+	int r_val, g_val, b_val;
+
+	ret = sscanf(buf, "%d %d %d", &r_val, &g_val, &b_val);
+	if (ret != 3) {
+		ret = sscanf(buf, "H %x %x %x", &r_val, &g_val, &b_val);
+		if (ret != 3)
+			return ret;
+	}
+	r_val = r_val * 4095 / 255 + ((r_val * 4095) % 255 > 127 ? 1 : 0);
+	g_val = g_val * 4095 / 255 + ((g_val * 4095) % 255 > 127 ? 1 : 0);
+	b_val = b_val * 4095 / 255 + ((b_val * 4095) % 255 > 127 ? 1 : 0);
+
+	ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[COLOR_H4], r_val & 0x0f);
+	ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[COLOR_L8], r_val >> 4);
+	ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[COLOR_H4], g_val & 0x0f);
+	ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[COLOR_L8], g_val >> 4);
+	ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[COLOR_H4], b_val & 0x0f);
+	ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[COLOR_L8], b_val >> 4);
+
+	if (ret)
+		return -EINVAL;
+	return count;
+}
+
+/*
+ *Show an integer which indicates current brightness.
+ */
+static ssize_t analog_Dim_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	unsigned int val;
+
+	regmap_field_read(chip->channel_fields[led->red]->regmap_fields[BRIGHTNESS], &val);
+
+	return  sysfs_emit(buf, "%u\n", val);
+}
+/*
+ * Input an integer value which changes the brightness.
+ * The integer is range from 0 to 63
+ */
+static ssize_t analog_Dim_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	ssize_t ret;
+	long long_val;
+
+	ret = kstrtol(buf, 10, &long_val);
+	if (long_val < 0)
+		return -EINVAL;
+	if (long_val > cdev->max_brightness) {
+		led->AnalogDim = cdev->max_brightness;
+		cdev->brightness = cdev->max_brightness;
+	} else {
+		led->AnalogDim = long_val;
+		cdev->brightness = long_val;
+	}
+
+	ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+	if (ret)
+		return -EINVAL;
+
+	ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+	if (ret)
+		return -EINVAL;
+
+	ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+	if (ret)
+		return -EINVAL;
+	return count;
+}
+
+/*
+ * Show a string which indicates enable all channels of rgb.
+ * True  indicates all channels of rgb enable.
+ * False indicates all channels of rgb disable.
+ */
+static ssize_t rgb_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	unsigned int rval = 0, val;
+
+	regmap_field_read(chip->channel_fields[led->red]->regmap_fields[ENABLE], &val);
+	rval |= val;
+	regmap_field_read(chip->channel_fields[led->green]->regmap_fields[ENABLE], &val);
+	rval |= val;
+	regmap_field_read(chip->channel_fields[led->blue]->regmap_fields[ENABLE], &val);
+	rval |= val;
+	regmap_read(chip->regmap, 0x05, &val);
+	if (rval)
+		return  sysfs_emit(buf, "%s\n", "True");
+
+	return  sysfs_emit(buf, "%s\n", "False");
+}
+
+/*
+ * Input 1 or 0 to control the channels of rgb enable/disable.
+ * If input 1, the channels of rgb is enable
+ * If input 0, the channels of rgb is disable
+ */
+static ssize_t rgb_enable_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	ssize_t ret;
+	long long_val;
+
+	ret = kstrtol(buf, 10, &long_val);
+	if (long_val) {
+		ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+		if (ret)
+			return -EINVAL;
+
+		ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+		if (ret)
+			return -EINVAL;
+
+		ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[BRIGHTNESS], led->AnalogDim);
+		if (ret)
+			return -EINVAL;
+
+
+		ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[ENABLE], long_val);
+		if (ret)
+			return -EINVAL;
+
+		ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[ENABLE], long_val);
+		if (ret)
+			return -EINVAL;
+
+		ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[ENABLE], long_val);
+		if (ret)
+			return -EINVAL;
+
+		led->IsAlive = true;
+	} else {
+		ret = regmap_field_write(chip->channel_fields[led->red]->regmap_fields[ENABLE], long_val);
+		if (ret)
+			return -EINVAL;
+
+
+		ret = regmap_field_write(chip->channel_fields[led->green]->regmap_fields[ENABLE], long_val);
+		if (ret)
+			return -EINVAL;
+
+		ret = regmap_field_write(chip->channel_fields[led->blue]->regmap_fields[ENABLE], long_val);
+		if (ret)
+			return -EINVAL;
+
+		led->IsAlive = false;
+	}
+
+	return count;
+}
+
+/*
+ * Show a string which indicates short fault of rgb.
+ * True  indicates the rgb has a short fault.
+ * False indicates the rgb is normal.
+ */
+static ssize_t short_fault_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	unsigned int rval = 0, val;
+
+	regmap_field_read(chip->channel_fields[led->red]->regmap_fields[SHORT_FAULT], &val);
+	rval |= val;
+	regmap_field_read(chip->channel_fields[led->green]->regmap_fields[SHORT_FAULT], &val);
+	rval |= val;
+	regmap_field_read(chip->channel_fields[led->blue]->regmap_fields[SHORT_FAULT], &val);
+	rval |= val;
+	if (rval)
+		return  sysfs_emit(buf, "%s\n", "True");
+
+	return  sysfs_emit(buf, "%s\n", "False");
+}
+
+/*
+ * Show a string which indicates open load fault of rgb.
+ * True  indicates the rgb has an open load fault.
+ * False indicates the rgb is normal.
+ */
+static ssize_t open_fault_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct RGB *led = container_of(cdev, struct RGB, cdev);
+	struct  MP3326_Led *chip = led->chip;
+	unsigned int rval = 0, val;
+
+	regmap_field_read(chip->channel_fields[led->red]->regmap_fields[OPEN_FAULT], &val);
+	rval |= val;
+	regmap_field_read(chip->channel_fields[led->green]->regmap_fields[OPEN_FAULT], &val);
+	rval |= val;
+	regmap_field_read(chip->channel_fields[led->blue]->regmap_fields[OPEN_FAULT], &val);
+	rval |= val;
+
+	if (rval)
+		return  sysfs_emit(buf, "%s\n", "True");
+
+	return  sysfs_emit(buf, "%s\n", "False");
+}
+
+
+static DEVICE_ATTR_RW(rgb_enable);
+static DEVICE_ATTR_RW(pwm_Dim);
+static DEVICE_ATTR_RW(analog_Dim);
+static DEVICE_ATTR_RO(open_fault);
+static DEVICE_ATTR_RO(short_fault);
+static struct attribute *MP3266_led_sysfs_attrs[] = {
+	&dev_attr_rgb_enable.attr,
+	&dev_attr_pwm_Dim.attr,
+	&dev_attr_analog_Dim.attr,
+	&dev_attr_open_fault.attr,
+	&dev_attr_short_fault.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(MP3266_led_sysfs);
+
+static int MP3266_probe_fw(struct device *dev, struct MP3326_Led *chip)
+{
+	struct fwnode_handle *child;
+	int ret;
+	int i;
+
+	device_for_each_child_node(dev, child) {
+		struct RGB *rgb;
+		struct led_init_data  init_data = {};
+		u32 source, Red = 0, Green, Blue;
+
+		ret = fwnode_property_read_u32(child, "rgb_r", &Red);
+		if (ret) {
+			dev_err(dev, "Missing rgb_r property\n");
+			chip->num_rgbs--;
+			continue;
+		}
+
+		if (Red <= 0 || Red > Max_Channel) {
+			dev_err(dev, "LED reg %u out of range (max %u)(min 1)\n",
+					Red, Max_Channel);
+			chip->num_rgbs--;
+			continue;
+		}
+
+		ret = fwnode_property_read_u32(child, "rgb_g", &Green);
+
+		if (ret) {
+			dev_err(dev, "Missing rgb_g property\n");
+			chip->num_rgbs--;
+			continue;
+	}
+
+	if (Green <= 0 || Green > Max_Channel) {
+		dev_err(dev, "LED reg %u out of range (max %u)(min 1)\n",
+				Green, Max_Channel);
+		chip->num_rgbs--;
+		continue;
+	}
+
+	ret = fwnode_property_read_u32(child, "rgb_b", &Blue);
+
+	if (ret) {
+		dev_err(dev, "Missing rgb_b property\n");
+		chip->num_rgbs--;
+		continue;
+	}
+
+	if (Blue <= 0 || Blue > Max_Channel) {
+		dev_err(dev, "LED reg %u out of range (max %u)(min 1)\n",
+				Blue, Max_Channel);
+		chip->num_rgbs--;
+		continue;
+	}
+
+	ret = fwnode_property_read_u32(child, "brightness", &source);
+
+	if (ret) {
+		dev_err(dev, "Missing brightness property\n");
+		chip->num_rgbs--;
+		continue;
+	}
+
+	if (source < 0) {
+		dev_err(dev, "Brightness: %u must be greater than 0\n",
+				source);
+		chip->num_rgbs--;
+		continue;
+	} else if (source > 63) {
+		source = 63;
+	}
+	rgb = &chip->rgbs[i];
+	rgb->chip = chip;
+	rgb->red = Red-1;
+	rgb->green = Green-1;
+	rgb->blue = Blue-1;
+	rgb->AnalogDim = source;
+
+	rgb->cdev.groups = MP3266_led_sysfs_groups;
+	rgb->cdev.brightness_set_blocking = MP3326_led_set;
+	rgb->cdev.brightness = source;
+	rgb->cdev.max_brightness = 63;
+	init_data.fwnode = child;
+
+	ret = devm_led_classdev_register_ext(dev, &rgb->cdev,
+						     &init_data);
+	if (ret) {
+		fwnode_handle_put(child);
+		break;
+	}
+	i++;
+	ret = regmap_field_write(chip->channel_fields[rgb->red]->regmap_fields[BRIGHTNESS], source);
+	ret = regmap_field_write(chip->channel_fields[rgb->green]->regmap_fields[BRIGHTNESS], source);
+	ret = regmap_field_write(chip->channel_fields[rgb->blue]->regmap_fields[BRIGHTNESS], source);
+	}
+	if (!chip->num_rgbs)
+		return -EINVAL;
+	return 0;
+}
+
+static int MP3326_leds_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct MP3326_Led *chip;
+	struct ChannelField *channel;
+	struct device_node *np = client->dev.of_node;
+	int ret, count, source;
+	int i, j;
+
+	count = device_get_child_node_count(&client->dev);
+	if (!count || count > 5) {
+		return dev_err_probe(&client->dev, -EINVAL,
+				"Incorrect number of rgbs (%d)", count);
+	}
+
+	chip = devm_kzalloc(&client->dev, struct_size(chip, rgbs, count), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+	channel = devm_kzalloc(&client->dev, sizeof(struct ChannelField) * Max_Channel, GFP_KERNEL);
+	if (!channel)
+		return -ENOMEM;
+
+	chip->client = client;
+	chip->num_rgbs = count;
+
+	i2c_set_clientdata(client, chip);
+	chip->regmap = devm_regmap_init_i2c(client, &MP3326_regmap_config);
+	if (IS_ERR(chip->regmap))
+		return PTR_ERR(chip->regmap);
+
+
+	for (i = 0; i < Max_Channel; i++) {
+		for (j = 0; j < Max_CTRL; j++) {
+			channel[i].regmap_fields[j] = devm_regmap_field_alloc(&client->dev,
+			chip->regmap, MP3266_reg_fields[i].regmap_fields[j]);
+
+			if (IS_ERR(channel[i].regmap_fields[j])) {
+				dev_err(&client->dev, "regmap field alloc fail %d\n", i);
+				return PTR_ERR(channel[i].regmap_fields[j]);
+			}
+		}
+		chip->channel_fields[i] = &channel[i];
+		channel++;
+	}
+
+	if (IS_ERR(chip->channel_fields))
+		return PTR_ERR(chip->channel_fields);
+
+	ret = of_property_read_u32(np, "led-protect", &source);
+	if (!ret)
+		ret = regmap_update_bits(chip->regmap, 0x01, BIT(4) | BIT(5), source<<4);
+
+	ret = of_property_read_u32(np, "switch_status", &source);
+	if (ret)
+		regmap_update_bits(chip->regmap, 0x01, BIT(0), 0);
+	else
+		regmap_update_bits(chip->regmap, 0x01, BIT(0), source);
+
+	/*close all channel*/
+	regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_9_16, 0);
+	regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_1_8, 0);
+	mutex_init(&chip->mutex);
+	mutex_lock(&chip->mutex);
+
+	ret = MP3266_probe_fw(&client->dev, chip);
+	if (ret)
+		goto out_unlock;
+
+out_unlock:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static int MP3326_leds_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+static const struct i2c_device_id MP3326_led_id[] = {
+	{"MP3326", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, MP3326_led_id);
+
+static const struct of_device_id MP3326_led_of_match[] = {
+	{ .compatible = "mps,MP3326" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, MP3326_led_of_match);
+
+static struct i2c_driver MP3326_leds_driver = {
+	.probe = MP3326_leds_probe,
+	.remove = MP3326_leds_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "MP3326_led",
+		.of_match_table = MP3326_led_of_match,
+		},
+
+	.id_table = MP3326_led_id,
+};
+
+module_i2c_driver(MP3326_leds_driver);
+
+MODULE_AUTHOR("Yuxi Wang <Yuxi.Wang@xxxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("MP3326 Led driver");
+MODULE_LICENSE("GPL");
-- 
2.25.1





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux