diff --git a/drivers/gpu/drm/nouveau/Kbuild b/drivers/gpu/drm/nouveau/Kbuild index 2527bf4ca5d9..fde6e3656636 100644 --- a/drivers/gpu/drm/nouveau/Kbuild +++ b/drivers/gpu/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/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h index a47d46dda704..b7a54e605469 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h +++ b/drivers/gpu/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/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 3100fd88a015..6adf94789417 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/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/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 822a0212cd48..c0e2b3207503 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/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/drivers/gpu/drm/nouveau/nouveau_led.c b/drivers/gpu/drm/nouveau/nouveau_led.c new file mode 100644 index 000000000000..3e2f1b6cd4df --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_led.c @@ -0,0 +1,139 @@ +/* + * 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 + */ + +#include + +#include "nouveau_led.h" +#include + +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; + + if (div > 0) + return duty * LED_FULL / div; + else + return 0; +} + +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; + + if (!gpio) + return 0; + + /* 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); + + if (drm->led) + led_classdev_suspend(&drm->led->led); +} + +void +nouveau_led_resume(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (drm->led) + 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/drivers/gpu/drm/nouveau/nouveau_led.h b/drivers/gpu/drm/nouveau/nouveau_led.h new file mode 100644 index 000000000000..187ecdb82002 --- /dev/null +++ b/drivers/gpu/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 + */ + +#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; }; +static inline void nouveau_led_suspend(struct drm_device *dev) { }; +static inline void nouveau_led_resume(struct drm_device *dev) { }; +static inline void nouveau_led_fini(struct drm_device *dev) { }; +#endif + +#endif