Commit 93cfa25b authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.7/nintendo' into for-linus

- cleanup of LED handling in hid-nintendo (Martino Fontana)
parents 54021f90 92827607
Loading
Loading
Loading
Loading
+76 −57
Original line number Diff line number Diff line
@@ -410,6 +410,18 @@ static const char * const joycon_player_led_names[] = {
	LED_FUNCTION_PLAYER4,
};
#define JC_NUM_LEDS		ARRAY_SIZE(joycon_player_led_names)
#define JC_NUM_LED_PATTERNS 8
/* Taken from https://www.nintendo.com/my/support/qa/detail/33822 */
static const enum led_brightness joycon_player_led_patterns[JC_NUM_LED_PATTERNS][JC_NUM_LEDS] = {
	{ 1, 0, 0, 0 },
	{ 1, 1, 0, 0 },
	{ 1, 1, 1, 0 },
	{ 1, 1, 1, 1 },
	{ 1, 0, 0, 1 },
	{ 1, 0, 1, 0 },
	{ 1, 0, 1, 1 },
	{ 0, 1, 1, 0 },
};

/* Each physical controller is associated with a joycon_ctlr struct */
struct joycon_ctlr {
@@ -699,6 +711,25 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
	return joycon_send_subcmd(ctlr, req, 1, HZ/4);
}

static int joycon_set_home_led(struct joycon_ctlr *ctlr, enum led_brightness brightness)
{
	struct joycon_subcmd_request *req;
	u8 buffer[sizeof(*req) + 5] = { 0 };
	u8 *data;

	req = (struct joycon_subcmd_request *)buffer;
	req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
	data = req->data;
	data[0] = 0x01;
	data[1] = brightness << 4;
	data[2] = brightness | (brightness << 4);
	data[3] = 0x11;
	data[4] = 0x11;

	hid_dbg(ctlr->hdev, "setting home led brightness\n");
	return joycon_send_subcmd(ctlr, req, 5, HZ/4);
}

static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
					 u32 start_addr, u8 size, u8 **reply)
{
@@ -1840,6 +1871,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
	return 0;
}

/* Because the subcommand sets all the leds at once, the brightness argument is ignored */
static int joycon_player_led_brightness_set(struct led_classdev *led,
					    enum led_brightness brightness)
{
@@ -1849,7 +1881,6 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
	int val = 0;
	int i;
	int ret;
	int num;

	ctlr = hid_get_drvdata(hdev);
	if (!ctlr) {
@@ -1857,21 +1888,10 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
		return -ENODEV;
	}

	/* determine which player led this is */
	for (num = 0; num < JC_NUM_LEDS; num++) {
		if (&ctlr->leds[num] == led)
			break;
	}
	if (num >= JC_NUM_LEDS)
		return -EINVAL;
	for (i = 0; i < JC_NUM_LEDS; i++)
		val |= ctlr->leds[i].brightness << i;

	mutex_lock(&ctlr->output_mutex);
	for (i = 0; i < JC_NUM_LEDS; i++) {
		if (i == num)
			val |= brightness << i;
		else
			val |= ctlr->leds[i].brightness << i;
	}
	ret = joycon_set_player_leds(ctlr, 0, val);
	mutex_unlock(&ctlr->output_mutex);

@@ -1884,9 +1904,6 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
	struct device *dev = led->dev->parent;
	struct hid_device *hdev = to_hid_device(dev);
	struct joycon_ctlr *ctlr;
	struct joycon_subcmd_request *req;
	u8 buffer[sizeof(*req) + 5] = { 0 };
	u8 *data;
	int ret;

	ctlr = hid_get_drvdata(hdev);
@@ -1894,43 +1911,35 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
		hid_err(hdev, "No controller data\n");
		return -ENODEV;
	}

	req = (struct joycon_subcmd_request *)buffer;
	req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
	data = req->data;
	data[0] = 0x01;
	data[1] = brightness << 4;
	data[2] = brightness | (brightness << 4);
	data[3] = 0x11;
	data[4] = 0x11;

	hid_dbg(hdev, "setting home led brightness\n");
	mutex_lock(&ctlr->output_mutex);
	ret = joycon_send_subcmd(ctlr, req, 5, HZ/4);
	ret = joycon_set_home_led(ctlr, brightness);
	mutex_unlock(&ctlr->output_mutex);

	return ret;
}

static DEFINE_MUTEX(joycon_input_num_mutex);
static DEFINE_SPINLOCK(joycon_input_num_spinlock);
static int joycon_leds_create(struct joycon_ctlr *ctlr)
{
	struct hid_device *hdev = ctlr->hdev;
	struct device *dev = &hdev->dev;
	const char *d_name = dev_name(dev);
	struct led_classdev *led;
	int led_val = 0;
	char *name;
	int ret = 0;
	int ret;
	int i;
	static int input_num = 1;
	unsigned long flags;
	int player_led_pattern;
	static int input_num;

	/* Set the default controller player leds based on controller number */
	mutex_lock(&joycon_input_num_mutex);
	mutex_lock(&ctlr->output_mutex);
	ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
	if (ret)
		hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
	mutex_unlock(&ctlr->output_mutex);
	/*
	 * Set the player leds based on controller number
	 * Because there is no standard concept of "player number", the pattern
	 * number will simply increase by 1 every time a controller is connected.
	 */
	spin_lock_irqsave(&joycon_input_num_spinlock, flags);
	player_led_pattern = input_num++ % JC_NUM_LED_PATTERNS;
	spin_unlock_irqrestore(&joycon_input_num_spinlock, flags);

	/* configure the player LEDs */
	for (i = 0; i < JC_NUM_LEDS; i++) {
@@ -1938,31 +1947,37 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
				      d_name,
				      "green",
				      joycon_player_led_names[i]);
		if (!name) {
			mutex_unlock(&joycon_input_num_mutex);
		if (!name)
			return -ENOMEM;
		}

		led = &ctlr->leds[i];
		led->name = name;
		led->brightness = ((i + 1) <= input_num) ? 1 : 0;
		led->brightness = joycon_player_led_patterns[player_led_pattern][i];
		led->max_brightness = 1;
		led->brightness_set_blocking =
					joycon_player_led_brightness_set;
		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;

		led_val |= joycon_player_led_patterns[player_led_pattern][i] << i;
	}
	mutex_lock(&ctlr->output_mutex);
	ret = joycon_set_player_leds(ctlr, 0, led_val);
	mutex_unlock(&ctlr->output_mutex);
	if (ret) {
		hid_warn(hdev, "Failed to set players LEDs, skipping registration; ret=%d\n", ret);
		goto home_led;
	}

	for (i = 0; i < JC_NUM_LEDS; i++) {
		led = &ctlr->leds[i];
		ret = devm_led_classdev_register(&hdev->dev, led);
		if (ret) {
			hid_err(hdev, "Failed registering %s LED\n", led->name);
			mutex_unlock(&joycon_input_num_mutex);
			hid_err(hdev, "Failed to register player %d LED; ret=%d\n", i + 1, ret);
			return ret;
		}
	}

	if (++input_num > 4)
		input_num = 1;
	mutex_unlock(&joycon_input_num_mutex);

home_led:
	/* configure the home LED */
	if (jc_type_has_right(ctlr)) {
		name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
@@ -1978,16 +1993,20 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
		led->max_brightness = 0xF;
		led->brightness_set_blocking = joycon_home_led_brightness_set;
		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
		ret = devm_led_classdev_register(&hdev->dev, led);

		/* Set the home LED to 0 as default state */
		mutex_lock(&ctlr->output_mutex);
		ret = joycon_set_home_led(ctlr, 0);
		mutex_unlock(&ctlr->output_mutex);
		if (ret) {
			hid_err(hdev, "Failed registering home led\n");
			return ret;
			hid_warn(hdev, "Failed to set home LED, skipping registration; ret=%d\n", ret);
			return 0;
		}
		/* Set the home LED to 0 as default state */
		ret = joycon_home_led_brightness_set(led, 0);

		ret = devm_led_classdev_register(&hdev->dev, led);
		if (ret) {
			hid_warn(hdev, "Failed to set home LED default, unregistering home LED");
			devm_led_classdev_unregister(&hdev->dev, led);
			hid_err(hdev, "Failed to register home LED; ret=%d\n", ret);
			return ret;
		}
	}