in fbdev/aty/atyfb_base.c [3146:3374]
static void aty_init_lcd(struct atyfb_par *par, u32 bios_base)
{
u32 driv_inf_tab, sig;
u16 lcd_ofs;
/*
* To support an LCD panel, we should know it's dimensions and
* it's desired pixel clock.
* There are two ways to do it:
* - Check the startup video mode and calculate the panel
* size from it. This is unreliable.
* - Read it from the driver information table in the video BIOS.
*/
/* Address of driver information table is at offset 0x78. */
driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78));
/* Check for the driver information table signature. */
sig = *(u32 *)driv_inf_tab;
if ((sig == 0x54504c24) || /* Rage LT pro */
(sig == 0x544d5224) || /* Rage mobility */
(sig == 0x54435824) || /* Rage XC */
(sig == 0x544c5824)) { /* Rage XL */
PRINTKI("BIOS contains driver information table.\n");
lcd_ofs = *(u16 *)(driv_inf_tab + 10);
par->lcd_table = 0;
if (lcd_ofs != 0)
par->lcd_table = bios_base + lcd_ofs;
}
if (par->lcd_table != 0) {
char model[24];
char strbuf[16];
char refresh_rates_buf[100];
int id, tech, f, i, m, default_refresh_rate;
char *txtcolour;
char *txtmonitor;
char *txtdual;
char *txtformat;
u16 width, height, panel_type, refresh_rates;
u16 *lcdmodeptr;
u32 format;
u8 lcd_refresh_rates[16] = { 50, 56, 60, 67, 70, 72, 75, 76, 85,
90, 100, 120, 140, 150, 160, 200 };
/*
* The most important information is the panel size at
* offset 25 and 27, but there's some other nice information
* which we print to the screen.
*/
id = *(u8 *)par->lcd_table;
strncpy(model, (char *)par->lcd_table+1, 24);
model[23] = 0;
width = par->lcd_width = *(u16 *)(par->lcd_table+25);
height = par->lcd_height = *(u16 *)(par->lcd_table+27);
panel_type = *(u16 *)(par->lcd_table+29);
if (panel_type & 1)
txtcolour = "colour";
else
txtcolour = "monochrome";
if (panel_type & 2)
txtdual = "dual (split) ";
else
txtdual = "";
tech = (panel_type >> 2) & 63;
switch (tech) {
case 0:
txtmonitor = "passive matrix";
break;
case 1:
txtmonitor = "active matrix";
break;
case 2:
txtmonitor = "active addressed STN";
break;
case 3:
txtmonitor = "EL";
break;
case 4:
txtmonitor = "plasma";
break;
default:
txtmonitor = "unknown";
}
format = *(u32 *)(par->lcd_table+57);
if (tech == 0 || tech == 2) {
switch (format & 7) {
case 0:
txtformat = "12 bit interface";
break;
case 1:
txtformat = "16 bit interface";
break;
case 2:
txtformat = "24 bit interface";
break;
default:
txtformat = "unknown format";
}
} else {
switch (format & 7) {
case 0:
txtformat = "8 colours";
break;
case 1:
txtformat = "512 colours";
break;
case 2:
txtformat = "4096 colours";
break;
case 4:
txtformat = "262144 colours (LT mode)";
break;
case 5:
txtformat = "16777216 colours";
break;
case 6:
txtformat = "262144 colours (FDPI-2 mode)";
break;
default:
txtformat = "unknown format";
}
}
PRINTKI("%s%s %s monitor detected: %s\n",
txtdual, txtcolour, txtmonitor, model);
PRINTKI(" id=%d, %dx%d pixels, %s\n",
id, width, height, txtformat);
refresh_rates_buf[0] = 0;
refresh_rates = *(u16 *)(par->lcd_table+62);
m = 1;
f = 0;
for (i = 0; i < 16; i++) {
if (refresh_rates & m) {
if (f == 0) {
sprintf(strbuf, "%d",
lcd_refresh_rates[i]);
f++;
} else {
sprintf(strbuf, ",%d",
lcd_refresh_rates[i]);
}
strcat(refresh_rates_buf, strbuf);
}
m = m << 1;
}
default_refresh_rate = (*(u8 *)(par->lcd_table+61) & 0xf0) >> 4;
PRINTKI(" supports refresh rates [%s], default %d Hz\n",
refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]);
par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate];
/*
* We now need to determine the crtc parameters for the
* LCD monitor. This is tricky, because they are not stored
* individually in the BIOS. Instead, the BIOS contains a
* table of display modes that work for this monitor.
*
* The idea is that we search for a mode of the same dimensions
* as the dimensions of the LCD monitor. Say our LCD monitor
* is 800x600 pixels, we search for a 800x600 monitor.
* The CRTC parameters we find here are the ones that we need
* to use to simulate other resolutions on the LCD screen.
*/
lcdmodeptr = (u16 *)(par->lcd_table + 64);
while (*lcdmodeptr != 0) {
u32 modeptr;
u16 mwidth, mheight, lcd_hsync_start, lcd_vsync_start;
modeptr = bios_base + *lcdmodeptr;
mwidth = *((u16 *)(modeptr+0));
mheight = *((u16 *)(modeptr+2));
if (mwidth == width && mheight == height) {
par->lcd_pixclock = 100000000 / *((u16 *)(modeptr+9));
par->lcd_htotal = *((u16 *)(modeptr+17)) & 511;
par->lcd_hdisp = *((u16 *)(modeptr+19)) & 511;
lcd_hsync_start = *((u16 *)(modeptr+21)) & 511;
par->lcd_hsync_dly = (*((u16 *)(modeptr+21)) >> 9) & 7;
par->lcd_hsync_len = *((u8 *)(modeptr+23)) & 63;
par->lcd_vtotal = *((u16 *)(modeptr+24)) & 2047;
par->lcd_vdisp = *((u16 *)(modeptr+26)) & 2047;
lcd_vsync_start = *((u16 *)(modeptr+28)) & 2047;
par->lcd_vsync_len = (*((u16 *)(modeptr+28)) >> 11) & 31;
par->lcd_htotal = (par->lcd_htotal + 1) * 8;
par->lcd_hdisp = (par->lcd_hdisp + 1) * 8;
lcd_hsync_start = (lcd_hsync_start + 1) * 8;
par->lcd_hsync_len = par->lcd_hsync_len * 8;
par->lcd_vtotal++;
par->lcd_vdisp++;
lcd_vsync_start++;
par->lcd_right_margin = lcd_hsync_start - par->lcd_hdisp;
par->lcd_lower_margin = lcd_vsync_start - par->lcd_vdisp;
par->lcd_hblank_len = par->lcd_htotal - par->lcd_hdisp;
par->lcd_vblank_len = par->lcd_vtotal - par->lcd_vdisp;
break;
}
lcdmodeptr++;
}
if (*lcdmodeptr == 0) {
PRINTKE("LCD monitor CRTC parameters not found!!!\n");
/* To do: Switch to CRT if possible. */
} else {
PRINTKI(" LCD CRTC parameters: %d.%d %d %d %d %d %d %d %d %d\n",
1000000 / par->lcd_pixclock, 1000000 % par->lcd_pixclock,
par->lcd_hdisp,
par->lcd_hdisp + par->lcd_right_margin,
par->lcd_hdisp + par->lcd_right_margin
+ par->lcd_hsync_dly + par->lcd_hsync_len,
par->lcd_htotal,
par->lcd_vdisp,
par->lcd_vdisp + par->lcd_lower_margin,
par->lcd_vdisp + par->lcd_lower_margin + par->lcd_vsync_len,
par->lcd_vtotal);
PRINTKI(" : %d %d %d %d %d %d %d %d %d\n",
par->lcd_pixclock,
par->lcd_hblank_len - (par->lcd_right_margin +
par->lcd_hsync_dly + par->lcd_hsync_len),
par->lcd_hdisp,
par->lcd_right_margin,
par->lcd_hsync_len,
par->lcd_vblank_len - (par->lcd_lower_margin + par->lcd_vsync_len),
par->lcd_vdisp,
par->lcd_lower_margin,
par->lcd_vsync_len);
}
}
}