[Nouveau] [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo

Karol Herbst karolherbst at gmail.com
Tue Aug 23 08:31:09 UTC 2016


maybe it makes sense to expose the SLI LED, too.

Regardless of my comments this patch is reviewed-by me.

2016-08-23 1:39 GMT+02:00 Martin Peres <martin.peres at free.fr>:
> We received a donation of a Titan which has this useless feature
> allowing users to control the brightness of the LED behind the
> logo of NVIDIA. In the true spirit of open source, let's expose
> that to the users of very expensive cards!
>
> This patch hooks up this LED/PWM to the LED subsystem which allows
> blinking it in sync with cpu/disk/network/whatever activity (heartbeat
> is quite nice!). Users may also implement some breathing effect or
> morse code support in the userspace if they feel like it.
>
> Signed-off-by: Martin Peres <martin.peres at free.fr>
> ---
>  drm/nouveau/Kbuild                          |   1 +
>  drm/nouveau/include/nvkm/subdev/bios/gpio.h |   1 +
>  drm/nouveau/nouveau_drm.c                   |   7 ++
>  drm/nouveau/nouveau_drm.h                   |   3 +
>  drm/nouveau/nouveau_led.c                   | 131 ++++++++++++++++++++++++++++
>  drm/nouveau/nouveau_led.h                   |  48 ++++++++++
>  6 files changed, 191 insertions(+)
>  create mode 100644 drm/nouveau/nouveau_led.c
>  create mode 100644 drm/nouveau/nouveau_led.h
>
> diff --git a/drm/nouveau/Kbuild b/drm/nouveau/Kbuild
> index 2527bf4..312bca9 100644
> --- a/drm/nouveau/Kbuild
> +++ b/drm/nouveau/Kbuild
> @@ -22,6 +22,7 @@ nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o
>  nouveau-y += nouveau_drm.o
>  nouveau-y += nouveau_hwmon.o
>  nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
> +nouveau-y += nouveau_led.o
>  nouveau-y += nouveau_nvif.o
>  nouveau-$(CONFIG_NOUVEAU_PLATFORM_DRIVER) += nouveau_platform.o
>  nouveau-y += nouveau_usif.o # userspace <-> nvif
> diff --git a/drm/nouveau/include/nvkm/subdev/bios/gpio.h b/drm/nouveau/include/nvkm/subdev/bios/gpio.h
> index a47d46d..b7a54e6 100644
> --- a/drm/nouveau/include/nvkm/subdev/bios/gpio.h
> +++ b/drm/nouveau/include/nvkm/subdev/bios/gpio.h
> @@ -6,6 +6,7 @@ enum dcb_gpio_func_name {
>         DCB_GPIO_TVDAC1 = 0x2d,
>         DCB_GPIO_FAN = 0x09,
>         DCB_GPIO_FAN_SENSE = 0x3d,
> +       DCB_GPIO_LOGO_LED_PWM = 0x84,
>         DCB_GPIO_UNUSED = 0xff,
>         DCB_GPIO_VID0 = 0x04,
>         DCB_GPIO_VID1 = 0x05,
> diff --git a/drm/nouveau/nouveau_drm.c b/drm/nouveau/nouveau_drm.c
> index d06877d..dc97401 100644
> --- a/drm/nouveau/nouveau_drm.c
> +++ b/drm/nouveau/nouveau_drm.c
> @@ -49,6 +49,7 @@
>  #include "nouveau_ttm.h"
>  #include "nouveau_gem.h"
>  #include "nouveau_vga.h"
> +#include "nouveau_led.h"
>  #include "nouveau_hwmon.h"
>  #include "nouveau_acpi.h"
>  #include "nouveau_bios.h"
> @@ -468,6 +469,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
>         nouveau_hwmon_init(dev);
>         nouveau_accel_init(drm);
>         nouveau_fbcon_init(dev);
> +       nouveau_led_init(dev);
>
>         if (nouveau_runtime_pm != 0) {
>                 pm_runtime_use_autosuspend(dev->dev);
> @@ -499,6 +501,7 @@ nouveau_drm_unload(struct drm_device *dev)
>         struct nouveau_drm *drm = nouveau_drm(dev);
>
>         pm_runtime_get_sync(dev->dev);
> +       nouveau_led_fini(dev);
>         nouveau_fbcon_fini(dev);
>         nouveau_accel_fini(drm);
>         nouveau_hwmon_fini(dev);
> @@ -550,6 +553,8 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
>         struct nouveau_cli *cli;
>         int ret;
>
> +       nouveau_led_suspend(dev);
> +
>         if (dev->mode_config.num_crtc) {
>                 NV_INFO(drm, "suspending console...\n");
>                 nouveau_fbcon_set_suspend(dev, 1);
> @@ -638,6 +643,8 @@ nouveau_do_resume(struct drm_device *dev, bool runtime)
>                 nouveau_fbcon_set_suspend(dev, 0);
>         }
>
> +       nouveau_led_resume(dev);
> +
>         return 0;
>  }
>
> diff --git a/drm/nouveau/nouveau_drm.h b/drm/nouveau/nouveau_drm.h
> index 5c363ed..b148dcb 100644
> --- a/drm/nouveau/nouveau_drm.h
> +++ b/drm/nouveau/nouveau_drm.h
> @@ -166,6 +166,9 @@ struct nouveau_drm {
>         struct nouveau_hwmon *hwmon;
>         struct nouveau_debugfs *debugfs;
>
> +       /* led management */
> +       struct nouveau_led *led;
> +
>         /* display power reference */
>         bool have_disp_power_ref;
>
> diff --git a/drm/nouveau/nouveau_led.c b/drm/nouveau/nouveau_led.c
> new file mode 100644
> index 0000000..3f23ff6
> --- /dev/null
> +++ b/drm/nouveau/nouveau_led.c
> @@ -0,0 +1,131 @@
> +/*
> + * Copyright (C) 2016 Martin Peres
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial
> + * portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
> + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +/*
> + * Authors:
> + *  Martin Peres <martin.peres at free.fr>
> + */
> +
> +#include <linux/leds.h>
> +
> +#include "nouveau_drm.h"
> +#include "nouveau_led.h"
> +#include <nvkm/subdev/gpio.h>
> +
> +static enum led_brightness
> +nouveau_led_get_brightness(struct led_classdev *led)
> +{
> +       struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
> +       struct nouveau_drm *drm = nouveau_drm(drm_dev);
> +       struct nvif_object *device = &drm->device.object;
> +       u32 div, duty;
> +
> +       div =  nvif_rd32(device, 0x61c880) & 0x00ffffff;
> +       duty = nvif_rd32(device, 0x61c884) & 0x00ffffff;
> +
> +       return duty * LED_FULL / div;
> +}
> +
> +static void
> +nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value)
> +{
> +       struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
> +       struct nouveau_drm *drm = nouveau_drm(drm_dev);
> +       struct nvif_object *device = &drm->device.object;
> +
> +       u32 input_clk = 27e6; /* PDISPLAY.SOR[1].PWM is connected to the crystal */
> +       u32 freq = 100; /* this is what nvidia uses and it should be good-enough */
> +       u32 div, duty;
> +
> +       div = input_clk / freq;
> +       duty = value * div / LED_FULL;
> +
> +       /* for now, this is safe to directly poke those registers because:
> +        *  - A: nvidia never puts the logo led to any other PWM controler
> +        *       than PDISPLAY.SOR[1].PWM.
> +        *  - B: nouveau does not touch these registers anywhere else
> +        */
> +       nvif_wr32(device, 0x61c880, div);
> +       nvif_wr32(device, 0x61c884, 0xc0000000 | duty);

I feel like there should be a comment explaining the 0x40000000
(connected to crystal). Or maybe we should be in general more strict
about using constants, I feel like those hardcoded magic numbers are
hard to understand by users.

> +}
> +
> +int
> +nouveau_led_init(struct drm_device *dev)
> +{
> +       struct nouveau_drm *drm = nouveau_drm(dev);
> +       struct nvkm_gpio *gpio = nvxx_gpio(&drm->device);
> +       struct dcb_gpio_func logo_led;
> +       int ret;
> +
> +       /* check that there is a GPIO controlling the logo LED */
> +       if (nvkm_gpio_find(gpio, 0, DCB_GPIO_LOGO_LED_PWM, 0xff, &logo_led))
> +               return 0;
> +
> +       drm->led = kzalloc(sizeof(*drm->led), GFP_KERNEL);
> +       if (!drm->led)
> +               return -ENOMEM;
> +       drm->led->dev = dev;
> +
> +       drm->led->led.name = "nvidia-logo";
> +       drm->led->led.max_brightness = 255;
> +       drm->led->led.brightness_get = nouveau_led_get_brightness;
> +       drm->led->led.brightness_set = nouveau_led_set_brightness;
> +
> +       ret = led_classdev_register(dev->dev, &drm->led->led);
> +       if (ret) {
> +               kfree(drm->led);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +void
> +nouveau_led_suspend(struct drm_device *dev)
> +{
> +       struct nouveau_drm *drm = nouveau_drm(dev);
> +
> +       led_classdev_suspend(&drm->led->led);
> +}
> +
> +void
> +nouveau_led_resume(struct drm_device *dev)
> +{
> +       struct nouveau_drm *drm = nouveau_drm(dev);
> +
> +       led_classdev_resume(&drm->led->led);
> +}
> +
> +void
> +nouveau_led_fini(struct drm_device *dev)
> +{
> +       struct nouveau_drm *drm = nouveau_drm(dev);
> +
> +       if (drm->led) {
> +               led_classdev_unregister(&drm->led->led);
> +               kfree(drm->led);
> +               drm->led = NULL;
> +       }
> +}
> diff --git a/drm/nouveau/nouveau_led.h b/drm/nouveau/nouveau_led.h
> new file mode 100644
> index 0000000..6c986ba
> --- /dev/null
> +++ b/drm/nouveau/nouveau_led.h
> @@ -0,0 +1,48 @@
> +/*
> + * Copyright 2015 Martin Peres
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors: Martin Peres <martin.peres at free.fr>
> + */
> +
> +#ifndef __NOUVEAU_LED_H__
> +#define __NOUVEAU_LED_H__
> +
> +struct led_classdev;
> +
> +struct nouveau_led {
> +       struct drm_device *dev;
> +
> +       struct led_classdev led;
> +};
> +
> +static inline struct nouveau_led *
> +nouveau_led(struct drm_device *dev)
> +{
> +       return nouveau_drm(dev)->led;
> +}
> +
> +/* nouveau_led.c */
> +int  nouveau_led_init(struct drm_device *dev);
> +void nouveau_led_suspend(struct drm_device *dev);
> +void nouveau_led_resume(struct drm_device *dev);
> +void nouveau_led_fini(struct drm_device *dev);
> +
> +#endif
> --
> 2.8.0
>
> _______________________________________________
> Nouveau mailing list
> Nouveau at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/nouveau


More information about the Nouveau mailing list