mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 05:44:11 +00:00
Merge branch 'for-6.7/nintendo' into for-linus
- cleanup of LED handling in hid-nintendo (Martino Fontana)
This commit is contained in:
commit
93cfa25b17
1 changed files with 76 additions and 57 deletions
|
@ -410,6 +410,18 @@ static const char * const joycon_player_led_names[] = {
|
||||||
LED_FUNCTION_PLAYER4,
|
LED_FUNCTION_PLAYER4,
|
||||||
};
|
};
|
||||||
#define JC_NUM_LEDS ARRAY_SIZE(joycon_player_led_names)
|
#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 */
|
/* Each physical controller is associated with a joycon_ctlr struct */
|
||||||
struct joycon_ctlr {
|
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);
|
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,
|
static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
|
||||||
u32 start_addr, u8 size, u8 **reply)
|
u32 start_addr, u8 size, u8 **reply)
|
||||||
{
|
{
|
||||||
|
@ -1840,6 +1871,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
|
||||||
return 0;
|
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,
|
static int joycon_player_led_brightness_set(struct led_classdev *led,
|
||||||
enum led_brightness brightness)
|
enum led_brightness brightness)
|
||||||
{
|
{
|
||||||
|
@ -1849,7 +1881,6 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
|
||||||
int val = 0;
|
int val = 0;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
int num;
|
|
||||||
|
|
||||||
ctlr = hid_get_drvdata(hdev);
|
ctlr = hid_get_drvdata(hdev);
|
||||||
if (!ctlr) {
|
if (!ctlr) {
|
||||||
|
@ -1857,21 +1888,10 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* determine which player led this is */
|
for (i = 0; i < JC_NUM_LEDS; i++)
|
||||||
for (num = 0; num < JC_NUM_LEDS; num++) {
|
val |= ctlr->leds[i].brightness << i;
|
||||||
if (&ctlr->leds[num] == led)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (num >= JC_NUM_LEDS)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
mutex_lock(&ctlr->output_mutex);
|
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);
|
ret = joycon_set_player_leds(ctlr, 0, val);
|
||||||
mutex_unlock(&ctlr->output_mutex);
|
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 device *dev = led->dev->parent;
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct joycon_ctlr *ctlr;
|
struct joycon_ctlr *ctlr;
|
||||||
struct joycon_subcmd_request *req;
|
|
||||||
u8 buffer[sizeof(*req) + 5] = { 0 };
|
|
||||||
u8 *data;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ctlr = hid_get_drvdata(hdev);
|
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");
|
hid_err(hdev, "No controller data\n");
|
||||||
return -ENODEV;
|
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);
|
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);
|
mutex_unlock(&ctlr->output_mutex);
|
||||||
|
|
||||||
return ret;
|
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)
|
static int joycon_leds_create(struct joycon_ctlr *ctlr)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = ctlr->hdev;
|
struct hid_device *hdev = ctlr->hdev;
|
||||||
struct device *dev = &hdev->dev;
|
struct device *dev = &hdev->dev;
|
||||||
const char *d_name = dev_name(dev);
|
const char *d_name = dev_name(dev);
|
||||||
struct led_classdev *led;
|
struct led_classdev *led;
|
||||||
|
int led_val = 0;
|
||||||
char *name;
|
char *name;
|
||||||
int ret = 0;
|
int ret;
|
||||||
int i;
|
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);
|
* Set the player leds based on controller number
|
||||||
mutex_lock(&ctlr->output_mutex);
|
* Because there is no standard concept of "player number", the pattern
|
||||||
ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
|
* number will simply increase by 1 every time a controller is connected.
|
||||||
if (ret)
|
*/
|
||||||
hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
|
spin_lock_irqsave(&joycon_input_num_spinlock, flags);
|
||||||
mutex_unlock(&ctlr->output_mutex);
|
player_led_pattern = input_num++ % JC_NUM_LED_PATTERNS;
|
||||||
|
spin_unlock_irqrestore(&joycon_input_num_spinlock, flags);
|
||||||
|
|
||||||
/* configure the player LEDs */
|
/* configure the player LEDs */
|
||||||
for (i = 0; i < JC_NUM_LEDS; i++) {
|
for (i = 0; i < JC_NUM_LEDS; i++) {
|
||||||
|
@ -1938,31 +1947,37 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
|
||||||
d_name,
|
d_name,
|
||||||
"green",
|
"green",
|
||||||
joycon_player_led_names[i]);
|
joycon_player_led_names[i]);
|
||||||
if (!name) {
|
if (!name)
|
||||||
mutex_unlock(&joycon_input_num_mutex);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
led = &ctlr->leds[i];
|
led = &ctlr->leds[i];
|
||||||
led->name = name;
|
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->max_brightness = 1;
|
||||||
led->brightness_set_blocking =
|
led->brightness_set_blocking =
|
||||||
joycon_player_led_brightness_set;
|
joycon_player_led_brightness_set;
|
||||||
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
|
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);
|
ret = devm_led_classdev_register(&hdev->dev, led);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "Failed registering %s LED\n", led->name);
|
hid_err(hdev, "Failed to register player %d LED; ret=%d\n", i + 1, ret);
|
||||||
mutex_unlock(&joycon_input_num_mutex);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++input_num > 4)
|
home_led:
|
||||||
input_num = 1;
|
|
||||||
mutex_unlock(&joycon_input_num_mutex);
|
|
||||||
|
|
||||||
/* configure the home LED */
|
/* configure the home LED */
|
||||||
if (jc_type_has_right(ctlr)) {
|
if (jc_type_has_right(ctlr)) {
|
||||||
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
|
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
|
||||||
|
@ -1978,17 +1993,21 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
|
||||||
led->max_brightness = 0xF;
|
led->max_brightness = 0xF;
|
||||||
led->brightness_set_blocking = joycon_home_led_brightness_set;
|
led->brightness_set_blocking = joycon_home_led_brightness_set;
|
||||||
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
|
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
|
||||||
|
|
||||||
|
/* 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_warn(hdev, "Failed to set home LED, skipping registration; ret=%d\n", ret);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ret = devm_led_classdev_register(&hdev->dev, led);
|
ret = devm_led_classdev_register(&hdev->dev, led);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "Failed registering home led\n");
|
hid_err(hdev, "Failed to register home LED; ret=%d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
/* Set the home LED to 0 as default state */
|
|
||||||
ret = joycon_home_led_brightness_set(led, 0);
|
|
||||||
if (ret) {
|
|
||||||
hid_warn(hdev, "Failed to set home LED default, unregistering home LED");
|
|
||||||
devm_led_classdev_unregister(&hdev->dev, led);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue