All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rahul Rameshbabu <rrameshbabu@nvidia.com>
To: Benjamin Tissoires <benjamin.tissoires@redhat.com>,
	Jiri Kosina <jikos@kernel.org>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Rahul Rameshbabu <rrameshbabu@nvidia.com>
Subject: [PATCH RFC v1 2/3] HID: nvidia-shield: Support LED functionality for Thunderstrike
Date: Mon, 29 May 2023 15:20:51 -0700	[thread overview]
Message-ID: <20230529222052.68913-3-rrameshbabu@nvidia.com> (raw)
In-Reply-To: <20230529222052.68913-1-rrameshbabu@nvidia.com>

Expose the 2017 SHIELD controller (Thunderstrike) LED through the kernel
LED API.

Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
---
 drivers/hid/hid-nvidia-shield.c | 116 ++++++++++++++++++++++++++++++--
 1 file changed, 111 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c
index 1a9d32b2c80c..7dfaec1c07e2 100644
--- a/drivers/hid/hid-nvidia-shield.c
+++ b/drivers/hid/hid-nvidia-shield.c
@@ -8,6 +8,7 @@
 #include <linux/hid.h>
 #include <linux/input-event-codes.h>
 #include <linux/input.h>
+#include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
@@ -35,6 +36,7 @@ enum {
 	THUNDERSTRIKE_FW_VERSION_UPDATE = 0,
 	THUNDERSTRIKE_BOARD_INFO_UPDATE,
 	THUNDERSTRIKE_HAPTICS_UPDATE,
+	THUNDERSTRIKE_LED_UPDATE,
 };
 
 enum {
@@ -45,12 +47,19 @@ enum {
 
 enum {
 	THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
+	THUNDERSTRIKE_HOSTCMD_ID_LED = 6,
 	THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
 	THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
 	THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
 	THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58,
 };
 
+enum thunderstrike_led_state {
+	THUNDERSTRIKE_LED_OFF = 1,
+	THUNDERSTRIKE_LED_ON = 8,
+} __packed;
+static_assert(sizeof(enum thunderstrike_led_state) == 1);
+
 struct thunderstrike_hostcmd_board_info {
 	__le16 revision;
 	__le16 serial[7];
@@ -70,6 +79,7 @@ struct thunderstrike_hostcmd_resp_report {
 		struct thunderstrike_hostcmd_board_info board_info;
 		struct thunderstrike_hostcmd_haptics motors;
 		__le16 fw_version;
+		enum thunderstrike_led_state led_state;
 		u8 payload[30];
 	};
 } __packed;
@@ -81,10 +91,16 @@ struct thunderstrike_hostcmd_req_report {
 	u8 cmd_id;
 	u8 reserved_at_10;
 
-	struct {
-		u8 update;
-		struct thunderstrike_hostcmd_haptics motors;
-	} haptics;
+	union {
+		struct {
+			u8 update;
+			enum thunderstrike_led_state state;
+		} led;
+		struct {
+			u8 update;
+			struct thunderstrike_hostcmd_haptics motors;
+		} haptics;
+	};
 	u8 reserved_at_30[27];
 } __packed;
 static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
@@ -108,12 +124,15 @@ struct thunderstrike {
 
 	/* Sub-devices */
 	struct input_dev *haptics_dev;
+	struct led_classdev led_dev;
 
 	/* Resources */
 	void *req_report_dmabuf;
 	unsigned long update_flags;
 	struct thunderstrike_hostcmd_haptics haptics_val;
 	spinlock_t haptics_update_lock;
+	u8 led_state : 1;
+	enum thunderstrike_led_state led_value;
 	struct work_struct hostcmd_req_work;
 };
 
@@ -221,6 +240,13 @@ static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work)
 		thunderstrike_send_hostcmd_request(ts);
 	}
 
+	if (test_and_clear_bit(THUNDERSTRIKE_LED_UPDATE, &ts->update_flags)) {
+		thunderstrike_hostcmd_req_report_init(report, THUNDERSTRIKE_HOSTCMD_ID_LED);
+		report->led.update = 1;
+		report->led.state = ts->led_value;
+		thunderstrike_send_hostcmd_request(ts);
+	}
+
 	if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) {
 		thunderstrike_hostcmd_req_report_init(
 			report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO);
@@ -292,6 +318,40 @@ static int thunderstrike_play_effect(struct input_dev *idev, void *data,
 	return thunderstrike_update_haptics(ts, &motors);
 }
 
+static enum led_brightness
+thunderstrike_led_get_brightness(struct led_classdev *led)
+{
+	struct hid_device *hdev = to_hid_device(led->dev->parent);
+	struct shield_device *shield_dev = hid_get_drvdata(hdev);
+	struct thunderstrike *ts;
+
+	ts = container_of(shield_dev, struct thunderstrike, base);
+
+	return ts->led_state;
+}
+
+static void thunderstrike_led_set_brightness(struct led_classdev *led,
+					    enum led_brightness value)
+{
+	struct hid_device *hdev = to_hid_device(led->dev->parent);
+	struct shield_device *shield_dev = hid_get_drvdata(hdev);
+	struct thunderstrike *ts;
+
+	ts = container_of(shield_dev, struct thunderstrike, base);
+
+	switch (value) {
+	case LED_OFF:
+		ts->led_value = THUNDERSTRIKE_LED_OFF;
+		break;
+	default:
+		ts->led_value = THUNDERSTRIKE_LED_ON;
+		break;
+	}
+
+	set_bit(THUNDERSTRIKE_LED_UPDATE, &ts->update_flags);
+	schedule_work(&ts->hostcmd_req_work);
+}
+
 static void
 thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev,
 				       __le16 fw_version)
