2 * Backlight code for ATI Radeon based graphic cards
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>
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.
14 #include <linux/backlight.h>
15 #include <linux/slab.h>
17 #ifdef CONFIG_PMAC_BACKLIGHT
18 #include <asm/backlight.h>
21 #define MAX_RADEON_LEVEL 0xFF
23 struct radeon_bl_privdata
{
24 struct radeonfb_info
*rinfo
;
28 static int radeon_bl_get_level_brightness(struct radeon_bl_privdata
*pdata
,
33 /* Get and convert the value */
34 /* No locking of bl_curve since we read a single value */
35 rlevel
= pdata
->rinfo
->info
->bl_curve
[level
] *
36 FB_BACKLIGHT_MAX
/ MAX_RADEON_LEVEL
;
40 else if (rlevel
> MAX_RADEON_LEVEL
)
41 rlevel
= MAX_RADEON_LEVEL
;
44 rlevel
= MAX_RADEON_LEVEL
- rlevel
;
49 static int radeon_bl_update_status(struct backlight_device
*bd
)
51 struct radeon_bl_privdata
*pdata
= bl_get_data(bd
);
52 struct radeonfb_info
*rinfo
= pdata
->rinfo
;
53 u32 lvds_gen_cntl
, tmpPixclksCntl
;
56 if (rinfo
->mon1_type
!= MT_LCD
)
59 /* We turn off the LCD completely instead of just dimming the
60 * backlight. This provides some greater power saving and the display
61 * is useless without backlight anyway.
63 if (bd
->props
.power
!= FB_BLANK_UNBLANK
||
64 bd
->props
.fb_blank
!= FB_BLANK_UNBLANK
)
67 level
= bd
->props
.brightness
;
69 del_timer_sync(&rinfo
->lvds_timer
);
72 lvds_gen_cntl
= INREG(LVDS_GEN_CNTL
);
74 lvds_gen_cntl
&= ~LVDS_DISPLAY_DIS
;
75 if (!(lvds_gen_cntl
& LVDS_BLON
) || !(lvds_gen_cntl
& LVDS_ON
)) {
76 lvds_gen_cntl
|= (rinfo
->init_state
.lvds_gen_cntl
& LVDS_DIGON
);
77 lvds_gen_cntl
|= LVDS_BLON
| LVDS_EN
;
78 OUTREG(LVDS_GEN_CNTL
, lvds_gen_cntl
);
79 lvds_gen_cntl
&= ~LVDS_BL_MOD_LEVEL_MASK
;
81 (radeon_bl_get_level_brightness(pdata
, level
) <<
82 LVDS_BL_MOD_LEVEL_SHIFT
);
83 lvds_gen_cntl
|= LVDS_ON
;
84 lvds_gen_cntl
|= (rinfo
->init_state
.lvds_gen_cntl
& LVDS_BL_MOD_EN
);
85 rinfo
->pending_lvds_gen_cntl
= lvds_gen_cntl
;
86 mod_timer(&rinfo
->lvds_timer
,
87 jiffies
+ msecs_to_jiffies(rinfo
->panel_info
.pwr_delay
));
89 lvds_gen_cntl
&= ~LVDS_BL_MOD_LEVEL_MASK
;
91 (radeon_bl_get_level_brightness(pdata
, level
) <<
92 LVDS_BL_MOD_LEVEL_SHIFT
);
93 OUTREG(LVDS_GEN_CNTL
, lvds_gen_cntl
);
95 rinfo
->init_state
.lvds_gen_cntl
&= ~LVDS_STATE_MASK
;
96 rinfo
->init_state
.lvds_gen_cntl
|= rinfo
->pending_lvds_gen_cntl
99 /* Asic bug, when turning off LVDS_ON, we have to make sure
100 RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
102 tmpPixclksCntl
= INPLL(PIXCLKS_CNTL
);
103 if (rinfo
->is_mobility
|| rinfo
->is_IGP
)
104 OUTPLLP(PIXCLKS_CNTL
, 0, ~PIXCLK_LVDS_ALWAYS_ONb
);
105 lvds_gen_cntl
&= ~(LVDS_BL_MOD_LEVEL_MASK
| LVDS_BL_MOD_EN
);
106 lvds_gen_cntl
|= (radeon_bl_get_level_brightness(pdata
, 0) <<
107 LVDS_BL_MOD_LEVEL_SHIFT
);
108 lvds_gen_cntl
|= LVDS_DISPLAY_DIS
;
109 OUTREG(LVDS_GEN_CNTL
, lvds_gen_cntl
);
111 lvds_gen_cntl
&= ~(LVDS_ON
| LVDS_EN
);
112 OUTREG(LVDS_GEN_CNTL
, lvds_gen_cntl
);
113 lvds_gen_cntl
&= ~(LVDS_DIGON
);
114 rinfo
->pending_lvds_gen_cntl
= lvds_gen_cntl
;
115 mod_timer(&rinfo
->lvds_timer
,
116 jiffies
+ msecs_to_jiffies(rinfo
->panel_info
.pwr_delay
));
117 if (rinfo
->is_mobility
|| rinfo
->is_IGP
)
118 OUTPLL(PIXCLKS_CNTL
, tmpPixclksCntl
);
120 rinfo
->init_state
.lvds_gen_cntl
&= ~LVDS_STATE_MASK
;
121 rinfo
->init_state
.lvds_gen_cntl
|= (lvds_gen_cntl
& LVDS_STATE_MASK
);
126 static int radeon_bl_get_brightness(struct backlight_device
*bd
)
128 return bd
->props
.brightness
;
131 static const struct backlight_ops radeon_bl_data
= {
132 .get_brightness
= radeon_bl_get_brightness
,
133 .update_status
= radeon_bl_update_status
,
136 void radeonfb_bl_init(struct radeonfb_info
*rinfo
)
138 struct backlight_properties props
;
139 struct backlight_device
*bd
;
140 struct radeon_bl_privdata
*pdata
;
143 if (rinfo
->mon1_type
!= MT_LCD
)
146 #ifdef CONFIG_PMAC_BACKLIGHT
147 if (!pmac_has_backlight_type("ati") &&
148 !pmac_has_backlight_type("mnca"))
152 pdata
= kmalloc(sizeof(struct radeon_bl_privdata
), GFP_KERNEL
);
154 printk("radeonfb: Memory allocation failed\n");
158 snprintf(name
, sizeof(name
), "radeonbl%d", rinfo
->info
->node
);
160 memset(&props
, 0, sizeof(struct backlight_properties
));
161 props
.type
= BACKLIGHT_RAW
;
162 props
.max_brightness
= FB_BACKLIGHT_LEVELS
- 1;
163 bd
= backlight_device_register(name
, rinfo
->info
->dev
, pdata
,
164 &radeon_bl_data
, &props
);
166 rinfo
->info
->bl_dev
= NULL
;
167 printk("radeonfb: Backlight registration failed\n");
171 pdata
->rinfo
= rinfo
;
173 /* Pardon me for that hack... maybe some day we can figure out in what
174 * direction backlight should work on a given panel?
177 (rinfo
->family
!= CHIP_FAMILY_RV200
&&
178 rinfo
->family
!= CHIP_FAMILY_RV250
&&
179 rinfo
->family
!= CHIP_FAMILY_RV280
&&
180 rinfo
->family
!= CHIP_FAMILY_RV350
);
182 #ifdef CONFIG_PMAC_BACKLIGHT
183 pdata
->negative
= pdata
->negative
||
184 of_machine_is_compatible("PowerBook4,3") ||
185 of_machine_is_compatible("PowerBook6,3") ||
186 of_machine_is_compatible("PowerBook6,5");
189 rinfo
->info
->bl_dev
= bd
;
190 fb_bl_default_curve(rinfo
->info
, 0,
191 63 * FB_BACKLIGHT_MAX
/ MAX_RADEON_LEVEL
,
192 217 * FB_BACKLIGHT_MAX
/ MAX_RADEON_LEVEL
);
194 bd
->props
.brightness
= bd
->props
.max_brightness
;
195 bd
->props
.power
= FB_BLANK_UNBLANK
;
196 backlight_update_status(bd
);
198 printk("radeonfb: Backlight initialized (%s)\n", name
);
207 void radeonfb_bl_exit(struct radeonfb_info
*rinfo
)
209 struct backlight_device
*bd
= rinfo
->info
->bl_dev
;
212 struct radeon_bl_privdata
*pdata
;
214 pdata
= bl_get_data(bd
);
215 backlight_device_unregister(bd
);
217 rinfo
->info
->bl_dev
= NULL
;
219 printk("radeonfb: Backlight unloaded\n");