Re: [PATCH v2 6/7] HID: lg-g15: Add support for controlling the G510's RGB backlight

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Aug 22, 2019 at 5:26 PM Hans de Goede <hdegoede@xxxxxxxxxx> wrote:
>
> Hi,
>
> On 22-08-19 17:00, Benjamin Tissoires wrote:
> > Hi Hans,
> >
> > On Wed, Aug 21, 2019 at 10:59 PM Hans de Goede <hdegoede@xxxxxxxxxx> wrote:
> >>
> >> Note that the keyboard has a backlight on/off toggle button. If the
> >> backlight is turned off through that button, then any changes we make
> >> will be ignored and we cannot turn it back on again from the host.
> >>
> >> To workaround this we write the last set RGB values when we receive an
> >> event indicating that the backlight has been turned on again.
> >
> > Not a formal review, yet, but I have the new sparse complain with this patch:
> >
> > drivers/hid/hid-lg-g15.c:xxx:5:.warning: symbol
> > 'lg_g15_get_initial_led_brightness' was not declared. Should it be
> > static?
>
> Yes that is a valid sparse remark. I will wait for the full review
> before respinning the series, or do you want me to respin the series now?

nah, no need to respin the series now. Please keep that in mind for v2.
I just noticed that the CI failed for this series, and if I do not
send the notification right now, I'll likely forget about it later.

Cheers,
Benjamin

