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