xref: /openbmc/linux/drivers/video/fbdev/aty/radeon_backlight.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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