[PATCH] savagefb: Driver updates
- Fallback to firmware EDID if chipset has no DDC/I2C support or if I2C probing failed - Add fb_blank hook - Fix savagefb_suspend/resume to enable driver to successfully suspend and resume from S3, memory or disk Signed-off-by: Antonino Daplas <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
committed by
Linus Torvalds
parent
5e518d7672
commit
13776711ce
@@ -259,8 +259,9 @@ static u8 *savage_do_probe_i2c_edid(struct savagefb_i2c_chan *chan)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
int savagefb_probe_i2c_connector(struct savagefb_par *par, u8 **out_edid)
|
int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid)
|
||||||
{
|
{
|
||||||
|
struct savagefb_par *par = info->par;
|
||||||
u8 *edid = NULL;
|
u8 *edid = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -270,12 +271,19 @@ int savagefb_probe_i2c_connector(struct savagefb_par *par, u8 **out_edid)
|
|||||||
if (edid)
|
if (edid)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!edid) {
|
||||||
|
/* try to get from firmware */
|
||||||
|
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
||||||
|
if (edid)
|
||||||
|
memcpy(edid, fb_firmware_edid(info->device),
|
||||||
|
EDID_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
if (out_edid)
|
if (out_edid)
|
||||||
*out_edid = edid;
|
*out_edid = edid;
|
||||||
if (!edid)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
return (edid) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
#define S3_SAVAGE_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000))
|
#define S3_SAVAGE_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000))
|
||||||
|
|
||||||
|
#define S3_MOBILE_TWISTER_SERIES(chip) ((chip==S3_TWISTER) || (chip == S3_PROSAVAGEDDR))
|
||||||
|
|
||||||
/* Chip tags. These are used to group the adapters into
|
/* Chip tags. These are used to group the adapters into
|
||||||
* related families.
|
* related families.
|
||||||
@@ -73,6 +74,8 @@ typedef enum {
|
|||||||
S3_PROSAVAGE,
|
S3_PROSAVAGE,
|
||||||
S3_SUPERSAVAGE,
|
S3_SUPERSAVAGE,
|
||||||
S3_SAVAGE2000,
|
S3_SAVAGE2000,
|
||||||
|
S3_PROSAVAGEDDR,
|
||||||
|
S3_TWISTER,
|
||||||
S3_LAST
|
S3_LAST
|
||||||
} savage_chipset;
|
} savage_chipset;
|
||||||
|
|
||||||
@@ -128,6 +131,10 @@ typedef enum {
|
|||||||
#define BCI_CMD_SET_ROP(cmd, rop) ((cmd) |= ((rop & 0xFF) << 16))
|
#define BCI_CMD_SET_ROP(cmd, rop) ((cmd) |= ((rop & 0xFF) << 16))
|
||||||
#define BCI_CMD_SEND_COLOR 0x00008000
|
#define BCI_CMD_SEND_COLOR 0x00008000
|
||||||
|
|
||||||
|
#define DISP_CRT 1
|
||||||
|
#define DISP_LCD 2
|
||||||
|
#define DISP_DFP 3
|
||||||
|
|
||||||
struct xtimings {
|
struct xtimings {
|
||||||
unsigned int Clock;
|
unsigned int Clock;
|
||||||
unsigned int HDisplay;
|
unsigned int HDisplay;
|
||||||
@@ -166,6 +173,10 @@ struct savagefb_par {
|
|||||||
struct savagefb_i2c_chan chan;
|
struct savagefb_i2c_chan chan;
|
||||||
unsigned char *edid;
|
unsigned char *edid;
|
||||||
u32 pseudo_palette[16];
|
u32 pseudo_palette[16];
|
||||||
|
int pm_state;
|
||||||
|
int display_type;
|
||||||
|
int dvi;
|
||||||
|
int crtonly;
|
||||||
int dacSpeedBpp;
|
int dacSpeedBpp;
|
||||||
int maxClock;
|
int maxClock;
|
||||||
int minClock;
|
int minClock;
|
||||||
@@ -338,7 +349,7 @@ do { \
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int savagefb_probe_i2c_connector(struct savagefb_par *par,
|
extern int savagefb_probe_i2c_connector(struct fb_info *info,
|
||||||
u8 **out_edid);
|
u8 **out_edid);
|
||||||
extern void savagefb_create_i2c_busses(struct fb_info *info);
|
extern void savagefb_create_i2c_busses(struct fb_info *info);
|
||||||
extern void savagefb_delete_i2c_busses(struct fb_info *info);
|
extern void savagefb_delete_i2c_busses(struct fb_info *info);
|
||||||
|
@@ -1400,6 +1400,58 @@ static int savagefb_pan_display (struct fb_var_screeninfo *var,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int savagefb_blank(int blank, struct fb_info *info)
|
||||||
|
{
|
||||||
|
struct savagefb_par *par = info->par;
|
||||||
|
u8 sr8 = 0, srd = 0;
|
||||||
|
|
||||||
|
if (par->display_type == DISP_CRT) {
|
||||||
|
vga_out8(0x3c4, 0x08);
|
||||||
|
sr8 = vga_in8(0x3c5);
|
||||||
|
sr8 |= 0x06;
|
||||||
|
vga_out8(0x3c5, sr8);
|
||||||
|
vga_out8(0x3c4, 0x0d);
|
||||||
|
srd = vga_in8(0x3c5);
|
||||||
|
srd &= 0x03;
|
||||||
|
|
||||||
|
switch (blank) {
|
||||||
|
case FB_BLANK_UNBLANK:
|
||||||
|
case FB_BLANK_NORMAL:
|
||||||
|
break;
|
||||||
|
case FB_BLANK_VSYNC_SUSPEND:
|
||||||
|
srd |= 0x10;
|
||||||
|
break;
|
||||||
|
case FB_BLANK_HSYNC_SUSPEND:
|
||||||
|
srd |= 0x40;
|
||||||
|
break;
|
||||||
|
case FB_BLANK_POWERDOWN:
|
||||||
|
srd |= 0x50;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_out8(0x3c4, 0x0d);
|
||||||
|
vga_out8(0x3c5, srd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (par->display_type == DISP_LCD ||
|
||||||
|
par->display_type == DISP_DFP) {
|
||||||
|
switch(blank) {
|
||||||
|
case FB_BLANK_UNBLANK:
|
||||||
|
case FB_BLANK_NORMAL:
|
||||||
|
vga_out8(0x3c4, 0x31); /* SR31 bit 4 - FP enable */
|
||||||
|
vga_out8(0x3c5, vga_in8(0x3c5) | 0x10);
|
||||||
|
break;
|
||||||
|
case FB_BLANK_VSYNC_SUSPEND:
|
||||||
|
case FB_BLANK_HSYNC_SUSPEND:
|
||||||
|
case FB_BLANK_POWERDOWN:
|
||||||
|
vga_out8(0x3c4, 0x31); /* SR31 bit 4 - FP enable */
|
||||||
|
vga_out8(0x3c5, vga_in8(0x3c5) & ~0x10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (blank == FB_BLANK_NORMAL) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct fb_ops savagefb_ops = {
|
static struct fb_ops savagefb_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
@@ -1407,6 +1459,7 @@ static struct fb_ops savagefb_ops = {
|
|||||||
.fb_set_par = savagefb_set_par,
|
.fb_set_par = savagefb_set_par,
|
||||||
.fb_setcolreg = savagefb_setcolreg,
|
.fb_setcolreg = savagefb_setcolreg,
|
||||||
.fb_pan_display = savagefb_pan_display,
|
.fb_pan_display = savagefb_pan_display,
|
||||||
|
.fb_blank = savagefb_blank,
|
||||||
#if defined(CONFIG_FB_SAVAGE_ACCEL)
|
#if defined(CONFIG_FB_SAVAGE_ACCEL)
|
||||||
.fb_fillrect = savagefb_fillrect,
|
.fb_fillrect = savagefb_fillrect,
|
||||||
.fb_copyarea = savagefb_copyarea,
|
.fb_copyarea = savagefb_copyarea,
|
||||||
@@ -1583,8 +1636,7 @@ static int __devinit savage_init_hw (struct savagefb_par *par)
|
|||||||
static unsigned char RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 };
|
static unsigned char RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 };
|
||||||
static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
|
static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
|
||||||
static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
|
static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
|
||||||
|
int videoRam, videoRambytes, dvi;
|
||||||
int videoRam, videoRambytes;
|
|
||||||
|
|
||||||
DBG("savage_init_hw");
|
DBG("savage_init_hw");
|
||||||
|
|
||||||
@@ -1705,6 +1757,30 @@ static int __devinit savage_init_hw (struct savagefb_par *par)
|
|||||||
printk (KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
|
printk (KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
|
||||||
par->MCLK);
|
par->MCLK);
|
||||||
|
|
||||||
|
/* check for DVI/flat panel */
|
||||||
|
dvi = 0;
|
||||||
|
|
||||||
|
if (par->chip == S3_SAVAGE4) {
|
||||||
|
unsigned char sr30 = 0x00;
|
||||||
|
|
||||||
|
vga_out8(0x3c4, 0x30);
|
||||||
|
/* clear bit 1 */
|
||||||
|
vga_out8(0x3c5, vga_in8(0x3c5) & ~0x02);
|
||||||
|
sr30 = vga_in8(0x3c5);
|
||||||
|
if (sr30 & 0x02 /*0x04 */) {
|
||||||
|
dvi = 1;
|
||||||
|
printk("savagefb: Digital Flat Panel Detected\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
|
||||||
|
(S3_MOBILE_TWISTER_SERIES(par->chip) && !par->crtonly))
|
||||||
|
par->display_type = DISP_LCD;
|
||||||
|
else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi))
|
||||||
|
par->display_type = DISP_DFP;
|
||||||
|
else
|
||||||
|
par->display_type = DISP_CRT;
|
||||||
|
|
||||||
/* Check LCD panel parrmation */
|
/* Check LCD panel parrmation */
|
||||||
|
|
||||||
if (par->chip == S3_SAVAGE_MX) {
|
if (par->chip == S3_SAVAGE_MX) {
|
||||||
@@ -1759,7 +1835,8 @@ static int __devinit savage_init_hw (struct savagefb_par *par)
|
|||||||
par->SavagePanelWidth = panelX;
|
par->SavagePanelWidth = panelX;
|
||||||
par->SavagePanelHeight = panelY;
|
par->SavagePanelHeight = panelY;
|
||||||
|
|
||||||
}
|
} else
|
||||||
|
par->display_type = DISP_CRT;
|
||||||
}
|
}
|
||||||
|
|
||||||
savage_get_default_par (par);
|
savage_get_default_par (par);
|
||||||
@@ -1845,15 +1922,15 @@ static int __devinit savage_init_fb_info (struct fb_info *info,
|
|||||||
snprintf (info->fix.id, 16, "ProSavageKM");
|
snprintf (info->fix.id, 16, "ProSavageKM");
|
||||||
break;
|
break;
|
||||||
case FB_ACCEL_S3TWISTER_P:
|
case FB_ACCEL_S3TWISTER_P:
|
||||||
par->chip = S3_PROSAVAGE;
|
par->chip = S3_TWISTER;
|
||||||
snprintf (info->fix.id, 16, "TwisterP");
|
snprintf (info->fix.id, 16, "TwisterP");
|
||||||
break;
|
break;
|
||||||
case FB_ACCEL_S3TWISTER_K:
|
case FB_ACCEL_S3TWISTER_K:
|
||||||
par->chip = S3_PROSAVAGE;
|
par->chip = S3_TWISTER;
|
||||||
snprintf (info->fix.id, 16, "TwisterK");
|
snprintf (info->fix.id, 16, "TwisterK");
|
||||||
break;
|
break;
|
||||||
case FB_ACCEL_PROSAVAGE_DDR:
|
case FB_ACCEL_PROSAVAGE_DDR:
|
||||||
par->chip = S3_PROSAVAGE;
|
par->chip = S3_PROSAVAGEDDR;
|
||||||
snprintf (info->fix.id, 16, "ProSavageDDR");
|
snprintf (info->fix.id, 16, "ProSavageDDR");
|
||||||
break;
|
break;
|
||||||
case FB_ACCEL_PROSAVAGE_DDRK:
|
case FB_ACCEL_PROSAVAGE_DDRK:
|
||||||
@@ -1959,7 +2036,8 @@ static int __devinit savagefb_probe (struct pci_dev* dev,
|
|||||||
INIT_LIST_HEAD(&info->modelist);
|
INIT_LIST_HEAD(&info->modelist);
|
||||||
#if defined(CONFIG_FB_SAVAGE_I2C)
|
#if defined(CONFIG_FB_SAVAGE_I2C)
|
||||||
savagefb_create_i2c_busses(info);
|
savagefb_create_i2c_busses(info);
|
||||||
savagefb_probe_i2c_connector(par, &par->edid);
|
savagefb_probe_i2c_connector(info, &par->edid);
|
||||||
|
kfree(par->edid);
|
||||||
fb_edid_to_monspecs(par->edid, &info->monspecs);
|
fb_edid_to_monspecs(par->edid, &info->monspecs);
|
||||||
fb_videomode_to_modelist(info->monspecs.modedb,
|
fb_videomode_to_modelist(info->monspecs.modedb,
|
||||||
info->monspecs.modedb_len,
|
info->monspecs.modedb_len,
|
||||||
@@ -2111,13 +2189,30 @@ static int savagefb_suspend (struct pci_dev* dev, pm_message_t state)
|
|||||||
|
|
||||||
DBG("savagefb_suspend");
|
DBG("savagefb_suspend");
|
||||||
|
|
||||||
acquire_console_sem();
|
|
||||||
fb_set_suspend(info, pci_choose_state(dev, state));
|
|
||||||
savage_disable_mmio(par);
|
|
||||||
release_console_sem();
|
|
||||||
|
|
||||||
|
par->pm_state = state.event;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For PM_EVENT_FREEZE, do not power down so the console
|
||||||
|
* can remain active.
|
||||||
|
*/
|
||||||
|
if (state.event == PM_EVENT_FREEZE) {
|
||||||
|
dev->dev.power.power_state = state;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
acquire_console_sem();
|
||||||
|
fb_set_suspend(info, 1);
|
||||||
|
|
||||||
|
if (info->fbops->fb_sync)
|
||||||
|
info->fbops->fb_sync(info);
|
||||||
|
|
||||||
|
savagefb_blank(FB_BLANK_POWERDOWN, info);
|
||||||
|
savage_disable_mmio(par);
|
||||||
|
pci_save_state(dev);
|
||||||
pci_disable_device(dev);
|
pci_disable_device(dev);
|
||||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||||
|
release_console_sem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -2127,22 +2222,34 @@ static int savagefb_resume (struct pci_dev* dev)
|
|||||||
struct fb_info *info =
|
struct fb_info *info =
|
||||||
(struct fb_info *)pci_get_drvdata(dev);
|
(struct fb_info *)pci_get_drvdata(dev);
|
||||||
struct savagefb_par *par = (struct savagefb_par *)info->par;
|
struct savagefb_par *par = (struct savagefb_par *)info->par;
|
||||||
|
int cur_state = par->pm_state;
|
||||||
|
|
||||||
DBG("savage_resume");
|
DBG("savage_resume");
|
||||||
|
|
||||||
pci_set_power_state(dev, 0);
|
par->pm_state = PM_EVENT_ON;
|
||||||
pci_restore_state(dev);
|
|
||||||
if(pci_enable_device(dev))
|
|
||||||
DBG("err");
|
|
||||||
|
|
||||||
SavagePrintRegs();
|
/*
|
||||||
|
* The adapter was not powered down coming back from a
|
||||||
|
* PM_EVENT_FREEZE.
|
||||||
|
*/
|
||||||
|
if (cur_state == PM_EVENT_FREEZE) {
|
||||||
|
pci_set_power_state(dev, PCI_D0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
acquire_console_sem();
|
acquire_console_sem();
|
||||||
|
|
||||||
|
pci_set_power_state(dev, PCI_D0);
|
||||||
|
pci_restore_state(dev);
|
||||||
|
|
||||||
|
if(pci_enable_device(dev))
|
||||||
|
DBG("err");
|
||||||
|
|
||||||
|
pci_set_master(dev);
|
||||||
savage_enable_mmio(par);
|
savage_enable_mmio(par);
|
||||||
savage_init_hw(par);
|
savage_init_hw(par);
|
||||||
savagefb_set_par (info);
|
savagefb_set_par (info);
|
||||||
|
savagefb_blank(FB_BLANK_UNBLANK, info);
|
||||||
fb_set_suspend (info, 0);
|
fb_set_suspend (info, 0);
|
||||||
release_console_sem();
|
release_console_sem();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user