via-pmu-backlight.c 4.6 KB

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