via-pmu-backlight.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Backlight code for via-pmu
  4. *
  5. * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
  6. * Copyright (C) 2001-2002 Benjamin Herrenschmidt
  7. * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
  8. *
  9. */
  10. #include <asm/ptrace.h>
  11. #include <linux/adb.h>
  12. #include <linux/pmu.h>
  13. #include <asm/backlight.h>
  14. #include <asm/prom.h>
  15. #define MAX_PMU_LEVEL 0xFF
  16. static const struct backlight_ops pmu_backlight_data;
  17. static DEFINE_SPINLOCK(pmu_backlight_lock);
  18. static int sleeping, uses_pmu_bl;
  19. static u8 bl_curve[FB_BACKLIGHT_LEVELS];
  20. static void pmu_backlight_init_curve(u8 off, u8 min, u8 max)
  21. {
  22. int i, flat, count, range = (max - min);
  23. bl_curve[0] = off;
  24. for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
  25. bl_curve[flat] = min;
  26. count = FB_BACKLIGHT_LEVELS * 15 / 16;
  27. for (i = 0; i < count; ++i)
  28. bl_curve[flat + i] = min + (range * (i + 1) / count);
  29. }
  30. static int pmu_backlight_curve_lookup(int value)
  31. {
  32. int level = (FB_BACKLIGHT_LEVELS - 1);
  33. int i, max = 0;
  34. /* Look for biggest value */
  35. for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
  36. max = max((int)bl_curve[i], max);
  37. /* Look for nearest value */
  38. for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
  39. int diff = abs(bl_curve[i] - value);
  40. if (diff < max) {
  41. max = diff;
  42. level = i;
  43. }
  44. }
  45. return level;
  46. }
  47. static int pmu_backlight_get_level_brightness(int level)
  48. {
  49. int pmulevel;
  50. /* Get and convert the value */
  51. pmulevel = bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL;
  52. if (pmulevel < 0)
  53. pmulevel = 0;
  54. else if (pmulevel > MAX_PMU_LEVEL)
  55. pmulevel = MAX_PMU_LEVEL;
  56. return pmulevel;
  57. }
  58. static int __pmu_backlight_update_status(struct backlight_device *bd)
  59. {
  60. struct adb_request req;
  61. int level = bd->props.brightness;
  62. if (bd->props.power != FB_BLANK_UNBLANK ||
  63. bd->props.fb_blank != FB_BLANK_UNBLANK)
  64. level = 0;
  65. if (level > 0) {
  66. int pmulevel = pmu_backlight_get_level_brightness(level);
  67. pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel);
  68. pmu_wait_complete(&req);
  69. pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
  70. PMU_POW_BACKLIGHT | PMU_POW_ON);
  71. pmu_wait_complete(&req);
  72. } else {
  73. pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
  74. PMU_POW_BACKLIGHT | PMU_POW_OFF);
  75. pmu_wait_complete(&req);
  76. }
  77. return 0;
  78. }
  79. static int pmu_backlight_update_status(struct backlight_device *bd)
  80. {
  81. unsigned long flags;
  82. int rc = 0;
  83. spin_lock_irqsave(&pmu_backlight_lock, flags);
  84. /* Don't update brightness when sleeping */
  85. if (!sleeping)
  86. rc = __pmu_backlight_update_status(bd);
  87. spin_unlock_irqrestore(&pmu_backlight_lock, flags);
  88. return rc;
  89. }
  90. static const struct backlight_ops pmu_backlight_data = {
  91. .update_status = pmu_backlight_update_status,
  92. };
  93. #ifdef CONFIG_PM
  94. void pmu_backlight_set_sleep(int sleep)
  95. {
  96. unsigned long flags;
  97. spin_lock_irqsave(&pmu_backlight_lock, flags);
  98. sleeping = sleep;
  99. if (pmac_backlight && uses_pmu_bl) {
  100. if (sleep) {
  101. struct adb_request req;
  102. pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
  103. PMU_POW_BACKLIGHT | PMU_POW_OFF);
  104. pmu_wait_complete(&req);
  105. } else
  106. __pmu_backlight_update_status(pmac_backlight);
  107. }
  108. spin_unlock_irqrestore(&pmu_backlight_lock, flags);
  109. }
  110. #endif /* CONFIG_PM */
  111. void __init pmu_backlight_init(void)
  112. {
  113. struct backlight_properties props;
  114. struct backlight_device *bd;
  115. char name[10];
  116. int level, autosave;
  117. /* Special case for the old PowerBook since I can't test on it */
  118. autosave =
  119. of_machine_is_compatible("AAPL,3400/2400") ||
  120. of_machine_is_compatible("AAPL,3500");
  121. if (!autosave &&
  122. !pmac_has_backlight_type("pmu") &&
  123. !of_machine_is_compatible("AAPL,PowerBook1998") &&
  124. !of_machine_is_compatible("PowerBook1,1"))
  125. return;
  126. snprintf(name, sizeof(name), "pmubl");
  127. memset(&props, 0, sizeof(struct backlight_properties));
  128. props.type = BACKLIGHT_PLATFORM;
  129. props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
  130. bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,
  131. &props);
  132. if (IS_ERR(bd)) {
  133. printk(KERN_ERR "PMU Backlight registration failed\n");
  134. return;
  135. }
  136. uses_pmu_bl = 1;
  137. pmu_backlight_init_curve(0x7F, 0x46, 0x0E);
  138. level = bd->props.max_brightness;
  139. if (autosave) {
  140. /* read autosaved value if available */
  141. struct adb_request req;
  142. pmu_request(&req, NULL, 2, 0xd9, 0);
  143. pmu_wait_complete(&req);
  144. level = pmu_backlight_curve_lookup(
  145. (req.reply[0] >> 4) *
  146. bd->props.max_brightness / 15);
  147. }
  148. bd->props.brightness = level;
  149. bd->props.power = FB_BLANK_UNBLANK;
  150. backlight_update_status(bd);
  151. printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);
  152. }