[PATCH] Rewritten backlight infrastructure for portable Apple computers

This patch contains a total rewrite of the backlight infrastructure for
portable Apple computers.  Backward compatibility is retained.  A sysfs
interface allows userland to control the brightness with more steps than
before.  Userland is allowed to upload a brightness curve for different
monitors, similar to Mac OS X.

[[email protected]: add needed exports]
Signed-off-by: Michael Hanselmann <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
Cc: Richard Purdie <[email protected]>
Cc: "Antonino A. Daplas" <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
This commit is contained in:
Michael Hanselmann 2006-06-25 05:47:08 -07:00 • committed by Linus Torvalds
commit 5474c120aa
26 changed files with 1529 additions and 711 deletions

View file

@ -86,6 +86,11 @@ config FB_FIRMWARE_EDID
combination with certain motherboards and monitors are known to
suffer from this problem.
config FB_BACKLIGHT
bool
depends on FB
default n
config FB_MODE_HELPERS
bool "Enable Video Mode Handling Helpers"
depends on FB
@ -717,6 +722,16 @@ config FB_NVIDIA_I2C
independently validate video mode parameters, you should say Y
here.
config FB_NVIDIA_BACKLIGHT
bool "Support for backlight control"
depends on FB_NVIDIA && PPC_PMAC
select FB_BACKLIGHT
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
default y
help
Say Y here if you want to control the backlight of your display.
config FB_RIVA
tristate "nVidia Riva support"
depends on FB && PCI
@ -755,6 +770,16 @@ config FB_RIVA_DEBUG
of debugging informations to provide to the maintainer when
something goes wrong.
config FB_RIVA_BACKLIGHT
bool "Support for backlight control"
depends on FB_RIVA && PPC_PMAC
select FB_BACKLIGHT
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
default y
help
Say Y here if you want to control the backlight of your display.
config FB_I810
tristate "Intel 810/815 support (EXPERIMENTAL)"
depends on FB && EXPERIMENTAL && PCI && X86_32
@ -993,6 +1018,7 @@ config FB_RADEON
There is a product page at
http://apps.ati.com/ATIcompare/
config FB_RADEON_I2C
bool "DDC/I2C for ATI Radeon support"
depends on FB_RADEON
@ -1000,6 +1026,16 @@ config FB_RADEON_I2C
help
Say Y here if you want DDC/I2C support for your Radeon board.
config FB_RADEON_BACKLIGHT
bool "Support for backlight control"
depends on FB_RADEON && PPC_PMAC
select FB_BACKLIGHT
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
default y
help
Say Y here if you want to control the backlight of your display.
config FB_RADEON_DEBUG
bool "Lots of debug output from Radeon driver"
depends on FB_RADEON
@ -1024,6 +1060,16 @@ config FB_ATY128
To compile this driver as a module, choose M here: the
module will be called aty128fb.
config FB_ATY128_BACKLIGHT
bool "Support for backlight control"
depends on FB_ATY128 && PPC_PMAC
select FB_BACKLIGHT
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
default y
help
Say Y here if you want to control the backlight of your display.
config FB_ATY
tristate "ATI Mach64 display support" if PCI || ATARI
depends on FB && !SPARC32
@ -1066,6 +1112,16 @@ config FB_ATY_GX
is at
<http://support.ati.com/products/pc/mach64/graphics_xpression.html>.
config FB_ATY_BACKLIGHT
bool "Support for backlight control"
depends on FB_ATY && PPC_PMAC
select FB_BACKLIGHT
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
default y
help
Say Y here if you want to control the backlight of your display.
config FB_S3TRIO
bool "S3 Trio display support"
depends on (FB = y) && PPC && BROKEN

View file

@ -10,5 +10,6 @@ atyfb-objs := $(atyfb-y)
radeonfb-y := radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o
radeonfb-$(CONFIG_FB_RADEON_I2C) += radeon_i2c.o
radeonfb-$(CONFIG_FB_RADEON_BACKLIGHT) += radeon_backlight.o
radeonfb-objs := $(radeonfb-y)

View file

