xref: /linux/drivers/platform/x86/amd/amd_halo_led.c (revision 5b05bb3f6c5716fab6911e12d60dd1f43ad9806a)
1*4ed460ceSYo-Jung Leo Lin (AMD) // SPDX-License-Identifier: GPL-2.0
2*4ed460ceSYo-Jung Leo Lin (AMD) /*
3*4ed460ceSYo-Jung Leo Lin (AMD)  * AMD Halo Box RGB LED Driver
4*4ed460ceSYo-Jung Leo Lin (AMD)  *
5*4ed460ceSYo-Jung Leo Lin (AMD)  * Copyright (C) 2026 Advanced Micro Devices, Inc.
6*4ed460ceSYo-Jung Leo Lin (AMD)  *
7*4ed460ceSYo-Jung Leo Lin (AMD)  * This driver provides RGB LED control for AMD Halo Box devices through
8*4ed460ceSYo-Jung Leo Lin (AMD)  * the LED multicolor subsystem. The Halo Box light bar can be controlled
9*4ed460ceSYo-Jung Leo Lin (AMD)  * via sysfs to display any RGB color combination.
10*4ed460ceSYo-Jung Leo Lin (AMD)  */
11*4ed460ceSYo-Jung Leo Lin (AMD) 
12*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/array_size.h>
13*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/cleanup.h>
14*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/compiler_attributes.h>
15*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/container_of.h>
16*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/dev_printk.h>
17*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/device.h>
18*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/device/devres.h>
19*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/led-class-multicolor.h>
20*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/leds.h>
21*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/module.h>
22*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/mutex.h>
23*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/slab.h>
24*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/types.h>
25*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/wmi.h>
26*4ed460ceSYo-Jung Leo Lin (AMD) 
27*4ed460ceSYo-Jung Leo Lin (AMD) #include <linux/byteorder/generic.h>
28*4ed460ceSYo-Jung Leo Lin (AMD) 
29*4ed460ceSYo-Jung Leo Lin (AMD) #define AMD_HALO_GUID "081E747B-E028-4232-AF24-EAAEAB2B1E86"
30*4ed460ceSYo-Jung Leo Lin (AMD) 
31*4ed460ceSYo-Jung Leo Lin (AMD) /* WMI method IDs from MOF */
32*4ed460ceSYo-Jung Leo Lin (AMD) enum {
33*4ed460ceSYo-Jung Leo Lin (AMD) 	AMD_HALO_WMI_TURN_OFF	= 0x04,
34*4ed460ceSYo-Jung Leo Lin (AMD) 	AMD_HALO_WMI_RGB	= 0x07,
35*4ed460ceSYo-Jung Leo Lin (AMD) };
36*4ed460ceSYo-Jung Leo Lin (AMD) 
37*4ed460ceSYo-Jung Leo Lin (AMD) /* Arg0 of the AMD_HALO_WMI_RGB Method */
38*4ed460ceSYo-Jung Leo Lin (AMD) enum {
39*4ed460ceSYo-Jung Leo Lin (AMD) 	AMD_HALO_RGB_CMD_GET	= 0x0,
40*4ed460ceSYo-Jung Leo Lin (AMD) 	AMD_HALO_RGB_CMD_SET	= 0x1,
41*4ed460ceSYo-Jung Leo Lin (AMD) };
42*4ed460ceSYo-Jung Leo Lin (AMD) 
43*4ed460ceSYo-Jung Leo Lin (AMD) /* Status codes from spec */
44*4ed460ceSYo-Jung Leo Lin (AMD) #define AMD_HALO_STATUS_SUCCESS		0x0000
45*4ed460ceSYo-Jung Leo Lin (AMD) #define AMD_HALO_STATUS_INVALID_PARAM	0xFFFD
46*4ed460ceSYo-Jung Leo Lin (AMD) 
47*4ed460ceSYo-Jung Leo Lin (AMD) /* Brightness uses 0-100 range */
48*4ed460ceSYo-Jung Leo Lin (AMD) #define AMD_HALO_MAX_HW_BRIGHTNESS	100
49*4ed460ceSYo-Jung Leo Lin (AMD) 
50*4ed460ceSYo-Jung Leo Lin (AMD) /**
51*4ed460ceSYo-Jung Leo Lin (AMD)  * struct amd_halo_led_data - Driver private data
52*4ed460ceSYo-Jung Leo Lin (AMD)  * @wdev: WMI device pointer
53*4ed460ceSYo-Jung Leo Lin (AMD)  * @led_mc: LED multicolor class device
54*4ed460ceSYo-Jung Leo Lin (AMD)  * @subled_info: RGB channel information
55*4ed460ceSYo-Jung Leo Lin (AMD)  * @lock: Mutex to protect WMI calls
56*4ed460ceSYo-Jung Leo Lin (AMD)  */
57*4ed460ceSYo-Jung Leo Lin (AMD) struct amd_halo_led_data {
58*4ed460ceSYo-Jung Leo Lin (AMD) 	struct wmi_device *wdev;
59*4ed460ceSYo-Jung Leo Lin (AMD) 	struct led_classdev_mc led_mc;
60*4ed460ceSYo-Jung Leo Lin (AMD) 	struct mc_subled subled_info[3];
61*4ed460ceSYo-Jung Leo Lin (AMD) 	struct mutex lock;
62*4ed460ceSYo-Jung Leo Lin (AMD) };
63*4ed460ceSYo-Jung Leo Lin (AMD) 
64*4ed460ceSYo-Jung Leo Lin (AMD) struct amd_halo_wmi_args {
65*4ed460ceSYo-Jung Leo Lin (AMD) 	__le32 arg0;
66*4ed460ceSYo-Jung Leo Lin (AMD) 	__le32 arg1;
67*4ed460ceSYo-Jung Leo Lin (AMD) };
68*4ed460ceSYo-Jung Leo Lin (AMD) 
69*4ed460ceSYo-Jung Leo Lin (AMD) struct amd_halo_wmi_args_rgb {
70*4ed460ceSYo-Jung Leo Lin (AMD) 	__le32 cmd;
71*4ed460ceSYo-Jung Leo Lin (AMD) 	__le32 red;
72*4ed460ceSYo-Jung Leo Lin (AMD) 	__le32 green;
73*4ed460ceSYo-Jung Leo Lin (AMD) 	__le32 blue;
74*4ed460ceSYo-Jung Leo Lin (AMD) };
75*4ed460ceSYo-Jung Leo Lin (AMD) 
76*4ed460ceSYo-Jung Leo Lin (AMD) struct amd_halo_wmi_output_rgb {
77*4ed460ceSYo-Jung Leo Lin (AMD) 	__le16 status;
78*4ed460ceSYo-Jung Leo Lin (AMD) 	u8 red;
79*4ed460ceSYo-Jung Leo Lin (AMD) 	u8 green;
80*4ed460ceSYo-Jung Leo Lin (AMD) 	u8 blue;
81*4ed460ceSYo-Jung Leo Lin (AMD) } __packed;
82*4ed460ceSYo-Jung Leo Lin (AMD) 
wmi_status_to_err(u16 status)83*4ed460ceSYo-Jung Leo Lin (AMD) static inline int wmi_status_to_err(u16 status)
84*4ed460ceSYo-Jung Leo Lin (AMD) {
85*4ed460ceSYo-Jung Leo Lin (AMD) 	switch (status) {
86*4ed460ceSYo-Jung Leo Lin (AMD) 	case AMD_HALO_STATUS_SUCCESS:
87*4ed460ceSYo-Jung Leo Lin (AMD) 		return 0;
88*4ed460ceSYo-Jung Leo Lin (AMD) 	case AMD_HALO_STATUS_INVALID_PARAM:
89*4ed460ceSYo-Jung Leo Lin (AMD) 		return -EINVAL;
90*4ed460ceSYo-Jung Leo Lin (AMD) 	default:
91*4ed460ceSYo-Jung Leo Lin (AMD) 		return -EIO;
92*4ed460ceSYo-Jung Leo Lin (AMD) 	}
93*4ed460ceSYo-Jung Leo Lin (AMD) }
94*4ed460ceSYo-Jung Leo Lin (AMD) 
__amd_halo_wmi_call(struct wmi_device * wdev,u32 method_id,void * data,size_t length)95*4ed460ceSYo-Jung Leo Lin (AMD) static int __amd_halo_wmi_call(struct wmi_device *wdev,
96*4ed460ceSYo-Jung Leo Lin (AMD) 			       u32 method_id, void *data, size_t length)
97*4ed460ceSYo-Jung Leo Lin (AMD) {
98*4ed460ceSYo-Jung Leo Lin (AMD) 	struct wmi_buffer input = {
99*4ed460ceSYo-Jung Leo Lin (AMD) 		.length = length,
100*4ed460ceSYo-Jung Leo Lin (AMD) 		.data = data,
101*4ed460ceSYo-Jung Leo Lin (AMD) 	};
102*4ed460ceSYo-Jung Leo Lin (AMD) 	struct wmi_buffer output = { };
103*4ed460ceSYo-Jung Leo Lin (AMD) 	int ret;
104*4ed460ceSYo-Jung Leo Lin (AMD) 
105*4ed460ceSYo-Jung Leo Lin (AMD) 	/* Return buffer per spec: Bytes[0:1] = Status (little-endian) */
106*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = wmidev_invoke_method(wdev, 0, method_id,
107*4ed460ceSYo-Jung Leo Lin (AMD) 				   &input, &output, sizeof(__le16));
108*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
109*4ed460ceSYo-Jung Leo Lin (AMD) 		return ret;
110*4ed460ceSYo-Jung Leo Lin (AMD) 
111*4ed460ceSYo-Jung Leo Lin (AMD) 	__le16 *result_status __free(kfree) = output.data;
112*4ed460ceSYo-Jung Leo Lin (AMD) 
113*4ed460ceSYo-Jung Leo Lin (AMD) 	return wmi_status_to_err(le16_to_cpu(*result_status));
114*4ed460ceSYo-Jung Leo Lin (AMD) }
115*4ed460ceSYo-Jung Leo Lin (AMD) 
116*4ed460ceSYo-Jung Leo Lin (AMD) /**
117*4ed460ceSYo-Jung Leo Lin (AMD)  * amd_halo_wmi_turn_off - Turn off all LED channels
118*4ed460ceSYo-Jung Leo Lin (AMD)  * @wdev: WMI device pointer
119*4ed460ceSYo-Jung Leo Lin (AMD)  *
120*4ed460ceSYo-Jung Leo Lin (AMD)  * Return: 0 on success, negative error code on failure
121*4ed460ceSYo-Jung Leo Lin (AMD)  */
amd_halo_wmi_turn_off(struct wmi_device * wdev)122*4ed460ceSYo-Jung Leo Lin (AMD) static int amd_halo_wmi_turn_off(struct wmi_device *wdev)
123*4ed460ceSYo-Jung Leo Lin (AMD) {
124*4ed460ceSYo-Jung Leo Lin (AMD) 	struct amd_halo_wmi_args args = { };
125*4ed460ceSYo-Jung Leo Lin (AMD) 
126*4ed460ceSYo-Jung Leo Lin (AMD) 	return __amd_halo_wmi_call(wdev, AMD_HALO_WMI_TURN_OFF, &args, sizeof(args));
127*4ed460ceSYo-Jung Leo Lin (AMD) }
128*4ed460ceSYo-Jung Leo Lin (AMD) 
129*4ed460ceSYo-Jung Leo Lin (AMD) /**
130*4ed460ceSYo-Jung Leo Lin (AMD)  * amd_halo_wmi_set_rgb - Set all RGB channels atomically
131*4ed460ceSYo-Jung Leo Lin (AMD)  * @wdev: WMI device pointer
132*4ed460ceSYo-Jung Leo Lin (AMD)  * @r: brightness for red channel (0 - 100)
133*4ed460ceSYo-Jung Leo Lin (AMD)  * @g: brightness for green channel (0 - 100)
134*4ed460ceSYo-Jung Leo Lin (AMD)  * @b: brightness for blue channel (0 - 100)
135*4ed460ceSYo-Jung Leo Lin (AMD)  *
136*4ed460ceSYo-Jung Leo Lin (AMD)  * Return: 0 on success, negative error code on failure
137*4ed460ceSYo-Jung Leo Lin (AMD)  */
amd_halo_wmi_set_rgb(struct wmi_device * wdev,u32 r,u32 g,u32 b)138*4ed460ceSYo-Jung Leo Lin (AMD) static int amd_halo_wmi_set_rgb(struct wmi_device *wdev, u32 r, u32 g, u32 b)
139*4ed460ceSYo-Jung Leo Lin (AMD) {
140*4ed460ceSYo-Jung Leo Lin (AMD) 	struct amd_halo_wmi_args_rgb args = {
141*4ed460ceSYo-Jung Leo Lin (AMD) 		.cmd = cpu_to_le32(AMD_HALO_RGB_CMD_SET),
142*4ed460ceSYo-Jung Leo Lin (AMD) 		.red = cpu_to_le32(r),
143*4ed460ceSYo-Jung Leo Lin (AMD) 		.green = cpu_to_le32(g),
144*4ed460ceSYo-Jung Leo Lin (AMD) 		.blue = cpu_to_le32(b),
145*4ed460ceSYo-Jung Leo Lin (AMD) 	};
146*4ed460ceSYo-Jung Leo Lin (AMD) 
147*4ed460ceSYo-Jung Leo Lin (AMD) 	if (r > AMD_HALO_MAX_HW_BRIGHTNESS ||
148*4ed460ceSYo-Jung Leo Lin (AMD) 	    g > AMD_HALO_MAX_HW_BRIGHTNESS ||
149*4ed460ceSYo-Jung Leo Lin (AMD) 	    b > AMD_HALO_MAX_HW_BRIGHTNESS) {
150*4ed460ceSYo-Jung Leo Lin (AMD) 		return -EINVAL;
151*4ed460ceSYo-Jung Leo Lin (AMD) 	}
152*4ed460ceSYo-Jung Leo Lin (AMD) 
153*4ed460ceSYo-Jung Leo Lin (AMD) 	return __amd_halo_wmi_call(wdev, AMD_HALO_WMI_RGB, &args, sizeof(args));
154*4ed460ceSYo-Jung Leo Lin (AMD) }
155*4ed460ceSYo-Jung Leo Lin (AMD) 
156*4ed460ceSYo-Jung Leo Lin (AMD) /**
157*4ed460ceSYo-Jung Leo Lin (AMD)  * amd_halo_wmi_get_rgb - Get RGB values
158*4ed460ceSYo-Jung Leo Lin (AMD)  * @wdev: WMI device pointer
159*4ed460ceSYo-Jung Leo Lin (AMD)  * @r: output buffer for red value
160*4ed460ceSYo-Jung Leo Lin (AMD)  * @g: output buffer for green value
161*4ed460ceSYo-Jung Leo Lin (AMD)  * @b: output buffer for blue value
162*4ed460ceSYo-Jung Leo Lin (AMD)  *
163*4ed460ceSYo-Jung Leo Lin (AMD)  * Return: 0 on success, negative error code on failure
164*4ed460ceSYo-Jung Leo Lin (AMD)  */
amd_halo_wmi_get_rgb(struct wmi_device * wdev,u8 * r,u8 * g,u8 * b)165*4ed460ceSYo-Jung Leo Lin (AMD) static int amd_halo_wmi_get_rgb(struct wmi_device *wdev, u8 *r, u8 *g, u8 *b)
166*4ed460ceSYo-Jung Leo Lin (AMD) {
167*4ed460ceSYo-Jung Leo Lin (AMD) 	struct amd_halo_wmi_args_rgb args = {
168*4ed460ceSYo-Jung Leo Lin (AMD) 		.cmd = cpu_to_le32(AMD_HALO_RGB_CMD_GET),
169*4ed460ceSYo-Jung Leo Lin (AMD) 	};
170*4ed460ceSYo-Jung Leo Lin (AMD) 	struct wmi_buffer input = {
171*4ed460ceSYo-Jung Leo Lin (AMD) 		.length = sizeof(args),
172*4ed460ceSYo-Jung Leo Lin (AMD) 		.data = &args,
173*4ed460ceSYo-Jung Leo Lin (AMD) 	};
174*4ed460ceSYo-Jung Leo Lin (AMD) 	struct wmi_buffer output = { };
175*4ed460ceSYo-Jung Leo Lin (AMD) 	int ret;
176*4ed460ceSYo-Jung Leo Lin (AMD) 
177*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = wmidev_invoke_method(wdev, 0, AMD_HALO_WMI_RGB,
178*4ed460ceSYo-Jung Leo Lin (AMD) 				   &input, &output, sizeof(struct amd_halo_wmi_output_rgb));
179*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
180*4ed460ceSYo-Jung Leo Lin (AMD) 		return ret;
181*4ed460ceSYo-Jung Leo Lin (AMD) 
182*4ed460ceSYo-Jung Leo Lin (AMD) 	struct amd_halo_wmi_output_rgb *data __free(kfree) = output.data;
183*4ed460ceSYo-Jung Leo Lin (AMD) 
184*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = wmi_status_to_err(le16_to_cpu(data->status));
185*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
186*4ed460ceSYo-Jung Leo Lin (AMD) 		return ret;
187*4ed460ceSYo-Jung Leo Lin (AMD) 
188*4ed460ceSYo-Jung Leo Lin (AMD) 	if (data->red > AMD_HALO_MAX_HW_BRIGHTNESS ||
189*4ed460ceSYo-Jung Leo Lin (AMD) 	    data->green > AMD_HALO_MAX_HW_BRIGHTNESS ||
190*4ed460ceSYo-Jung Leo Lin (AMD) 	    data->blue > AMD_HALO_MAX_HW_BRIGHTNESS) {
191*4ed460ceSYo-Jung Leo Lin (AMD) 		return -EPROTO;
192*4ed460ceSYo-Jung Leo Lin (AMD) 	}
193*4ed460ceSYo-Jung Leo Lin (AMD) 
194*4ed460ceSYo-Jung Leo Lin (AMD) 	*r = data->red;
195*4ed460ceSYo-Jung Leo Lin (AMD) 	*g = data->green;
196*4ed460ceSYo-Jung Leo Lin (AMD) 	*b = data->blue;
197*4ed460ceSYo-Jung Leo Lin (AMD) 
198*4ed460ceSYo-Jung Leo Lin (AMD) 	return 0;
199*4ed460ceSYo-Jung Leo Lin (AMD) }
200*4ed460ceSYo-Jung Leo Lin (AMD) 
201*4ed460ceSYo-Jung Leo Lin (AMD) /**
202*4ed460ceSYo-Jung Leo Lin (AMD)  * amd_halo_brightness_set - Set LED brightness
203*4ed460ceSYo-Jung Leo Lin (AMD)  * @cdev: LED class device
204*4ed460ceSYo-Jung Leo Lin (AMD)  * @brightness: Brightness value
205*4ed460ceSYo-Jung Leo Lin (AMD)  *
206*4ed460ceSYo-Jung Leo Lin (AMD)  * Return: 0 on success, negative error code on failure
207*4ed460ceSYo-Jung Leo Lin (AMD)  */
amd_halo_brightness_set(struct led_classdev * cdev,enum led_brightness brightness)208*4ed460ceSYo-Jung Leo Lin (AMD) static int amd_halo_brightness_set(struct led_classdev *cdev,
209*4ed460ceSYo-Jung Leo Lin (AMD) 				   enum led_brightness brightness)
210*4ed460ceSYo-Jung Leo Lin (AMD) {
211*4ed460ceSYo-Jung Leo Lin (AMD) 	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
212*4ed460ceSYo-Jung Leo Lin (AMD) 	struct amd_halo_led_data *data = container_of(mc_cdev,
213*4ed460ceSYo-Jung Leo Lin (AMD) 						      struct amd_halo_led_data,
214*4ed460ceSYo-Jung Leo Lin (AMD) 						      led_mc);
215*4ed460ceSYo-Jung Leo Lin (AMD) 	u32 red_hw, green_hw, blue_hw;
216*4ed460ceSYo-Jung Leo Lin (AMD) 	int ret;
217*4ed460ceSYo-Jung Leo Lin (AMD) 
218*4ed460ceSYo-Jung Leo Lin (AMD) 	guard(mutex)(&data->lock);
219*4ed460ceSYo-Jung Leo Lin (AMD) 
220*4ed460ceSYo-Jung Leo Lin (AMD) 	led_mc_calc_color_components(mc_cdev, brightness);
221*4ed460ceSYo-Jung Leo Lin (AMD) 
222*4ed460ceSYo-Jung Leo Lin (AMD) 	if (brightness == 0)
223*4ed460ceSYo-Jung Leo Lin (AMD) 		return amd_halo_wmi_turn_off(data->wdev);
224*4ed460ceSYo-Jung Leo Lin (AMD) 
225*4ed460ceSYo-Jung Leo Lin (AMD) 	red_hw = mc_cdev->subled_info[0].brightness;
226*4ed460ceSYo-Jung Leo Lin (AMD) 	green_hw = mc_cdev->subled_info[1].brightness;
227*4ed460ceSYo-Jung Leo Lin (AMD) 	blue_hw = mc_cdev->subled_info[2].brightness;
228*4ed460ceSYo-Jung Leo Lin (AMD) 
229*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = amd_halo_wmi_set_rgb(data->wdev, red_hw, green_hw, blue_hw);
230*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
231*4ed460ceSYo-Jung Leo Lin (AMD) 		goto out;
232*4ed460ceSYo-Jung Leo Lin (AMD) 
233*4ed460ceSYo-Jung Leo Lin (AMD) 	return 0;
234*4ed460ceSYo-Jung Leo Lin (AMD) 
235*4ed460ceSYo-Jung Leo Lin (AMD) out:
236*4ed460ceSYo-Jung Leo Lin (AMD) 	/*
237*4ed460ceSYo-Jung Leo Lin (AMD) 	 * Consider the light bar non-functional if AMD_HALO_WMI_RGB failed.
238*4ed460ceSYo-Jung Leo Lin (AMD) 	 * Attempt to turn the LED off completely as clean-up.
239*4ed460ceSYo-Jung Leo Lin (AMD) 	 */
240*4ed460ceSYo-Jung Leo Lin (AMD) 	if (amd_halo_wmi_turn_off(data->wdev))
241*4ed460ceSYo-Jung Leo Lin (AMD) 		dev_warn_ratelimited(&data->wdev->dev, "Failed to turn LED off on cleanup\n");
242*4ed460ceSYo-Jung Leo Lin (AMD) 
243*4ed460ceSYo-Jung Leo Lin (AMD) 	return ret;
244*4ed460ceSYo-Jung Leo Lin (AMD) }
245*4ed460ceSYo-Jung Leo Lin (AMD) 
amd_halo_probe(struct wmi_device * wdev,const void * context)246*4ed460ceSYo-Jung Leo Lin (AMD) static int amd_halo_probe(struct wmi_device *wdev, const void *context)
247*4ed460ceSYo-Jung Leo Lin (AMD) {
248*4ed460ceSYo-Jung Leo Lin (AMD) 	struct led_init_data led_init_data = {
249*4ed460ceSYo-Jung Leo Lin (AMD) 		.devicename = "amd_halo",
250*4ed460ceSYo-Jung Leo Lin (AMD) 		.default_label = "multicolor:" LED_FUNCTION_STATUS,
251*4ed460ceSYo-Jung Leo Lin (AMD) 		.devname_mandatory = true,
252*4ed460ceSYo-Jung Leo Lin (AMD) 	};
253*4ed460ceSYo-Jung Leo Lin (AMD) 	struct amd_halo_led_data *data;
254*4ed460ceSYo-Jung Leo Lin (AMD) 	u8 r, g, b;
255*4ed460ceSYo-Jung Leo Lin (AMD) 	int ret;
256*4ed460ceSYo-Jung Leo Lin (AMD) 
257*4ed460ceSYo-Jung Leo Lin (AMD) 	data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
258*4ed460ceSYo-Jung Leo Lin (AMD) 	if (!data)
259*4ed460ceSYo-Jung Leo Lin (AMD) 		return -ENOMEM;
260*4ed460ceSYo-Jung Leo Lin (AMD) 
261*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = devm_mutex_init(&wdev->dev, &data->lock);
262*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
263*4ed460ceSYo-Jung Leo Lin (AMD) 		return ret;
264*4ed460ceSYo-Jung Leo Lin (AMD) 
265*4ed460ceSYo-Jung Leo Lin (AMD) 	data->wdev = wdev;
266*4ed460ceSYo-Jung Leo Lin (AMD) 	dev_set_drvdata(&wdev->dev, data);
267*4ed460ceSYo-Jung Leo Lin (AMD) 
268*4ed460ceSYo-Jung Leo Lin (AMD) 	data->subled_info[0].color_index = LED_COLOR_ID_RED;
269*4ed460ceSYo-Jung Leo Lin (AMD) 	data->subled_info[1].color_index = LED_COLOR_ID_GREEN;
270*4ed460ceSYo-Jung Leo Lin (AMD) 	data->subled_info[2].color_index = LED_COLOR_ID_BLUE;
271*4ed460ceSYo-Jung Leo Lin (AMD) 
272*4ed460ceSYo-Jung Leo Lin (AMD) 	data->led_mc.led_cdev.brightness = AMD_HALO_MAX_HW_BRIGHTNESS;
273*4ed460ceSYo-Jung Leo Lin (AMD) 	data->led_mc.led_cdev.max_brightness = AMD_HALO_MAX_HW_BRIGHTNESS;
274*4ed460ceSYo-Jung Leo Lin (AMD) 	data->led_mc.led_cdev.brightness_set_blocking = amd_halo_brightness_set;
275*4ed460ceSYo-Jung Leo Lin (AMD) 	data->led_mc.led_cdev.flags = LED_CORE_SUSPENDRESUME | LED_RETAIN_AT_SHUTDOWN;
276*4ed460ceSYo-Jung Leo Lin (AMD) 	data->led_mc.num_colors = ARRAY_SIZE(data->subled_info);
277*4ed460ceSYo-Jung Leo Lin (AMD) 	data->led_mc.subled_info = data->subled_info;
278*4ed460ceSYo-Jung Leo Lin (AMD) 
279*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = amd_halo_wmi_get_rgb(wdev, &r, &g, &b);
280*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
281*4ed460ceSYo-Jung Leo Lin (AMD) 		return ret;
282*4ed460ceSYo-Jung Leo Lin (AMD) 
283*4ed460ceSYo-Jung Leo Lin (AMD) 	data->subled_info[0].intensity = r;
284*4ed460ceSYo-Jung Leo Lin (AMD) 	data->subled_info[1].intensity = g;
285*4ed460ceSYo-Jung Leo Lin (AMD) 	data->subled_info[2].intensity = b;
286*4ed460ceSYo-Jung Leo Lin (AMD) 
287*4ed460ceSYo-Jung Leo Lin (AMD) 	ret = devm_led_classdev_multicolor_register_ext(&wdev->dev, &data->led_mc,
288*4ed460ceSYo-Jung Leo Lin (AMD) 							&led_init_data);
289*4ed460ceSYo-Jung Leo Lin (AMD) 	if (ret)
290*4ed460ceSYo-Jung Leo Lin (AMD) 		return dev_err_probe(&wdev->dev, ret,
291*4ed460ceSYo-Jung Leo Lin (AMD) 				     "Failed to register multicolor LED\n");
292*4ed460ceSYo-Jung Leo Lin (AMD) 	return 0;
293*4ed460ceSYo-Jung Leo Lin (AMD) }
294*4ed460ceSYo-Jung Leo Lin (AMD) 
295*4ed460ceSYo-Jung Leo Lin (AMD) static const struct wmi_device_id amd_halo_id_table[] = {
296*4ed460ceSYo-Jung Leo Lin (AMD) 	{ .guid_string = AMD_HALO_GUID },
297*4ed460ceSYo-Jung Leo Lin (AMD) 	{ }
298*4ed460ceSYo-Jung Leo Lin (AMD) };
299*4ed460ceSYo-Jung Leo Lin (AMD) MODULE_DEVICE_TABLE(wmi, amd_halo_id_table);
300*4ed460ceSYo-Jung Leo Lin (AMD) 
301*4ed460ceSYo-Jung Leo Lin (AMD) static struct wmi_driver amd_halo_driver = {
302*4ed460ceSYo-Jung Leo Lin (AMD) 	.driver = {
303*4ed460ceSYo-Jung Leo Lin (AMD) 		.name = "amd_halo_led",
304*4ed460ceSYo-Jung Leo Lin (AMD) 	},
305*4ed460ceSYo-Jung Leo Lin (AMD) 	.id_table = amd_halo_id_table,
306*4ed460ceSYo-Jung Leo Lin (AMD) 	.probe = amd_halo_probe,
307*4ed460ceSYo-Jung Leo Lin (AMD) 	.no_singleton = true,
308*4ed460ceSYo-Jung Leo Lin (AMD) };
309*4ed460ceSYo-Jung Leo Lin (AMD) 
310*4ed460ceSYo-Jung Leo Lin (AMD) module_wmi_driver(amd_halo_driver);
311*4ed460ceSYo-Jung Leo Lin (AMD) 
312*4ed460ceSYo-Jung Leo Lin (AMD) MODULE_AUTHOR("Mario Limonciello (AMD) <superm1@kernel.org>");
313*4ed460ceSYo-Jung Leo Lin (AMD) MODULE_AUTHOR("Yo-Jung Leo Lin (AMD) <Leo.Lin@amd.com>");
314*4ed460ceSYo-Jung Leo Lin (AMD) MODULE_DESCRIPTION("AMD Halo Box RGB LED Control Driver");
315*4ed460ceSYo-Jung Leo Lin (AMD) MODULE_LICENSE("GPL");
316