1 // SPDX-License-Identifier: GPL-2.0-only
3 * GCA230718 LED support (e.g. for D-Link M30) using I2C
5 * Copyright 2022 Roland Reinl <reinlroland+github@gmail.com>
7 * This driver can control RGBW LEDs which are connected to a GCA230718.
10 #include <linux/delay.h>
11 #include <linux/led-class-multicolor.h>
12 #include <linux/leds.h>
13 #include <linux/module.h>
14 #include <linux/of_device.h>
15 #include <linux/property.h>
16 #include <linux/i2c.h>
17 #include <linux/mutex.h>
18 #include <linux/version.h>
20 #define GCA230718_MAX_LEDS (4u)
22 #define GCA230718_OPMODE_DISABLED (0x00u)
23 #define GCA230718_OPMODE_NO_TOGGLE (0x01u)
24 #define GCA230718_OPMODE_TOGGLE_RAMP_CONTROL_DISABLED (0x02u)
25 #define GCA230718_OPMODE_TOGGLE_RAMP_CONTROL_ENSABLED (0x03u)
27 #define GCA230718_1ST_SEQUENCE_BYTE_1 (0x02u)
28 #define GCA230718_2ND_SEQUENCE_BYTE_1 (0x01u)
29 #define GCA230718_3RD_SEQUENCE_BYTE_1 (0x03u)
33 enum led_brightness brightness
;
34 struct i2c_client
*client
;
35 struct led_classdev ledClassDev
;
38 struct gca230718_private
41 struct gca230718_led leds
[GCA230718_MAX_LEDS
];
44 static void gca230718_init_private_led_data(struct gca230718_private
* data
)
47 for (ledIndex
= 0; ledIndex
< GCA230718_MAX_LEDS
; ledIndex
++)
49 data
->leds
[ledIndex
].client
= NULL
;
53 static void gca230718_send_sequence(struct i2c_client
*client
, u8 byte0
, struct gca230718_private
* gca230718_privateData
)
57 const u8 resetCommand
[2] = { 0x81, 0xE4 };
58 const u8 resetCommandRegister
= 0x00;
60 u8 controlCommand
[13];
61 const u8 controlCommandRegister
= 0x03;
63 controlCommand
[0] = 0x0C; /* Unknown */
64 controlCommand
[1] = byte0
;
65 controlCommand
[2] = GCA230718_OPMODE_NO_TOGGLE
;
66 /* Byte 3-6 are set below to the brighness value of the individual LEDs */
67 controlCommand
[7] = 0x01; /* Frequency, doesn't care as long as GCA230718_OPMODE_NO_TOGGLE is used above */
68 /* Byte 8-11 are set below to the brighness value of the individual LEDs */
69 controlCommand
[12] = 0x87;
71 for (ledIndex
= 0; ledIndex
< GCA230718_MAX_LEDS
; ledIndex
++)
73 controlCommand
[3 + ledIndex
] = gca230718_privateData
->leds
[ledIndex
].brightness
;
74 controlCommand
[8 + ledIndex
] = gca230718_privateData
->leds
[ledIndex
].brightness
;
77 mutex_lock(&(gca230718_privateData
->lock
));
79 if ((status
= i2c_smbus_write_i2c_block_data(client
, resetCommandRegister
, sizeof(resetCommand
), resetCommand
)) != 0)
81 pr_info("Error %i during call of i2c_smbus_write_i2c_block_data for reset command\n", status
);
83 else if ((status
= i2c_smbus_write_i2c_block_data(client
, controlCommandRegister
, sizeof(controlCommand
), controlCommand
)) != 0)
85 pr_info("Error %i during call of i2c_smbus_write_i2c_block_data for control command\n", status
);
88 mutex_unlock(&(gca230718_privateData
->lock
));
91 static int gca230718_set_brightness(struct led_classdev
*led_cdev
, enum led_brightness value
)
93 struct gca230718_led
* led
;
94 struct i2c_client
* client
;
96 led
= container_of(led_cdev
, struct gca230718_led
, ledClassDev
);
101 struct gca230718_private
* gca230718_privateData
;
103 led
->brightness
= value
;
104 gca230718_privateData
= i2c_get_clientdata(client
);
106 gca230718_send_sequence(client
, GCA230718_2ND_SEQUENCE_BYTE_1
, gca230718_privateData
);
112 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)
113 static int gca230718_probe(struct i2c_client
*client
)
115 static int gca230718_probe(struct i2c_client
*client
, const struct i2c_device_id
*id
)
119 struct gca230718_private
* gca230718_privateData
;
121 pr_info("Enter gca230718_probe for device address %u\n", client
->addr
);
122 gca230718_privateData
= devm_kzalloc(&(client
->dev
), sizeof(struct gca230718_private
), GFP_KERNEL
);
124 if (gca230718_privateData
== NULL
)
126 pr_info("Error during allocating memory for private data\n");
131 struct device_node
* ledNode
;
132 mutex_init(&gca230718_privateData
->lock
);
133 gca230718_init_private_led_data(gca230718_privateData
);
134 i2c_set_clientdata(client
, gca230718_privateData
);
136 for_each_child_of_node(client
->dev
.of_node
, ledNode
)
139 if (of_property_read_u32(ledNode
, "reg", ®Value
) != 0)
141 pr_info("Missing entry \"reg\" in node %s\n", ledNode
->name
);
143 else if (regValue
>= GCA230718_MAX_LEDS
)
145 pr_info("Invalid entry \"reg\" in node %s (%u)\n", ledNode
->name
, regValue
);
149 struct led_classdev
* ledClassDev
= &(gca230718_privateData
->leds
[regValue
].ledClassDev
);
150 struct led_init_data init_data
= {};
152 gca230718_privateData
->leds
[regValue
].client
= client
;
153 init_data
.fwnode
= of_fwnode_handle(ledNode
);
155 pr_info("Creating LED for node %s: reg=%u\n", ledNode
->name
, regValue
);
157 ledClassDev
->name
= of_get_property(ledNode
, "label", NULL
);
158 if (ledClassDev
->name
== NULL
)
160 ledClassDev
->name
= ledNode
->name
;
163 ledClassDev
->brightness
= LED_OFF
;
164 ledClassDev
->max_brightness
= LED_FULL
;
165 ledClassDev
->brightness_set_blocking
= gca230718_set_brightness
;
167 if (devm_led_classdev_register_ext(&(client
->dev
), ledClassDev
, &init_data
) != 0)
169 pr_info("Error during call of devm_led_classdev_register_ext");
178 Send full initialization sequence.
179 Afterwards only GCA230718_2ND_SEQUENCE_BYTE_1 must be send to upddate the brightness values.
181 gca230718_send_sequence(client
, GCA230718_1ST_SEQUENCE_BYTE_1
, gca230718_privateData
);
182 gca230718_send_sequence(client
, GCA230718_2ND_SEQUENCE_BYTE_1
, gca230718_privateData
);
183 gca230718_send_sequence(client
, GCA230718_3RD_SEQUENCE_BYTE_1
, gca230718_privateData
);
189 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,18,0)
190 static void gca230718_remove(struct i2c_client
*client
)
192 static int gca230718_remove(struct i2c_client
*client
)
195 struct gca230718_private
* gca230718_privateData
;
196 gca230718_privateData
= i2c_get_clientdata(client
);
197 mutex_destroy(&gca230718_privateData
->lock
);
198 gca230718_init_private_led_data(gca230718_privateData
);
200 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
205 static const struct i2c_device_id gca230718_i2c_ids
[] = {
209 MODULE_DEVICE_TABLE(i2c
, gca230718_i2c_ids
);
211 static const struct of_device_id gca230718_dt_ids
[] = {
212 { .compatible
= "unknown,gca230718" },
215 MODULE_DEVICE_TABLE(of
, gca230718_dt_ids
);
217 static struct i2c_driver gca230718_driver
= {
218 .probe
= gca230718_probe
,
219 .remove
= gca230718_remove
,
220 .id_table
= gca230718_i2c_ids
,
222 .name
= KBUILD_MODNAME
,
223 .of_match_table
= gca230718_dt_ids
,
227 module_i2c_driver(gca230718_driver
);
229 MODULE_AUTHOR("Roland Reinl <reinlroland+github@gmail.com>");
230 MODULE_DESCRIPTION("GCA230718 LED support (e.g. for D-Link M30) using I2C");
231 MODULE_LICENSE("GPL");