From: Roderick Colenbrander <roderick.colenbrander@xxxxxxxx> Provide initial support for the DualSense lightbar and configure it with a default PlayStation blue color. Signed-off-by: Roderick Colenbrander <roderick.colenbrander@xxxxxxxx> Reviewed-by: Barnabás Pőcze <pobrn@xxxxxxxxxxxxxx> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx> --- drivers/hid/hid-playstation.c | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index f279064e74a5..b141b1be6453 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -103,6 +103,10 @@ struct ps_calibration_data { /* Flags for DualSense output report. */ #define DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION BIT(0) #define DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT BIT(1) +#define DS_OUTPUT_VALID_FLAG1_LIGHTBAR_CONTROL_ENABLE BIT(2) +#define DS_OUTPUT_VALID_FLAG1_RELEASE_LEDS BIT(3) +#define DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE BIT(1) +#define DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT BIT(1) /* DualSense hardware limits */ #define DS_ACC_RES_PER_G 8192 @@ -132,6 +136,12 @@ struct dualsense { uint8_t motor_left; uint8_t motor_right; + /* RGB lightbar */ + bool update_lightbar; + uint8_t lightbar_red; + uint8_t lightbar_green; + uint8_t lightbar_blue; + struct work_struct output_worker; void *output_report_dmabuf; uint8_t output_seq; /* Sequence number for output report. */ @@ -796,6 +806,15 @@ static void dualsense_output_worker(struct work_struct *work) ds->update_rumble = false; } + if (ds->update_lightbar) { + common->valid_flag1 |= DS_OUTPUT_VALID_FLAG1_LIGHTBAR_CONTROL_ENABLE; + common->lightbar_red = ds->lightbar_red; + common->lightbar_green = ds->lightbar_green; + common->lightbar_blue = ds->lightbar_blue; + + ds->update_lightbar = false; + } + spin_unlock_irqrestore(&ds->base.lock, flags); dualsense_send_output_report(ds, &report); @@ -980,6 +999,41 @@ static int dualsense_play_effect(struct input_dev *dev, void *data, struct ff_ef return 0; } +static int dualsense_reset_leds(struct dualsense *ds) +{ + struct dualsense_output_report report; + uint8_t *buf; + + buf = kzalloc(sizeof(struct dualsense_output_report_bt), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + dualsense_init_output_report(ds, &report, buf); + /* + * On Bluetooth the DualSense outputs an animation on the lightbar + * during startup and maintains a color afterwards. We need to explicitly + * reconfigure the lightbar before we can do any programming later on. + * In USB the lightbar is not on by default, but redoing the setup there + * doesn't hurt. + */ + report.common->valid_flag2 = DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE; + report.common->lightbar_setup = DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT; /* Fade light out. */ + dualsense_send_output_report(ds, &report); + + kfree(buf); + return 0; +} + +static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue) +{ + ds->update_lightbar = true; + ds->lightbar_red = red; + ds->lightbar_green = green; + ds->lightbar_blue = blue; + + schedule_work(&ds->output_worker); +} + static struct ps_device *dualsense_create(struct hid_device *hdev) { struct dualsense *ds; @@ -1057,6 +1111,17 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) if (ret) goto err; + /* + * The hardware may have control over the LEDs (e.g. in Bluetooth on startup). + * Reset the LEDs (lightbar, mute, player leds), so we can control them + * from software. + */ + ret = dualsense_reset_leds(ds); + if (ret) + goto err; + + dualsense_set_lightbar(ds, 0, 0, 128); /* blue */ + /* * Reporting hardware and firmware is important as there are frequent updates, which * can change behavior. -- 2.29.2