@ -64,6 +64,7 @@
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/console.h>
#include <linux/backlight.h>
#include <asm/io.h>
#ifdef CONFIG_PPC_PMAC
@ -480,16 +481,6 @@ static struct fb_ops aty128fb_ops = {
.fb_imageblit = cfb_imageblit,
};
#ifdef CONFIG_PMAC_BACKLIGHT
static int aty128_set_backlight_enable(int on, int level, void* data);
static int aty128_set_backlight_level(int level, void* data);
static struct backlight_controller aty128_backlight_controller = {
aty128_set_backlight_enable,
aty128_set_backlight_level
};
#endif /* CONFIG_PMAC_BACKLIGHT */
/*
* Functions to read from/write to the mmio registers
* - endian conversions may possibly be avoided by
@ -1258,19 +1249,35 @@ static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
{
u32 reg;
#ifdef CONFIG_FB_ATY128_BACKLIGHT
struct fb_info *info = pci_get_drvdata(par->pdev);
#endif
if (on) {
reg = aty_ld_le32(LVDS_GEN_CNTL);
reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
reg &= ~LVDS_DISPLAY_DIS;
aty_st_le32(LVDS_GEN_CNTL, reg);
#ifdef CONFIG_PMAC_BACKLIGHT
aty128_set_backlight_enable(get_backlight_enable(),
get_backlight_level(), par);
#ifdef CONFIG_FB_ATY128_BACKLIGHT
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
#endif
} else {
#ifdef CONFIG_PMAC_BACKLIGHT
aty128_set_backlight_enable(0, 0, par);
#ifdef CONFIG_FB_ATY128_BACKLIGHT
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->brightness = 0;
info->bl_dev->props->power = FB_BLANK_POWERDOWN;
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
#endif
reg = aty_ld_le32(LVDS_GEN_CNTL);
reg |= LVDS_DISPLAY_DIS;
@ -1691,6 +1698,184 @@ static int __init aty128fb_setup(char *options)
}
#endif /* MODULE */
/* Backlight */
#ifdef CONFIG_FB_ATY128_BACKLIGHT
#define MAX_LEVEL 0xFF
static struct backlight_properties aty128_bl_data;
static int aty128_bl_get_level_brightness(struct aty128fb_par *par,
int level)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
int atylevel;
/* Get and convert the value */
mutex_lock(&info->bl_mutex);
atylevel = MAX_LEVEL -
(info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL);
mutex_unlock(&info->bl_mutex);
if (atylevel < 0)
atylevel = 0;
else if (atylevel > MAX_LEVEL)
atylevel = MAX_LEVEL;
return atylevel;
}
/* We turn off the LCD completely instead of just dimming the backlight.
* This provides greater power saving and the display is useless without
* backlight anyway
*/
#define BACKLIGHT_LVDS_OFF
/* That one prevents proper CRT output with LCD off */
#undef BACKLIGHT_DAC_OFF
static int aty128_bl_update_status(struct backlight_device *bd)
{
struct aty128fb_par *par = class_get_devdata(&bd->class_dev);
unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
int level;
if (bd->props->power != FB_BLANK_UNBLANK ||
bd->props->fb_blank != FB_BLANK_UNBLANK ||
!par->lcd_on)
level = 0;
else
level = bd->props->brightness;
reg |= LVDS_BL_MOD_EN | LVDS_BLON;
if (level > 0) {
reg |= LVDS_DIGION;
if (!(reg & LVDS_ON)) {
reg &= ~LVDS_BLON;
aty_st_le32(LVDS_GEN_CNTL, reg);
aty_ld_le32(LVDS_GEN_CNTL);
mdelay(10);
reg |= LVDS_BLON;
aty_st_le32(LVDS_GEN_CNTL, reg);
}
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
reg |= (aty128_bl_get_level_brightness(par, level) << LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_ON | LVDS_EN;
reg &= ~LVDS_DISPLAY_DIS;
#endif
aty_st_le32(LVDS_GEN_CNTL, reg);
#ifdef BACKLIGHT_DAC_OFF
aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
#endif
} else {
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
reg |= (aty128_bl_get_level_brightness(par, 0) << LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_DISPLAY_DIS;
aty_st_le32(LVDS_GEN_CNTL, reg);
aty_ld_le32(LVDS_GEN_CNTL);
udelay(10);
reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
#endif
aty_st_le32(LVDS_GEN_CNTL, reg);
#ifdef BACKLIGHT_DAC_OFF
aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
#endif
}
return 0;
}
static int aty128_bl_get_brightness(struct backlight_device *bd)
{
return bd->props->brightness;
}
static struct backlight_properties aty128_bl_data = {
.owner = THIS_MODULE,
.get_brightness = aty128_bl_get_brightness,
.update_status = aty128_bl_update_status,
.max_brightness = (FB_BACKLIGHT_LEVELS - 1),
};
static void aty128_bl_init(struct aty128fb_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
struct backlight_device *bd;
char name[12];
/* Could be extended to Rage128Pro LVDS output too */
if (par->chip_gen != rage_M3)
return;
#ifdef CONFIG_PMAC_BACKLIGHT
if (!pmac_has_backlight_type("ati"))
return;
#endif
snprintf(name, sizeof(name), "aty128bl%d", info->node);
bd = backlight_device_register(name, par, &aty128_bl_data);
if (IS_ERR(bd)) {
info->bl_dev = NULL;
printk("aty128: Backlight registration failed\n");
goto error;
}
mutex_lock(&info->bl_mutex);
info->bl_dev = bd;
fb_bl_default_curve(info, 0,
63 * FB_BACKLIGHT_MAX / MAX_LEVEL,
219 * FB_BACKLIGHT_MAX / MAX_LEVEL);
mutex_unlock(&info->bl_mutex);
up(&bd->sem);
bd->props->brightness = aty128_bl_data.max_brightness;
bd->props->power = FB_BLANK_UNBLANK;
bd->props->update_status(bd);
down(&bd->sem);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
if (!pmac_backlight)
pmac_backlight = bd;
mutex_unlock(&pmac_backlight_mutex);
#endif
printk("aty128: Backlight initialized (%s)\n", name);
return;
error:
return;
}
static void aty128_bl_exit(struct aty128fb_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
#endif
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
#ifdef CONFIG_PMAC_BACKLIGHT
if (pmac_backlight == info->bl_dev)
pmac_backlight = NULL;
#endif
backlight_device_unregister(info->bl_dev);
info->bl_dev = NULL;
printk("aty128: Backlight unloaded\n");
}
mutex_unlock(&info->bl_mutex);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_unlock(&pmac_backlight_mutex);
#endif
}
#endif /* CONFIG_FB_ATY128_BACKLIGHT */
/*
* Initialisation
@ -1835,17 +2020,15 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id *
if (register_framebuffer(info) < 0)
return 0;
#ifdef CONFIG_PMAC_BACKLIGHT
/* Could be extended to Rage128Pro LVDS output too */
if (par->chip_gen == rage_M3)
register_backlight_controller(&aty128_backlight_controller, par, "ati");
#endif /* CONFIG_PMAC_BACKLIGHT */
par->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM);
par->pdev = pdev;
par->asleep = 0;
par->lock_blank = 0;
#ifdef CONFIG_FB_ATY128_BACKLIGHT
aty128_bl_init(par);
#endif
printk(KERN_INFO "fb%d: %s frame buffer device on %s\n",
info->node, info->fix.id, video_card);
@ -1981,6 +2164,10 @@ static void __devexit aty128_remove(struct pci_dev *pdev)
par = info->par;
#ifdef CONFIG_FB_ATY128_BACKLIGHT
aty128_bl_exit(par);
#endif
unregister_framebuffer(info);
#ifdef CONFIG_MTRR
if (par->mtrr.vram_valid)
@ -2011,10 +2198,14 @@ static int aty128fb_blank(int blank, struct fb_info *fb)
if (par->lock_blank || par->asleep)
return 0;
#ifdef CONFIG_PMAC_BACKLIGHT
if (machine_is(powermac) && blank)
set_backlight_enable(0);
#endif /* CONFIG_PMAC_BACKLIGHT */
#ifdef CONFIG_FB_ATY128_BACKLIGHT
if (machine_is(powermac) && blank) {
down(&fb->bl_dev->sem);
fb->bl_dev->props->power = FB_BLANK_POWERDOWN;
fb->bl_dev->props->update_status(fb->bl_dev);
up(&fb->bl_dev->sem);
}
#endif
if (blank & FB_BLANK_VSYNC_SUSPEND)
state |= 2;
@ -2029,10 +2220,14 @@ static int aty128fb_blank(int blank, struct fb_info *fb)
aty128_set_crt_enable(par, par->crt_on && !blank);
aty128_set_lcd_enable(par, par->lcd_on && !blank);
}
#ifdef CONFIG_PMAC_BACKLIGHT
if (machine_is(powermac) && !blank)
set_backlight_enable(1);
#endif /* CONFIG_PMAC_BACKLIGHT */
#ifdef CONFIG_FB_ATY128_BACKLIGHT
if (machine_is(powermac) && !blank) {
down(&fb->bl_dev->sem);
fb->bl_dev->props->power = FB_BLANK_UNBLANK;
fb->bl_dev->props->update_status(fb->bl_dev);
up(&fb->bl_dev->sem);
}
#endif
return 0;
}
@ -2138,73 +2333,6 @@ static int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
return -EINVAL;
}
#ifdef CONFIG_PMAC_BACKLIGHT
static int backlight_conv[] = {
0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
};
/* We turn off the LCD completely instead of just dimming the backlight.
* This provides greater power saving and the display is useless without
* backlight anyway
*/
#define BACKLIGHT_LVDS_OFF
/* That one prevents proper CRT output with LCD off */
#undef BACKLIGHT_DAC_OFF
static int aty128_set_backlight_enable(int on, int level, void *data)
{
struct aty128fb_par *par = data;
unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
if (!par->lcd_on)
on = 0;
reg |= LVDS_BL_MOD_EN | LVDS_BLON;
if (on && level > BACKLIGHT_OFF) {
reg |= LVDS_DIGION;
if (!(reg & LVDS_ON)) {
reg &= ~LVDS_BLON;
aty_st_le32(LVDS_GEN_CNTL, reg);
(void)aty_ld_le32(LVDS_GEN_CNTL);
mdelay(10);
reg |= LVDS_BLON;
aty_st_le32(LVDS_GEN_CNTL, reg);
}
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
reg |= (backlight_conv[level] << LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_ON | LVDS_EN;
reg &= ~LVDS_DISPLAY_DIS;
#endif
aty_st_le32(LVDS_GEN_CNTL, reg);
#ifdef BACKLIGHT_DAC_OFF
aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
#endif
} else {
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
reg |= (backlight_conv[0] << LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_DISPLAY_DIS;
aty_st_le32(LVDS_GEN_CNTL, reg);
(void)aty_ld_le32(LVDS_GEN_CNTL);
udelay(10);
reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
#endif
aty_st_le32(LVDS_GEN_CNTL, reg);
#ifdef BACKLIGHT_DAC_OFF
aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
#endif
}
return 0;
}
static int aty128_set_backlight_level(int level, void* data)
{
return aty128_set_backlight_enable(1, level, data);
}
#endif /* CONFIG_PMAC_BACKLIGHT */
#if 0
/*
* Accelerated functions

View file

@ -151,6 +151,7 @@ struct atyfb_par {
int lock_blank;
unsigned long res_start;
unsigned long res_size;
struct pci_dev *pdev;
#ifdef __sparc__
struct pci_mmap_map *mmap_map;
u8 mmaped;

View file

@ -66,6 +66,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/backlight.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@ -2115,45 +2116,142 @@ static int atyfb_pci_resume(struct pci_dev *pdev)
#endif /* defined(CONFIG_PM) && defined(CONFIG_PCI) */
#ifdef CONFIG_PMAC_BACKLIGHT
/* Backlight */
#ifdef CONFIG_FB_ATY_BACKLIGHT
#define MAX_LEVEL 0xFF
/*
* LCD backlight control
*/
static struct backlight_properties aty_bl_data;
static int backlight_conv[] = {
0x00, 0x3f, 0x4c, 0x59, 0x66, 0x73, 0x80, 0x8d,
0x9a, 0xa7, 0xb4, 0xc1, 0xcf, 0xdc, 0xe9, 0xff
};
static int aty_set_backlight_enable(int on, int level, void *data)
static int aty_bl_get_level_brightness(struct atyfb_par *par, int level)
{
struct fb_info *info = (struct fb_info *) data;
struct atyfb_par *par = (struct atyfb_par *) info->par;
struct fb_info *info = pci_get_drvdata(par->pdev);
int atylevel;
/* Get and convert the value */
mutex_lock(&info->bl_mutex);
atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL;
mutex_unlock(&info->bl_mutex);
if (atylevel < 0)
atylevel = 0;
else if (atylevel > MAX_LEVEL)
atylevel = MAX_LEVEL;
return atylevel;
}
static int aty_bl_update_status(struct backlight_device *bd)
{
struct atyfb_par *par = class_get_devdata(&bd->class_dev);
unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par);
int level;
if (bd->props->power != FB_BLANK_UNBLANK ||
bd->props->fb_blank != FB_BLANK_UNBLANK)
level = 0;
else
level = bd->props->brightness;
reg |= (BLMOD_EN | BIASMOD_EN);
if (on && level > BACKLIGHT_OFF) {
if (level > 0) {
reg &= ~BIAS_MOD_LEVEL_MASK;
reg |= (backlight_conv[level] << BIAS_MOD_LEVEL_SHIFT);
reg |= (aty_bl_get_level_brightness(par, level) << BIAS_MOD_LEVEL_SHIFT);
} else {
reg &= ~BIAS_MOD_LEVEL_MASK;
reg |= (backlight_conv[0] << BIAS_MOD_LEVEL_SHIFT);
reg |= (aty_bl_get_level_brightness(par, 0) << BIAS_MOD_LEVEL_SHIFT);
}
aty_st_lcd(LCD_MISC_CNTL, reg, par);
return 0;
}
static int aty_set_backlight_level(int level, void *data)
static int aty_bl_get_brightness(struct backlight_device *bd)
{
return aty_set_backlight_enable(1, level, data);
return bd->props->brightness;
}
static struct backlight_controller aty_backlight_controller = {
aty_set_backlight_enable,
aty_set_backlight_level
static struct backlight_properties aty_bl_data = {
.owner = THIS_MODULE,
.get_brightness = aty_bl_get_brightness,
.update_status = aty_bl_update_status,
.max_brightness = (FB_BACKLIGHT_LEVELS - 1),
};
#endif /* CONFIG_PMAC_BACKLIGHT */
static void aty_bl_init(struct atyfb_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
struct backlight_device *bd;
char name[12];
#ifdef CONFIG_PMAC_BACKLIGHT
if (!pmac_has_backlight_type("ati"))
return;
#endif
snprintf(name, sizeof(name), "atybl%d", info->node);
bd = backlight_device_register(name, par, &aty_bl_data);
if (IS_ERR(bd)) {
info->bl_dev = NULL;
printk("aty: Backlight registration failed\n");
goto error;
}
mutex_lock(&info->bl_mutex);
info->bl_dev = bd;
fb_bl_default_curve(info, 0,
0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL,
0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL);
mutex_unlock(&info->bl_mutex);
up(&bd->sem);
bd->props->brightness = aty_bl_data.max_brightness;
bd->props->power = FB_BLANK_UNBLANK;
bd->props->update_status(bd);
down(&bd->sem);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
if (!pmac_backlight)
pmac_backlight = bd;
mutex_unlock(&pmac_backlight_mutex);
#endif
printk("aty: Backlight initialized (%s)\n", name);
return;
error:
return;
}
static void aty_bl_exit(struct atyfb_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
#endif
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
#ifdef CONFIG_PMAC_BACKLIGHT
if (pmac_backlight == info->bl_dev)
pmac_backlight = NULL;
#endif
backlight_device_unregister(info->bl_dev);
printk("aty: Backlight unloaded\n");
}
mutex_unlock(&info->bl_mutex);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_unlock(&pmac_backlight_mutex);
#endif
}
#endif /* CONFIG_FB_ATY_BACKLIGHT */
static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
{
@ -2513,9 +2611,13 @@ static int __init aty_init(struct fb_info *info, const char *name)
/* these bits let the 101 powerbook wake up from sleep -- paulus */
aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par)
| (USE_F32KHZ | TRISTATE_MEM_EN), par);
} else if (M64_HAS(MOBIL_BUS))
register_backlight_controller(&aty_backlight_controller, info, "ati");
#endif /* CONFIG_PMAC_BACKLIGHT */
} else
#endif
if (M64_HAS(MOBIL_BUS)) {
#ifdef CONFIG_FB_ATY_BACKLIGHT
aty_bl_init (par);
#endif
}
memset(&var, 0, sizeof(var));
#ifdef CONFIG_PPC
@ -2674,8 +2776,16 @@ static int atyfb_blank(int blank, struct fb_info *info)
return 0;
#ifdef CONFIG_PMAC_BACKLIGHT
if (machine_is(powermac) && blank > FB_BLANK_NORMAL)
set_backlight_enable(0);
if (machine_is(powermac) && blank > FB_BLANK_NORMAL) {
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->power = FB_BLANK_POWERDOWN;
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
}
#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
if (par->lcd_table && blank > FB_BLANK_NORMAL &&
(aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
@ -2706,8 +2816,16 @@ static int atyfb_blank(int blank, struct fb_info *info)
aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par);
#ifdef CONFIG_PMAC_BACKLIGHT
if (machine_is(powermac) && blank <= FB_BLANK_NORMAL)
set_backlight_enable(1);
if (machine_is(powermac) && blank <= FB_BLANK_NORMAL) {
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->power = FB_BLANK_UNBLANK;
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
}
#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
if (par->lcd_table && blank <= FB_BLANK_NORMAL &&
(aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
@ -3440,6 +3558,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi
par->res_start = res_start;
par->res_size = res_size;
par->irq = pdev->irq;
par->pdev = pdev;
/* Setup "info" structure */
#ifdef __sparc__
@ -3571,6 +3690,11 @@ static void __devexit atyfb_remove(struct fb_info *info)
aty_set_crtc(par, &saved_crtc);
par->pll_ops->set_pll(info, &saved_pll);
#ifdef CONFIG_FB_ATY_BACKLIGHT
if (M64_HAS(MOBIL_BUS))
aty_bl_exit(par);
#endif
unregister_framebuffer(info);
#ifdef CONFIG_MTRR

View file

@ -0,0 +1,247 @@
/*
* Backlight code for ATI Radeon based graphic cards
*
* Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org>
* Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
* Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "radeonfb.h"
#include <linux/backlight.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
#define MAX_RADEON_LEVEL 0xFF
static struct backlight_properties radeon_bl_data;
struct radeon_bl_privdata {
struct radeonfb_info *rinfo;
uint8_t negative;
};
static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata,
int level)
{
struct fb_info *info = pdata->rinfo->info;
int rlevel;
mutex_lock(&info->bl_mutex);
/* Get and convert the value */
rlevel = pdata->rinfo->info->bl_curve[level] *
FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL;
mutex_unlock(&info->bl_mutex);
if (pdata->negative)
rlevel = MAX_RADEON_LEVEL - rlevel;
if (rlevel < 0)
rlevel = 0;
else if (rlevel > MAX_RADEON_LEVEL)
rlevel = MAX_RADEON_LEVEL;
return rlevel;
}
static int radeon_bl_update_status(struct backlight_device *bd)
{
struct radeon_bl_privdata *pdata = class_get_devdata(&bd->class_dev);
struct radeonfb_info *rinfo = pdata->rinfo;
u32 lvds_gen_cntl, tmpPixclksCntl;
int level;
if (rinfo->mon1_type != MT_LCD)
return 0;
/* We turn off the LCD completely instead of just dimming the
* backlight. This provides some greater power saving and the display
* is useless without backlight anyway.
*/
if (bd->props->power != FB_BLANK_UNBLANK ||
bd->props->fb_blank != FB_BLANK_UNBLANK)
level = 0;
else
level = bd->props->brightness;
del_timer_sync(&rinfo->lvds_timer);
radeon_engine_idle();
lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
if (level > 0) {
lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |=
(radeon_bl_get_level_brightness(pdata, level) <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_ON;
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
} else {
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |=
(radeon_bl_get_level_brightness(pdata, level) <<
LVDS_BL_MOD_LEVEL_SHIFT);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
& LVDS_STATE_MASK;
} else {
/* Asic bug, when turning off LVDS_ON, we have to make sure
RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
*/
tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_DISPLAY_DIS;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
udelay(100);
lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~(LVDS_DIGON);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
return 0;
}
static int radeon_bl_get_brightness(struct backlight_device *bd)
{
return bd->props->brightness;
}
static struct backlight_properties radeon_bl_data = {
.owner = THIS_MODULE,
.get_brightness = radeon_bl_get_brightness,
.update_status = radeon_bl_update_status,
.max_brightness = (FB_BACKLIGHT_LEVELS - 1),
};
void radeonfb_bl_init(struct radeonfb_info *rinfo)
{
struct backlight_device *bd;
struct radeon_bl_privdata *pdata;
char name[12];
if (rinfo->mon1_type != MT_LCD)
return;
#ifdef CONFIG_PMAC_BACKLIGHT
if (!pmac_has_backlight_type("ati") &&
!pmac_has_backlight_type("mnca"))
return;
#endif
pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL);
if (!pdata) {
printk("radeonfb: Memory allocation failed\n");
goto error;
}
snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node);
bd = backlight_device_register(name, pdata, &radeon_bl_data);
if (IS_ERR(bd)) {
rinfo->info->bl_dev = NULL;
printk("radeonfb: Backlight registration failed\n");
goto error;
}
pdata->rinfo = rinfo;
/* Pardon me for that hack... maybe some day we can figure out in what
* direction backlight should work on a given panel?
*/
pdata->negative =
(rinfo->family != CHIP_FAMILY_RV200 &&
rinfo->family != CHIP_FAMILY_RV250 &&
rinfo->family != CHIP_FAMILY_RV280 &&
rinfo->family != CHIP_FAMILY_RV350);
#ifdef CONFIG_PMAC_BACKLIGHT
pdata->negative = pdata->negative ||
machine_is_compatible("PowerBook4,3") ||
machine_is_compatible("PowerBook6,3") ||
machine_is_compatible("PowerBook6,5");
#endif
mutex_lock(&rinfo->info->bl_mutex);
rinfo->info->bl_dev = bd;
fb_bl_default_curve(rinfo->info, 0,
63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL,
217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL);
mutex_unlock(&rinfo->info->bl_mutex);
up(&bd->sem);
bd->props->brightness = radeon_bl_data.max_brightness;
bd->props->power = FB_BLANK_UNBLANK;
bd->props->update_status(bd);
down(&bd->sem);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
if (!pmac_backlight)
pmac_backlight = bd;
mutex_unlock(&pmac_backlight_mutex);
#endif
printk("radeonfb: Backlight initialized (%s)\n", name);
return;
error:
kfree(pdata);
return;
}
void radeonfb_bl_exit(struct radeonfb_info *rinfo)
{
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
#endif
mutex_lock(&rinfo->info->bl_mutex);
if (rinfo->info->bl_dev) {
struct radeon_bl_privdata *pdata;
#ifdef CONFIG_PMAC_BACKLIGHT
if (pmac_backlight == rinfo->info->bl_dev)
pmac_backlight = NULL;
#endif
pdata = class_get_devdata(&rinfo->info->bl_dev->class_dev);
backlight_device_unregister(rinfo->info->bl_dev);
kfree(pdata);
rinfo->info->bl_dev = NULL;
printk("radeonfb: Backlight unloaded\n");
}
mutex_unlock(&rinfo->info->bl_mutex);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_unlock(&pmac_backlight_mutex);
#endif
}

View file

@ -78,10 +78,6 @@
#include <asm/pci-bridge.h>
#include "../macmodes.h"
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
#ifdef CONFIG_BOOTX_TEXT
#include <asm/btext.h>
#endif
@ -277,20 +273,6 @@ static int nomtrr = 0;
* prototypes
*/
#ifdef CONFIG_PPC_OF
#ifdef CONFIG_PMAC_BACKLIGHT
static int radeon_set_backlight_enable(int on, int level, void *data);
static int radeon_set_backlight_level(int level, void *data);
static struct backlight_controller radeon_backlight_controller = {
radeon_set_backlight_enable,
radeon_set_backlight_level
};
#endif /* CONFIG_PMAC_BACKLIGHT */
#endif /* CONFIG_PPC_OF */
static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
{
if (!rinfo->bios_seg)
@ -1913,116 +1895,6 @@ static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
return 0;
}
#ifdef CONFIG_PMAC_BACKLIGHT
/* TODO: Dbl check these tables, we don't go up to full ON backlight
* in these, possibly because we noticed MacOS doesn't, but I'd prefer
* having some more official numbers from ATI
*/
static int backlight_conv_m6[] = {
0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
};
static int backlight_conv_m7[] = {
0x00, 0x3f, 0x4a, 0x55, 0x60, 0x6b, 0x76, 0x81,
0x8c, 0x97, 0xa2, 0xad, 0xb8, 0xc3, 0xce, 0xd9
};
#define BACKLIGHT_LVDS_OFF
#undef BACKLIGHT_DAC_OFF
/* We turn off the LCD completely instead of just dimming the backlight.
* This provides some greater power saving and the display is useless
* without backlight anyway.
*/
static int radeon_set_backlight_enable(int on, int level, void *data)
{
struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
u32 lvds_gen_cntl, tmpPixclksCntl;
int* conv_table;
if (rinfo->mon1_type != MT_LCD)
return 0;
/* Pardon me for that hack... maybe some day we can figure
* out in what direction backlight should work on a given
* panel ?
*/
if ((rinfo->family == CHIP_FAMILY_RV200 ||
rinfo->family == CHIP_FAMILY_RV250 ||
rinfo->family == CHIP_FAMILY_RV280 ||
rinfo->family == CHIP_FAMILY_RV350) &&
!machine_is_compatible("PowerBook4,3") &&
!machine_is_compatible("PowerBook6,3") &&
!machine_is_compatible("PowerBook6,5"))
conv_table = backlight_conv_m7;
else
conv_table = backlight_conv_m6;
del_timer_sync(&rinfo->lvds_timer);
radeon_engine_idle();
lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
if (on && (level > BACKLIGHT_OFF)) {
lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |= (conv_table[level] <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_ON;
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
} else {
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |= (conv_table[level] <<
LVDS_BL_MOD_LEVEL_SHIFT);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
& LVDS_STATE_MASK;
} else {
/* Asic bug, when turning off LVDS_ON, we have to make sure
RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
*/
tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
lvds_gen_cntl |= (conv_table[0] <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_DISPLAY_DIS;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
udelay(100);
lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~(LVDS_DIGON);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
return 0;
}
static int radeon_set_backlight_level(int level, void *data)
{
return radeon_set_backlight_enable(1, level, data);
}
#endif /* CONFIG_PMAC_BACKLIGHT */
/*
* This reconfigure the card's internal memory map. In theory, we'd like
* to setup the card's memory at the same address as it's PCI bus address,
@ -2477,14 +2349,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
MTRR_TYPE_WRCOMB, 1);
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
if (rinfo->mon1_type == MT_LCD) {
register_backlight_controller(&radeon_backlight_controller,
rinfo, "ati");
register_backlight_controller(&radeon_backlight_controller,
rinfo, "mnca");
}
#endif
radeonfb_bl_init(rinfo);
printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name);
@ -2528,7 +2393,8 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
if (!rinfo)
return;
radeonfb_bl_exit(rinfo);
radeonfb_pm_exit(rinfo);
if (rinfo->mon1_EDID)

View file

@ -625,4 +625,13 @@ extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_
extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
int reg_only);
/* Backlight functions */
#ifdef CONFIG_FB_RADEON_BACKLIGHT
extern void radeonfb_bl_init(struct radeonfb_info *rinfo);
extern void radeonfb_bl_exit(struct radeonfb_info *rinfo);
#else
static inline void radeonfb_bl_init(struct radeonfb_info *rinfo) {}
static inline void radeonfb_bl_exit(struct radeonfb_info *rinfo) {}
#endif
#endif /* __RADEONFB_H__ */

View file

@ -148,9 +148,24 @@ static int chipsfb_set_par(struct fb_info *info)
static int chipsfb_blank(int blank, struct fb_info *info)
{
#ifdef CONFIG_PMAC_BACKLIGHT
// used to disable backlight only for blank > 1, but it seems
// useful at blank = 1 too (saves battery, extends backlight life)
set_backlight_enable(!blank);
mutex_lock(&pmac_backlight_mutex);
if (pmac_backlight) {
down(&pmac_backlight->sem);
/* used to disable backlight only for blank > 1, but it seems
* useful at blank = 1 too (saves battery, extends backlight
* life)
*/
if (blank)
pmac_backlight->props->power = FB_BLANK_POWERDOWN;
else
pmac_backlight->props->power = FB_BLANK_UNBLANK;
pmac_backlight->props->update_status(pmac_backlight);
up(&pmac_backlight->sem);
}
mutex_unlock(&pmac_backlight_mutex);
#endif /* CONFIG_PMAC_BACKLIGHT */
return 1; /* get fb_blank to set the colormap to all black */
@ -401,7 +416,14 @@ chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
#ifdef CONFIG_PMAC_BACKLIGHT
/* turn on the backlight */
set_backlight_enable(1);
mutex_lock(&pmac_backlight_mutex);
if (pmac_backlight) {
down(&pmac_backlight->sem);
pmac_backlight->props->power = FB_BLANK_UNBLANK;
pmac_backlight->props->update_status(pmac_backlight);
up(&pmac_backlight->sem);
}
mutex_unlock(&pmac_backlight_mutex);
#endif /* CONFIG_PMAC_BACKLIGHT */
#ifdef CONFIG_PPC

View file

@ -18,6 +18,7 @@
#include <linux/kernel.h>
#include <linux/fb.h>
#include <linux/console.h>
#include <linux/module.h>
/**
* framebuffer_alloc - creates a new frame buffer info structure
@ -55,6 +56,10 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
info->device = dev;
#ifdef CONFIG_FB_BACKLIGHT
mutex_init(&info->bl_mutex);
#endif
return info;
#undef PADDING
#undef BYTES_PER_LONG
@ -414,6 +419,65 @@ static ssize_t show_fbstate(struct class_device *class_device, char *buf)
return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
}
#ifdef CONFIG_FB_BACKLIGHT
static ssize_t store_bl_curve(struct class_device *class_device,
const char *buf, size_t count)
{
struct fb_info *fb_info = class_get_devdata(class_device);
u8 tmp_curve[FB_BACKLIGHT_LEVELS];
unsigned int i;
if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
return -EINVAL;
for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
if (sscanf(&buf[i * 24],
"%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
&tmp_curve[i * 8 + 0],
&tmp_curve[i * 8 + 1],
&tmp_curve[i * 8 + 2],
&tmp_curve[i * 8 + 3],
&tmp_curve[i * 8 + 4],
&tmp_curve[i * 8 + 5],
&tmp_curve[i * 8 + 6],
&tmp_curve[i * 8 + 7]) != 8)
return -EINVAL;
/* If there has been an error in the input data, we won't
* reach this loop.
*/
mutex_lock(&fb_info->bl_mutex);
for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
fb_info->bl_curve[i] = tmp_curve[i];
mutex_unlock(&fb_info->bl_mutex);
return count;
}
static ssize_t show_bl_curve(struct class_device *class_device, char *buf)
{
struct fb_info *fb_info = class_get_devdata(class_device);
ssize_t len = 0;
unsigned int i;
mutex_lock(&fb_info->bl_mutex);
for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
len += snprintf(&buf[len], PAGE_SIZE,
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
fb_info->bl_curve[i + 0],
fb_info->bl_curve[i + 1],
fb_info->bl_curve[i + 2],
fb_info->bl_curve[i + 3],
fb_info->bl_curve[i + 4],
fb_info->bl_curve[i + 5],
fb_info->bl_curve[i + 6],
fb_info->bl_curve[i + 7]);
mutex_unlock(&fb_info->bl_mutex);
return len;
}
#endif
/* When cmap is added back in it should be a binary attribute
* not a text one. Consideration should also be given to converting
* fbdev to use configfs instead of sysfs */
@ -432,6 +496,9 @@ static struct class_device_attribute class_device_attrs[] = {
__ATTR(con_rotate, S_IRUGO|S_IWUSR, show_con_rotate, store_con_rotate),
__ATTR(con_rotate_all, S_IWUSR, NULL, store_con_rotate_all),
__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
#ifdef CONFIG_FB_BACKLIGHT
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
#endif
};
int fb_init_class_device(struct fb_info *fb_info)
@ -454,4 +521,25 @@ void fb_cleanup_class_device(struct fb_info *fb_info)
&class_device_attrs[i]);
}
#ifdef CONFIG_FB_BACKLIGHT
/* This function generates a linear backlight curve
*
* 0: off
* 1-7: min
* 8-127: linear from min to max
*/
void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
{
unsigned int i, flat, count, range = (max - min);
fb_info->bl_curve[0] = off;
for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
fb_info->bl_curve[flat] = min;
count = FB_BACKLIGHT_LEVELS * 15 / 16;
for (i = 0; i < count; ++i)
fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
}
EXPORT_SYMBOL_GPL(fb_bl_default_curve);
#endif

View file

@ -7,6 +7,7 @@ obj-$(CONFIG_FB_NVIDIA) += nvidiafb.o
nvidiafb-y := nvidia.o nv_hw.o nv_setup.o \
nv_accel.o
nvidiafb-$(CONFIG_FB_NVIDIA_I2C) += nv_i2c.o
nvidiafb-$(CONFIG_FB_NVIDIA_BACKLIGHT) += nv_backlight.o
nvidiafb-$(CONFIG_PPC_OF) += nv_of.o
nvidiafb-objs := $(nvidiafb-y)
nvidiafb-objs := $(nvidiafb-y)

View file

@ -0,0 +1,175 @@
/*
* Backlight code for nVidia based graphic cards
*
* Copyright 2004 Antonino Daplas <adaplas@pol.net>
* Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/pci.h>
#include "nv_local.h"
#include "nv_type.h"
#include "nv_proto.h"
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#include <asm/machdep.h>
#endif
/* We do not have any information about which values are allowed, thus
* we used safe values.
*/
#define MIN_LEVEL 0x158
#define MAX_LEVEL 0x534
static struct backlight_properties nvidia_bl_data;
static int nvidia_bl_get_level_brightness(struct nvidia_par *par,
int level)
{
struct fb_info *info = pci_get_drvdata(par->pci_dev);
int nlevel;
/* Get and convert the value */
mutex_lock(&info->bl_mutex);
nlevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL;
mutex_unlock(&info->bl_mutex);
if (nlevel < 0)
nlevel = 0;
else if (nlevel < MIN_LEVEL)
nlevel = MIN_LEVEL;
else if (nlevel > MAX_LEVEL)
nlevel = MAX_LEVEL;
return nlevel;
}
static int nvidia_bl_update_status(struct backlight_device *bd)
{
struct nvidia_par *par = class_get_devdata(&bd->class_dev);
u32 tmp_pcrt, tmp_pmc, fpcontrol;
int level;
if (!par->FlatPanel)
return 0;
if (bd->props->power != FB_BLANK_UNBLANK ||
bd->props->fb_blank != FB_BLANK_UNBLANK)
level = 0;
else
level = bd->props->brightness;
tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
if (level > 0) {
tmp_pcrt |= 0x1;
tmp_pmc |= (1 << 31); /* backlight bit */
tmp_pmc |= nvidia_bl_get_level_brightness(par, level) << 16;
fpcontrol |= par->fpSyncs;
} else
fpcontrol |= 0x20000022;
NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
NV_WR32(par->PMC, 0x10F0, tmp_pmc);
NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
return 0;
}
static int nvidia_bl_get_brightness(struct backlight_device *bd)
{
return bd->props->brightness;
}
static struct backlight_properties nvidia_bl_data = {
.owner = THIS_MODULE,
.get_brightness = nvidia_bl_get_brightness,
.update_status = nvidia_bl_update_status,
.max_brightness = (FB_BACKLIGHT_LEVELS - 1),
};
void nvidia_bl_init(struct nvidia_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pci_dev);
struct backlight_device *bd;
char name[12];
if (!par->FlatPanel)
return;
#ifdef CONFIG_PMAC_BACKLIGHT
if (!machine_is(powermac) ||
!pmac_has_backlight_type("mnca"))
return;
#endif
snprintf(name, sizeof(name), "nvidiabl%d", info->node);
bd = backlight_device_register(name, par, &nvidia_bl_data);
if (IS_ERR(bd)) {
info->bl_dev = NULL;
printk("nvidia: Backlight registration failed\n");
goto error;
}
mutex_lock(&info->bl_mutex);
info->bl_dev = bd;
fb_bl_default_curve(info, 0,
0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL,
0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL);
mutex_unlock(&info->bl_mutex);
up(&bd->sem);
bd->props->brightness = nvidia_bl_data.max_brightness;
bd->props->power = FB_BLANK_UNBLANK;
bd->props->update_status(bd);
down(&bd->sem);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
if (!pmac_backlight)
pmac_backlight = bd;
mutex_unlock(&pmac_backlight_mutex);
#endif
printk("nvidia: Backlight initialized (%s)\n", name);
return;
error:
return;
}
void nvidia_bl_exit(struct nvidia_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pci_dev);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
#endif
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
#ifdef CONFIG_PMAC_BACKLIGHT
if (pmac_backlight == info->bl_dev)
pmac_backlight = NULL;
#endif
backlight_device_unregister(info->bl_dev);
printk("nvidia: Backlight unloaded\n");
}
mutex_unlock(&info->bl_mutex);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_unlock(&pmac_backlight_mutex);
#endif
}

