diff options
Diffstat (limited to 'drivers/video/aty/radeon_backlight.c')
-rw-r--r-- | drivers/video/aty/radeon_backlight.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c new file mode 100644 index 000000000000..7de66b855d4e --- /dev/null +++ b/drivers/video/aty/radeon_backlight.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * Backlight code for ATI Radeon based graphic cards | ||
3 | * | ||
4 | * Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org> | ||
5 | * Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org> | ||
6 | * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include "radeonfb.h" | ||
14 | #include <linux/backlight.h> | ||
15 | |||
16 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
17 | #include <asm/backlight.h> | ||
18 | #endif | ||
19 | |||
20 | #define MAX_RADEON_LEVEL 0xFF | ||
21 | |||
22 | static struct backlight_properties radeon_bl_data; | ||
23 | |||
24 | struct radeon_bl_privdata { | ||
25 | struct radeonfb_info *rinfo; | ||
26 | uint8_t negative; | ||
27 | }; | ||
28 | |||
29 | static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata, | ||
30 | int level) | ||
31 | { | ||
32 | struct fb_info *info = pdata->rinfo->info; | ||
33 | int rlevel; | ||
34 | |||
35 | mutex_lock(&info->bl_mutex); | ||
36 | |||
37 | /* Get and convert the value */ | ||
38 | rlevel = pdata->rinfo->info->bl_curve[level] * | ||
39 | FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL; | ||
40 | |||
41 | mutex_unlock(&info->bl_mutex); | ||
42 | |||
43 | if (pdata->negative) | ||
44 | rlevel = MAX_RADEON_LEVEL - rlevel; | ||
45 | |||
46 | if (rlevel < 0) | ||
47 | rlevel = 0; | ||
48 | else if (rlevel > MAX_RADEON_LEVEL) | ||
49 | rlevel = MAX_RADEON_LEVEL; | ||
50 | |||
51 | return rlevel; | ||
52 | } | ||
53 | |||
54 | static int radeon_bl_update_status(struct backlight_device *bd) | ||
55 | { | ||
56 | struct radeon_bl_privdata *pdata = class_get_devdata(&bd->class_dev); | ||
57 | struct radeonfb_info *rinfo = pdata->rinfo; | ||
58 | u32 lvds_gen_cntl, tmpPixclksCntl; | ||
59 | int level; | ||
60 | |||
61 | if (rinfo->mon1_type != MT_LCD) | ||
62 | return 0; | ||
63 | |||
64 | /* We turn off the LCD completely instead of just dimming the | ||
65 | * backlight. This provides some greater power saving and the display | ||
66 | * is useless without backlight anyway. | ||
67 | */ | ||
68 | if (bd->props->power != FB_BLANK_UNBLANK || | ||
69 | bd->props->fb_blank != FB_BLANK_UNBLANK) | ||
70 | level = 0; | ||
71 | else | ||
72 | level = bd->props->brightness; | ||
73 | |||
74 | del_timer_sync(&rinfo->lvds_timer); | ||
75 | radeon_engine_idle(); | ||
76 | |||
77 | lvds_gen_cntl = INREG(LVDS_GEN_CNTL); | ||
78 | if (level > 0) { | ||
79 | lvds_gen_cntl &= ~LVDS_DISPLAY_DIS; | ||
80 | if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) { | ||
81 | lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON); | ||
82 | lvds_gen_cntl |= LVDS_BLON | LVDS_EN; | ||
83 | OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); | ||
84 | lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; | ||
85 | lvds_gen_cntl |= | ||
86 | (radeon_bl_get_level_brightness(pdata, level) << | ||
87 | LVDS_BL_MOD_LEVEL_SHIFT); | ||
88 | lvds_gen_cntl |= LVDS_ON; | ||
89 | lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN); | ||
90 | rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; | ||
91 | mod_timer(&rinfo->lvds_timer, | ||
92 | jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); | ||
93 | } else { | ||
94 | lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; | ||
95 | lvds_gen_cntl |= | ||
96 | (radeon_bl_get_level_brightness(pdata, level) << | ||
97 | LVDS_BL_MOD_LEVEL_SHIFT); | ||
98 | OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); | ||
99 | } | ||
100 | rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; | ||
101 | rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl | ||
102 | & LVDS_STATE_MASK; | ||
103 | } else { | ||
104 | /* Asic bug, when turning off LVDS_ON, we have to make sure | ||
105 | RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off | ||
106 | */ | ||
107 | tmpPixclksCntl = INPLL(PIXCLKS_CNTL); | ||
108 | if (rinfo->is_mobility || rinfo->is_IGP) | ||
109 | OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); | ||
110 | lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN); | ||
111 | lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) << | ||
112 | LVDS_BL_MOD_LEVEL_SHIFT); | ||
113 | lvds_gen_cntl |= LVDS_DISPLAY_DIS; | ||
114 | OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); | ||
115 | udelay(100); | ||
116 | lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN); | ||
117 | OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); | ||
118 | lvds_gen_cntl &= ~(LVDS_DIGON); | ||
119 | rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; | ||
120 | mod_timer(&rinfo->lvds_timer, | ||
121 | jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); | ||
122 | if (rinfo->is_mobility || rinfo->is_IGP) | ||
123 | OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl); | ||
124 | } | ||
125 | rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; | ||
126 | rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int radeon_bl_get_brightness(struct backlight_device *bd) | ||
132 | { | ||
133 | return bd->props->brightness; | ||
134 | } | ||
135 | |||
136 | static struct backlight_properties radeon_bl_data = { | ||
137 | .owner = THIS_MODULE, | ||
138 | .get_brightness = radeon_bl_get_brightness, | ||
139 | .update_status = radeon_bl_update_status, | ||
140 | .max_brightness = (FB_BACKLIGHT_LEVELS - 1), | ||
141 | }; | ||
142 | |||
143 | void radeonfb_bl_init(struct radeonfb_info *rinfo) | ||
144 | { | ||
145 | struct backlight_device *bd; | ||
146 | struct radeon_bl_privdata *pdata; | ||
147 | char name[12]; | ||
148 | |||
149 | if (rinfo->mon1_type != MT_LCD) | ||
150 | return; | ||
151 | |||
152 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
153 | if (!pmac_has_backlight_type("ati") && | ||
154 | !pmac_has_backlight_type("mnca")) | ||
155 | return; | ||
156 | #endif | ||
157 | |||
158 | pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL); | ||
159 | if (!pdata) { | ||
160 | printk("radeonfb: Memory allocation failed\n"); | ||
161 | goto error; | ||
162 | } | ||
163 | |||
164 | snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node); | ||
165 | |||
166 | bd = backlight_device_register(name, pdata, &radeon_bl_data); | ||
167 | if (IS_ERR(bd)) { | ||
168 | rinfo->info->bl_dev = NULL; | ||
169 | printk("radeonfb: Backlight registration failed\n"); | ||
170 | goto error; | ||
171 | } | ||
172 | |||
173 | pdata->rinfo = rinfo; | ||
174 | |||
175 | /* Pardon me for that hack... maybe some day we can figure out in what | ||
176 | * direction backlight should work on a given panel? | ||
177 | */ | ||
178 | pdata->negative = | ||
179 | (rinfo->family != CHIP_FAMILY_RV200 && | ||
180 | rinfo->family != CHIP_FAMILY_RV250 && | ||
181 | rinfo->family != CHIP_FAMILY_RV280 && | ||
182 | rinfo->family != CHIP_FAMILY_RV350); | ||
183 | |||
184 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
185 | pdata->negative = pdata->negative || | ||
186 | machine_is_compatible("PowerBook4,3") || | ||
187 | machine_is_compatible("PowerBook6,3") || | ||
188 | machine_is_compatible("PowerBook6,5"); | ||
189 | #endif | ||
190 | |||
191 | mutex_lock(&rinfo->info->bl_mutex); | ||
192 | rinfo->info->bl_dev = bd; | ||
193 | fb_bl_default_curve(rinfo->info, 0, | ||
194 | 63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL, | ||
195 | 217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL); | ||
196 | mutex_unlock(&rinfo->info->bl_mutex); | ||
197 | |||
198 | up(&bd->sem); | ||
199 | bd->props->brightness = radeon_bl_data.max_brightness; | ||
200 | bd->props->power = FB_BLANK_UNBLANK; | ||
201 | bd->props->update_status(bd); | ||
202 | down(&bd->sem); | ||
203 | |||
204 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
205 | mutex_lock(&pmac_backlight_mutex); | ||
206 | if (!pmac_backlight) | ||
207 | pmac_backlight = bd; | ||
208 | mutex_unlock(&pmac_backlight_mutex); | ||
209 | #endif | ||
210 | |||
211 | printk("radeonfb: Backlight initialized (%s)\n", name); | ||
212 | |||
213 | return; | ||
214 | |||
215 | error: | ||
216 | kfree(pdata); | ||
217 | return; | ||
218 | } | ||
219 | |||
220 | void radeonfb_bl_exit(struct radeonfb_info *rinfo) | ||
221 | { | ||
222 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
223 | mutex_lock(&pmac_backlight_mutex); | ||
224 | #endif | ||
225 | |||
226 | mutex_lock(&rinfo->info->bl_mutex); | ||
227 | if (rinfo->info->bl_dev) { | ||
228 | struct radeon_bl_privdata *pdata; | ||
229 | |||
230 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
231 | if (pmac_backlight == rinfo->info->bl_dev) | ||
232 | pmac_backlight = NULL; | ||
233 | #endif | ||
234 | |||
235 | pdata = class_get_devdata(&rinfo->info->bl_dev->class_dev); | ||
236 | backlight_device_unregister(rinfo->info->bl_dev); | ||
237 | kfree(pdata); | ||
238 | rinfo->info->bl_dev = NULL; | ||
239 | |||
240 | printk("radeonfb: Backlight unloaded\n"); | ||
241 | } | ||
242 | mutex_unlock(&rinfo->info->bl_mutex); | ||
243 | |||
244 | #ifdef CONFIG_PMAC_BACKLIGHT | ||
245 | mutex_unlock(&pmac_backlight_mutex); | ||
246 | #endif | ||
247 | } | ||