@@ -338,6 +398,24 @@ thunderstrike_parse_haptics_payload(struct shield_device *shield_dev,
 		haptics->motor_left, haptics->motor_right);
 }
 
+static void
+thunderstrike_parse_led_payload(struct shield_device *shield_dev,
+				enum thunderstrike_led_state led_state)
+{
+	struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+
+	switch (led_state) {
+	case THUNDERSTRIKE_LED_OFF:
+		ts->led_state = 0;
+		break;
+	case THUNDERSTRIKE_LED_ON:
+		ts->led_state = 1;
+		break;
+	}
+
+	hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n", led_state);
+}
+
 static int thunderstrike_parse_report(struct shield_device *shield_dev,
 				      struct hid_report *report, u8 *data,
 				      int size)
@@ -364,6 +442,9 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
 			thunderstrike_parse_fw_version_payload(
 				shield_dev, hostcmd_resp_report->fw_version);
 			break;
+		case THUNDERSTRIKE_HOSTCMD_ID_LED:
+			thunderstrike_parse_led_payload(shield_dev, hostcmd_resp_report->led_state);
+			break;
 		case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO:
 			thunderstrike_parse_board_info_payload(
 				shield_dev, &hostcmd_resp_report->board_info);
@@ -395,10 +476,24 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
 	return 0;
 }
 
+static inline int thunderstrike_led_create(struct thunderstrike *ts)
+{
+	struct led_classdev *led = &ts->led_dev;
+
+	led->name = "thunderstrike:blue:led";
+	led->max_brightness = 1;
+	led->flags = LED_CORE_SUSPENDRESUME;
+	led->brightness_get = &thunderstrike_led_get_brightness;
+	led->brightness_set = &thunderstrike_led_set_brightness;
+
+	return led_classdev_register(&ts->base.hdev->dev, led);
+}
+
 static struct shield_device *thunderstrike_create(struct hid_device *hdev)
 {
 	struct shield_device *shield_dev;
 	struct thunderstrike *ts;
+	int ret;
 
 	ts = devm_kzalloc(&hdev->dev, sizeof(*ts), GFP_KERNEL);
 	if (!ts)
@@ -418,12 +513,22 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev)
 
 	hid_set_drvdata(hdev, shield_dev);
 
+	ret = thunderstrike_led_create(ts);
+	if (ret) {
+		hid_err(hdev, "Failed to create Thunderstrike LED instance\n");
+		return ERR_PTR(ret);
+	}
+
 	ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
 	if (IS_ERR(ts->haptics_dev))
-		return ERR_CAST(ts->haptics_dev);
+		goto err;
 
 	hid_info(hdev, "Registered Thunderstrike controller\n");
 	return shield_dev;
+
+err:
+	led_classdev_unregister(&ts->led_dev);
+	return ERR_CAST(ts->haptics_dev);
 }
 
 static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -599,6 +704,7 @@ static void shield_remove(struct hid_device *hdev)
 	ts = container_of(dev, struct thunderstrike, base);
 
 	hid_hw_close(hdev);
+	led_classdev_unregister(&ts->led_dev);
 	if (ts->haptics_dev)
 		input_unregister_device(ts->haptics_dev);
 	cancel_work_sync(&ts->hostcmd_req_work);
-- 
2.38.4


  parent reply	other threads:[~2023-05-29 22:21 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-29 22:20 [PATCH RFC v1 0/3] HID: nvidia-shield: More functionality on top of initial driver Rahul Rameshbabu
2023-05-29 22:20 ` [PATCH RFC v1 1/3] HID: nvidia-shield: Add mappings for consumer HID USAGE buttons Rahul Rameshbabu
2023-05-29 22:20 ` Rahul Rameshbabu [this message]
2023-05-29 22:20 ` [PATCH RFC v1 3/3] HID: nvidia-shield: Remove space prefix from label in shield_haptics_create Rahul Rameshbabu
2023-06-09 15:53 ` [PATCH RFC v1 0/3] HID: nvidia-shield: More functionality on top of initial driver Jiri Kosina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230529222052.68913-3-rrameshbabu@nvidia.com \
    --to=rrameshbabu@nvidia.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.