View file

@ -63,4 +63,14 @@ extern void nvidiafb_imageblit(struct fb_info *info,
const struct fb_image *image);
extern int nvidiafb_sync(struct fb_info *info);
extern u8 byte_rev[256];
/* in nv_backlight.h */
#ifdef CONFIG_FB_NVIDIA_BACKLIGHT
extern void nvidia_bl_init(struct nvidia_par *par);
extern void nvidia_bl_exit(struct nvidia_par *par);
#else
static inline void nvidia_bl_init(struct nvidia_par *par) {}
static inline void nvidia_bl_exit(struct nvidia_par *par) {}
#endif
#endif /* __NV_PROTO_H__ */

View file

@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/console.h>
#include <linux/backlight.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
@ -29,10 +30,6 @@
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/machdep.h>
#include <asm/backlight.h>
#endif
#include "nv_local.h"
#include "nv_type.h"
@ -470,75 +467,6 @@ static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
.vmode = FB_VMODE_NONINTERLACED
};
/*
* Backlight control
*/
#ifdef CONFIG_PMAC_BACKLIGHT
static int nvidia_backlight_levels[] = {
0x158,
0x192,
0x1c6,
0x200,
0x234,
0x268,
0x2a2,
0x2d6,
0x310,
0x344,
0x378,
0x3b2,
0x3e6,
0x41a,
0x454,
0x534,
};
/* ------------------------------------------------------------------------- *
*
* Backlight operations
*
* ------------------------------------------------------------------------- */
static int nvidia_set_backlight_enable(int on, int level, void *data)
{
struct nvidia_par *par = data;
u32 tmp_pcrt, tmp_pmc, fpcontrol;
tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
if (on && (level > BACKLIGHT_OFF)) {
tmp_pcrt |= 0x1;
tmp_pmc |= (1 << 31); // backlight bit
tmp_pmc |= nvidia_backlight_levels[level - 1] << 16;
}
if (on)
fpcontrol |= par->fpSyncs;
else
fpcontrol |= 0x20000022;
NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
NV_WR32(par->PMC, 0x10F0, tmp_pmc);
NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
return 0;
}
static int nvidia_set_backlight_level(int level, void *data)
{
return nvidia_set_backlight_enable(1, level, data);
}
static struct backlight_controller nvidia_backlight_controller = {
nvidia_set_backlight_enable,
nvidia_set_backlight_level
};
#endif /* CONFIG_PMAC_BACKLIGHT */
static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
u16 bg, u16 fg, u32 w, u32 h)
{
@ -1355,10 +1283,15 @@ static int nvidiafb_blank(int blank, struct fb_info *info)
NVWriteSeq(par, 0x01, tmp);
NVWriteCrtc(par, 0x1a, vesa);
#ifdef CONFIG_PMAC_BACKLIGHT
if (par->FlatPanel && machine_is(powermac)) {
set_backlight_enable(!blank);
#ifdef CONFIG_FB_NVIDIA_BACKLIGHT
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->power = blank;
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
#endif
NVTRACE_LEAVE();
@ -1741,11 +1674,9 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd,
"PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
info->fix.id,
par->FbMapSize / (1024 * 1024), info->fix.smem_start);
#ifdef CONFIG_PMAC_BACKLIGHT
if (par->FlatPanel && machine_is(powermac))
register_backlight_controller(&nvidia_backlight_controller,
par, "mnca");
#endif
nvidia_bl_init(par);
NVTRACE_LEAVE();
return 0;
@ -1775,6 +1706,8 @@ static void __exit nvidiafb_remove(struct pci_dev *pd)
NVTRACE_ENTER();
nvidia_bl_exit(par);
unregister_framebuffer(info);
#ifdef CONFIG_MTRR
if (par->mtrr.vram_valid)

View file

@ -41,6 +41,7 @@
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/backlight.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
@ -272,34 +273,154 @@ static const struct riva_regs reg_template = {
/*
* Backlight control
*/
#ifdef CONFIG_FB_RIVA_BACKLIGHT
/* We do not have any information about which values are allowed, thus
* we used safe values.
*/
#define MIN_LEVEL 0x158
#define MAX_LEVEL 0x534
static struct backlight_properties riva_bl_data;
static int riva_bl_get_level_brightness(struct riva_par *par,
int level)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
int nlevel;
/* Get and convert the value */
mutex_lock(&info->bl_mutex);
nlevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL;
mutex_unlock(&info->bl_mutex);
if (nlevel < 0)
nlevel = 0;
else if (nlevel < MIN_LEVEL)
nlevel = MIN_LEVEL;
else if (nlevel > MAX_LEVEL)
nlevel = MAX_LEVEL;
return nlevel;
}
static int riva_bl_update_status(struct backlight_device *bd)
{
struct riva_par *par = class_get_devdata(&bd->class_dev);
U032 tmp_pcrt, tmp_pmc;
int level;
if (bd->props->power != FB_BLANK_UNBLANK ||
bd->props->fb_blank != FB_BLANK_UNBLANK)
level = 0;
else
level = bd->props->brightness;
tmp_pmc = par->riva.PMC[0x10F0/4] & 0x0000FFFF;
tmp_pcrt = par->riva.PCRTC0[0x081C/4] & 0xFFFFFFFC;
if(level > 0) {
tmp_pcrt |= 0x1;
tmp_pmc |= (1 << 31); /* backlight bit */
tmp_pmc |= riva_bl_get_level_brightness(par, level) << 16; /* level */
}
par->riva.PCRTC0[0x081C/4] = tmp_pcrt;
par->riva.PMC[0x10F0/4] = tmp_pmc;
return 0;
}
static int riva_bl_get_brightness(struct backlight_device *bd)
{
return bd->props->brightness;
}
static struct backlight_properties riva_bl_data = {
.owner = THIS_MODULE,
.get_brightness = riva_bl_get_brightness,
.update_status = riva_bl_update_status,
.max_brightness = (FB_BACKLIGHT_LEVELS - 1),
};
static void riva_bl_init(struct riva_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
struct backlight_device *bd;
char name[12];
if (!par->FlatPanel)
return;
#ifdef CONFIG_PMAC_BACKLIGHT
if (!machine_is(powermac) ||
!pmac_has_backlight_type("mnca"))
return;
#endif
static int riva_backlight_levels[] = {
0x158,
0x192,
0x1c6,
0x200,
0x234,
0x268,
0x2a2,
0x2d6,
0x310,
0x344,
0x378,
0x3b2,
0x3e6,
0x41a,
0x454,
0x534,
};
snprintf(name, sizeof(name), "rivabl%d", info->node);
static int riva_set_backlight_enable(int on, int level, void *data);
static int riva_set_backlight_level(int level, void *data);
static struct backlight_controller riva_backlight_controller = {
riva_set_backlight_enable,
riva_set_backlight_level
};
#endif /* CONFIG_PMAC_BACKLIGHT */
bd = backlight_device_register(name, par, &riva_bl_data);
if (IS_ERR(bd)) {
info->bl_dev = NULL;
printk("riva: Backlight registration failed\n");
goto error;
}
mutex_lock(&info->bl_mutex);
info->bl_dev = bd;
fb_bl_default_curve(info, 0,
0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL,
0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL);
mutex_unlock(&info->bl_mutex);
up(&bd->sem);
bd->props->brightness = riva_bl_data.max_brightness;
bd->props->power = FB_BLANK_UNBLANK;
bd->props->update_status(bd);
down(&bd->sem);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
if (!pmac_backlight)
pmac_backlight = bd;
mutex_unlock(&pmac_backlight_mutex);
#endif
printk("riva: Backlight initialized (%s)\n", name);
return;
error:
return;
}
static void riva_bl_exit(struct riva_par *par)
{
struct fb_info *info = pci_get_drvdata(par->pdev);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock(&pmac_backlight_mutex);
#endif
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
#ifdef CONFIG_PMAC_BACKLIGHT
if (pmac_backlight == info->bl_dev)
pmac_backlight = NULL;
#endif
backlight_device_unregister(info->bl_dev);
printk("riva: Backlight unloaded\n");
}
mutex_unlock(&info->bl_mutex);
#ifdef CONFIG_PMAC_BACKLIGHT
mutex_unlock(&pmac_backlight_mutex);
#endif
}
#else
static inline void riva_bl_init(struct riva_par *par) {}
static inline void riva_bl_exit(struct riva_par *par) {}
#endif /* CONFIG_FB_RIVA_BACKLIGHT */
/* ------------------------------------------------------------------------- *
*
@ -971,36 +1092,6 @@ static int riva_get_cmap_len(const struct fb_var_screeninfo *var)
return rc;
}
/* ------------------------------------------------------------------------- *
*
* Backlight operations
*
* ------------------------------------------------------------------------- */
#ifdef CONFIG_PMAC_BACKLIGHT
static int riva_set_backlight_enable(int on, int level, void *data)
{
struct riva_par *par = data;
U032 tmp_pcrt, tmp_pmc;
tmp_pmc = par->riva.PMC[0x10F0/4] & 0x0000FFFF;
tmp_pcrt = par->riva.PCRTC0[0x081C/4] & 0xFFFFFFFC;
if(on && (level > BACKLIGHT_OFF)) {
tmp_pcrt |= 0x1;
tmp_pmc |= (1 << 31); // backlight bit
tmp_pmc |= riva_backlight_levels[level-1] << 16; // level
}
par->riva.PCRTC0[0x081C/4] = tmp_pcrt;
par->riva.PMC[0x10F0/4] = tmp_pmc;
return 0;
}
static int riva_set_backlight_level(int level, void *data)
{
return riva_set_backlight_enable(1, level, data);
}
#endif /* CONFIG_PMAC_BACKLIGHT */
/* ------------------------------------------------------------------------- *
*
* framebuffer operations
@ -1247,10 +1338,15 @@ static int rivafb_blank(int blank, struct fb_info *info)
SEQout(par, 0x01, tmp);
CRTCout(par, 0x1a, vesa);
#ifdef CONFIG_PMAC_BACKLIGHT
if ( par->FlatPanel && machine_is(powermac)) {
set_backlight_enable(!blank);
#ifdef CONFIG_FB_RIVA_BACKLIGHT
mutex_lock(&info->bl_mutex);
if (info->bl_dev) {
down(&info->bl_dev->sem);
info->bl_dev->props->power = blank;
info->bl_dev->props->update_status(info->bl_dev);
up(&info->bl_dev->sem);
}
mutex_unlock(&info->bl_mutex);
#endif
NVTRACE_LEAVE();
@ -2037,11 +2133,9 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
RIVAFB_VERSION,
info->fix.smem_len / (1024 * 1024),
info->fix.smem_start);
#ifdef CONFIG_PMAC_BACKLIGHT
if (default_par->FlatPanel && machine_is(powermac))
register_backlight_controller(&riva_backlight_controller,
default_par, "mnca");
#endif
riva_bl_init(info->par);
NVTRACE_LEAVE();
return 0;
@ -2074,6 +2168,8 @@ static void __exit rivafb_remove(struct pci_dev *pd)
NVTRACE_ENTER();
riva_bl_exit(par);
#ifdef CONFIG_FB_RIVA_I2C
riva_delete_i2c_busses(par);
kfree(par->EDID);