Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
via-pmu-backlight.c
Go to the documentation of this file.
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 <[email protected]>
7  *
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 
16 #define MAX_PMU_LEVEL 0xFF
17 
18 static const struct backlight_ops pmu_backlight_data;
19 static DEFINE_SPINLOCK(pmu_backlight_lock);
20 static int sleeping, uses_pmu_bl;
21 static u8 bl_curve[FB_BACKLIGHT_LEVELS];
22 
23 static void pmu_backlight_init_curve(u8 off, u8 min, u8 max)
24 {
25  int i, flat, count, range = (max - min);
26 
27  bl_curve[0] = off;
28 
29  for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
30  bl_curve[flat] = min;
31 
32  count = FB_BACKLIGHT_LEVELS * 15 / 16;
33  for (i = 0; i < count; ++i)
34  bl_curve[flat + i] = min + (range * (i + 1) / count);
35 }
36 
37 static int pmu_backlight_curve_lookup(int value)
38 {
39  int level = (FB_BACKLIGHT_LEVELS - 1);
40  int i, max = 0;
41 
42  /* Look for biggest value */
43  for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
44  max = max((int)bl_curve[i], max);
45 
46  /* Look for nearest value */
47  for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
48  int diff = abs(bl_curve[i] - value);
49  if (diff < max) {
50  max = diff;
51  level = i;
52  }
53  }
54  return level;
55 }
56 
57 static int pmu_backlight_get_level_brightness(int level)
58 {
59  int pmulevel;
60 
61  /* Get and convert the value */
62  pmulevel = bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL;
63  if (pmulevel < 0)
64  pmulevel = 0;
65  else if (pmulevel > MAX_PMU_LEVEL)
66  pmulevel = MAX_PMU_LEVEL;
67 
68  return pmulevel;
69 }
70 
71 static int __pmu_backlight_update_status(struct backlight_device *bd)
72 {
73  struct adb_request req;
74  int level = bd->props.brightness;
75 
76 
77  if (bd->props.power != FB_BLANK_UNBLANK ||
78  bd->props.fb_blank != FB_BLANK_UNBLANK)
79  level = 0;
80 
81  if (level > 0) {
82  int pmulevel = pmu_backlight_get_level_brightness(level);
83 
84  pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel);
86 
90  } else {
94  }
95 
96  return 0;
97 }
98 
99 static int pmu_backlight_update_status(struct backlight_device *bd)
100 {
101  unsigned long flags;
102  int rc = 0;
103 
104  spin_lock_irqsave(&pmu_backlight_lock, flags);
105  /* Don't update brightness when sleeping */
106  if (!sleeping)
107  rc = __pmu_backlight_update_status(bd);
108  spin_unlock_irqrestore(&pmu_backlight_lock, flags);
109  return rc;
110 }
111 
112 
113 static int pmu_backlight_get_brightness(struct backlight_device *bd)
114 {
115  return bd->props.brightness;
116 }
117 
118 static const struct backlight_ops pmu_backlight_data = {
119  .get_brightness = pmu_backlight_get_brightness,
120  .update_status = pmu_backlight_update_status,
121 
122 };
123 
124 #ifdef CONFIG_PM
125 void pmu_backlight_set_sleep(int sleep)
126 {
127  unsigned long flags;
128 
129  spin_lock_irqsave(&pmu_backlight_lock, flags);
130  sleeping = sleep;
131  if (pmac_backlight && uses_pmu_bl) {
132  if (sleep) {
133  struct adb_request req;
134 
138  } else
139  __pmu_backlight_update_status(pmac_backlight);
140  }
141  spin_unlock_irqrestore(&pmu_backlight_lock, flags);
142 }
143 #endif /* CONFIG_PM */
144 
146 {
147  struct backlight_properties props;
148  struct backlight_device *bd;
149  char name[10];
150  int level, autosave;
151 
152  /* Special case for the old PowerBook since I can't test on it */
153  autosave =
154  of_machine_is_compatible("AAPL,3400/2400") ||
155  of_machine_is_compatible("AAPL,3500");
156 
157  if (!autosave &&
158  !pmac_has_backlight_type("pmu") &&
159  !of_machine_is_compatible("AAPL,PowerBook1998") &&
160  !of_machine_is_compatible("PowerBook1,1"))
161  return;
162 
163  snprintf(name, sizeof(name), "pmubl");
164 
165  memset(&props, 0, sizeof(struct backlight_properties));
166  props.type = BACKLIGHT_PLATFORM;
167  props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
168  bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,
169  &props);
170  if (IS_ERR(bd)) {
171  printk(KERN_ERR "PMU Backlight registration failed\n");
172  return;
173  }
174  uses_pmu_bl = 1;
175  pmu_backlight_init_curve(0x7F, 0x46, 0x0E);
176 
177  level = bd->props.max_brightness;
178 
179  if (autosave) {
180  /* read autosaved value if available */
181  struct adb_request req;
182  pmu_request(&req, NULL, 2, 0xd9, 0);
183  pmu_wait_complete(&req);
184 
185  level = pmu_backlight_curve_lookup(
186  (req.reply[0] >> 4) *
187  bd->props.max_brightness / 15);
188  }
189 
190  bd->props.brightness = level;
191  bd->props.power = FB_BLANK_UNBLANK;
192  backlight_update_status(bd);
193 
194  printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);
195 }