On Tue, May 3, 2016 at 7:51 PM, Martin Peres <martin.pe...@free.fr> wrote: > 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.pe...@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.pe...@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);
Isn't the location of PDISP.SOR[1] different for different gens? e.g. the nv50 disp vs the nvd0 disp vs others (I don't remember the exact details). > + nvif_wr32(device, 0x61c884, 0xc0000000 | duty); > +} > + > +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.pe...@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@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau _______________________________________________ Nouveau mailing list Nouveau@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/nouveau