linux/drivers/gpu/drm/nouveau/nouveau_led.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 Martin Peres
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining
   5 * a copy of this software and associated documentation files (the
   6 * "Software"), to deal in the Software without restriction, including
   7 * without limitation the rights to use, copy, modify, merge, publish,
   8 * distribute, sublicense, and/or sell copies of the Software, and to
   9 * permit persons to whom the Software is furnished to do so, subject to
  10 * the following conditions:
  11 *
  12 * The above copyright notice and this permission notice (including the
  13 * next paragraph) shall be included in all copies or substantial
  14 * portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  19 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
  20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23 *
  24 */
  25
  26/*
  27 * Authors:
  28 *  Martin Peres <martin.peres@free.fr>
  29 */
  30
  31#include <linux/leds.h>
  32
  33#include "nouveau_led.h"
  34#include <nvkm/subdev/gpio.h>
  35
  36static enum led_brightness
  37nouveau_led_get_brightness(struct led_classdev *led)
  38{
  39        struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
  40        struct nouveau_drm *drm = nouveau_drm(drm_dev);
  41        struct nvif_object *device = &drm->client.device.object;
  42        u32 div, duty;
  43
  44        div =  nvif_rd32(device, 0x61c880) & 0x00ffffff;
  45        duty = nvif_rd32(device, 0x61c884) & 0x00ffffff;
  46
  47        if (div > 0)
  48                return duty * LED_FULL / div;
  49        else
  50                return 0;
  51}
  52
  53static void
  54nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value)
  55{
  56        struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
  57        struct nouveau_drm *drm = nouveau_drm(drm_dev);
  58        struct nvif_object *device = &drm->client.device.object;
  59
  60        u32 input_clk = 27e6; /* PDISPLAY.SOR[1].PWM is connected to the crystal */
  61        u32 freq = 100; /* this is what nvidia uses and it should be good-enough */
  62        u32 div, duty;
  63
  64        div = input_clk / freq;
  65        duty = value * div / LED_FULL;
  66
  67        /* for now, this is safe to directly poke those registers because:
  68         *  - A: nvidia never puts the logo led to any other PWM controler
  69         *       than PDISPLAY.SOR[1].PWM.
  70         *  - B: nouveau does not touch these registers anywhere else
  71         */
  72        nvif_wr32(device, 0x61c880, div);
  73        nvif_wr32(device, 0x61c884, 0xc0000000 | duty);
  74}
  75
  76
  77int
  78nouveau_led_init(struct drm_device *dev)
  79{
  80        struct nouveau_drm *drm = nouveau_drm(dev);
  81        struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device);
  82        struct dcb_gpio_func logo_led;
  83        int ret;
  84
  85        if (!gpio)
  86                return 0;
  87
  88        /* check that there is a GPIO controlling the logo LED */
  89        if (nvkm_gpio_find(gpio, 0, DCB_GPIO_LOGO_LED_PWM, 0xff, &logo_led))
  90                return 0;
  91
  92        drm->led = kzalloc(sizeof(*drm->led), GFP_KERNEL);
  93        if (!drm->led)
  94                return -ENOMEM;
  95        drm->led->dev = dev;
  96
  97        drm->led->led.name = "nvidia-logo";
  98        drm->led->led.max_brightness = 255;
  99        drm->led->led.brightness_get = nouveau_led_get_brightness;
 100        drm->led->led.brightness_set = nouveau_led_set_brightness;
 101
 102        ret = led_classdev_register(dev->dev, &drm->led->led);
 103        if (ret) {
 104                kfree(drm->led);
 105                drm->led = NULL;
 106                return ret;
 107        }
 108
 109        return 0;
 110}
 111
 112void
 113nouveau_led_suspend(struct drm_device *dev)
 114{
 115        struct nouveau_drm *drm = nouveau_drm(dev);
 116
 117        if (drm->led)
 118                led_classdev_suspend(&drm->led->led);
 119}
 120
 121void
 122nouveau_led_resume(struct drm_device *dev)
 123{
 124        struct nouveau_drm *drm = nouveau_drm(dev);
 125
 126        if (drm->led)
 127                led_classdev_resume(&drm->led->led);
 128}
 129
 130void
 131nouveau_led_fini(struct drm_device *dev)
 132{
 133        struct nouveau_drm *drm = nouveau_drm(dev);
 134
 135        if (drm->led) {
 136                led_classdev_unregister(&drm->led->led);
 137                kfree(drm->led);
 138                drm->led = NULL;
 139        }
 140}
 141