1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen * Backlight code for ATI Radeon based graphic cards
4f7018c21STomi Valkeinen *
5f7018c21STomi Valkeinen * Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org>
6f7018c21STomi Valkeinen * Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
7f7018c21STomi Valkeinen * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
8f7018c21STomi Valkeinen */
9f7018c21STomi Valkeinen
10f7018c21STomi Valkeinen #include "radeonfb.h"
11f7018c21STomi Valkeinen #include <linux/backlight.h>
12f7018c21STomi Valkeinen #include <linux/slab.h>
13f7018c21STomi Valkeinen
14f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
15f7018c21STomi Valkeinen #include <asm/backlight.h>
16f7018c21STomi Valkeinen #endif
17f7018c21STomi Valkeinen
18f7018c21STomi Valkeinen #define MAX_RADEON_LEVEL 0xFF
19f7018c21STomi Valkeinen
20f7018c21STomi Valkeinen struct radeon_bl_privdata {
21f7018c21STomi Valkeinen struct radeonfb_info *rinfo;
22f7018c21STomi Valkeinen uint8_t negative;
23f7018c21STomi Valkeinen };
24f7018c21STomi Valkeinen
radeon_bl_get_level_brightness(struct radeon_bl_privdata * pdata,int level)25f7018c21STomi Valkeinen static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata,
26f7018c21STomi Valkeinen int level)
27f7018c21STomi Valkeinen {
28f7018c21STomi Valkeinen int rlevel;
29f7018c21STomi Valkeinen
30f7018c21STomi Valkeinen /* Get and convert the value */
31f7018c21STomi Valkeinen /* No locking of bl_curve since we read a single value */
32f7018c21STomi Valkeinen rlevel = pdata->rinfo->info->bl_curve[level] *
33f7018c21STomi Valkeinen FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL;
34f7018c21STomi Valkeinen
35f7018c21STomi Valkeinen if (rlevel < 0)
36f7018c21STomi Valkeinen rlevel = 0;
37f7018c21STomi Valkeinen else if (rlevel > MAX_RADEON_LEVEL)
38f7018c21STomi Valkeinen rlevel = MAX_RADEON_LEVEL;
39f7018c21STomi Valkeinen
40f7018c21STomi Valkeinen if (pdata->negative)
41f7018c21STomi Valkeinen rlevel = MAX_RADEON_LEVEL - rlevel;
42f7018c21STomi Valkeinen
43f7018c21STomi Valkeinen return rlevel;
44f7018c21STomi Valkeinen }
45f7018c21STomi Valkeinen
radeon_bl_update_status(struct backlight_device * bd)46f7018c21STomi Valkeinen static int radeon_bl_update_status(struct backlight_device *bd)
47f7018c21STomi Valkeinen {
48f7018c21STomi Valkeinen struct radeon_bl_privdata *pdata = bl_get_data(bd);
49f7018c21STomi Valkeinen struct radeonfb_info *rinfo = pdata->rinfo;
50f7018c21STomi Valkeinen u32 lvds_gen_cntl, tmpPixclksCntl;
51f7018c21STomi Valkeinen int level;
52f7018c21STomi Valkeinen
53f7018c21STomi Valkeinen if (rinfo->mon1_type != MT_LCD)
54f7018c21STomi Valkeinen return 0;
55f7018c21STomi Valkeinen
56f7018c21STomi Valkeinen /* We turn off the LCD completely instead of just dimming the
57f7018c21STomi Valkeinen * backlight. This provides some greater power saving and the display
58f7018c21STomi Valkeinen * is useless without backlight anyway.
59f7018c21STomi Valkeinen */
60c28509efSStephen Kitt level = backlight_get_brightness(bd);
61f7018c21STomi Valkeinen
62f7018c21STomi Valkeinen del_timer_sync(&rinfo->lvds_timer);
63f7018c21STomi Valkeinen radeon_engine_idle();
64f7018c21STomi Valkeinen
65f7018c21STomi Valkeinen lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
66f7018c21STomi Valkeinen if (level > 0) {
67f7018c21STomi Valkeinen lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
68f7018c21STomi Valkeinen if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
69f7018c21STomi Valkeinen lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
70f7018c21STomi Valkeinen lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
71f7018c21STomi Valkeinen OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
72f7018c21STomi Valkeinen lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
73f7018c21STomi Valkeinen lvds_gen_cntl |=
74f7018c21STomi Valkeinen (radeon_bl_get_level_brightness(pdata, level) <<
75f7018c21STomi Valkeinen LVDS_BL_MOD_LEVEL_SHIFT);
76f7018c21STomi Valkeinen lvds_gen_cntl |= LVDS_ON;
77f7018c21STomi Valkeinen lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
78f7018c21STomi Valkeinen rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
79f7018c21STomi Valkeinen mod_timer(&rinfo->lvds_timer,
80f7018c21STomi Valkeinen jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
81f7018c21STomi Valkeinen } else {
82f7018c21STomi Valkeinen lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
83f7018c21STomi Valkeinen lvds_gen_cntl |=
84f7018c21STomi Valkeinen (radeon_bl_get_level_brightness(pdata, level) <<
85f7018c21STomi Valkeinen LVDS_BL_MOD_LEVEL_SHIFT);
86f7018c21STomi Valkeinen OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
87f7018c21STomi Valkeinen }
88f7018c21STomi Valkeinen rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
89f7018c21STomi Valkeinen rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
90f7018c21STomi Valkeinen & LVDS_STATE_MASK;
91f7018c21STomi Valkeinen } else {
92f7018c21STomi Valkeinen /* Asic bug, when turning off LVDS_ON, we have to make sure
93f7018c21STomi Valkeinen RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
94f7018c21STomi Valkeinen */
95f7018c21STomi Valkeinen tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
96f7018c21STomi Valkeinen if (rinfo->is_mobility || rinfo->is_IGP)
97f7018c21STomi Valkeinen OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
98f7018c21STomi Valkeinen lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
99f7018c21STomi Valkeinen lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) <<
100f7018c21STomi Valkeinen LVDS_BL_MOD_LEVEL_SHIFT);
101f7018c21STomi Valkeinen lvds_gen_cntl |= LVDS_DISPLAY_DIS;
102f7018c21STomi Valkeinen OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
103f7018c21STomi Valkeinen udelay(100);
104f7018c21STomi Valkeinen lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
105f7018c21STomi Valkeinen OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
106f7018c21STomi Valkeinen lvds_gen_cntl &= ~(LVDS_DIGON);
107f7018c21STomi Valkeinen rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
108f7018c21STomi Valkeinen mod_timer(&rinfo->lvds_timer,
109f7018c21STomi Valkeinen jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
110f7018c21STomi Valkeinen if (rinfo->is_mobility || rinfo->is_IGP)
111f7018c21STomi Valkeinen OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
112f7018c21STomi Valkeinen }
113f7018c21STomi Valkeinen rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
114f7018c21STomi Valkeinen rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
115f7018c21STomi Valkeinen
116f7018c21STomi Valkeinen return 0;
117f7018c21STomi Valkeinen }
118f7018c21STomi Valkeinen
119f7018c21STomi Valkeinen static const struct backlight_ops radeon_bl_data = {
120f7018c21STomi Valkeinen .update_status = radeon_bl_update_status,
121f7018c21STomi Valkeinen };
122f7018c21STomi Valkeinen
radeonfb_bl_init(struct radeonfb_info * rinfo)123f7018c21STomi Valkeinen void radeonfb_bl_init(struct radeonfb_info *rinfo)
124f7018c21STomi Valkeinen {
125f7018c21STomi Valkeinen struct backlight_properties props;
126f7018c21STomi Valkeinen struct backlight_device *bd;
127f7018c21STomi Valkeinen struct radeon_bl_privdata *pdata;
128f7018c21STomi Valkeinen char name[12];
129f7018c21STomi Valkeinen
130f7018c21STomi Valkeinen if (rinfo->mon1_type != MT_LCD)
131f7018c21STomi Valkeinen return;
132f7018c21STomi Valkeinen
133f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
134f7018c21STomi Valkeinen if (!pmac_has_backlight_type("ati") &&
135f7018c21STomi Valkeinen !pmac_has_backlight_type("mnca"))
136f7018c21STomi Valkeinen return;
137f7018c21STomi Valkeinen #endif
138f7018c21STomi Valkeinen
139f7018c21STomi Valkeinen pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL);
140f7018c21STomi Valkeinen if (!pdata) {
141f7018c21STomi Valkeinen printk("radeonfb: Memory allocation failed\n");
142f7018c21STomi Valkeinen goto error;
143f7018c21STomi Valkeinen }
144f7018c21STomi Valkeinen
145f7018c21STomi Valkeinen snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node);
146f7018c21STomi Valkeinen
147f7018c21STomi Valkeinen memset(&props, 0, sizeof(struct backlight_properties));
148f7018c21STomi Valkeinen props.type = BACKLIGHT_RAW;
149f7018c21STomi Valkeinen props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
150*e7df4a9bSThomas Zimmermann bd = backlight_device_register(name, rinfo->info->device, pdata,
151f7018c21STomi Valkeinen &radeon_bl_data, &props);
152f7018c21STomi Valkeinen if (IS_ERR(bd)) {
153f7018c21STomi Valkeinen rinfo->info->bl_dev = NULL;
154f7018c21STomi Valkeinen printk("radeonfb: Backlight registration failed\n");
155f7018c21STomi Valkeinen goto error;
156f7018c21STomi Valkeinen }
157f7018c21STomi Valkeinen
158f7018c21STomi Valkeinen pdata->rinfo = rinfo;
159f7018c21STomi Valkeinen
160f7018c21STomi Valkeinen /* Pardon me for that hack... maybe some day we can figure out in what
161f7018c21STomi Valkeinen * direction backlight should work on a given panel?
162f7018c21STomi Valkeinen */
163f7018c21STomi Valkeinen pdata->negative =
164f7018c21STomi Valkeinen (rinfo->family != CHIP_FAMILY_RV200 &&
165f7018c21STomi Valkeinen rinfo->family != CHIP_FAMILY_RV250 &&
166f7018c21STomi Valkeinen rinfo->family != CHIP_FAMILY_RV280 &&
167f7018c21STomi Valkeinen rinfo->family != CHIP_FAMILY_RV350);
168f7018c21STomi Valkeinen
169f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
170f7018c21STomi Valkeinen pdata->negative = pdata->negative ||
171f7018c21STomi Valkeinen of_machine_is_compatible("PowerBook4,3") ||
172f7018c21STomi Valkeinen of_machine_is_compatible("PowerBook6,3") ||
173f7018c21STomi Valkeinen of_machine_is_compatible("PowerBook6,5");
174f7018c21STomi Valkeinen #endif
175f7018c21STomi Valkeinen
176f7018c21STomi Valkeinen rinfo->info->bl_dev = bd;
177f7018c21STomi Valkeinen fb_bl_default_curve(rinfo->info, 0,
178f7018c21STomi Valkeinen 63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL,
179f7018c21STomi Valkeinen 217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL);
180f7018c21STomi Valkeinen
181f7018c21STomi Valkeinen bd->props.brightness = bd->props.max_brightness;
182f7018c21STomi Valkeinen bd->props.power = FB_BLANK_UNBLANK;
183f7018c21STomi Valkeinen backlight_update_status(bd);
184f7018c21STomi Valkeinen
185f7018c21STomi Valkeinen printk("radeonfb: Backlight initialized (%s)\n", name);
186f7018c21STomi Valkeinen
187f7018c21STomi Valkeinen return;
188f7018c21STomi Valkeinen
189f7018c21STomi Valkeinen error:
190f7018c21STomi Valkeinen kfree(pdata);
191f7018c21STomi Valkeinen return;
192f7018c21STomi Valkeinen }
193f7018c21STomi Valkeinen
radeonfb_bl_exit(struct radeonfb_info * rinfo)194f7018c21STomi Valkeinen void radeonfb_bl_exit(struct radeonfb_info *rinfo)
195f7018c21STomi Valkeinen {
196f7018c21STomi Valkeinen struct backlight_device *bd = rinfo->info->bl_dev;
197f7018c21STomi Valkeinen
198f7018c21STomi Valkeinen if (bd) {
199f7018c21STomi Valkeinen struct radeon_bl_privdata *pdata;
200f7018c21STomi Valkeinen
201f7018c21STomi Valkeinen pdata = bl_get_data(bd);
202f7018c21STomi Valkeinen backlight_device_unregister(bd);
203f7018c21STomi Valkeinen kfree(pdata);
204f7018c21STomi Valkeinen rinfo->info->bl_dev = NULL;
205f7018c21STomi Valkeinen
206f7018c21STomi Valkeinen printk("radeonfb: Backlight unloaded\n");
207f7018c21STomi Valkeinen }
208f7018c21STomi Valkeinen }
209