nouveau_led.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. * Authors:
  27. * Martin Peres <martin.peres@free.fr>
  28. */
  29. #include <linux/leds.h>
  30. #include "nouveau_led.h"
  31. #include <nvkm/subdev/gpio.h>
  32. static enum led_brightness
  33. nouveau_led_get_brightness(struct led_classdev *led)
  34. {
  35. struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
  36. struct nouveau_drm *drm = nouveau_drm(drm_dev);
  37. struct nvif_object *device = &drm->client.device.object;
  38. u32 div, duty;
  39. div = nvif_rd32(device, 0x61c880) & 0x00ffffff;
  40. duty = nvif_rd32(device, 0x61c884) & 0x00ffffff;
  41. if (div > 0)
  42. return duty * LED_FULL / div;
  43. else
  44. return 0;
  45. }
  46. static void
  47. nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value)
  48. {
  49. struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev;
  50. struct nouveau_drm *drm = nouveau_drm(drm_dev);
  51. struct nvif_object *device = &drm->client.device.object;
  52. u32 input_clk = 27e6; /* PDISPLAY.SOR[1].PWM is connected to the crystal */
  53. u32 freq = 100; /* this is what nvidia uses and it should be good-enough */
  54. u32 div, duty;
  55. div = input_clk / freq;
  56. duty = value * div / LED_FULL;
  57. /* for now, this is safe to directly poke those registers because:
  58. * - A: nvidia never puts the logo led to any other PWM controler
  59. * than PDISPLAY.SOR[1].PWM.
  60. * - B: nouveau does not touch these registers anywhere else
  61. */
  62. nvif_wr32(device, 0x61c880, div);
  63. nvif_wr32(device, 0x61c884, 0xc0000000 | duty);
  64. }
  65. int
  66. nouveau_led_init(struct drm_device *dev)
  67. {
  68. struct nouveau_drm *drm = nouveau_drm(dev);
  69. struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device);
  70. struct dcb_gpio_func logo_led;
  71. int ret;
  72. if (!gpio)
  73. return 0;
  74. /* check that there is a GPIO controlling the logo LED */
  75. if (nvkm_gpio_find(gpio, 0, DCB_GPIO_LOGO_LED_PWM, 0xff, &logo_led))
  76. return 0;
  77. drm->led = kzalloc(sizeof(*drm->led), GFP_KERNEL);
  78. if (!drm->led)
  79. return -ENOMEM;
  80. drm->led->dev = dev;
  81. drm->led->led.name = "nvidia-logo";
  82. drm->led->led.max_brightness = 255;
  83. drm->led->led.brightness_get = nouveau_led_get_brightness;
  84. drm->led->led.brightness_set = nouveau_led_set_brightness;
  85. ret = led_classdev_register(dev->dev, &drm->led->led);
  86. if (ret) {
  87. kfree(drm->led);
  88. drm->led = NULL;
  89. return ret;
  90. }
  91. return 0;
  92. }
  93. void
  94. nouveau_led_suspend(struct drm_device *dev)
  95. {
  96. struct nouveau_drm *drm = nouveau_drm(dev);
  97. if (drm->led)
  98. led_classdev_suspend(&drm->led->led);
  99. }
  100. void
  101. nouveau_led_resume(struct drm_device *dev)
  102. {
  103. struct nouveau_drm *drm = nouveau_drm(dev);
  104. if (drm->led)
  105. led_classdev_resume(&drm->led->led);
  106. }
  107. void
  108. nouveau_led_fini(struct drm_device *dev)
  109. {
  110. struct nouveau_drm *drm = nouveau_drm(dev);
  111. if (drm->led) {
  112. led_classdev_unregister(&drm->led->led);
  113. kfree(drm->led);
  114. drm->led = NULL;
  115. }
  116. }