From: Roderick Colenbrander <roderick.colenbran...@sony.com>

Provide initial support for the DualSense lightbar and configure it
with a default PlayStation blue color.

Signed-off-by: Roderick Colenbrander <roderick.colenbran...@sony.com>
Reviewed-by: Barnabás Pőcze <po...@protonmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoi...@redhat.com>
---
 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

Reply via email to