162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Backlight code for ATI Radeon based graphic cards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org>
662306a36Sopenharmony_ci * Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
762306a36Sopenharmony_ci * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "radeonfb.h"
1162306a36Sopenharmony_ci#include <linux/backlight.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
1562306a36Sopenharmony_ci#include <asm/backlight.h>
1662306a36Sopenharmony_ci#endif
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define MAX_RADEON_LEVEL 0xFF
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct radeon_bl_privdata {
2162306a36Sopenharmony_ci	struct radeonfb_info *rinfo;
2262306a36Sopenharmony_ci	uint8_t negative;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata,
2662306a36Sopenharmony_ci		int level)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	int rlevel;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/* Get and convert the value */
3162306a36Sopenharmony_ci	/* No locking of bl_curve since we read a single value */
3262306a36Sopenharmony_ci	rlevel = pdata->rinfo->info->bl_curve[level] *
3362306a36Sopenharmony_ci		 FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (rlevel < 0)
3662306a36Sopenharmony_ci		rlevel = 0;
3762306a36Sopenharmony_ci	else if (rlevel > MAX_RADEON_LEVEL)
3862306a36Sopenharmony_ci		rlevel = MAX_RADEON_LEVEL;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (pdata->negative)
4162306a36Sopenharmony_ci		rlevel = MAX_RADEON_LEVEL - rlevel;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return rlevel;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int radeon_bl_update_status(struct backlight_device *bd)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct radeon_bl_privdata *pdata = bl_get_data(bd);
4962306a36Sopenharmony_ci	struct radeonfb_info *rinfo = pdata->rinfo;
5062306a36Sopenharmony_ci	u32 lvds_gen_cntl, tmpPixclksCntl;
5162306a36Sopenharmony_ci	int level;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (rinfo->mon1_type != MT_LCD)
5462306a36Sopenharmony_ci		return 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* We turn off the LCD completely instead of just dimming the
5762306a36Sopenharmony_ci	 * backlight. This provides some greater power saving and the display
5862306a36Sopenharmony_ci	 * is useless without backlight anyway.
5962306a36Sopenharmony_ci	 */
6062306a36Sopenharmony_ci	level = backlight_get_brightness(bd);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	del_timer_sync(&rinfo->lvds_timer);
6362306a36Sopenharmony_ci	radeon_engine_idle();
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
6662306a36Sopenharmony_ci	if (level > 0) {
6762306a36Sopenharmony_ci		lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
6862306a36Sopenharmony_ci		if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
6962306a36Sopenharmony_ci			lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
7062306a36Sopenharmony_ci			lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
7162306a36Sopenharmony_ci			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
7262306a36Sopenharmony_ci			lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
7362306a36Sopenharmony_ci			lvds_gen_cntl |=
7462306a36Sopenharmony_ci				(radeon_bl_get_level_brightness(pdata, level) <<
7562306a36Sopenharmony_ci				 LVDS_BL_MOD_LEVEL_SHIFT);
7662306a36Sopenharmony_ci			lvds_gen_cntl |= LVDS_ON;
7762306a36Sopenharmony_ci			lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
7862306a36Sopenharmony_ci			rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
7962306a36Sopenharmony_ci			mod_timer(&rinfo->lvds_timer,
8062306a36Sopenharmony_ci				  jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
8162306a36Sopenharmony_ci		} else {
8262306a36Sopenharmony_ci			lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
8362306a36Sopenharmony_ci			lvds_gen_cntl |=
8462306a36Sopenharmony_ci				(radeon_bl_get_level_brightness(pdata, level) <<
8562306a36Sopenharmony_ci				 LVDS_BL_MOD_LEVEL_SHIFT);
8662306a36Sopenharmony_ci			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci		rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
8962306a36Sopenharmony_ci		rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
9062306a36Sopenharmony_ci			& LVDS_STATE_MASK;
9162306a36Sopenharmony_ci	} else {
9262306a36Sopenharmony_ci		/* Asic bug, when turning off LVDS_ON, we have to make sure
9362306a36Sopenharmony_ci		   RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
9462306a36Sopenharmony_ci		*/
9562306a36Sopenharmony_ci		tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
9662306a36Sopenharmony_ci		if (rinfo->is_mobility || rinfo->is_IGP)
9762306a36Sopenharmony_ci			OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
9862306a36Sopenharmony_ci		lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
9962306a36Sopenharmony_ci		lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) <<
10062306a36Sopenharmony_ci				  LVDS_BL_MOD_LEVEL_SHIFT);
10162306a36Sopenharmony_ci		lvds_gen_cntl |= LVDS_DISPLAY_DIS;
10262306a36Sopenharmony_ci		OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
10362306a36Sopenharmony_ci		udelay(100);
10462306a36Sopenharmony_ci		lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
10562306a36Sopenharmony_ci		OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
10662306a36Sopenharmony_ci		lvds_gen_cntl &= ~(LVDS_DIGON);
10762306a36Sopenharmony_ci		rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
10862306a36Sopenharmony_ci		mod_timer(&rinfo->lvds_timer,
10962306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
11062306a36Sopenharmony_ci		if (rinfo->is_mobility || rinfo->is_IGP)
11162306a36Sopenharmony_ci			OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
11462306a36Sopenharmony_ci	rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic const struct backlight_ops radeon_bl_data = {
12062306a36Sopenharmony_ci	.update_status	= radeon_bl_update_status,
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_civoid radeonfb_bl_init(struct radeonfb_info *rinfo)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct backlight_properties props;
12662306a36Sopenharmony_ci	struct backlight_device *bd;
12762306a36Sopenharmony_ci	struct radeon_bl_privdata *pdata;
12862306a36Sopenharmony_ci	char name[12];
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (rinfo->mon1_type != MT_LCD)
13162306a36Sopenharmony_ci		return;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
13462306a36Sopenharmony_ci	if (!pmac_has_backlight_type("ati") &&
13562306a36Sopenharmony_ci	    !pmac_has_backlight_type("mnca"))
13662306a36Sopenharmony_ci		return;
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL);
14062306a36Sopenharmony_ci	if (!pdata) {
14162306a36Sopenharmony_ci		printk("radeonfb: Memory allocation failed\n");
14262306a36Sopenharmony_ci		goto error;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
14862306a36Sopenharmony_ci	props.type = BACKLIGHT_RAW;
14962306a36Sopenharmony_ci	props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
15062306a36Sopenharmony_ci	bd = backlight_device_register(name, rinfo->info->device, pdata,
15162306a36Sopenharmony_ci				       &radeon_bl_data, &props);
15262306a36Sopenharmony_ci	if (IS_ERR(bd)) {
15362306a36Sopenharmony_ci		rinfo->info->bl_dev = NULL;
15462306a36Sopenharmony_ci		printk("radeonfb: Backlight registration failed\n");
15562306a36Sopenharmony_ci		goto error;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	pdata->rinfo = rinfo;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Pardon me for that hack... maybe some day we can figure out in what
16162306a36Sopenharmony_ci	 * direction backlight should work on a given panel?
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	pdata->negative =
16462306a36Sopenharmony_ci		(rinfo->family != CHIP_FAMILY_RV200 &&
16562306a36Sopenharmony_ci		 rinfo->family != CHIP_FAMILY_RV250 &&
16662306a36Sopenharmony_ci		 rinfo->family != CHIP_FAMILY_RV280 &&
16762306a36Sopenharmony_ci		 rinfo->family != CHIP_FAMILY_RV350);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
17062306a36Sopenharmony_ci	pdata->negative = pdata->negative ||
17162306a36Sopenharmony_ci		of_machine_is_compatible("PowerBook4,3") ||
17262306a36Sopenharmony_ci		of_machine_is_compatible("PowerBook6,3") ||
17362306a36Sopenharmony_ci		of_machine_is_compatible("PowerBook6,5");
17462306a36Sopenharmony_ci#endif
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	rinfo->info->bl_dev = bd;
17762306a36Sopenharmony_ci	fb_bl_default_curve(rinfo->info, 0,
17862306a36Sopenharmony_ci		 63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL,
17962306a36Sopenharmony_ci		217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	bd->props.brightness = bd->props.max_brightness;
18262306a36Sopenharmony_ci	bd->props.power = FB_BLANK_UNBLANK;
18362306a36Sopenharmony_ci	backlight_update_status(bd);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	printk("radeonfb: Backlight initialized (%s)\n", name);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cierror:
19062306a36Sopenharmony_ci	kfree(pdata);
19162306a36Sopenharmony_ci	return;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_civoid radeonfb_bl_exit(struct radeonfb_info *rinfo)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct backlight_device *bd = rinfo->info->bl_dev;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (bd) {
19962306a36Sopenharmony_ci		struct radeon_bl_privdata *pdata;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		pdata = bl_get_data(bd);
20262306a36Sopenharmony_ci		backlight_device_unregister(bd);
20362306a36Sopenharmony_ci		kfree(pdata);
20462306a36Sopenharmony_ci		rinfo->info->bl_dev = NULL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		printk("radeonfb: Backlight unloaded\n");
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci}
209