Ilia Mirkin
2016-May-03 23:57 UTC
[Nouveau] [PATCH] drm/nouveau: add a LED driver for the NVIDIA logo
On Tue, May 3, 2016 at 7:51 PM, Martin Peres <martin.peres at 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.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);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.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
Martin Peres
2016-May-04 00:09 UTC
[Nouveau] [PATCH] drm/nouveau: add a LED driver for the NVIDIA logo
On 04/05/16 02:57, Ilia Mirkin wrote:> On Tue, May 3, 2016 at 7:51 PM, Martin Peres <martin.peres at 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.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); > 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).It did not change since G80, and this GPIO got added only in Fermi+.
karol herbst
2016-May-07 20:44 UTC
[Nouveau] [PATCH] drm/nouveau: add a LED driver for the NVIDIA logo
looks good, just a minor thing: You need to check for CONFIG_LEDS_CLASS, otherwise the compile throws out "warnings": WARNING: "led_classdev_register" [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] undefined! WARNING: "led_classdev_resume" [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] undefined! WARNING: "led_classdev_unregister" [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] undefined! WARNING: "led_classdev_suspend" [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] undefined! And then we may want to thing about if we want to have a nouveau config option for this to force enable LEDS_CLASS. Or maybe this isn't needed at all and we just enable this code when CONFIG_LEDS_CLASS is enabled in the kernel. Good stuff though 2016-05-04 1:51 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 */shouldn't that be like 0x27e6 ?> + 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); > +} > + > +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
Martin Peres
2016-May-07 21:25 UTC
[Nouveau] [PATCH] drm/nouveau: add a LED driver for the NVIDIA logo
On 07/05/16 23:44, karol herbst wrote:> looks good, just a minor thing: You need to check for > CONFIG_LEDS_CLASS, otherwise the compile throws out "warnings": > > WARNING: "led_classdev_register" > [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] > undefined! > WARNING: "led_classdev_resume" > [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] > undefined! > WARNING: "led_classdev_unregister" > [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] > undefined! > WARNING: "led_classdev_suspend" > [/home/karol/Dokumente/repos/nouveau/drm/nouveau/nouveau.ko] > undefined! > > And then we may want to thing about if we want to have a nouveau > config option for this to force enable LEDS_CLASS. > Or maybe this isn't needed at all and we just enable this code when > CONFIG_LEDS_CLASS is enabled in the kernel. > > Good stuff thoughOops, I had sent the following to myself, not including the ML: "It just occurred to me that I have not checked if I needed to conditionally-compile this code or not whether the LED class is available or not. I designed the code so as to be able to do it, but failed to remember. I will check and send a v2 if needed." Anyways, yeah, my plan is simply to not expose the LED if the config is not set. No need to force a dependency! I will cook up another patch tomorrow. Martin
Martin Peres
2016-Aug-22 23:39 UTC
[Nouveau] [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo
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); +} + +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
Martin Peres
2016-Aug-22 23:42 UTC
[Nouveau] [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo
v2: - guard LED framework calls with ifdef CONFIG_LEDS_CLASS Signed-off-by: Martin Peres <martin.peres at free.fr> --- For real this time! Sorry for the noise drm/nouveau/Kbuild | 1 + drm/nouveau/include/nvkm/subdev/bios/gpio.h | 1 + drm/nouveau/nouveau_drm.c | 7 ++ drm/nouveau/nouveau_drv.h | 3 + drm/nouveau/nouveau_led.c | 140 ++++++++++++++++++++++++++++ drm/nouveau/nouveau_led.h | 48 ++++++++++ 6 files changed, 200 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 66c1280..0f16652 100644 --- a/drm/nouveau/nouveau_drm.c +++ b/drm/nouveau/nouveau_drm.c @@ -47,6 +47,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" @@ -475,6 +476,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); @@ -510,6 +512,7 @@ nouveau_drm_unload(struct drm_device *dev) pm_runtime_forbid(dev->dev); } + nouveau_led_fini(dev); nouveau_fbcon_fini(dev); nouveau_accel_fini(drm); nouveau_hwmon_fini(dev); @@ -561,6 +564,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); @@ -649,6 +654,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_drv.h b/drm/nouveau/nouveau_drv.h index 822a021..c0e2b32 100644 --- a/drm/nouveau/nouveau_drv.h +++ b/drm/nouveau/nouveau_drv.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..5c19efe --- /dev/null +++ b/drm/nouveau/nouveau_led.c @@ -0,0 +1,140 @@ +/* + * 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_led.h" +#include <nvkm/subdev/gpio.h> + +#ifdef CONFIG_LEDS_CLASS +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); +} +#endif + +int +nouveau_led_init(struct drm_device *dev) +{ +#ifdef CONFIG_LEDS_CLASS + 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; + } +#endif + + return 0; +} + +void +nouveau_led_suspend(struct drm_device *dev) +{ +#ifdef CONFIG_LEDS_CLASS + struct nouveau_drm *drm = nouveau_drm(dev); + + led_classdev_suspend(&drm->led->led); +#endif +} + +void +nouveau_led_resume(struct drm_device *dev) +{ +#ifdef CONFIG_LEDS_CLASS + struct nouveau_drm *drm = nouveau_drm(dev); + + led_classdev_resume(&drm->led->led); +#endif +} + +void +nouveau_led_fini(struct drm_device *dev) +{ +#ifdef CONFIG_LEDS_CLASS + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->led) { + led_classdev_unregister(&drm->led->led); + kfree(drm->led); + drm->led = NULL; + } +#endif +} 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
Karol Herbst
2016-Aug-23 08:31 UTC
[Nouveau] [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo
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
Martin Peres
2016-Aug-23 14:06 UTC
[Nouveau] [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo
On 23/08/16 11:31, Karol Herbst wrote:> maybe it makes sense to expose the SLI LED, too. > > Regardless of my comments this patch is reviewed-by me.You reviewed the wrong patch, I should have named the re-send v3. I accidentally sent the v1 patch as a v2 :s> > 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.Well, true, but as long as it is well documented in rnndb, I think it is the proper way to go. Otherwise, we would need to maintain macros which we tried and failed at :s In this particular case, the comments at the top of the function should give all the information. We need to make the use of lookup known: $ lookup -a E4 0x61c884 0xc0000000 PDISPLAY.SOR[0x1].PWM_CTL => { DUTY = 0 | CLOCK_SELECT = UNK1 | TRIGGER } OK, I forgot to update rnnDB :s
Emil Velikov
2016-Aug-23 14:43 UTC
[Nouveau] [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo
On 23 August 2016 at 00:42, Martin Peres <martin.peres at free.fr> wrote:> v2: > - guard LED framework calls with ifdef CONFIG_LEDS_CLASS >IIRC kernel has the tendency of using static inlines in the headers when CONFIG_foo is not set. Worth using that and removing the ifdef from the source file ? -Emil
Martin Peres
2016-Aug-25 00:48 UTC
[Nouveau] [PATCH v3] drm/nouveau: add a LED driver for the NVIDIA logo
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. v2: - surround the use of the LED framework with ifdef CONFIG_LEDS_CLASS v3: - avoid using ifdefs everywhere, follow the recommendations of /doc/Documentation/CodingStyle. Suggested by Emil Velikov. Reviewed-by: Karol Herbst <karolherbst at gmail.com> 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_drv.h | 3 + drm/nouveau/nouveau_led.c | 148 ++++++++++++++++++++++++++++ drm/nouveau/nouveau_led.h | 50 ++++++++++ 6 files changed, 210 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 66c1280..0f16652 100644 --- a/drm/nouveau/nouveau_drm.c +++ b/drm/nouveau/nouveau_drm.c @@ -47,6 +47,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" @@ -475,6 +476,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); @@ -510,6 +512,7 @@ nouveau_drm_unload(struct drm_device *dev) pm_runtime_forbid(dev->dev); } + nouveau_led_fini(dev); nouveau_fbcon_fini(dev); nouveau_accel_fini(drm); nouveau_hwmon_fini(dev); @@ -561,6 +564,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); @@ -649,6 +654,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_drv.h b/drm/nouveau/nouveau_drv.h index 822a021..c0e2b32 100644 --- a/drm/nouveau/nouveau_drv.h +++ b/drm/nouveau/nouveau_drv.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..824a9c6 --- /dev/null +++ b/drm/nouveau/nouveau_led.c @@ -0,0 +1,148 @@ +/* + * 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_led.h" +#include <nvkm/subdev/gpio.h> + +/* define the led symbols if the class is not available */ +#if !IS_ENABLED(CONFIG_LEDS_CLASS) +int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) +{ return 0; }; +void led_classdev_unregister(struct led_classdev *led_cdev) { return; }; +void led_classdev_suspend(struct led_classdev *led_cdev) { return; }; +void led_classdev_resume(struct led_classdev *led_cdev) { return; }; +#endif /* !IS_ENABLED(CONFIG_LEDS_CLASS) */ + +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); +} + + +int +nouveau_led_init(struct drm_device *dev) +{ + if (IS_ENABLED(CONFIG_LEDS_CLASS)) { + 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) +{ + if (IS_ENABLED(CONFIG_LEDS_CLASS)) { + struct nouveau_drm *drm = nouveau_drm(dev); + + led_classdev_suspend(&drm->led->led); + } +} + +void +nouveau_led_resume(struct drm_device *dev) +{ + if (IS_ENABLED(CONFIG_LEDS_CLASS)) { + struct nouveau_drm *drm = nouveau_drm(dev); + + led_classdev_resume(&drm->led->led); + } +} + +void +nouveau_led_fini(struct drm_device *dev) +{ + if (IS_ENABLED(CONFIG_LEDS_CLASS)) { + 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..a14723a --- /dev/null +++ b/drm/nouveau/nouveau_led.h @@ -0,0 +1,50 @@ +/* + * 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__ + +#include "nouveau_drv.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
Martin Peres
2016-Aug-25 00:57 UTC
[Nouveau] [PATCH v3 take 2] drm/nouveau: add a LED driver for the NVIDIA logo
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. v2: - surround the use of the LED framework with ifdef CONFIG_LEDS_CLASS v3: - avoid using ifdefs everywhere, follow the recommendations of /doc/Documentation/CodingStyle. Suggested by Emil Velikov. Signed-off-by: Martin Peres <martin.peres at free.fr> --- I again managed to send the wrong patch... I guess porting patches between kernels and then scp'ing at 4am is not something I should be doing. Sorry *again* for the noise! This time, it looks sane though and actually follows what Emil suggested! drm/nouveau/Kbuild | 1 + drm/nouveau/include/nvkm/subdev/bios/gpio.h | 1 + drm/nouveau/nouveau_drm.c | 7 ++ drm/nouveau/nouveau_drv.h | 3 + drm/nouveau/nouveau_led.c | 132 ++++++++++++++++++++++++++++ drm/nouveau/nouveau_led.h | 57 ++++++++++++ 6 files changed, 201 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..fde6e36 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-$(CONFIG_LEDS_CLASS) += 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 66c1280..0f16652 100644 --- a/drm/nouveau/nouveau_drm.c +++ b/drm/nouveau/nouveau_drm.c @@ -47,6 +47,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" @@ -475,6 +476,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); @@ -510,6 +512,7 @@ nouveau_drm_unload(struct drm_device *dev) pm_runtime_forbid(dev->dev); } + nouveau_led_fini(dev); nouveau_fbcon_fini(dev); nouveau_accel_fini(drm); nouveau_hwmon_fini(dev); @@ -561,6 +564,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); @@ -649,6 +654,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_drv.h b/drm/nouveau/nouveau_drv.h index 822a021..c0e2b32 100644 --- a/drm/nouveau/nouveau_drv.h +++ b/drm/nouveau/nouveau_drv.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..9eed5a6 --- /dev/null +++ b/drm/nouveau/nouveau_led.c @@ -0,0 +1,132 @@ +/* + * 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_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); +} + + +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..750a0d9 --- /dev/null +++ b/drm/nouveau/nouveau_led.h @@ -0,0 +1,57 @@ +/* + * 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__ + +#include "nouveau_drv.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 */ +#if IS_ENABLED(CONFIG_LEDS_CLASS) +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); +#else +static inline int nouveau_led_init(struct drm_device *dev) { return 0; }; +void nouveau_led_suspend(struct drm_device *dev) { return; }; +void nouveau_led_resume(struct drm_device *dev) { return; }; +void nouveau_led_fini(struct drm_device *dev) { return; }; +#endif + +#endif -- 2.8.0
Possibly Parallel Threads
- [PATCH v2] drm/nouveau: add a LED driver for the NVIDIA logo
- [PATCH 1/3] drm/nouveau/led: don't access led subdev if it wasn't initialized
- [PATCH] drm/nouveau: fix LEDS_CLASS=m configuration
- [PATCH] drm/nouveau: fix LEDS_CLASS=m configuration
- [PATCH] drm/nouveau: fix LEDS_CLASS=m configuration