18d021d71SMartin Peres /* 28d021d71SMartin Peres * Copyright (C) 2016 Martin Peres 38d021d71SMartin Peres * 48d021d71SMartin Peres * Permission is hereby granted, free of charge, to any person obtaining 58d021d71SMartin Peres * a copy of this software and associated documentation files (the 68d021d71SMartin Peres * "Software"), to deal in the Software without restriction, including 78d021d71SMartin Peres * without limitation the rights to use, copy, modify, merge, publish, 88d021d71SMartin Peres * distribute, sublicense, and/or sell copies of the Software, and to 98d021d71SMartin Peres * permit persons to whom the Software is furnished to do so, subject to 108d021d71SMartin Peres * the following conditions: 118d021d71SMartin Peres * 128d021d71SMartin Peres * The above copyright notice and this permission notice (including the 138d021d71SMartin Peres * next paragraph) shall be included in all copies or substantial 148d021d71SMartin Peres * portions of the Software. 158d021d71SMartin Peres * 168d021d71SMartin Peres * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 178d021d71SMartin Peres * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 188d021d71SMartin Peres * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 198d021d71SMartin Peres * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 208d021d71SMartin Peres * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 218d021d71SMartin Peres * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 228d021d71SMartin Peres * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 238d021d71SMartin Peres * 248d021d71SMartin Peres */ 258d021d71SMartin Peres 268d021d71SMartin Peres /* 278d021d71SMartin Peres * Authors: 288d021d71SMartin Peres * Martin Peres <martin.peres@free.fr> 298d021d71SMartin Peres */ 308d021d71SMartin Peres 318d021d71SMartin Peres #include <linux/leds.h> 328d021d71SMartin Peres 338d021d71SMartin Peres #include "nouveau_led.h" 348d021d71SMartin Peres #include <nvkm/subdev/gpio.h> 358d021d71SMartin Peres 368d021d71SMartin Peres static enum led_brightness 378d021d71SMartin Peres nouveau_led_get_brightness(struct led_classdev *led) 388d021d71SMartin Peres { 398d021d71SMartin Peres struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev; 408d021d71SMartin Peres struct nouveau_drm *drm = nouveau_drm(drm_dev); 411167c6bcSBen Skeggs struct nvif_object *device = &drm->client.device.object; 428d021d71SMartin Peres u32 div, duty; 438d021d71SMartin Peres 448d021d71SMartin Peres div = nvif_rd32(device, 0x61c880) & 0x00ffffff; 458d021d71SMartin Peres duty = nvif_rd32(device, 0x61c884) & 0x00ffffff; 468d021d71SMartin Peres 478d021d71SMartin Peres if (div > 0) 488d021d71SMartin Peres return duty * LED_FULL / div; 498d021d71SMartin Peres else 508d021d71SMartin Peres return 0; 518d021d71SMartin Peres } 528d021d71SMartin Peres 538d021d71SMartin Peres static void 548d021d71SMartin Peres nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value) 558d021d71SMartin Peres { 568d021d71SMartin Peres struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev; 578d021d71SMartin Peres struct nouveau_drm *drm = nouveau_drm(drm_dev); 581167c6bcSBen Skeggs struct nvif_object *device = &drm->client.device.object; 598d021d71SMartin Peres 608d021d71SMartin Peres u32 input_clk = 27e6; /* PDISPLAY.SOR[1].PWM is connected to the crystal */ 618d021d71SMartin Peres u32 freq = 100; /* this is what nvidia uses and it should be good-enough */ 628d021d71SMartin Peres u32 div, duty; 638d021d71SMartin Peres 648d021d71SMartin Peres div = input_clk / freq; 658d021d71SMartin Peres duty = value * div / LED_FULL; 668d021d71SMartin Peres 678d021d71SMartin Peres /* for now, this is safe to directly poke those registers because: 688d021d71SMartin Peres * - A: nvidia never puts the logo led to any other PWM controler 698d021d71SMartin Peres * than PDISPLAY.SOR[1].PWM. 708d021d71SMartin Peres * - B: nouveau does not touch these registers anywhere else 718d021d71SMartin Peres */ 728d021d71SMartin Peres nvif_wr32(device, 0x61c880, div); 738d021d71SMartin Peres nvif_wr32(device, 0x61c884, 0xc0000000 | duty); 748d021d71SMartin Peres } 758d021d71SMartin Peres 768d021d71SMartin Peres 778d021d71SMartin Peres int 788d021d71SMartin Peres nouveau_led_init(struct drm_device *dev) 798d021d71SMartin Peres { 808d021d71SMartin Peres struct nouveau_drm *drm = nouveau_drm(dev); 81*6901f1d6SBen Skeggs struct nvkm_gpio *gpio = nvxx_gpio(drm); 828d021d71SMartin Peres struct dcb_gpio_func logo_led; 838d021d71SMartin Peres int ret; 848d021d71SMartin Peres 858d021d71SMartin Peres if (!gpio) 868d021d71SMartin Peres return 0; 878d021d71SMartin Peres 888d021d71SMartin Peres /* check that there is a GPIO controlling the logo LED */ 898d021d71SMartin Peres if (nvkm_gpio_find(gpio, 0, DCB_GPIO_LOGO_LED_PWM, 0xff, &logo_led)) 908d021d71SMartin Peres return 0; 918d021d71SMartin Peres 928d021d71SMartin Peres drm->led = kzalloc(sizeof(*drm->led), GFP_KERNEL); 938d021d71SMartin Peres if (!drm->led) 948d021d71SMartin Peres return -ENOMEM; 958d021d71SMartin Peres drm->led->dev = dev; 968d021d71SMartin Peres 978d021d71SMartin Peres drm->led->led.name = "nvidia-logo"; 988d021d71SMartin Peres drm->led->led.max_brightness = 255; 998d021d71SMartin Peres drm->led->led.brightness_get = nouveau_led_get_brightness; 1008d021d71SMartin Peres drm->led->led.brightness_set = nouveau_led_set_brightness; 1018d021d71SMartin Peres 1028d021d71SMartin Peres ret = led_classdev_register(dev->dev, &drm->led->led); 1038d021d71SMartin Peres if (ret) { 1048d021d71SMartin Peres kfree(drm->led); 105ca33fafdSMartin Peres drm->led = NULL; 1068d021d71SMartin Peres return ret; 1078d021d71SMartin Peres } 1088d021d71SMartin Peres 1098d021d71SMartin Peres return 0; 1108d021d71SMartin Peres } 1118d021d71SMartin Peres 1128d021d71SMartin Peres void 1138d021d71SMartin Peres nouveau_led_suspend(struct drm_device *dev) 1148d021d71SMartin Peres { 1158d021d71SMartin Peres struct nouveau_drm *drm = nouveau_drm(dev); 1168d021d71SMartin Peres 1178d021d71SMartin Peres if (drm->led) 1188d021d71SMartin Peres led_classdev_suspend(&drm->led->led); 1198d021d71SMartin Peres } 1208d021d71SMartin Peres 1218d021d71SMartin Peres void 1228d021d71SMartin Peres nouveau_led_resume(struct drm_device *dev) 1238d021d71SMartin Peres { 1248d021d71SMartin Peres struct nouveau_drm *drm = nouveau_drm(dev); 1258d021d71SMartin Peres 1268d021d71SMartin Peres if (drm->led) 1278d021d71SMartin Peres led_classdev_resume(&drm->led->led); 1288d021d71SMartin Peres } 1298d021d71SMartin Peres 1308d021d71SMartin Peres void 1318d021d71SMartin Peres nouveau_led_fini(struct drm_device *dev) 1328d021d71SMartin Peres { 1338d021d71SMartin Peres struct nouveau_drm *drm = nouveau_drm(dev); 1348d021d71SMartin Peres 1358d021d71SMartin Peres if (drm->led) { 1368d021d71SMartin Peres led_classdev_unregister(&drm->led->led); 1378d021d71SMartin Peres kfree(drm->led); 1388d021d71SMartin Peres drm->led = NULL; 1398d021d71SMartin Peres } 1408d021d71SMartin Peres } 141