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