>
> Regards,
>
> Hans
>
>
> >> Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
> >> ---
> >>   drivers/hid/hid-lg-g15.c | 272 ++++++++++++++++++++++++++++++++++++---
> >>   1 file changed, 257 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
> >> index 62b3ae8a59a6..57640417d49d 100644
> >> --- a/drivers/hid/hid-lg-g15.c
> >> +++ b/drivers/hid/hid-lg-g15.c
> >> @@ -19,6 +19,10 @@
> >>
> >>   #define LG_G15_FEATURE_REPORT          0x02
> >>
> >> +#define LG_G510_FEATURE_M_KEYS_LEDS    0x04
> >> +#define LG_G510_FEATURE_BACKLIGHT_RGB  0x05
> >> +#define LG_G510_FEATURE_POWER_ON_RGB   0x06
> >> +
> >>   enum lg_g15_model {
> >>          LG_G15,
> >>          LG_G15_V2,
> >> @@ -41,6 +45,7 @@ struct lg_g15_led {
> >>          struct led_classdev cdev;
> >>          enum led_brightness brightness;
> >>          enum lg_g15_led_type led;
> >> +       u8 red, green, blue;
> >>   };
> >>
> >>   struct lg_g15_data {
> >> @@ -56,13 +61,12 @@ struct lg_g15_data {
> >>          bool game_mode_enabled;
> >>   };
> >>
> >> +/******** G15 and G15 v2 LED functions ********/
> >> +
> >>   static int lg_g15_update_led_brightness(struct lg_g15_data *g15)
> >>   {
> >>          int ret;
> >>
> >> -       if (g15->model == LG_G510 || g15->model == LG_G510_USB_AUDIO)
> >> -               return 0;
> >> -
> >>          ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT,
> >>                                   g15->transfer_buf, 4,
> >>                                   HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> >> @@ -183,6 +187,198 @@ static void lg_g15_leds_changed_work(struct work_struct *work)
> >>          }
> >>   }
> >>
> >> +/******** G510 LED functions ********/
> >> +
> >> +static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i)
> >> +{
> >> +       int ret, high;
> >> +
> >> +       ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_BACKLIGHT_RGB + i,
> >> +                                g15->transfer_buf, 4,
> >> +                                HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> >> +       if (ret != 4) {
> >> +               hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret);
> >> +               return (ret < 0) ? ret : -EIO;
> >> +       }
> >> +
> >> +       high = max3(g15->transfer_buf[1], g15->transfer_buf[2],
> >> +                   g15->transfer_buf[3]);
> >> +
> >> +       if (high) {
> >> +               g15->leds[i].red =
> >> +                       DIV_ROUND_CLOSEST(g15->transfer_buf[1] * 255, high);
> >> +               g15->leds[i].green =
> >> +                       DIV_ROUND_CLOSEST(g15->transfer_buf[2] * 255, high);
> >> +               g15->leds[i].blue =
> >> +                       DIV_ROUND_CLOSEST(g15->transfer_buf[3] * 255, high);
> >> +               g15->leds[i].brightness = high;
> >> +       } else {
> >> +               g15->leds[i].red   = 255;
> >> +               g15->leds[i].green = 255;
> >> +               g15->leds[i].blue  = 255;
> >> +               g15->leds[i].brightness = 0;
> >> +       }
> >> +
> >> +       return 0;
> >> +}
> >> +
> >> +/* Must be called with g15->mutex locked */
> >> +static int lg_g510_kbd_led_write(struct lg_g15_data *g15,
> >> +                                struct lg_g15_led *g15_led,
> >> +                                enum led_brightness brightness)
> >> +{
> >> +       int ret;
> >> +
> >> +       g15->transfer_buf[0] = 5 + g15_led->led;
> >> +       g15->transfer_buf[1] =
> >> +               DIV_ROUND_CLOSEST(g15_led->red * brightness, 255);
> >> +       g15->transfer_buf[2] =
> >> +               DIV_ROUND_CLOSEST(g15_led->green * brightness, 255);
> >> +       g15->transfer_buf[3] =
> >> +               DIV_ROUND_CLOSEST(g15_led->blue * brightness, 255);
> >> +
> >> +       ret = hid_hw_raw_request(g15->hdev,
> >> +                                LG_G510_FEATURE_BACKLIGHT_RGB + g15_led->led,
> >> +                                g15->transfer_buf, 4,
> >> +                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> >> +       if (ret == 4) {
> >> +               /* Success */
> >> +               g15_led->brightness = brightness;
> >> +               ret = 0;
> >> +       } else {
> >> +               hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);
> >> +               ret = (ret < 0) ? ret : -EIO;
> >> +       }
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static int lg_g510_kbd_led_set(struct led_classdev *led_cdev,
> >> +                              enum led_brightness brightness)
> >> +{
> >> +       struct lg_g15_led *g15_led =
> >> +               container_of(led_cdev, struct lg_g15_led, cdev);
> >> +       struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
> >> +       int ret;
> >> +
> >> +       /* Ignore LED off on unregister / keyboard unplug */
> >> +       if (led_cdev->flags & LED_UNREGISTERING)
> >> +               return 0;
> >> +
> >> +       mutex_lock(&g15->mutex);
> >> +       ret = lg_g510_kbd_led_write(g15, g15_led, brightness);
> >> +       mutex_unlock(&g15->mutex);
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static enum led_brightness lg_g510_kbd_led_get(struct led_classdev *led_cdev)
> >> +{
> >> +       struct lg_g15_led *g15_led =
> >> +               container_of(led_cdev, struct lg_g15_led, cdev);
> >> +
> >> +       return g15_led->brightness;
> >> +}
> >> +
> >> +static ssize_t color_store(struct device *dev, struct device_attribute *attr,
> >> +                          const char *buf, size_t count)
> >> +{
> >> +       struct led_classdev *led_cdev = dev_get_drvdata(dev);
> >> +       struct lg_g15_led *g15_led =
> >> +               container_of(led_cdev, struct lg_g15_led, cdev);
> >> +       struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
> >> +       unsigned long value;
> >> +       int ret;
> >> +
> >> +       if (count < 7 || (count == 8 && buf[7] != '\n') || count > 8)
> >> +               return -EINVAL;
> >> +
> >> +       if (buf[0] != '#')
> >> +               return -EINVAL;
> >> +
> >> +       ret = kstrtoul(buf + 1, 16, &value);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       mutex_lock(&g15->mutex);
> >> +       g15_led->red   = (value & 0xff0000) >> 16;
> >> +       g15_led->green = (value & 0x00ff00) >> 8;
> >> +       g15_led->blue  = (value & 0x0000ff);
> >> +       ret = lg_g510_kbd_led_write(g15, g15_led, g15_led->brightness);
> >> +       mutex_unlock(&g15->mutex);
> >> +
> >> +       return (ret < 0) ? ret : count;
> >> +}
> >> +
> >> +static ssize_t color_show(struct device *dev, struct device_attribute *attr,
> >> +                         char *buf)
> >> +{
> >> +       struct led_classdev *led_cdev = dev_get_drvdata(dev);
> >> +       struct lg_g15_led *g15_led =
> >> +               container_of(led_cdev, struct lg_g15_led, cdev);
> >> +       struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
> >> +       ssize_t ret;
> >> +
> >> +       mutex_lock(&g15->mutex);
> >> +       ret = sprintf(buf, "#%02x%02x%02x\n",
> >> +                     g15_led->red, g15_led->green, g15_led->blue);
> >> +       mutex_unlock(&g15->mutex);
> >> +
> >> +       return ret;
> >> +}
> >> +
> >> +static DEVICE_ATTR_RW(color);
> >> +
> >> +static struct attribute *lg_g510_kbd_led_attrs[] = {
> >> +       &dev_attr_color.attr,
> >> +       NULL,
> >> +};
> >> +
> >> +static const struct attribute_group lg_g510_kbd_led_group = {
> >> +       .attrs = lg_g510_kbd_led_attrs,
> >> +};
> >> +
> >> +static const struct attribute_group *lg_g510_kbd_led_groups[] = {
> >> +       &lg_g510_kbd_led_group,
> >> +       NULL,
> >> +};
> >> +
> >> +static void lg_g510_leds_sync_work(struct work_struct *work)
> >> +{
> >> +       struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work);
> >> +
> >> +       mutex_lock(&g15->mutex);
> >> +       lg_g510_kbd_led_write(g15, &g15->leds[LG_G15_KBD_BRIGHTNESS],
> >> +                             g15->leds[LG_G15_KBD_BRIGHTNESS].brightness);
> >> +       mutex_unlock(&g15->mutex);
> >> +}
> >> +
> >> +/******** Generic LED functions ********/
> >> +int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
> >> +{
> >> +       int ret;
> >> +
> >> +       switch (g15->model) {
> >> +       case LG_G15:
> >> +       case LG_G15_V2:
> >> +               return lg_g15_update_led_brightness(g15);
> >> +       case LG_G510:
> >> +       case LG_G510_USB_AUDIO:
> >> +               ret = lg_g510_get_initial_led_brightness(g15, 0);
> >> +               if (ret)
> >> +                       return ret;
> >> +
> >> +               ret = lg_g510_get_initial_led_brightness(g15, 1);
> >> +               if (ret)
> >> +                       return ret;
> >> +
> >> +               return 0;
> >> +       }
> >> +       return -EINVAL; /* Never reached */
> >> +}
> >> +
> >> +/******** Input functions ********/
> >> +
> >>   /* On the G15 Mark I Logitech has been quite creative with which bit is what */
> >>   static int lg_g15_event(struct lg_g15_data *g15, u8 *data, int size)
> >>   {
> >> @@ -306,6 +502,22 @@ static int lg_g510_event(struct lg_g15_data *g15, u8 *data, int size)
> >>          return 0;
> >>   }
> >>
> >> +static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data, int size)
> >> +{
> >> +       bool backlight_disabled;
> >> +
> >> +       /*
> >> +        * The G510 ignores backlight updates when the backlight is turned off
> >> +        * through the light toggle button on the keyboard, to work around this
> >> +        * we queue a workitem to sync values when the backlight is turned on.
> >> +        */
> >> +       backlight_disabled = data[1] & 0x04;
> >> +       if (!backlight_disabled)
> >> +               schedule_work(&g15->work);
> >> +
> >> +       return 0;
> >> +}
> >> +
> >>   static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
> >>                              u8 *data, int size)
> >>   {
> >> @@ -327,6 +539,8 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
> >>          case LG_G510_USB_AUDIO:
> >>                  if (data[0] == 0x03 && size == 5)
> >>                          return lg_g510_event(g15, data, size);
> >> +               if (data[0] == 0x04 && size == 2)
> >> +                       return lg_g510_leds_event(g15, data, size);
> >>                  break;
> >>          }
> >>
> >> @@ -360,13 +574,42 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i)
> >>
> >>          g15->leds[i].led = i;
> >>          g15->leds[i].cdev.name = led_names[i];
> >> -       g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set;
> >> -       g15->leds[i].cdev.brightness_get = lg_g15_led_get;
> >> -       if (i < LG_G15_BRIGHTNESS_MAX) {
> >> -               g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED;
> >> -               g15->leds[i].cdev.max_brightness = 2;
> >> -       } else {
> >> -               g15->leds[i].cdev.max_brightness = 1;
> >> +
> >> +       switch (g15->model) {
> >> +       case LG_G15:
> >> +       case LG_G15_V2:
> >> +               g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set;
> >> +               g15->leds[i].cdev.brightness_get = lg_g15_led_get;
> >> +               if (i < LG_G15_BRIGHTNESS_MAX) {
> >> +                       g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED;
> >> +                       g15->leds[i].cdev.max_brightness = 2;
> >> +               } else {
> >> +                       g15->leds[i].cdev.max_brightness = 1;
> >> +               }
> >> +               break;
> >> +       case LG_G510:
> >> +       case LG_G510_USB_AUDIO:
> >> +               switch (i) {
> >> +               case LG_G15_LCD_BRIGHTNESS:
> >> +                       /*
> >> +                        * The G510 does not have a separate LCD brightness,
> >> +                        * but it does have a separate power-on (reset) value.
> >> +                        */
> >> +                       g15->leds[i].cdev.name = "g15::power_on_backlight_val";
> >> +                       /* fall through */
> >> +               case LG_G15_KBD_BRIGHTNESS:
> >> +                       g15->leds[i].cdev.brightness_set_blocking =
> >> +                               lg_g510_kbd_led_set;
> >> +                       g15->leds[i].cdev.brightness_get =
> >> +                               lg_g510_kbd_led_get;
> >> +                       g15->leds[i].cdev.max_brightness = 255;
> >> +                       g15->leds[i].cdev.groups = lg_g510_kbd_led_groups;
> >> +                       break;
> >> +               default:
> >> +                       /* TODO: Add support for M1 - M3 and MR leds */
> >> +                       return 0;
> >> +               }
> >> +               break;
> >>          }
> >>
> >>          return devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev);
> >> @@ -414,11 +657,11 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
> >>
> >>          g15->hdev = hdev;
> >>          g15->model = id->driver_data;
> >> -       INIT_WORK(&g15->work, lg_g15_leds_changed_work);
> >>          hid_set_drvdata(hdev, (void *)g15);
> >>
> >>          switch (g15->model) {
> >>          case LG_G15:
> >> +               INIT_WORK(&g15->work, lg_g15_leds_changed_work);
> >>                  /*
> >>                   * The G15 and G15 v2 use a separate usb-device (on a builtin
> >>                   * hub) which emulates a keyboard for the F1 - F12 emulation
> >> @@ -430,12 +673,14 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
> >>                  gkeys = 18;
> >>                  break;
> >>          case LG_G15_V2:
> >> +               INIT_WORK(&g15->work, lg_g15_leds_changed_work);
> >>                  connect_mask = HID_CONNECT_HIDRAW;
> >>                  gkeys_settings_output_report = 0x02;
> >>                  gkeys = 6;
> >>                  break;
> >>          case LG_G510:
> >>          case LG_G510_USB_AUDIO:
> >> +               INIT_WORK(&g15->work, lg_g510_leds_sync_work);
> >>                  connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW;
> >>                  gkeys_settings_feature_report = 0x01;
> >>                  gkeys = 18;
> >> @@ -476,7 +721,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
> >>          }
> >>
> >>          /* Get initial brightness levels */
> >> -       ret = lg_g15_update_led_brightness(g15);
> >> +       ret = lg_g15_get_initial_led_brightness(g15);
> >>          if (ret)
> >>                  goto error_hw_stop;
> >>
> >> @@ -523,9 +768,6 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
> >>          if (ret)
> >>                  goto error_hw_stop;
> >>
> >> -       if (g15->model == LG_G510 || g15->model == LG_G510_USB_AUDIO)
> >> -               return 0;
> >> -
> >>          /* Register LED devices */
> >>          for (i = 0; i < LG_G15_LED_MAX; i++) {
> >>                  ret = lg_g15_register_led(g15, i);
> >> --
> >> 2.23.0.rc2
> >>



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux