Convert Thomas's radeon driver to our build structure.
Added some card ids. Still needs work to properly enable logging and extra settings. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6982 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
SubDir OBOS_TOP src add-ons kernel drivers graphics radeon ;
|
||||||
|
|
||||||
|
UsePrivateHeaders graphics ;
|
||||||
|
UsePrivateHeaders [ FDirName graphics radeon ] ;
|
||||||
|
|
||||||
|
R5KernelAddon radeon.driver : kernel drivers bin :
|
||||||
|
driver.c
|
||||||
|
agp.c
|
||||||
|
bios.c
|
||||||
|
detect.c
|
||||||
|
dma.c
|
||||||
|
global_data.c
|
||||||
|
init.c
|
||||||
|
irq.c
|
||||||
|
memmgr.c
|
||||||
|
;
|
||||||
|
|
||||||
|
#Package openbeos-radeon-cvs :
|
||||||
|
# README.html UPDATE.html ;
|
||||||
|
Package openbeos-radeon-cvs :
|
||||||
|
radeon.driver :
|
||||||
|
boot home config add-ons kernel drivers bin ;
|
||||||
|
Package openbeos-radeon-cvs :
|
||||||
|
<kernel!drivers!dev!graphics>radeon.driver :
|
||||||
|
boot home config add-ons kernel drivers dev graphics ;
|
||||||
|
#Package openbeos-radeon-cvs :
|
||||||
|
# radeon.settings :
|
||||||
|
# boot home config settings kernel drivers ;
|
||||||
|
|
||||||
|
# Link to kernel/drivers/dev/graphics
|
||||||
|
{
|
||||||
|
local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev graphics ] ;
|
||||||
|
local instDriver = <kernel!drivers!dev!graphics>radeon.driver ;
|
||||||
|
MakeLocate $(instDriver) : $(dir) ;
|
||||||
|
RelSymLink $(instDriver) : radeon.driver ;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
AGP fix. Some motherboard BIOSes enable FastWrite even
|
||||||
|
though the graphics card doesn't support it. Here, we'll
|
||||||
|
fix that (hopefully it is generic enough).
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
|
||||||
|
|
||||||
|
// missing PCI definitions
|
||||||
|
#define PCI_status_cap_list 0x10 /* Support Capability List */
|
||||||
|
|
||||||
|
#define PCI_header_type_normal 0
|
||||||
|
#define PCI_header_type_bridge 1
|
||||||
|
//#define PCI_header_type_cardbus 2
|
||||||
|
|
||||||
|
#define PCI_capability_list 0x34 /* Offset of first capability list entry */
|
||||||
|
#define PCI_cb_capability_list 0x14
|
||||||
|
|
||||||
|
#define PCI_cap_list_id 0 /* Capability ID */
|
||||||
|
#define PCI_cap_id_pm 0x01 /* Power Management */
|
||||||
|
#define PCI_cap_id_agp 0x02 /* Accelerated Graphics Port */
|
||||||
|
#define PCI_cap_id_vpd 0x03 /* Vital Product Data */
|
||||||
|
#define PCI_cap_id_slotid 0x04 /* Slot Identification */
|
||||||
|
#define PCI_cap_id_msi 0x05 /* Message Signalled Interrupts */
|
||||||
|
#define PCI_cap_id_chswp 0x06 /* CompactPCI HotSwap */
|
||||||
|
#define PCI_cap_list_next 1 /* Next capability in the list */
|
||||||
|
#define PCI_cap_flags 2 /* Capability defined flags (16 bits) */
|
||||||
|
#define PCI_cap_sizeof 4
|
||||||
|
|
||||||
|
|
||||||
|
#define PCI_agp_status 4 /* Status register */
|
||||||
|
#define PCI_agp_status_rq_mask 0xff000000 /* Maximum number of requests - 1 */
|
||||||
|
#define PCI_agp_status_sba 0x0200 /* Sideband addressing supported */
|
||||||
|
#define PCI_agp_status_64bit 0x0020 /* 64-bit addressing supported */
|
||||||
|
#define PCI_agp_status_fw 0x0010 /* FW transfers supported */
|
||||||
|
#define PCI_agp_status_rate4 0x0004 /* 4x transfer rate supported */
|
||||||
|
#define PCI_agp_status_rate2 0x0002 /* 2x transfer rate supported */
|
||||||
|
#define PCI_agp_status_rate1 0x0001 /* 1x transfer rate supported */
|
||||||
|
|
||||||
|
#define PCI_agp_command 8 /* Control register */
|
||||||
|
#define PCI_agp_command_rq_mask 0xff000000 /* Master: Maximum number of requests */
|
||||||
|
#define PCI_agp_command_sba 0x0200 /* Sideband addressing enabled */
|
||||||
|
#define PCI_agp_command_agp 0x0100 /* Allow processing of AGP transactions */
|
||||||
|
#define PCI_agp_command_64bit 0x0020 /* Allow processing of 64-bit addresses */
|
||||||
|
#define PCI_agp_command_fw 0x0010 /* Force FW transfers */
|
||||||
|
#define PCI_agp_command_rate4 0x0004 /* Use 4x rate */
|
||||||
|
#define PCI_agp_command_rate2 0x0002 /* Use 2x rate */
|
||||||
|
#define PCI_agp_command_rate1 0x0001 /* Use 1x rate */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// helper macros for easier PCI access
|
||||||
|
#define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
|
||||||
|
#define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
|
||||||
|
|
||||||
|
int find_capability( pci_info *pcii, uint8 capability );
|
||||||
|
void Radeon_Fix_AGP( void );
|
||||||
|
|
||||||
|
|
||||||
|
// show AGP capabilities
|
||||||
|
static void show_agp_status( uint32 status )
|
||||||
|
{
|
||||||
|
SHOW_FLOW( 3, "Status (%08lx): %s%s%s%s%s%s", status,
|
||||||
|
(status & PCI_agp_status_sba) != 0 ? "Sideband addressing " : "",
|
||||||
|
(status & PCI_agp_status_64bit) != 0 ? "64-bit " : "",
|
||||||
|
(status & PCI_agp_status_fw) != 0 ? "FastWrite " : "",
|
||||||
|
(status & PCI_agp_status_rate4) != 0 ? "4x " : "",
|
||||||
|
(status & PCI_agp_status_rate2) != 0 ? "2x " : "",
|
||||||
|
(status & PCI_agp_status_rate1) != 0 ? "1x " : "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// show AGP settings
|
||||||
|
static void show_agp_command( uint32 command )
|
||||||
|
{
|
||||||
|
SHOW_FLOW( 3, "Command (%08lx): %s%s%s%s%s%s%s", command,
|
||||||
|
(command & PCI_agp_command_sba) != 0 ? "Sideband addressing " : "",
|
||||||
|
(command & PCI_agp_command_agp) != 0 ? "AGP-Enabled " : "AGP-Disabled ",
|
||||||
|
(command & PCI_agp_command_64bit) != 0 ? "64-bit " : "",
|
||||||
|
(command & PCI_agp_command_fw) != 0 ? "FastWrite " : "",
|
||||||
|
(command & PCI_agp_command_rate4) != 0 ? "4x " : "",
|
||||||
|
(command & PCI_agp_command_rate2) != 0 ? "2x " : "",
|
||||||
|
(command & PCI_agp_command_rate1) != 0 ? "1x " : "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// find PCI capability
|
||||||
|
int find_capability( pci_info *pcii, uint8 capability )
|
||||||
|
{
|
||||||
|
int try_count;
|
||||||
|
uint16 status;
|
||||||
|
uint8 pos;
|
||||||
|
|
||||||
|
// check whether PCI capabilities are supported at all
|
||||||
|
status = get_pci( PCI_status, 2 );
|
||||||
|
|
||||||
|
if( (status & PCI_status_cap_list) == 0 )
|
||||||
|
return B_NAME_NOT_FOUND;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "Device supports capabilities" );
|
||||||
|
|
||||||
|
// get offset of first capability in list
|
||||||
|
switch( pcii->header_type & PCI_header_type_mask ) {
|
||||||
|
case PCI_header_type_normal:
|
||||||
|
case PCI_header_type_bridge:
|
||||||
|
pos = get_pci( PCI_capability_list, 1 );
|
||||||
|
break;
|
||||||
|
case PCI_header_type_cardbus:
|
||||||
|
pos = get_pci( PCI_cb_capability_list, 1 );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SHOW_FLOW( 3, "Unknown type (%x)", pcii->header_type & PCI_header_type_mask );
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search for whished capability in linked list
|
||||||
|
for( try_count = 48; try_count > 0 && pos >= 0x40; --try_count ) {
|
||||||
|
uint8 id;
|
||||||
|
|
||||||
|
pos &= ~3;
|
||||||
|
|
||||||
|
id = get_pci( pos + PCI_cap_list_id, 1 );
|
||||||
|
if( id == 0xff )
|
||||||
|
return B_NAME_NOT_FOUND;
|
||||||
|
|
||||||
|
if( id == capability ) {
|
||||||
|
SHOW_FLOW( 3, "Found capability %d", capability );
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "Ignored capability %d", id );
|
||||||
|
|
||||||
|
pos = get_pci( pos + PCI_cap_list_next, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_NAME_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fix invalid AGP settings
|
||||||
|
void Radeon_Fix_AGP()
|
||||||
|
{
|
||||||
|
long pci_index;
|
||||||
|
pci_info pci_data, *pcii;
|
||||||
|
|
||||||
|
uint32 common_caps =
|
||||||
|
PCI_agp_status_sba | PCI_agp_status_64bit | PCI_agp_status_fw |
|
||||||
|
PCI_agp_status_rate4 | PCI_agp_status_rate2 | PCI_agp_status_rate1;
|
||||||
|
uint32 read_queue_depth = PCI_agp_status_rq_mask;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 4, "Composing common feature list" );
|
||||||
|
|
||||||
|
// only required to make get_pci/set_pci working
|
||||||
|
pcii = &pci_data;
|
||||||
|
|
||||||
|
// find common feature set
|
||||||
|
for( pci_index = 0;
|
||||||
|
(*pci_bus->get_nth_pci_info)(pci_index, &pci_data) == B_NO_ERROR;
|
||||||
|
++pci_index )
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "Checking bus %d, device %d, function %d (vendor_id=%04x, device_id=%04x):",
|
||||||
|
pcii->bus, pcii->device, pcii->function,
|
||||||
|
pcii->vendor_id, pcii->device_id );
|
||||||
|
|
||||||
|
offset = find_capability( pcii, PCI_cap_id_agp );
|
||||||
|
|
||||||
|
if( offset > 0 ) {
|
||||||
|
uint32 agp_status, agp_command;
|
||||||
|
|
||||||
|
agp_status = get_pci( offset + PCI_agp_status, 4 );
|
||||||
|
agp_command = get_pci( offset + PCI_agp_command, 4 );
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "bus %d, device %d, function %d (vendor_id=%04x, device_id=%04x):",
|
||||||
|
pcii->bus, pcii->device, pcii->function,
|
||||||
|
pcii->vendor_id, pcii->device_id );
|
||||||
|
show_agp_status( agp_status );
|
||||||
|
show_agp_command( agp_command );
|
||||||
|
|
||||||
|
common_caps &= agp_status;
|
||||||
|
read_queue_depth = min( read_queue_depth, agp_status & PCI_agp_status_rq_mask );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "Combined:" );
|
||||||
|
show_agp_command( common_caps );
|
||||||
|
|
||||||
|
// choose features that all devices support
|
||||||
|
for( pci_index = 0;
|
||||||
|
(*pci_bus->get_nth_pci_info)(pci_index, &pci_data) == B_NO_ERROR;
|
||||||
|
++pci_index )
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
offset = find_capability( pcii, PCI_cap_id_agp );
|
||||||
|
|
||||||
|
if( offset > 0 ) {
|
||||||
|
SHOW_FLOW( 3, "Modifying bus %d, device %d, function %d (vendor_id=%04x, device_id=%04x):",
|
||||||
|
pcii->bus, pcii->device, pcii->function,
|
||||||
|
pcii->vendor_id, pcii->device_id );
|
||||||
|
|
||||||
|
set_pci( offset + PCI_agp_command, 4, common_caps | read_queue_depth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,572 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
BIOS detection and retrieval of vital data
|
||||||
|
|
||||||
|
Most of this data should be gathered directly,
|
||||||
|
especially monitor detection should be done on
|
||||||
|
demand so not all monitors need to be connected
|
||||||
|
during boot
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
#include <mmio.h>
|
||||||
|
#include <bios_regs.h>
|
||||||
|
#include <config_regs.h>
|
||||||
|
#include <memcntrl_regs.h>
|
||||||
|
#include <fp_regs.h>
|
||||||
|
#include <crtc_regs.h>
|
||||||
|
#include <radeon_bios.h>
|
||||||
|
//#include "../common/utils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char ati_rom_sig[] = "761295520";
|
||||||
|
static const char *radeon_sig[] = {
|
||||||
|
"RG6", // r200
|
||||||
|
"RADEON", // r100
|
||||||
|
"M6", // mobile version of r100
|
||||||
|
// probably an M6P;
|
||||||
|
// anyway - this is the card I wrote this driver for!
|
||||||
|
// (perhaps ATI tries to make the card incompatible to standard drivers)
|
||||||
|
"P6",
|
||||||
|
"RV200", // rv200
|
||||||
|
"RV100", // rv100
|
||||||
|
"M7", // m7
|
||||||
|
"RV250", // rv250 R9100
|
||||||
|
"V280", // RV280 R9200
|
||||||
|
"R300", // R300 R9500 / R9700
|
||||||
|
"R350", // R350 R9800
|
||||||
|
"R360", // R360 R9800 XT
|
||||||
|
"V350", // RV350 R9600
|
||||||
|
"V360", // RV350 R9600 XT :guess
|
||||||
|
"M9" // guess: m9
|
||||||
|
};
|
||||||
|
|
||||||
|
void Radeon_DetectRAM( device_info *di );
|
||||||
|
|
||||||
|
|
||||||
|
// find address of ROM
|
||||||
|
static char *Radeon_FindRom( rom_info *ri )
|
||||||
|
{
|
||||||
|
uint32 segstart;
|
||||||
|
char *rom_base;
|
||||||
|
char *rom;
|
||||||
|
int stage;
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
for( segstart = 0x000c0000; segstart < 0x000f0000; segstart += 0x00001000 ) {
|
||||||
|
stage = 1;
|
||||||
|
|
||||||
|
// find ROM
|
||||||
|
rom_base = ri->bios_ptr + segstart - 0xc0000;
|
||||||
|
|
||||||
|
if( *rom_base == 0x55 && ((*(rom_base + 1)) & 0xff) == 0xaa )
|
||||||
|
stage = 2;
|
||||||
|
|
||||||
|
if (stage != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// find signature of ATI
|
||||||
|
rom = rom_base;
|
||||||
|
|
||||||
|
for( i = 0; i < 128 - (int)strlen( ati_rom_sig ) && stage != 3; i++ ) {
|
||||||
|
if( ati_rom_sig[0] == *rom ) {
|
||||||
|
if( strncmp(ati_rom_sig, rom, strlen( ati_rom_sig )) == 0 )
|
||||||
|
stage = 3;
|
||||||
|
}
|
||||||
|
rom++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( stage != 3 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// find signature of card
|
||||||
|
rom = rom_base;
|
||||||
|
|
||||||
|
for( i = 0; (i < 512) && (stage != 4); i++ ) {
|
||||||
|
for( j = 0; j < (int)sizeof( radeon_sig ) / (int)sizeof( radeon_sig[0] ); j++ ) {
|
||||||
|
if( radeon_sig[j][0] == *rom ) {
|
||||||
|
if( strncmp( radeon_sig[j], rom, strlen( radeon_sig[j] )) == 0 ) {
|
||||||
|
SHOW_INFO( 2, "Signature: %s", radeon_sig[j] );
|
||||||
|
stage = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rom++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( stage != 4 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SHOW_INFO( 2, "found ROM @0x%lx", segstart );
|
||||||
|
return rom_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_INFO0( 2, "no ROM found" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PLL info is stored in ROM, probably to easily replace it
|
||||||
|
// and thus produce cards with different timings
|
||||||
|
static void Radeon_GetPLLInfo( device_info *di )
|
||||||
|
{
|
||||||
|
uint8 *bios_header;
|
||||||
|
PLL_BLOCK pll, *pll_info;
|
||||||
|
|
||||||
|
bios_header = di->rom.rom_ptr + *(uint16 *)(di->rom.rom_ptr + 0x48);
|
||||||
|
pll_info = (PLL_BLOCK *)(di->rom.rom_ptr + *(uint16 *)(bios_header + 0x30));
|
||||||
|
|
||||||
|
memcpy( &pll, pll_info, sizeof( pll ));
|
||||||
|
|
||||||
|
di->pll.xclk = (uint32)pll.XCLK;
|
||||||
|
di->pll.ref_freq = (uint32)pll.PCLK_ref_freq;
|
||||||
|
di->pll.ref_div = (uint32)pll.PCLK_ref_divider;
|
||||||
|
di->pll.min_pll_freq = pll.PCLK_min_freq;
|
||||||
|
di->pll.max_pll_freq = pll.PCLK_max_freq;
|
||||||
|
|
||||||
|
SHOW_INFO( 2, "ref_clk=%ld, ref_div=%ld, xclk=%ld, min_freq=%ld, max_freq=%ld from BIOS",
|
||||||
|
di->pll.ref_freq, di->pll.ref_div, di->pll.xclk,
|
||||||
|
di->pll.min_pll_freq, di->pll.max_pll_freq );
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Mon2Str[] = {
|
||||||
|
"N/C",
|
||||||
|
"CRT",
|
||||||
|
"CRT",
|
||||||
|
"Laptop flatpanel",
|
||||||
|
"DVI (flatpanel)",
|
||||||
|
"secondary DVI (flatpanel) - unsupported",
|
||||||
|
"Composite TV - unsupported",
|
||||||
|
"S-Video out - unsupported"
|
||||||
|
};
|
||||||
|
|
||||||
|
// ask BIOS what kind of monitor is connected to each port
|
||||||
|
static void Radeon_GetMonType( device_info *di )
|
||||||
|
{
|
||||||
|
unsigned int tmp;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
di->disp_type[0] = di->disp_type[1] = dt_none;
|
||||||
|
|
||||||
|
if (di->has_crtc2) {
|
||||||
|
tmp = INREG( di->regs, RADEON_BIOS_4_SCRATCH );
|
||||||
|
|
||||||
|
// ordering of "if"s is important are multiple
|
||||||
|
// devices can be concurrently connected to one port
|
||||||
|
// (like both a CRT and a TV)
|
||||||
|
|
||||||
|
// primary port
|
||||||
|
if (tmp & 0x08)
|
||||||
|
di->disp_type[0] = dt_dvi_1;
|
||||||
|
else if (tmp & 0x4)
|
||||||
|
di->disp_type[0] = dt_lvds;
|
||||||
|
else if (tmp & 0x200)
|
||||||
|
di->disp_type[0] = dt_crt_1;
|
||||||
|
else if (tmp & 0x10)
|
||||||
|
di->disp_type[0] = dt_ctv;
|
||||||
|
else if (tmp & 0x20)
|
||||||
|
di->disp_type[0] = dt_stv;
|
||||||
|
|
||||||
|
// secondary port
|
||||||
|
if (tmp & 0x2)
|
||||||
|
di->disp_type[1] = dt_crt_2;
|
||||||
|
else if (tmp & 0x800)
|
||||||
|
di->disp_type[1] = dt_dvi_2;
|
||||||
|
else if (tmp & 0x400)
|
||||||
|
// this is unlikely - I only know about one LVDS unit
|
||||||
|
di->disp_type[1] = dt_lvds;
|
||||||
|
else if (tmp & 0x1000)
|
||||||
|
di->disp_type[1] = dt_ctv;
|
||||||
|
else if (tmp & 0x2000)
|
||||||
|
di->disp_type[1] = dt_stv;
|
||||||
|
} else {
|
||||||
|
// regular Radeon
|
||||||
|
di->disp_type[0] = dt_none;
|
||||||
|
|
||||||
|
tmp = INREG( di->regs, RADEON_FP_GEN_CNTL);
|
||||||
|
|
||||||
|
if( tmp & RADEON_FP_EN_TMDS )
|
||||||
|
di->disp_type[0] = dt_dvi_1;
|
||||||
|
else
|
||||||
|
di->disp_type[0] = dt_crt_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_INFO( 1, "BIOS reports %s on primary and %s on secondary port",
|
||||||
|
Mon2Str[di->disp_type[0]], Mon2Str[di->disp_type[1]]);
|
||||||
|
|
||||||
|
// remove unsupported devices
|
||||||
|
if( di->disp_type[0] >= dt_dvi_2 )
|
||||||
|
di->disp_type[0] = dt_none;
|
||||||
|
if( di->disp_type[1] >= dt_dvi_2 )
|
||||||
|
di->disp_type[1] = dt_none;
|
||||||
|
|
||||||
|
// HACK: overlays can only be shown on first CRTC;
|
||||||
|
// if there's nothing on first port, connect
|
||||||
|
// second port to first CRTC (proper signal routing
|
||||||
|
// is hopefully done by BIOS)
|
||||||
|
if( di->has_crtc2 ) {
|
||||||
|
if( di->disp_type[0] == dt_none && di->disp_type[1] == dt_crt_2 ) {
|
||||||
|
di->disp_type[0] = dt_crt_1;
|
||||||
|
di->disp_type[1] = dt_none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_INFO( 1, "Effective routing: %s on primary and %s on secondary port",
|
||||||
|
Mon2Str[di->disp_type[0]], Mon2Str[di->disp_type[1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get flat panel info (does only make sense for Laptops
|
||||||
|
// with integrated display, but looking for it doesn't hurt,
|
||||||
|
// who knows which strange kind of combination is out there?)
|
||||||
|
static bool Radeon_GetBIOSDFPInfo( device_info *di )
|
||||||
|
{
|
||||||
|
uint8 *bios_header;
|
||||||
|
uint16 fpi_offset;
|
||||||
|
FPI_BLOCK fpi;
|
||||||
|
char panel_name[30];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bios_header = di->rom.rom_ptr + *(uint16 *)(di->rom.rom_ptr + 0x48);
|
||||||
|
|
||||||
|
fpi_offset = *(uint16 *)(bios_header + 0x40);
|
||||||
|
|
||||||
|
if( !fpi_offset ) {
|
||||||
|
di->fp_info.panel_pwr_delay = 200;
|
||||||
|
SHOW_ERROR0( 2, "No Panel Info Table found in BIOS" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy( &fpi, di->rom.rom_ptr + fpi_offset, sizeof( fpi ));
|
||||||
|
|
||||||
|
memcpy( panel_name, &fpi.name, sizeof( fpi.name ) );
|
||||||
|
panel_name[sizeof( fpi.name )] = 0;
|
||||||
|
|
||||||
|
SHOW_INFO( 2, "Panel ID string: %s", panel_name );
|
||||||
|
|
||||||
|
di->fp_info.panel_xres = fpi.panel_xres;
|
||||||
|
di->fp_info.panel_yres = fpi.panel_yres;
|
||||||
|
|
||||||
|
SHOW_INFO( 2, "Panel Size from BIOS: %dx%d",
|
||||||
|
di->fp_info.panel_xres, di->fp_info.panel_yres);
|
||||||
|
|
||||||
|
di->fp_info.panel_pwr_delay = fpi.panel_pwr_delay;
|
||||||
|
if( di->fp_info.panel_pwr_delay > 2000 || di->fp_info.panel_pwr_delay < 0 )
|
||||||
|
di->fp_info.panel_pwr_delay = 2000;
|
||||||
|
|
||||||
|
// there might be multiple supported resolutions stored;
|
||||||
|
// we are looking for native resolution
|
||||||
|
for( i = 0; i < 20; ++i ) {
|
||||||
|
uint16 fpi_timing_ofs;
|
||||||
|
FPI_TIMING_BLOCK fpi_timing;
|
||||||
|
|
||||||
|
fpi_timing_ofs = fpi.fpi_timing_ofs[i];
|
||||||
|
|
||||||
|
if( fpi_timing_ofs == 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
memcpy( &fpi_timing, di->rom.rom_ptr + fpi_timing_ofs, sizeof( fpi_timing ));
|
||||||
|
|
||||||
|
if( fpi_timing.panel_xres != di->fp_info.panel_xres ||
|
||||||
|
fpi_timing.panel_yres != di->fp_info.panel_yres )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
di->fp_info.h_blank = (fpi_timing.h_total - fpi_timing.h_display) * 8;
|
||||||
|
// TBD: seems like upper four bits of hsync_start contain garbage
|
||||||
|
di->fp_info.h_over_plus = ((fpi_timing.h_sync_start & 0xfff) - fpi_timing.h_display - 1) * 8;
|
||||||
|
di->fp_info.h_sync_width = fpi_timing.h_sync_width * 8;
|
||||||
|
di->fp_info.v_blank = fpi_timing.v_total - fpi_timing.v_display;
|
||||||
|
di->fp_info.v_over_plus = (fpi_timing.v_sync & 0x7ff) - fpi_timing.v_display;
|
||||||
|
di->fp_info.v_sync_width = (fpi_timing.v_sync & 0xf800) >> 11;
|
||||||
|
di->fp_info.dot_clock = fpi_timing.dot_clock * 10;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_ERROR0( 2, "Radeon: couldn't get Panel Timing from BIOS" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// try to reverse engineer DFP specification from
|
||||||
|
// timing currently set up in graphics cards registers
|
||||||
|
// (effectively, we hope that BIOS has set it up correctly
|
||||||
|
// and noone has messed registers up yet; let's pray)
|
||||||
|
static void Radeon_RevEnvDFPSize( device_info *di )
|
||||||
|
{
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
/* uint32 r;
|
||||||
|
|
||||||
|
// take a look at flat_panel.c of the accelerant how register values
|
||||||
|
// are calculated - this is the inverse function
|
||||||
|
r = INREG( regs, RADEON_FP_VERT_STRETCH );
|
||||||
|
if( (r & RADEON_VERT_STRETCH_BLEND) == 0 ) {
|
||||||
|
di->fp_info.panel_yres =
|
||||||
|
((r & RADEON_VERT_PANEL_SIZE) >> RADEON_VERT_PANEL_SHIFT) + 1;
|
||||||
|
} else {
|
||||||
|
uint32 v_total = (INREG( regs, RADEON_FP_CRTC_V_TOTAL_DISP )
|
||||||
|
>> RADEON_FP_CRTC_V_DISP_SHIFT) + 1;
|
||||||
|
SHOW_INFO( 2, "stretched mode: v_total=%d", v_total );
|
||||||
|
di->fp_info.panel_yres =
|
||||||
|
(v_total * FIX_SCALE * RADEON_VERT_STRETCH_RATIO_MAX /
|
||||||
|
(r & RADEON_VERT_STRETCH_RATIO_MASK) + FIX_SCALE / 2) >> FIX_SHIFT;
|
||||||
|
// seems to be a BIOS bug - vertical size is 1 point too small
|
||||||
|
// (checked by re-calculating stretch factor)
|
||||||
|
++di->fp_info.panel_yres;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_FP_HORZ_STRETCH );
|
||||||
|
if( (r & RADEON_HORZ_STRETCH_BLEND) == 0 ) {
|
||||||
|
di->fp_info.panel_xres =
|
||||||
|
(((r & RADEON_HORZ_PANEL_SIZE) >> RADEON_HORZ_PANEL_SHIFT) + 1) * 8;
|
||||||
|
} else {
|
||||||
|
uint32 h_total = ((INREG( regs, RADEON_FP_CRTC_H_TOTAL_DISP )
|
||||||
|
>> RADEON_FP_CRTC_H_DISP_SHIFT) + 1) * 8;
|
||||||
|
SHOW_INFO( 2, "stretched mode: h_total=%d", h_total );
|
||||||
|
di->fp_info.panel_xres =
|
||||||
|
(h_total * FIX_SCALE * RADEON_HORZ_STRETCH_RATIO_MAX /
|
||||||
|
(r & RADEON_HORZ_STRETCH_RATIO_MASK) + FIX_SCALE / 2) >> FIX_SHIFT;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
di->fp_info.panel_yres =
|
||||||
|
((INREG( regs, RADEON_FP_VERT_STRETCH ) & RADEON_VERT_PANEL_SIZE)
|
||||||
|
>> RADEON_VERT_PANEL_SHIFT) + 1;
|
||||||
|
|
||||||
|
di->fp_info.panel_xres =
|
||||||
|
(((INREG( regs, RADEON_FP_HORZ_STRETCH ) & RADEON_HORZ_PANEL_SIZE)
|
||||||
|
>> RADEON_HORZ_PANEL_SHIFT) + 1) * 8;
|
||||||
|
|
||||||
|
SHOW_INFO( 2, "detected panel size from registers: %dx%d",
|
||||||
|
di->fp_info.panel_xres, di->fp_info.panel_yres);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// once more for getting precise timing
|
||||||
|
static void Radeon_RevEnvDFPTiming( device_info *di )
|
||||||
|
{
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
uint32 r;
|
||||||
|
uint16 a, b;
|
||||||
|
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_FP_CRTC_H_TOTAL_DISP );
|
||||||
|
// the magic "4" was found by trial and error and probably stems from fudge (see crtc.c)
|
||||||
|
a = (r & RADEON_FP_CRTC_H_TOTAL_MASK)/* + 4*/;
|
||||||
|
b = (r & RADEON_FP_CRTC_H_DISP_MASK) >> RADEON_FP_CRTC_H_DISP_SHIFT;
|
||||||
|
di->fp_info.h_blank = (a - b) * 8;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "h_total=%d, h_disp=%d", a * 8, b * 8 );
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_FP_H_SYNC_STRT_WID );
|
||||||
|
di->fp_info.h_over_plus =
|
||||||
|
((r & RADEON_FP_H_SYNC_STRT_CHAR_MASK)
|
||||||
|
>> RADEON_FP_H_SYNC_STRT_CHAR_SHIFT) - b/* - 1*/;
|
||||||
|
di->fp_info.h_over_plus *= 8;
|
||||||
|
di->fp_info.h_sync_width =
|
||||||
|
((r & RADEON_FP_H_SYNC_WID_MASK)
|
||||||
|
>> RADEON_FP_H_SYNC_WID_SHIFT);
|
||||||
|
// TBD: this seems to be wrong
|
||||||
|
// (BIOS tells 112, this calculation leads to 24!)
|
||||||
|
di->fp_info.h_sync_width *= 8;
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_FP_CRTC_V_TOTAL_DISP );
|
||||||
|
a = (r & RADEON_FP_CRTC_V_TOTAL_MASK)/* + 1*/;
|
||||||
|
b = (r & RADEON_FP_CRTC_V_DISP_MASK) >> RADEON_FP_CRTC_V_DISP_SHIFT;
|
||||||
|
di->fp_info.v_blank = a - b;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "v_total=%d, v_disp=%d", a, b );
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_FP_V_SYNC_STRT_WID );
|
||||||
|
di->fp_info.v_over_plus = (r & RADEON_FP_V_SYNC_STRT_MASK) - b;
|
||||||
|
di->fp_info.v_sync_width = ((r & RADEON_FP_V_SYNC_WID_MASK)
|
||||||
|
>> RADEON_FP_V_SYNC_WID_SHIFT)/* + 1*/;
|
||||||
|
|
||||||
|
// standard CRTC
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_CRTC_H_TOTAL_DISP );
|
||||||
|
a = (r & RADEON_CRTC_H_TOTAL);
|
||||||
|
b = (r & RADEON_CRTC_H_DISP) >> RADEON_CRTC_H_DISP_SHIFT;
|
||||||
|
di->fp_info.h_blank = (a - b) * 8;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "h_total=%d, h_disp=%d", a * 8, b * 8 );
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_CRTC_H_SYNC_STRT_WID );
|
||||||
|
di->fp_info.h_over_plus =
|
||||||
|
((r & RADEON_CRTC_H_SYNC_STRT_CHAR)
|
||||||
|
>> RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT) - b;
|
||||||
|
di->fp_info.h_over_plus *= 8;
|
||||||
|
di->fp_info.h_sync_width =
|
||||||
|
((r & RADEON_CRTC_H_SYNC_WID)
|
||||||
|
>> RADEON_CRTC_H_SYNC_WID_SHIFT);
|
||||||
|
di->fp_info.h_sync_width *= 8;
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_CRTC_V_TOTAL_DISP );
|
||||||
|
a = (r & RADEON_CRTC_V_TOTAL);
|
||||||
|
b = (r & RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
|
||||||
|
di->fp_info.v_blank = a - b;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "v_total=%d, v_disp=%d", a, b );
|
||||||
|
|
||||||
|
r = INREG( regs, RADEON_CRTC_V_SYNC_STRT_WID );
|
||||||
|
di->fp_info.v_over_plus = (r & RADEON_CRTC_V_SYNC_STRT) - b;
|
||||||
|
di->fp_info.v_sync_width = ((r & RADEON_CRTC_V_SYNC_WID)
|
||||||
|
>> RADEON_CRTC_V_SYNC_WID_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get everything in terms of monitors connected to the card
|
||||||
|
static void Radeon_GetBIOSMon( device_info *di )
|
||||||
|
{
|
||||||
|
Radeon_GetMonType( di );
|
||||||
|
|
||||||
|
// reset all Flat Panel Info;
|
||||||
|
// it gets filled out step by step, and this way we know what's still missing
|
||||||
|
memset( &di->fp_info, 0, sizeof( di->fp_info ));
|
||||||
|
|
||||||
|
// we assume that the only fp port is combined with standard port 0
|
||||||
|
di->fp_info.disp_type = di->disp_type[0];
|
||||||
|
|
||||||
|
if( di->disp_type[0] == dt_dvi_1 || di->disp_type[0] == dt_lvds )
|
||||||
|
{
|
||||||
|
// there is a flat panel - get info about it
|
||||||
|
Radeon_GetBIOSDFPInfo( di );
|
||||||
|
|
||||||
|
// if BIOS doesn't know, ask the registers
|
||||||
|
if( di->fp_info.panel_xres == 0 || di->fp_info.panel_yres == 0)
|
||||||
|
Radeon_RevEnvDFPSize( di );
|
||||||
|
|
||||||
|
if( di->fp_info.h_blank == 0 || di->fp_info.v_blank == 0)
|
||||||
|
Radeon_RevEnvDFPTiming( di );
|
||||||
|
|
||||||
|
SHOW_INFO( 2, "h_disp=%d, h_blank=%d, h_over_plus=%d, h_sync_width=%d",
|
||||||
|
di->fp_info.panel_xres, di->fp_info.h_blank, di->fp_info.h_over_plus, di->fp_info.h_sync_width );
|
||||||
|
SHOW_INFO( 2, "v_disp=%d, v_blank=%d, v_over_plus=%d, v_sync_width=%d",
|
||||||
|
di->fp_info.panel_yres, di->fp_info.v_blank, di->fp_info.v_over_plus, di->fp_info.v_sync_width );
|
||||||
|
SHOW_INFO( 2, "pixel_clock=%d", di->fp_info.dot_clock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// detect amount of graphics memory
|
||||||
|
void Radeon_DetectRAM( device_info *di )
|
||||||
|
{
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
|
||||||
|
di->local_mem_size = INREG( regs, RADEON_CONFIG_MEMSIZE ) & RADEON_CONFIG_MEMSIZE_MASK;
|
||||||
|
|
||||||
|
// some production boards of m6 will return 0 if it's 8 MB
|
||||||
|
if( di->local_mem_size == 0 )
|
||||||
|
di->local_mem_size = 8 * 1024 *1024;
|
||||||
|
|
||||||
|
switch( INREG( regs, RADEON_MEM_SDRAM_MODE_REG ) & RADEON_MEM_CFG_TYPE_MASK ) {
|
||||||
|
case RADEON_MEM_CFG_SDR:
|
||||||
|
// SDR SGRAM (2:1)
|
||||||
|
strcpy(di->ram_type, "SDR SGRAM");
|
||||||
|
di->ram.ml = 4;
|
||||||
|
di->ram.MB = 4;
|
||||||
|
di->ram.Trcd = 1;
|
||||||
|
di->ram.Trp = 2;
|
||||||
|
di->ram.Twr = 1;
|
||||||
|
di->ram.CL = 2;
|
||||||
|
di->ram.loop_latency = 16;
|
||||||
|
di->ram.Rloop = 16;
|
||||||
|
di->ram.Tr2w = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RADEON_MEM_CFG_DDR:
|
||||||
|
// DDR SGRAM
|
||||||
|
strcpy(di->ram_type, "DDR SGRAM");
|
||||||
|
di->ram.ml = 4;
|
||||||
|
di->ram.MB = 4;
|
||||||
|
di->ram.Trcd = 3;
|
||||||
|
di->ram.Trp = 3;
|
||||||
|
di->ram.Twr = 2;
|
||||||
|
di->ram.CL = 3;
|
||||||
|
di->ram.Tr2w = 1;
|
||||||
|
di->ram.loop_latency = 16;
|
||||||
|
di->ram.Rloop = 16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// only one bit, so there's no default
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_INFO( 1, "%ld MB %s found", di->local_mem_size / 1024 / 1024,
|
||||||
|
di->ram_type );
|
||||||
|
|
||||||
|
if( di->local_mem_size > 64 * 1024 * 1024 ) {
|
||||||
|
di->local_mem_size = 64 * 1024 * 1024;
|
||||||
|
|
||||||
|
SHOW_INFO0( 1, "restricted to 64 MB" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// map and verify card's BIOS to see whether this really is a Radeon
|
||||||
|
// (as we need BIOS for further info we have to make sure we use the right one)
|
||||||
|
status_t Radeon_MapBIOS( pci_info *pcii, rom_info *ri )
|
||||||
|
{
|
||||||
|
char buffer[B_OS_NAME_LENGTH];
|
||||||
|
|
||||||
|
sprintf(buffer, "%04X_%04X_%02X%02X%02X bios",
|
||||||
|
pcii->vendor_id, pcii->device_id,
|
||||||
|
pcii->bus, pcii->device, pcii->function);
|
||||||
|
|
||||||
|
// we only scan BIOS at legacy location in first MB;
|
||||||
|
// using the PCI location would improve detection, especially
|
||||||
|
// if multiple graphics cards are installed
|
||||||
|
// BUT: BeOS uses the first graphics card it finds (sorted by
|
||||||
|
// device name), thus you couldn't choose in BIOS which card
|
||||||
|
// to use; checking the legacy location ensures that the card is
|
||||||
|
// only detected if it's the primary card
|
||||||
|
ri->bios_area = map_physical_memory( buffer, (void *)0xc0000,
|
||||||
|
0x40000, B_ANY_KERNEL_ADDRESS, B_READ_AREA, (void **)&ri->bios_ptr );
|
||||||
|
if( ri->bios_area < 0 )
|
||||||
|
return ri->bios_area;
|
||||||
|
|
||||||
|
ri->rom_ptr = Radeon_FindRom( ri );
|
||||||
|
|
||||||
|
return ri->rom_ptr != NULL ? B_OK : B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// unmap card's BIOS
|
||||||
|
void Radeon_UnmapBIOS( rom_info *ri )
|
||||||
|
{
|
||||||
|
delete_area( ri->bios_area );
|
||||||
|
|
||||||
|
ri->bios_ptr = ri->rom_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get everything valuable from BIOS (BIOS must be mapped)
|
||||||
|
status_t Radeon_ReadBIOSData( device_info *di )
|
||||||
|
{
|
||||||
|
shared_info dummy_si;
|
||||||
|
status_t result = B_OK;
|
||||||
|
|
||||||
|
// give Radeon_MapDevice something to play with
|
||||||
|
di->si = &dummy_si;
|
||||||
|
|
||||||
|
// don't map frame buffer - we don't know its proper size yet!
|
||||||
|
result = Radeon_MapDevice( di, true );
|
||||||
|
if( result < 0 )
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
Radeon_GetPLLInfo( di );
|
||||||
|
Radeon_GetBIOSMon( di );
|
||||||
|
Radeon_DetectRAM( di );
|
||||||
|
|
||||||
|
Radeon_UnmapDevice( di );
|
||||||
|
|
||||||
|
err1:
|
||||||
|
di->si = NULL;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Graphics card detection
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
// this table is gathered from different sources
|
||||||
|
// and may even contain some wrong entries
|
||||||
|
#define VENDOR_ID_ATI 0x1002
|
||||||
|
|
||||||
|
// R100
|
||||||
|
#define DEVICE_ID_RADEON_QD 0x5144
|
||||||
|
#define DEVICE_ID_RADEON_QE 0x5145
|
||||||
|
#define DEVICE_ID_RADEON_QF 0x5146
|
||||||
|
#define DEVICE_ID_RADEON_QG 0x5147
|
||||||
|
|
||||||
|
// RV100
|
||||||
|
#define DEVICE_ID_RADEON_QY 0x5159
|
||||||
|
#define DEVICE_ID_RADEON_QZ 0x515a
|
||||||
|
|
||||||
|
// M6
|
||||||
|
#define DEVICE_ID_RADEON_LY 0x4c59
|
||||||
|
#define DEVICE_ID_RADEON_LZ 0x4c5a
|
||||||
|
|
||||||
|
// RV200
|
||||||
|
#define DEVICE_ID_RADEON_QW 0x5157
|
||||||
|
#define DEVICE_ID_RADEON_QX 0x5158
|
||||||
|
|
||||||
|
// R200 mobility
|
||||||
|
#define DEVICE_ID_RADEON_LW 0x4c57
|
||||||
|
#define DEVICE_ID_RADEON_LX 0x4c58
|
||||||
|
|
||||||
|
// R200
|
||||||
|
#define DEVICE_ID_RADEON_QH 0x5148
|
||||||
|
#define DEVICE_ID_RADEON_QI 0x5149
|
||||||
|
#define DEVICE_ID_RADEON_QJ 0x514a
|
||||||
|
#define DEVICE_ID_RADEON_QK 0x514b
|
||||||
|
#define DEVICE_ID_RADEON_QL 0x514c
|
||||||
|
#define DEVICE_ID_RADEON_QM 0x514d
|
||||||
|
|
||||||
|
#define DEVICE_ID_RADEON_Qh 0x5168
|
||||||
|
#define DEVICE_ID_RADEON_Qi 0x5169
|
||||||
|
#define DEVICE_ID_RADEON_Qj 0x516a
|
||||||
|
#define DEVICE_ID_RADEON_Qk 0x516b
|
||||||
|
|
||||||
|
#define DEVICE_ID_RADEON_BB 0x4242
|
||||||
|
|
||||||
|
// RV250
|
||||||
|
#define DEVICE_ID_RADEON_Id 0x4964
|
||||||
|
#define DEVICE_ID_RADEON_Ie 0x4965
|
||||||
|
#define DEVICE_ID_RADEON_If 0x4966
|
||||||
|
#define DEVICE_ID_RADEON_Ig 0x4967
|
||||||
|
|
||||||
|
// M9
|
||||||
|
#define DEVICE_ID_RADEON_Ld 0x4c64
|
||||||
|
#define DEVICE_ID_RADEON_Le 0x4c65
|
||||||
|
#define DEVICE_ID_RADEON_Lf 0x4c66
|
||||||
|
#define DEVICE_ID_RADEON_Lg 0x4c67
|
||||||
|
|
||||||
|
// RV280
|
||||||
|
#define DEVICE_ID_RADEON_Ya_ 0x5960
|
||||||
|
#define DEVICE_ID_RADEON_Ya 0x5961
|
||||||
|
#define DEVICE_ID_RADEON_Yd 0x5964
|
||||||
|
|
||||||
|
// r300
|
||||||
|
#define DEVICE_ID_RADEON_ND 0x4e44
|
||||||
|
#define DEVICE_ID_RADEON_NE 0x4e45
|
||||||
|
#define DEVICE_ID_RADEON_NF 0x4e46
|
||||||
|
#define DEVICE_ID_RADEON_NG 0x4e47
|
||||||
|
|
||||||
|
// r300-4P
|
||||||
|
#define DEVICE_ID_RADEON_AD 0x4144
|
||||||
|
#define DEVICE_ID_RADEON_AE 0x4145
|
||||||
|
#define DEVICE_ID_RADEON_AF 0x4146
|
||||||
|
#define DEVICE_ID_RADEON_AG 0x4147
|
||||||
|
|
||||||
|
// rv350
|
||||||
|
#define DEVICE_ID_RADEON_AP 0x4150
|
||||||
|
#define DEVICE_ID_RADEON_AQ 0x4151
|
||||||
|
#define DEVICE_ID_RADEON_NO 0x4e50
|
||||||
|
#define DEVICE_ID_RADEON_NS 0x4e54
|
||||||
|
|
||||||
|
// rv360
|
||||||
|
#define DEVICE_ID_RADEON_AR 0x4152
|
||||||
|
|
||||||
|
// r350
|
||||||
|
#define DEVICE_ID_RADEON_AH 0x4148
|
||||||
|
#define DEVICE_ID_RADEON_NH 0x4e48
|
||||||
|
#define DEVICE_ID_RADEON_NI 0x4e49
|
||||||
|
|
||||||
|
// r360
|
||||||
|
#define DEVICE_ID_RADEON_NJ 0x4e4a
|
||||||
|
|
||||||
|
#define DEVICE_ID_IGP320M 0x4336
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16 device_id;
|
||||||
|
radeon_type asic;
|
||||||
|
char *name;
|
||||||
|
} RadeonDevice;
|
||||||
|
|
||||||
|
// list of supported devices
|
||||||
|
RadeonDevice radeon_device_list[] = {
|
||||||
|
// original Radeons, now called r100
|
||||||
|
{ DEVICE_ID_RADEON_QD, rt_r100, "Radeon 7200 / Radeon / ALL-IN-WONDER Radeon" },
|
||||||
|
{ DEVICE_ID_RADEON_QE, rt_r100, "Radeon QE" },
|
||||||
|
{ DEVICE_ID_RADEON_QF, rt_r100, "Radeon QF" },
|
||||||
|
{ DEVICE_ID_RADEON_QG, rt_r100, "Radeon QG" },
|
||||||
|
|
||||||
|
// Radeon VE (low-cost, dual CRT, no TCL), now called RV100
|
||||||
|
{ DEVICE_ID_RADEON_QY, rt_ve, "Radeon 7000 / Radeon VE" },
|
||||||
|
{ DEVICE_ID_RADEON_QZ, rt_ve, "Radeon QZ VE" },
|
||||||
|
|
||||||
|
// mobility version of original Radeon (based on VE), now called M6
|
||||||
|
{ DEVICE_ID_RADEON_LY, rt_m6, "Radeon Mobility" },
|
||||||
|
{ DEVICE_ID_RADEON_LZ, rt_m6, "Radeon Mobility M6 LZ" },
|
||||||
|
|
||||||
|
// RV200 (dual CRT)
|
||||||
|
{ DEVICE_ID_RADEON_QW, rt_rv200, "Radeon 7500 / ALL-IN-WONDER Radeon 7500" },
|
||||||
|
{ DEVICE_ID_RADEON_QX, rt_rv200, "Radeon 7500 QX" },
|
||||||
|
|
||||||
|
// R200 mobility (based on RV200)
|
||||||
|
{ DEVICE_ID_RADEON_LW, rt_m7, "Radeon Mobility 7500" },
|
||||||
|
{ DEVICE_ID_RADEON_LX, rt_m7, "Radeon Mobility 7500 GL" },
|
||||||
|
|
||||||
|
// R200
|
||||||
|
{ DEVICE_ID_RADEON_QH, rt_r200, "Radeon 8500 QH" },
|
||||||
|
{ DEVICE_ID_RADEON_QI, rt_r200, "Radeon 8500 QI" },
|
||||||
|
{ DEVICE_ID_RADEON_QJ, rt_r200, "Radeon 8500 QJ" },
|
||||||
|
{ DEVICE_ID_RADEON_QK, rt_r200, "Radeon 8500 QK" },
|
||||||
|
{ DEVICE_ID_RADEON_QL, rt_r200, "Radeon 8500 / 8500LE / ALL-IN-WONDER Radeon 8500" },
|
||||||
|
{ DEVICE_ID_RADEON_QM, rt_r200, "Radeon 9100" },
|
||||||
|
|
||||||
|
{ DEVICE_ID_RADEON_Qh, rt_r200, "Radeon 8500 Qh" },
|
||||||
|
{ DEVICE_ID_RADEON_Qi, rt_r200, "Radeon 8500 Qi" },
|
||||||
|
{ DEVICE_ID_RADEON_Qj, rt_r200, "Radeon 8500 Qj" },
|
||||||
|
{ DEVICE_ID_RADEON_Qk, rt_r200, "Radeon 8500 Qk" },
|
||||||
|
{ DEVICE_ID_RADEON_BB, rt_r200, "ALL-IN-Wonder Radeon 8500 DV" },
|
||||||
|
|
||||||
|
// RV250 (cut-down R200)
|
||||||
|
{ DEVICE_ID_RADEON_Id, rt_rv250, "Radeon 9000 Id" },
|
||||||
|
{ DEVICE_ID_RADEON_Ie, rt_rv250, "Radeon 9000 Ie" },
|
||||||
|
{ DEVICE_ID_RADEON_If, rt_rv250, "Radeon 9000" },
|
||||||
|
{ DEVICE_ID_RADEON_Ig, rt_rv250, "Radeon 9000 Ig" },
|
||||||
|
|
||||||
|
// M9 (based on rv250)
|
||||||
|
{ DEVICE_ID_RADEON_Ld, rt_m9, "Radeon Mobility 9000 Ld" },
|
||||||
|
{ DEVICE_ID_RADEON_Le, rt_m9, "Radeon Mobility 9000 Le" },
|
||||||
|
{ DEVICE_ID_RADEON_Lf, rt_m9, "Radeon Mobility 9000 Lf" },
|
||||||
|
{ DEVICE_ID_RADEON_Lg, rt_m9, "Radeon Mobility 9000 Lg" },
|
||||||
|
|
||||||
|
// RV280
|
||||||
|
// the naming scheme can't properly handle this id
|
||||||
|
{ DEVICE_ID_RADEON_Ya_, rt_rv250, "Radeon 9200" },
|
||||||
|
{ DEVICE_ID_RADEON_Ya, rt_rv250, "Radeon 9200" },
|
||||||
|
{ DEVICE_ID_RADEON_Yd, rt_rv250, "Radeon 9200 SE" },
|
||||||
|
|
||||||
|
// R300
|
||||||
|
{ DEVICE_ID_RADEON_ND, rt_r300, "Radeon 9700 ND" },
|
||||||
|
{ DEVICE_ID_RADEON_NE, rt_r300, "Radeon 9700 NE" },
|
||||||
|
{ DEVICE_ID_RADEON_NF, rt_r300, "Radeon 9600 XT" },
|
||||||
|
{ DEVICE_ID_RADEON_NG, rt_r300, "Radeon 9700 NG" },
|
||||||
|
|
||||||
|
{ DEVICE_ID_RADEON_AD, rt_r300, "Radeon 9700 AD" },
|
||||||
|
{ DEVICE_ID_RADEON_AE, rt_r300, "Radeon 9700 AE" },
|
||||||
|
{ DEVICE_ID_RADEON_AF, rt_r300, "Radeon 9700 AF" },
|
||||||
|
{ DEVICE_ID_RADEON_AG, rt_r300, "Radeon 9700 AG" },
|
||||||
|
|
||||||
|
// RV350
|
||||||
|
{ DEVICE_ID_RADEON_AP, rt_rv350, "Radeon 9600 AP" },
|
||||||
|
{ DEVICE_ID_RADEON_AQ, rt_rv350, "Radeon 9600 AQ" },
|
||||||
|
{ DEVICE_ID_RADEON_NO, rt_rv350, "Radeon Mobility 9600 Pro Turbo" },
|
||||||
|
{ DEVICE_ID_RADEON_NS, rt_rv350, "Mobility FireGL T2" },
|
||||||
|
|
||||||
|
// RV360 (probably minor revision of rv350)
|
||||||
|
{ DEVICE_ID_RADEON_AR, rt_rv360, "Radeon 9600 AR" },
|
||||||
|
|
||||||
|
// R350
|
||||||
|
{ DEVICE_ID_RADEON_AH, rt_r350, "Radeon 9800 AH" },
|
||||||
|
{ DEVICE_ID_RADEON_NH, rt_r350, "Radeon 9800 Pro NH" },
|
||||||
|
{ DEVICE_ID_RADEON_NI, rt_r350, "Radeon 9800 NI" },
|
||||||
|
|
||||||
|
// R360 (probably minor revision of r350)
|
||||||
|
{ DEVICE_ID_RADEON_NJ, rt_r360, "Radeon 9800 XT" },
|
||||||
|
|
||||||
|
{ DEVICE_ID_IGP320M, rt_ve, "Radeon IGP 320M" },
|
||||||
|
|
||||||
|
{ 0, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// list of supported vendors (there aren't many ;)
|
||||||
|
static struct {
|
||||||
|
uint16 vendor_id;
|
||||||
|
RadeonDevice *devices;
|
||||||
|
} SupportedVendors[] = {
|
||||||
|
{ VENDOR_ID_ATI, radeon_device_list },
|
||||||
|
{ 0x0000, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
// check, whether there is *any* supported card plugged in
|
||||||
|
bool Radeon_CardDetect( void )
|
||||||
|
{
|
||||||
|
long pci_index = 0;
|
||||||
|
pci_info pcii;
|
||||||
|
bool found_one = FALSE;
|
||||||
|
|
||||||
|
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
while ((*pci_bus->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) {
|
||||||
|
int vendor = 0;
|
||||||
|
|
||||||
|
while (SupportedVendors[vendor].vendor_id) {
|
||||||
|
if (SupportedVendors[vendor].vendor_id == pcii.vendor_id) {
|
||||||
|
RadeonDevice *devices = SupportedVendors[vendor].devices;
|
||||||
|
|
||||||
|
while (devices->device_id) {
|
||||||
|
if (devices->device_id == pcii.device_id ) {
|
||||||
|
rom_info ri;
|
||||||
|
|
||||||
|
if( Radeon_MapBIOS( &pcii, &ri ) == B_OK ) {
|
||||||
|
Radeon_UnmapBIOS( &ri );
|
||||||
|
|
||||||
|
SHOW_INFO( 0, "found supported device pci index %ld, device 0x%04x/0x%04x",
|
||||||
|
pci_index, pcii.vendor_id, pcii.device_id );
|
||||||
|
found_one = TRUE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
devices++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vendor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_index++;
|
||||||
|
}
|
||||||
|
SHOW_INFO0( 0, "no supported devices found" );
|
||||||
|
|
||||||
|
done:
|
||||||
|
put_module(B_PCI_MODULE_NAME);
|
||||||
|
|
||||||
|
return (found_one ? B_OK : B_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// !extend this array whenever a new ASIC is a added!
|
||||||
|
static bool has_crtc2[] =
|
||||||
|
{
|
||||||
|
false, // only original Radeons have one crtc only
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *asic_name[] =
|
||||||
|
{
|
||||||
|
"r100",
|
||||||
|
"ve",
|
||||||
|
"m6",
|
||||||
|
"rv200",
|
||||||
|
"m7",
|
||||||
|
"r200",
|
||||||
|
"rv250",
|
||||||
|
"rv280",
|
||||||
|
"m9",
|
||||||
|
"r300",
|
||||||
|
"r300_4p",
|
||||||
|
"rv350",
|
||||||
|
"rv360",
|
||||||
|
"r350",
|
||||||
|
"r360",
|
||||||
|
};
|
||||||
|
|
||||||
|
// get next supported device
|
||||||
|
static bool probeDevice( device_info *di )
|
||||||
|
{
|
||||||
|
int vendor;
|
||||||
|
|
||||||
|
/* if we match a supported vendor */
|
||||||
|
for( vendor = 0; SupportedVendors[vendor].vendor_id; ++vendor ) {
|
||||||
|
RadeonDevice *device;
|
||||||
|
|
||||||
|
if( SupportedVendors[vendor].vendor_id != di->pcii.vendor_id )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for( device = SupportedVendors[vendor].devices; device->device_id; ++device ) {
|
||||||
|
// avoid double-detection
|
||||||
|
if (device->device_id != di->pcii.device_id )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
di->has_crtc2 = has_crtc2[device->asic];
|
||||||
|
di->asic = device->asic;
|
||||||
|
|
||||||
|
if( Radeon_MapBIOS( &di->pcii, &di->rom ) != B_OK )
|
||||||
|
// give up checking this device - no BIOS, no fun
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( Radeon_ReadBIOSData( di ) != B_OK ) {
|
||||||
|
Radeon_UnmapBIOS( &di->rom );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't need BIOS any more
|
||||||
|
Radeon_UnmapBIOS( &di->rom );
|
||||||
|
|
||||||
|
SHOW_INFO( 0, "found %s; ASIC: %s", device->name, asic_name[device->asic] );
|
||||||
|
|
||||||
|
sprintf(di->name, "graphics/%04X_%04X_%02X%02X%02X",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function);
|
||||||
|
SHOW_FLOW( 3, "making /dev/%s", di->name );
|
||||||
|
|
||||||
|
di->is_open = 0;
|
||||||
|
di->shared_area = -1;
|
||||||
|
di->si = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// gather list of supported devices
|
||||||
|
// (currently, we rely on proper BIOS detection, which
|
||||||
|
// only works for primary graphics adapter, so multiple
|
||||||
|
// devices won't really work)
|
||||||
|
void Radeon_ProbeDevices( void )
|
||||||
|
{
|
||||||
|
uint32 pci_index = 0;
|
||||||
|
uint32 count = 0;
|
||||||
|
device_info *di = devices->di;
|
||||||
|
|
||||||
|
while( count < MAX_DEVICES ) {
|
||||||
|
memset( di, 0, sizeof( *di ));
|
||||||
|
|
||||||
|
if( (*pci_bus->get_nth_pci_info)(pci_index, &(di->pcii)) != B_NO_ERROR)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( probeDevice( di )) {
|
||||||
|
devices->device_names[count] = di->name;
|
||||||
|
di++;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
devices->count = count;
|
||||||
|
devices->device_names[count] = NULL;
|
||||||
|
|
||||||
|
SHOW_INFO( 0, "%ld supported devices", count );
|
||||||
|
}
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
DMA initialization/clean-up.
|
||||||
|
|
||||||
|
Currently, we use PCI DMA. Changing to AGP would
|
||||||
|
only affect this file, but AGP-GART is specific to
|
||||||
|
the chipset of the motherboard, and as DMA is really
|
||||||
|
overkill for 2D, I cannot bother writing a dozen
|
||||||
|
of AGP drivers just to gain little extra speedup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <image.h>
|
||||||
|
#include <mmio.h>
|
||||||
|
#include <buscntrl_regs.h>
|
||||||
|
#include <memcntrl_regs.h>
|
||||||
|
#include <cp_regs.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// create actual DMA buffer
|
||||||
|
static status_t createDMABuffer( DMA_buffer *buffer, size_t size )
|
||||||
|
{
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
buffer->size = size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
|
||||||
|
|
||||||
|
// if this buffer is used for PCI BM, cache snooping
|
||||||
|
// takes care of syncing memory accesses; if used for AGP,
|
||||||
|
// we'll have to access via AGP aperture (and mark aperture
|
||||||
|
// as write-combined) as cache consistency doesn't need to
|
||||||
|
// be guaranteed
|
||||||
|
|
||||||
|
// the specs say that some chipsets do kind of lazy flushing
|
||||||
|
// so the graphics card may read obsolete data; up to now
|
||||||
|
// we use PCI only where this shouldn't happen by design;
|
||||||
|
// if we change to AGP we may tweak the pre-charge time of
|
||||||
|
// the write buffer pointer
|
||||||
|
|
||||||
|
// as some variables in accelerant point directly into
|
||||||
|
// the DMA buffer, we have to grant access for all apps
|
||||||
|
buffer->buffer_area = create_area( "Radeon DMA buffer",
|
||||||
|
&buffer->ptr, B_ANY_KERNEL_ADDRESS,
|
||||||
|
size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA );
|
||||||
|
if( buffer->buffer_area < 0 ) {
|
||||||
|
SHOW_ERROR( 1, "cannot create DMA buffer (%s)",
|
||||||
|
strerror( buffer->buffer_area ));
|
||||||
|
return buffer->buffer_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( buffer->ptr, 0, size );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static status_t createDMABuffer( DMA_buffer *buffer, size_t size )
|
||||||
|
{
|
||||||
|
physical_entry map[1];
|
||||||
|
void *unaligned_addr, *aligned_phys;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
buffer->size = size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
|
||||||
|
|
||||||
|
// we allocate an contiguous area having twice the size
|
||||||
|
// to be able to find an aligned, contiguous range within it;
|
||||||
|
// the graphics card doesn't care, but the CPU cannot
|
||||||
|
// make an arbitrary area WC'ed, at least elder ones
|
||||||
|
// question: is this necessary for a PCI GART because of bus snooping?
|
||||||
|
buffer->unaligned_area = create_area( "Radeon DMA buffer",
|
||||||
|
&unaligned_addr, B_ANY_KERNEL_ADDRESS,
|
||||||
|
2 * size, B_CONTIGUOUS/*B_FULL_LOCK*/, B_READ_AREA | B_WRITE_AREA );
|
||||||
|
if( buffer->unaligned_area < 0 ) {
|
||||||
|
SHOW_ERROR( 1, "cannot create DMA buffer (%s)",
|
||||||
|
strerror( buffer->unaligned_area ));
|
||||||
|
return buffer->unaligned_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_memory_map( unaligned_addr, B_PAGE_SIZE, map, 1 );
|
||||||
|
|
||||||
|
aligned_phys =
|
||||||
|
(void **)(((uint32)map[0].address + size - 1) & ~(size - 1));
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "aligned_phys=%p", aligned_phys );
|
||||||
|
|
||||||
|
buffer->buffer_area = map_physical_memory( "Radeon aligned DMA buffer",
|
||||||
|
aligned_phys,
|
||||||
|
size, B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
|
||||||
|
B_READ_AREA | B_WRITE_AREA, &buffer->ptr );
|
||||||
|
|
||||||
|
if( buffer->buffer_area < 0 ) {
|
||||||
|
SHOW_ERROR0( 3, "cannot map buffer with WC" );
|
||||||
|
buffer->buffer_area = map_physical_memory( "Radeon aligned DMA buffer",
|
||||||
|
aligned_phys,
|
||||||
|
size, B_ANY_KERNEL_BLOCK_ADDRESS,
|
||||||
|
B_READ_AREA | B_WRITE_AREA, &buffer->ptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( buffer->buffer_area < 0 ) {
|
||||||
|
SHOW_ERROR0( 1, "cannot map DMA buffer" );
|
||||||
|
delete_area( buffer->unaligned_area );
|
||||||
|
buffer->unaligned_area = -1;
|
||||||
|
return buffer->buffer_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( buffer->ptr, 0, size );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// init GART (could be used for both PCI and AGP)
|
||||||
|
static status_t initGART( DMA_buffer *buffer )
|
||||||
|
{
|
||||||
|
physical_entry *map;
|
||||||
|
physical_entry PTB_map[1];
|
||||||
|
size_t map_count;
|
||||||
|
int i;
|
||||||
|
uint32 *gart_entry;
|
||||||
|
size_t num_pages;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
num_pages = (buffer->size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
|
||||||
|
|
||||||
|
// GART must be contignuous
|
||||||
|
buffer->GART_area = create_area( "Radeon GART", (void **)&buffer->GART_ptr,
|
||||||
|
B_ANY_KERNEL_ADDRESS,
|
||||||
|
(num_pages * sizeof( uint32 ) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1),
|
||||||
|
B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA );
|
||||||
|
|
||||||
|
if( buffer->GART_area < 0 ) {
|
||||||
|
SHOW_ERROR( 1, "cannot create GART table (%s)",
|
||||||
|
strerror( buffer->GART_area ));
|
||||||
|
return buffer->GART_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_memory_map( buffer->GART_ptr, B_PAGE_SIZE, PTB_map, 1 );
|
||||||
|
buffer->GART_phys = (uint32)PTB_map[0].address;
|
||||||
|
|
||||||
|
SHOW_INFO( 3, "GART_ptr=%p, GART_phys=%p", buffer->GART_ptr,
|
||||||
|
(void *)buffer->GART_phys );
|
||||||
|
|
||||||
|
// get address mapping
|
||||||
|
memset( buffer->GART_ptr, 0, num_pages * sizeof( uint32 ));
|
||||||
|
|
||||||
|
map_count = num_pages + 1;
|
||||||
|
map = malloc( map_count * sizeof( physical_entry ) );
|
||||||
|
|
||||||
|
get_memory_map( buffer->ptr, buffer->size, map, map_count );
|
||||||
|
|
||||||
|
// the following looks a bit strange as the kernel
|
||||||
|
// combines successive entries
|
||||||
|
gart_entry = buffer->GART_ptr;
|
||||||
|
|
||||||
|
for( i = 0; i < (int)map_count; ++i ) {
|
||||||
|
uint32 addr = (uint32)map[i].address;
|
||||||
|
size_t size = map[i].size;
|
||||||
|
|
||||||
|
if( size == 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
while( size > 0 ) {
|
||||||
|
*gart_entry++ = addr;
|
||||||
|
//SHOW_FLOW( 3, "%lx", *(gart_entry-1) );
|
||||||
|
addr += ATI_PCIGART_PAGE_SIZE;
|
||||||
|
size -= ATI_PCIGART_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free( map );
|
||||||
|
|
||||||
|
if( i == (int)map_count ) {
|
||||||
|
// this case should never happen
|
||||||
|
SHOW_ERROR0( 0, "memory map of DMA buffer too large!" );
|
||||||
|
delete_area( buffer->GART_area );
|
||||||
|
buffer->GART_area = -1;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this might be a bit more than needed, as
|
||||||
|
// 1. Intel CPUs have "processor order", i.e. writes appear to external
|
||||||
|
// devices in program order, so a simple final write should be sufficient
|
||||||
|
// 2. if it is a PCI GART, bus snooping should provide cache coherence
|
||||||
|
// 3. this function is a no-op :(
|
||||||
|
clear_caches( buffer->GART_ptr, num_pages * sizeof( uint32 ),
|
||||||
|
B_FLUSH_DCACHE );
|
||||||
|
|
||||||
|
// back to real live - some chipsets have write buffers that
|
||||||
|
// proove all previous assumption wrong
|
||||||
|
asm volatile ( "wbinvd" ::: "memory" );
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// destroy DMA buffer
|
||||||
|
static void destroyDMABuffer( DMA_buffer *buffer )
|
||||||
|
{
|
||||||
|
if( buffer->buffer_area >= 0 )
|
||||||
|
delete_area( buffer->buffer_area );
|
||||||
|
|
||||||
|
buffer->buffer_area = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// destroy GART
|
||||||
|
static void destroyGART( DMA_buffer *buffer )
|
||||||
|
{
|
||||||
|
if( buffer->unaligned_area >= 0 )
|
||||||
|
delete_area( buffer->unaligned_area );
|
||||||
|
|
||||||
|
if( buffer->GART_area >= 0 )
|
||||||
|
delete_area( buffer->GART_area );
|
||||||
|
|
||||||
|
buffer->GART_area = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// initialize DMA (well - we initialize entire memory controller)
|
||||||
|
status_t Radeon_InitDMA( device_info *di )
|
||||||
|
{
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
shared_info *si = di->si;
|
||||||
|
status_t result;
|
||||||
|
|
||||||
|
// any address not covered by frame buffer, PCI GART or AGP aperture
|
||||||
|
// leads to a direct memory access
|
||||||
|
// -> this is dangerous, so we make sure entire address space is mapped
|
||||||
|
|
||||||
|
// locate PCI GART at top of address space
|
||||||
|
si->nonlocal_vm_start = -ATI_MAX_PCIGART_PAGES * ATI_PCIGART_PAGE_SIZE;
|
||||||
|
si->nonlocal_mem_size = ATI_MAX_PCIGART_PAGES * ATI_PCIGART_PAGE_SIZE;
|
||||||
|
|
||||||
|
SHOW_INFO( 3, "Non-local memory %lx@%lx (graphics card address)",
|
||||||
|
si->nonlocal_mem_size, si->nonlocal_vm_start );
|
||||||
|
|
||||||
|
// let AGP range overlap with frame buffer to hide it;
|
||||||
|
// according to spec, frame buffer should win but we better
|
||||||
|
// choose an unused-area to avoid trouble
|
||||||
|
// (specs don't talk about overlapping area, let's hope
|
||||||
|
// the memory controller won't choke if we ever access it)
|
||||||
|
si->AGP_vm_start = si->nonlocal_vm_start - 0x10000;
|
||||||
|
si->AGP_vm_size = 0x10000;
|
||||||
|
|
||||||
|
result = createDMABuffer( &di->DMABuffer, 0x80000 );
|
||||||
|
if( result < 0 )
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
result = initGART( &di->DMABuffer );
|
||||||
|
if( result < 0 )
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
SHOW_INFO( 3, "GART=%p", di->DMABuffer.ptr );
|
||||||
|
|
||||||
|
si->nonlocal_mem = di->DMABuffer.ptr;
|
||||||
|
|
||||||
|
// set PCI GART page-table base address
|
||||||
|
OUTREG( regs, RADEON_AIC_PT_BASE, di->DMABuffer.GART_phys );
|
||||||
|
|
||||||
|
// set address range for PCI address translate
|
||||||
|
OUTREG( regs, RADEON_AIC_LO_ADDR, si->nonlocal_vm_start );
|
||||||
|
OUTREG( regs, RADEON_AIC_HI_ADDR, si->nonlocal_vm_start +
|
||||||
|
di->DMABuffer.size - 1 );
|
||||||
|
|
||||||
|
// set AGP address range
|
||||||
|
OUTREG( regs, RADEON_MC_AGP_LOCATION,
|
||||||
|
(si->AGP_vm_start & 0xffff0000) |
|
||||||
|
((si->AGP_vm_start + si->AGP_vm_size - 1) >> 16 ));
|
||||||
|
|
||||||
|
// set address range of video memory
|
||||||
|
// (lower word = begin >> 16
|
||||||
|
// upper word = end >> 16)
|
||||||
|
// let it cover all remaining addresses;
|
||||||
|
// addresses are wrapped
|
||||||
|
OUTREG( regs, RADEON_MC_FB_LOCATION,
|
||||||
|
((si->nonlocal_vm_start - 1) & 0xffff0000) |
|
||||||
|
(0 >> 16) );
|
||||||
|
|
||||||
|
// disable AGP
|
||||||
|
OUTREG( regs, RADEON_AGP_COMMAND, 0 );
|
||||||
|
|
||||||
|
// Turn on PCI GART
|
||||||
|
#if 1
|
||||||
|
OUTREGP( regs, RADEON_AIC_CNTL, RADEON_PCIGART_TRANSLATE_EN,
|
||||||
|
~RADEON_PCIGART_TRANSLATE_EN );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err2:
|
||||||
|
destroyDMABuffer( &di->DMABuffer );
|
||||||
|
|
||||||
|
err1:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// cleanup DMA
|
||||||
|
void Radeon_CleanupDMA( device_info *di )
|
||||||
|
{
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
// perhaps we should wait for FIFO space before messing around with registers, but
|
||||||
|
// 1. I don't want to add all the sync stuff to the kernel driver
|
||||||
|
// 2. I doubt that these regs are buffered by FIFO
|
||||||
|
|
||||||
|
// disable CP BM
|
||||||
|
OUTREG( regs, RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS );
|
||||||
|
// disable bus mastering
|
||||||
|
OUTREGP( regs, RADEON_BUS_CNTL, RADEON_BUS_MASTER_DIS, ~RADEON_BUS_MASTER_DIS );
|
||||||
|
// disable PCI GART
|
||||||
|
OUTREGP( regs, RADEON_AIC_CNTL, 0, ~RADEON_PCIGART_TRANSLATE_EN );
|
||||||
|
|
||||||
|
destroyGART( &di->DMABuffer );
|
||||||
|
destroyDMABuffer( &di->DMABuffer );
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
DevFS interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
#ifdef ENABLE_LOGGING
|
||||||
|
#include "log_coll.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <graphic_driver.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <mmio.h>
|
||||||
|
#include <version.h>
|
||||||
|
|
||||||
|
// tell the kernel what revision of the driver API we support
|
||||||
|
//int32 api_version = 2;
|
||||||
|
int32 api_version = B_CUR_DRIVER_API_VERSION; // apsed, was 2, is 2 in R5
|
||||||
|
|
||||||
|
|
||||||
|
static status_t open_hook( const char *name, uint32 flags, void **cookie );
|
||||||
|
static status_t close_hook( void *dev );
|
||||||
|
static status_t free_hook( void *dev );
|
||||||
|
static status_t read_hook( void *dev, off_t pos, void *buf, size_t *len );
|
||||||
|
static status_t write_hook( void *dev, off_t pos, const void *buf, size_t *len );
|
||||||
|
static status_t control_hook( void *dev, uint32 msg, void *buf, size_t len );
|
||||||
|
|
||||||
|
|
||||||
|
static device_hooks graphics_device_hooks = {
|
||||||
|
open_hook,
|
||||||
|
close_hook,
|
||||||
|
free_hook,
|
||||||
|
control_hook,
|
||||||
|
read_hook,
|
||||||
|
write_hook,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// public function: check whether there is *any* supported hardware
|
||||||
|
status_t init_hardware( void )
|
||||||
|
{
|
||||||
|
SHOW_INFO0( 0, RADEON_DRIVER_VERSION );
|
||||||
|
if( Radeon_CardDetect() == B_OK )
|
||||||
|
return B_OK;
|
||||||
|
else
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: init driver
|
||||||
|
status_t init_driver( void )
|
||||||
|
{
|
||||||
|
if( get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK )
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
/* driver private data */
|
||||||
|
devices = (radeon_devices *)calloc( 1, sizeof( radeon_devices ));
|
||||||
|
if( devices == NULL ) {
|
||||||
|
put_module(B_PCI_MODULE_NAME);
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_BEN( "Radeon Kernel", devices->kernel);
|
||||||
|
|
||||||
|
Radeon_ProbeDevices();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: uninit driver
|
||||||
|
void uninit_driver( void )
|
||||||
|
{
|
||||||
|
DELETE_BEN( devices->kernel );
|
||||||
|
|
||||||
|
free( devices );
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
put_module( B_PCI_MODULE_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: return list of device names
|
||||||
|
const char **publish_devices( void )
|
||||||
|
{
|
||||||
|
return (const char **)devices->device_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: find hooks for specific device given its name
|
||||||
|
device_hooks *find_device( const char *name )
|
||||||
|
{
|
||||||
|
uint32 index;
|
||||||
|
|
||||||
|
// probably, we could always return standard hooks
|
||||||
|
for( index = 0; devices->device_names[index]; ++index ) {
|
||||||
|
if( strcmp( name, devices->device_names[index] ) == 0 )
|
||||||
|
return &graphics_device_hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: open device
|
||||||
|
static status_t open_hook( const char *name, uint32 flags, void **cookie )
|
||||||
|
{
|
||||||
|
int32 index = 0;
|
||||||
|
device_info *di;
|
||||||
|
status_t result = B_OK;
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "name=%s, flags=%ld, cookie=0x%08lx", name, flags, (uint32)cookie );
|
||||||
|
|
||||||
|
// find device info
|
||||||
|
while( devices->device_names[index] &&
|
||||||
|
strcmp(name, devices->device_names[index] ) != 0 )
|
||||||
|
index++;
|
||||||
|
|
||||||
|
di = &(devices->di[index]);
|
||||||
|
|
||||||
|
ACQUIRE_BEN( devices->kernel );
|
||||||
|
|
||||||
|
if( !di->is_open )
|
||||||
|
result = Radeon_FirstOpen( di );
|
||||||
|
|
||||||
|
if( result == B_OK ) {
|
||||||
|
di->is_open++;
|
||||||
|
*cookie = di;
|
||||||
|
}
|
||||||
|
|
||||||
|
RELEASE_BEN( devices->kernel );
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "returning 0x%08lx", result );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: read from device (denied)
|
||||||
|
static status_t read_hook( void *dev, off_t pos, void *buf, size_t *len )
|
||||||
|
{
|
||||||
|
*len = 0;
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: write to device (denied)
|
||||||
|
static status_t write_hook( void *dev, off_t pos, const void *buf, size_t *len )
|
||||||
|
{
|
||||||
|
*len = 0;
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: close device (ignored, wait for free_hook instead)
|
||||||
|
static status_t close_hook( void *dev )
|
||||||
|
{
|
||||||
|
return B_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: free device
|
||||||
|
static status_t free_hook( void *dev )
|
||||||
|
{
|
||||||
|
device_info *di = (device_info *)dev;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
ACQUIRE_BEN( devices->kernel );
|
||||||
|
|
||||||
|
mem_freetag( di->local_memmgr, dev );
|
||||||
|
|
||||||
|
if( di->is_open == 1 )
|
||||||
|
Radeon_LastClose( di );
|
||||||
|
|
||||||
|
di->is_open--;
|
||||||
|
RELEASE_BEN( devices->kernel );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function: ioctl
|
||||||
|
static status_t control_hook( void *dev, uint32 msg, void *buf, size_t len )
|
||||||
|
{
|
||||||
|
device_info *di = (device_info *)dev;
|
||||||
|
status_t result = B_DEV_INVALID_IOCTL;
|
||||||
|
|
||||||
|
switch (msg) {
|
||||||
|
// needed by app_server to load accelerant
|
||||||
|
case B_GET_ACCELERANT_SIGNATURE: {
|
||||||
|
char *sig = (char *)buf;
|
||||||
|
//strcpy(sig, "radeon2.accelerant");
|
||||||
|
strcpy(sig, "radeon.accelerant");
|
||||||
|
result = B_OK;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// needed to share data between kernel and accelerant
|
||||||
|
case RADEON_GET_PRIVATE_DATA: {
|
||||||
|
radeon_get_private_data *gpd = (radeon_get_private_data *)buf;
|
||||||
|
|
||||||
|
if (gpd->magic == RADEON_PRIVATE_DATA_MAGIC) {
|
||||||
|
gpd->shared_info_area = di->shared_area;
|
||||||
|
gpd->virtual_card_area = di->virtual_card_area;
|
||||||
|
result = B_OK;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// needed for cloning
|
||||||
|
case RADEON_DEVICE_NAME: {
|
||||||
|
radeon_device_name *dn = (radeon_device_name *)buf;
|
||||||
|
|
||||||
|
if( dn->magic == RADEON_PRIVATE_DATA_MAGIC ) {
|
||||||
|
strncpy( dn->name, di->name, MAX_RADEON_DEVICE_NAME_LENGTH );
|
||||||
|
result = B_OK;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// graphics mem manager
|
||||||
|
case RADEON_ALLOC_LOCAL_MEM: {
|
||||||
|
radeon_alloc_local_mem *am = (radeon_alloc_local_mem *)buf;
|
||||||
|
|
||||||
|
if( am->magic == RADEON_PRIVATE_DATA_MAGIC )
|
||||||
|
result = mem_alloc( di->local_memmgr, am->size, dev, &am->handle, &am->fb_offset );
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RADEON_FREE_LOCAL_MEM: {
|
||||||
|
radeon_free_local_mem *fm = (radeon_free_local_mem *)buf;
|
||||||
|
|
||||||
|
if( fm->magic == RADEON_PRIVATE_DATA_MAGIC )
|
||||||
|
result = mem_free( di->local_memmgr, fm->handle, dev );
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// interface to log data
|
||||||
|
#ifdef ENABLE_LOGGING
|
||||||
|
case RADEON_GET_LOG_SIZE:
|
||||||
|
*(uint32 *)buf = log_getsize( di->si->log );
|
||||||
|
result = B_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RADEON_GET_LOG_DATA:
|
||||||
|
log_getcopy( di->si->log, buf, ((uint32 *)buf)[0] );
|
||||||
|
result = B_OK;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// interface to i2c-bus
|
||||||
|
case RADEON_SET_I2C_SIGNALS: {
|
||||||
|
radeon_getset_i2c *data = (radeon_getset_i2c *)buf;
|
||||||
|
|
||||||
|
if( data->magic == RADEON_PRIVATE_DATA_MAGIC ) {
|
||||||
|
result = B_OK;
|
||||||
|
OUTREG( di->regs, data->port, data->value );
|
||||||
|
}
|
||||||
|
break; }
|
||||||
|
case RADEON_GET_I2C_SIGNALS: {
|
||||||
|
radeon_getset_i2c *data = (radeon_getset_i2c *)buf;
|
||||||
|
|
||||||
|
if( data->magic == RADEON_PRIVATE_DATA_MAGIC ) {
|
||||||
|
result = B_OK;
|
||||||
|
data->value = INREG( di->regs, data->port );
|
||||||
|
}
|
||||||
|
break; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Global variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
|
||||||
|
|
||||||
|
int debug_level_flow = 4;
|
||||||
|
int debug_level_info = 4;
|
||||||
|
int debug_level_error = 4;
|
||||||
|
|
||||||
|
radeon_devices *devices;
|
||||||
|
pci_module_info *pci_bus;
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Init and clean-up of devices
|
||||||
|
|
||||||
|
TBD: support for multiple virtual card per device is
|
||||||
|
not implemented yet - there is only one per device;
|
||||||
|
apart from additional device names, we need proper
|
||||||
|
management of graphics mem to not interfere.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
|
||||||
|
#include <PCI.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dac_regs.h>
|
||||||
|
#include <mmio.h>
|
||||||
|
|
||||||
|
// helper macros for easier PCI access
|
||||||
|
#define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
|
||||||
|
#define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
|
||||||
|
|
||||||
|
|
||||||
|
// map frame buffer and registers
|
||||||
|
// mmio_only - true = map registers only (used during detection)
|
||||||
|
status_t Radeon_MapDevice( device_info *di, bool mmio_only )
|
||||||
|
{
|
||||||
|
// framebuffer is stored in PCI range 0,
|
||||||
|
// register map in PCI range 2
|
||||||
|
int regs = 2;
|
||||||
|
int fb = 0;
|
||||||
|
char buffer[B_OS_NAME_LENGTH];
|
||||||
|
shared_info *si = di->si;
|
||||||
|
uint32 tmp;
|
||||||
|
pci_info *pcii = &(di->pcii);
|
||||||
|
status_t result;
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "device: %02X%02X%02X",
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function );
|
||||||
|
|
||||||
|
si->regs_area = si->fb_area = 0;
|
||||||
|
|
||||||
|
// enable memory mapped IO and frame buffer
|
||||||
|
// also, enable bus mastering (some BIOSes seem to
|
||||||
|
// disable that, like mine)
|
||||||
|
tmp = get_pci( PCI_command, 2 );
|
||||||
|
SHOW_FLOW( 3, "old PCI command state: 0x%08lx", tmp );
|
||||||
|
tmp |= PCI_command_io | PCI_command_memory | PCI_command_master;
|
||||||
|
set_pci( PCI_command, 2, tmp );
|
||||||
|
|
||||||
|
// registers cannot be accessed directly by user apps,
|
||||||
|
// they need to clone area for safety reasons
|
||||||
|
SHOW_INFO( 1, "physical address of memory-mapped I/O: 0x%8lx-0x%8lx",
|
||||||
|
di->pcii.u.h0.base_registers[regs],
|
||||||
|
di->pcii.u.h0.base_registers[regs] + di->pcii.u.h0.base_register_sizes[regs] - 1 );
|
||||||
|
|
||||||
|
sprintf( buffer, "%04X_%04X_%02X%02X%02X regs",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function );
|
||||||
|
|
||||||
|
si->regs_area = map_physical_memory(
|
||||||
|
buffer,
|
||||||
|
(void *) di->pcii.u.h0.base_registers[regs],
|
||||||
|
di->pcii.u.h0.base_register_sizes[regs],
|
||||||
|
B_ANY_KERNEL_ADDRESS,
|
||||||
|
0,
|
||||||
|
(void **)&(di->regs));
|
||||||
|
if( si->regs_area < 0 )
|
||||||
|
return si->regs_area;
|
||||||
|
|
||||||
|
if( mmio_only )
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
if( di->pcii.u.h0.base_register_sizes[fb] > di->local_mem_size ) {
|
||||||
|
// Radeons allocate more address range then really needed ->
|
||||||
|
// only map the area that contains physical memory
|
||||||
|
SHOW_INFO( 1, "restrict frame buffer from 0x%8lx to 0x%8lx bytes",
|
||||||
|
di->pcii.u.h0.base_register_sizes[fb],
|
||||||
|
di->local_mem_size
|
||||||
|
);
|
||||||
|
di->pcii.u.h0.base_register_sizes[fb] = di->local_mem_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// framebuffer can be accessed by everyone
|
||||||
|
// this is not a perfect solution; preferably, only
|
||||||
|
// those areas owned by an application are mapped into
|
||||||
|
// its address space
|
||||||
|
SHOW_INFO( 1, "physical address of framebuffer: 0x%8lx-0x%8lx",
|
||||||
|
di->pcii.u.h0.base_registers[fb],
|
||||||
|
di->pcii.u.h0.base_registers[fb] + di->pcii.u.h0.base_register_sizes[fb] - 1 );
|
||||||
|
|
||||||
|
sprintf(buffer, "%04X_%04X_%02X%02X%02X framebuffer",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function);
|
||||||
|
|
||||||
|
si->fb_area = map_physical_memory(
|
||||||
|
buffer,
|
||||||
|
(void *) di->pcii.u.h0.base_registers[fb],
|
||||||
|
di->pcii.u.h0.base_register_sizes[fb],
|
||||||
|
B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
|
||||||
|
B_READ_AREA + B_WRITE_AREA,
|
||||||
|
(void **)&(si->framebuffer));
|
||||||
|
|
||||||
|
if (si->fb_area < 0) {
|
||||||
|
SHOW_FLOW0( 3, "couldn't enable WC for frame buffer" );
|
||||||
|
si->fb_area = map_physical_memory(
|
||||||
|
buffer,
|
||||||
|
(void *) di->pcii.u.h0.base_registers[fb],
|
||||||
|
di->pcii.u.h0.base_register_sizes[fb],
|
||||||
|
B_ANY_KERNEL_BLOCK_ADDRESS,
|
||||||
|
B_READ_AREA + B_WRITE_AREA,
|
||||||
|
(void **)&(si->framebuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "mapped frame buffer @%p", si->framebuffer );
|
||||||
|
|
||||||
|
if (si->fb_area < 0) {
|
||||||
|
result = si->fb_area;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save physical address though noone can probably make
|
||||||
|
// any use of it
|
||||||
|
si->framebuffer_pci = (void *) di->pcii.u.h0.base_registers_pci[fb];
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
delete_area(si->regs_area);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// unmap PCI ranges
|
||||||
|
void Radeon_UnmapDevice(device_info *di)
|
||||||
|
{
|
||||||
|
shared_info *si = di->si;
|
||||||
|
pci_info *pcii = &(di->pcii);
|
||||||
|
uint32 tmp;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
// disable PCI ranges (though it probably won't
|
||||||
|
// hurt leaving them enabled)
|
||||||
|
tmp = get_pci( PCI_command, 2 );
|
||||||
|
tmp &= ~PCI_command_io | PCI_command_memory | PCI_command_master;
|
||||||
|
set_pci( PCI_command, 2, tmp );
|
||||||
|
|
||||||
|
if( si->regs_area > 0 )
|
||||||
|
delete_area( si->regs_area );
|
||||||
|
|
||||||
|
if( si->fb_area > 0 )
|
||||||
|
delete_area( si->fb_area );
|
||||||
|
|
||||||
|
si->regs_area = si->fb_area = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// initialize shared infos on first open
|
||||||
|
status_t Radeon_FirstOpen( device_info *di )
|
||||||
|
{
|
||||||
|
status_t result;
|
||||||
|
char buffer[B_OS_NAME_LENGTH];
|
||||||
|
shared_info *si;
|
||||||
|
//uint32 /*dma_block, */dma_offset;
|
||||||
|
|
||||||
|
// create shared info; don't allow access by apps -
|
||||||
|
// they'll clone it
|
||||||
|
sprintf( buffer, "%04X_%04X_%02X%02X%02X shared",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function );
|
||||||
|
|
||||||
|
di->shared_area = create_area(
|
||||||
|
buffer,
|
||||||
|
(void **)&(di->si),
|
||||||
|
B_ANY_KERNEL_ADDRESS,
|
||||||
|
(sizeof(shared_info) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1),
|
||||||
|
B_FULL_LOCK, 0);
|
||||||
|
if (di->shared_area < 0) {
|
||||||
|
result = di->shared_area;
|
||||||
|
goto err8;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( di->si, 0, sizeof( *di->si ));
|
||||||
|
|
||||||
|
si = di->si;
|
||||||
|
|
||||||
|
#ifdef ENABLE_LOGGING
|
||||||
|
si->log = log_init( 1000000 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// copy all info into shared info
|
||||||
|
si->vendor_id = di->pcii.vendor_id;
|
||||||
|
si->device_id = di->pcii.device_id;
|
||||||
|
si->revision = di->pcii.revision;
|
||||||
|
|
||||||
|
si->has_crtc2 = di->has_crtc2;
|
||||||
|
si->asic = di->asic;
|
||||||
|
|
||||||
|
si->ports[0].disp_type = di->disp_type[0];
|
||||||
|
si->ports[1].disp_type = di->disp_type[1];
|
||||||
|
si->fp_port = di->fp_info;
|
||||||
|
si->pll = di->pll;
|
||||||
|
/* si->ram = di->ram;
|
||||||
|
strcpy( si->ram_type, di->ram_type );*/
|
||||||
|
si->local_mem_size = di->local_mem_size;
|
||||||
|
|
||||||
|
// create virtual card info; don't allow access by apps -
|
||||||
|
// they'll clone it
|
||||||
|
sprintf( buffer, "%04X_%04X_%02X%02X%02X virtual card 0",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function );
|
||||||
|
di->virtual_card_area = create_area(
|
||||||
|
buffer,
|
||||||
|
(void **)&(di->vc),
|
||||||
|
B_ANY_KERNEL_ADDRESS,
|
||||||
|
(sizeof(virtual_card) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1),
|
||||||
|
B_FULL_LOCK, 0);
|
||||||
|
if (di->virtual_card_area < 0) {
|
||||||
|
result = di->virtual_card_area;
|
||||||
|
goto err5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// currently, we assign fixed ports to this virtual card
|
||||||
|
di->vc->num_ports = si->has_crtc2 ? 2 : 1;
|
||||||
|
di->vc->ports[0].is_crtc2 = false;
|
||||||
|
di->vc->ports[0].physical_port = 0;
|
||||||
|
di->vc->ports[1].is_crtc2 = true;
|
||||||
|
di->vc->ports[1].physical_port = 1;
|
||||||
|
|
||||||
|
di->vc->fb_mem_handle = 0;
|
||||||
|
di->vc->cursor.mem_handle = 0;
|
||||||
|
|
||||||
|
// create unique id
|
||||||
|
di->vc->id = di->virtual_card_area;
|
||||||
|
|
||||||
|
result = Radeon_MapDevice( di, false );
|
||||||
|
if (result < 0)
|
||||||
|
goto err4;
|
||||||
|
|
||||||
|
// save dac2_cntl register
|
||||||
|
// we need to restore that during uninit, else you only get
|
||||||
|
// garbage on screen on reboot
|
||||||
|
if( di->has_crtc2 )
|
||||||
|
di->dac2_cntl = INREG( di->regs, RADEON_DAC_CNTL2 );
|
||||||
|
|
||||||
|
result = Radeon_InitDMA( di );
|
||||||
|
if( result < 0 )
|
||||||
|
goto err3;
|
||||||
|
|
||||||
|
// currently, we don't support VLI - something is broken there
|
||||||
|
// (it doesn't change a thing apart from crashing)
|
||||||
|
/* result = Radeon_SetupIRQ( di, buffer );
|
||||||
|
if( result < 0 )
|
||||||
|
goto err2;*/
|
||||||
|
|
||||||
|
di->local_memmgr = mem_init( 0, di->local_mem_size, 1024,
|
||||||
|
di->local_mem_size / 1024 );
|
||||||
|
if( di->local_memmgr == NULL ) {
|
||||||
|
result = B_NO_MEMORY;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Radeon_Fix_AGP();
|
||||||
|
|
||||||
|
// mem_alloc( di->local_memmgr, 0x100000, (void *)-1, &dma_block, &dma_offset );
|
||||||
|
/* dma_offset = 15 * 1024 * 1024;
|
||||||
|
|
||||||
|
si->nonlocal_mem = (uint32 *)((uint32)si->framebuffer + dma_offset);
|
||||||
|
si->nonlocal_vm_start = (uint32)si->framebuffer_pci + dma_offset;*/
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err1:
|
||||||
|
/*Radeon_CleanupIRQ( di );
|
||||||
|
err2:*/
|
||||||
|
Radeon_CleanupDMA( di );
|
||||||
|
err3:
|
||||||
|
Radeon_UnmapDevice( di );
|
||||||
|
err4:
|
||||||
|
delete_area( di->virtual_card_area );
|
||||||
|
err5:
|
||||||
|
delete_area( di->shared_area );
|
||||||
|
err8:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up shared info on last close
|
||||||
|
// (we could for device destruction, but this makes
|
||||||
|
// testing easier as everythings gets cleaned up
|
||||||
|
// during tests)
|
||||||
|
void Radeon_LastClose( device_info *di )
|
||||||
|
{
|
||||||
|
if( di->has_crtc2 )
|
||||||
|
OUTREG( di->regs, RADEON_DAC_CNTL2, di->dac2_cntl );
|
||||||
|
|
||||||
|
mem_destroy( di->local_memmgr );
|
||||||
|
|
||||||
|
// Radeon_CleanupIRQ( di );
|
||||||
|
Radeon_CleanupDMA( di );
|
||||||
|
Radeon_UnmapDevice(di);
|
||||||
|
|
||||||
|
#ifdef ENABLE_LOGGING
|
||||||
|
log_exit( di->si->log );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete_area( di->virtual_card_area );
|
||||||
|
delete_area( di->shared_area );
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Interrupt handling. Currently, none of this is used
|
||||||
|
as I haven't got the HW spec.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <mmio.h>
|
||||||
|
#include <rbbm_regs.h>
|
||||||
|
|
||||||
|
|
||||||
|
// disable all interrupts
|
||||||
|
static void Radeon_DisableIRQ( device_info *di )
|
||||||
|
{
|
||||||
|
OUTREG( di->regs, RADEON_GEN_INT_CNTL, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32 thread_interrupt_work( vuint8 *regs, device_info *di, uint32 int_status )
|
||||||
|
{
|
||||||
|
shared_info *si = di->si;
|
||||||
|
uint32 handled = B_HANDLED_INTERRUPT;
|
||||||
|
|
||||||
|
if( (int_status & RADEON_CRTC_VBLANK_STAT) != 0 &&
|
||||||
|
si->ports[0].vblank >= 0 )
|
||||||
|
{
|
||||||
|
int32 blocked;
|
||||||
|
|
||||||
|
++di->vbi_count[0];
|
||||||
|
|
||||||
|
if( (get_sem_count( si->ports[0].vblank, &blocked ) == B_OK) && (blocked < 0) ) {
|
||||||
|
//SHOW_FLOW( 3, "Signalled VBI 0 (%ldx)", -blocked );
|
||||||
|
release_sem_etc( si->ports[0].vblank, -blocked, B_DO_NOT_RESCHEDULE );
|
||||||
|
handled = B_INVOKE_SCHEDULER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (int_status & RADEON_CRTC2_VBLANK_STAT) != 0 &&
|
||||||
|
si->ports[1].vblank >= 0 )
|
||||||
|
{
|
||||||
|
int32 blocked;
|
||||||
|
|
||||||
|
++di->vbi_count[1];
|
||||||
|
|
||||||
|
if( (get_sem_count( si->ports[1].vblank, &blocked ) == B_OK) && (blocked < 0) ) {
|
||||||
|
//SHOW_FLOW( 3, "Signalled VBI 1 (%ldx)", -blocked );
|
||||||
|
release_sem_etc( si->ports[1].vblank, -blocked, B_DO_NOT_RESCHEDULE );
|
||||||
|
handled = B_INVOKE_SCHEDULER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32
|
||||||
|
Radeon_Interrupt(void *data)
|
||||||
|
{
|
||||||
|
int32 handled = B_UNHANDLED_INTERRUPT;
|
||||||
|
device_info *di = (device_info *)data;
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
int32 int_status;
|
||||||
|
|
||||||
|
int_status = INREG( regs, RADEON_GEN_INT_STATUS );
|
||||||
|
|
||||||
|
if( int_status != 0 ) {
|
||||||
|
++di->interrupt_count;
|
||||||
|
|
||||||
|
handled = thread_interrupt_work( regs, di, int_status );
|
||||||
|
OUTREG( regs, RADEON_GEN_INT_STATUS, int_status );
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32 timer_interrupt_func( timer *te )
|
||||||
|
{
|
||||||
|
bigtime_t now = system_time();
|
||||||
|
/* get the pointer to the device we're handling this time */
|
||||||
|
device_info *di = ((timer_info *)te)->di;
|
||||||
|
shared_info *si = di->si;
|
||||||
|
vuint8 *regs = di->regs;
|
||||||
|
uint32 vbl_status = 0 /* read vertical blank status */;
|
||||||
|
int32 result = B_HANDLED_INTERRUPT;
|
||||||
|
|
||||||
|
/* are we suppoesed to handle interrupts still? */
|
||||||
|
if( !di->shutdown_virtual_irq ) {
|
||||||
|
/* reschedule with same period by default */
|
||||||
|
bigtime_t when = si->refresh_period;
|
||||||
|
timer *to;
|
||||||
|
|
||||||
|
/* if interrupts are "enabled", do our thing */
|
||||||
|
if( si->enable_virtual_irq ) {
|
||||||
|
/* insert code to sync to interrupts here */
|
||||||
|
if (!vbl_status) {
|
||||||
|
when -= si->blank_period - 4;
|
||||||
|
}
|
||||||
|
/* do the things we do when we notice a vertical retrace */
|
||||||
|
result = thread_interrupt_work( regs, di,
|
||||||
|
RADEON_CRTC_VBLANK_STAT |
|
||||||
|
(di->has_crtc2 ? RADEON_CRTC_VBLANK_STAT : 0 ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pick the "other" timer */
|
||||||
|
to = (timer *)&(di->ti_a);
|
||||||
|
if (to == te) to = (timer *)&(di->ti_b);
|
||||||
|
/* our guess as to when we should be back */
|
||||||
|
((timer_info *)to)->when_target = now + when;
|
||||||
|
/* reschedule the interrupt */
|
||||||
|
add_timer(to, timer_interrupt_func, ((timer_info *)to)->when_target, B_ONE_SHOT_ABSOLUTE_TIMER);
|
||||||
|
/* remember the currently active timer */
|
||||||
|
di->current_timer = (timer_info *)to;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Radeon_SetupIRQ( device_info *di, char *buffer )
|
||||||
|
{
|
||||||
|
shared_info *si = di->si;
|
||||||
|
status_t result;
|
||||||
|
thread_id thid;
|
||||||
|
thread_info thinfo;
|
||||||
|
|
||||||
|
sprintf( buffer, "%04X_%04X_%02X%02X%02X VBI 0",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function );
|
||||||
|
si->ports[0].vblank = create_sem( 0, buffer );
|
||||||
|
if( si->ports[0].vblank < 0 ) {
|
||||||
|
result = si->ports[0].vblank;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf( buffer, "%04X_%04X_%02X%02X%02X VBI 1",
|
||||||
|
di->pcii.vendor_id, di->pcii.device_id,
|
||||||
|
di->pcii.bus, di->pcii.device, di->pcii.function );
|
||||||
|
si->ports[1].vblank = create_sem( 0, buffer );
|
||||||
|
if( si->ports[1].vblank < 0 ) {
|
||||||
|
result = si->ports[1].vblank;
|
||||||
|
goto err2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* change the owner of the semaphores to the opener's team */
|
||||||
|
/* this is required because apps can't aquire kernel semaphores */
|
||||||
|
thid = find_thread(NULL);
|
||||||
|
get_thread_info(thid, &thinfo);
|
||||||
|
set_sem_owner(si->ports[0].vblank, thinfo.team);
|
||||||
|
set_sem_owner(si->ports[1].vblank, thinfo.team);
|
||||||
|
|
||||||
|
/* disable and clear any pending interrupts */
|
||||||
|
Radeon_DisableIRQ( di );
|
||||||
|
|
||||||
|
/* if we're faking interrupts */
|
||||||
|
if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){
|
||||||
|
SHOW_INFO0( 3, "We like to fake IRQ" );
|
||||||
|
/* fake some kind of interrupt with a timer */
|
||||||
|
di->shutdown_virtual_irq = false;
|
||||||
|
si->refresh_period = 16666; /* fake 60Hz to start */
|
||||||
|
si->blank_period = si->refresh_period / 20;
|
||||||
|
|
||||||
|
di->ti_a.di = di; /* refer to ourself */
|
||||||
|
di->ti_b.di = di;
|
||||||
|
di->current_timer = &(di->ti_a);
|
||||||
|
|
||||||
|
/* program the first timer interrupt, and it will handle the rest */
|
||||||
|
result = add_timer((timer *)(di->current_timer), timer_interrupt_func,
|
||||||
|
si->refresh_period, B_ONE_SHOT_RELATIVE_TIMER);
|
||||||
|
if( result != B_OK )
|
||||||
|
goto err3;
|
||||||
|
} else {
|
||||||
|
/* otherwise install our interrupt handler */
|
||||||
|
result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line,
|
||||||
|
Radeon_Interrupt, (void *)di, 0);
|
||||||
|
if( result != B_OK )
|
||||||
|
goto err3;
|
||||||
|
|
||||||
|
SHOW_INFO( 3, "installed IRQ @ %d", di->pcii.u.h0.interrupt_line );
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err3:
|
||||||
|
delete_sem( si->ports[1].vblank );
|
||||||
|
err2:
|
||||||
|
delete_sem( si->ports[0].vblank );
|
||||||
|
err1:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Radeon_CleanupIRQ( device_info *di )
|
||||||
|
{
|
||||||
|
shared_info *si = di->si;
|
||||||
|
|
||||||
|
Radeon_DisableIRQ( di );
|
||||||
|
|
||||||
|
/* if we were faking the interrupts */
|
||||||
|
if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){
|
||||||
|
/* stop our interrupt faking thread */
|
||||||
|
di->shutdown_virtual_irq = true;
|
||||||
|
/* cancel the timer */
|
||||||
|
/* we don't know which one is current, so cancel them both and ignore any error */
|
||||||
|
cancel_timer((timer *)&(di->ti_a));
|
||||||
|
cancel_timer((timer *)&(di->ti_b));
|
||||||
|
} else {
|
||||||
|
/* remove interrupt handler */
|
||||||
|
remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, Radeon_Interrupt, di);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_sem( si->ports[1].vblank );
|
||||||
|
delete_sem( si->ports[0].vblank );
|
||||||
|
|
||||||
|
si->ports[1].vblank = si->ports[0].vblank = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Memory manager used for graphics mem
|
||||||
|
|
||||||
|
|
||||||
|
It has the following features
|
||||||
|
- doesn't access memory to be managed
|
||||||
|
- memory block's owner is identified by tag,
|
||||||
|
tag is verified during free, and all memory
|
||||||
|
belonging to one tag can be freed at once
|
||||||
|
- multi-threading save
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "memmgr.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "radeon_driver.h"
|
||||||
|
|
||||||
|
// init manager
|
||||||
|
// start - start of address space
|
||||||
|
// len - len of address space
|
||||||
|
// block_size - granularity
|
||||||
|
// heap_entries - maximum number of blocks
|
||||||
|
mem_info *mem_init( uint32 start, uint32 len, uint32 block_size, uint32 heap_entries )
|
||||||
|
{
|
||||||
|
mem_block *first;
|
||||||
|
mem_info *mem;
|
||||||
|
uint i;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "start=%lx, len=%lx, block_size=%lx, heap_entries=%ld",
|
||||||
|
start, len, block_size, heap_entries );
|
||||||
|
|
||||||
|
mem = malloc( sizeof( *mem ));
|
||||||
|
if( mem == NULL )
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
mem->block_size = block_size;
|
||||||
|
mem->heap_entries = heap_entries;
|
||||||
|
mem->lock = create_sem( 1, "mem_lock" );
|
||||||
|
|
||||||
|
if( mem->lock < 0 )
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
mem->heap = malloc( heap_entries * sizeof( mem_block ));
|
||||||
|
if( mem->heap == NULL )
|
||||||
|
goto err3;
|
||||||
|
|
||||||
|
for( i = 1; i < heap_entries; ++i )
|
||||||
|
mem->heap[i].next = &mem->heap[i+1];
|
||||||
|
|
||||||
|
mem->heap[heap_entries - 1].next = NULL;
|
||||||
|
mem->unused = &mem->heap[1];
|
||||||
|
|
||||||
|
first = &mem->heap[0];
|
||||||
|
mem->first = first;
|
||||||
|
|
||||||
|
first->base = start;
|
||||||
|
first->size = len;
|
||||||
|
first->prev = first->next = NULL;
|
||||||
|
first->alloced = false;
|
||||||
|
|
||||||
|
return mem;
|
||||||
|
|
||||||
|
err3:
|
||||||
|
delete_sem( mem->lock );
|
||||||
|
err2:
|
||||||
|
free( mem );
|
||||||
|
err:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// destroy heap
|
||||||
|
void mem_destroy( mem_info *mem )
|
||||||
|
{
|
||||||
|
SHOW_FLOW0( 2, "" );
|
||||||
|
|
||||||
|
free( mem->heap );
|
||||||
|
delete_sem( mem->lock );
|
||||||
|
free( mem );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// allocate memory block
|
||||||
|
// in:
|
||||||
|
// mem - heap handle
|
||||||
|
// size - size in bytes
|
||||||
|
// tag - owner tag
|
||||||
|
// out:
|
||||||
|
// block - block id
|
||||||
|
// offset - start address of block
|
||||||
|
status_t mem_alloc( mem_info *mem, uint32 size, void *tag, uint32 *block, uint32 *offset )
|
||||||
|
{
|
||||||
|
mem_block *cur, *new_entry;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "size=%ld, tag=%p", size, tag );
|
||||||
|
|
||||||
|
acquire_sem( mem->lock );
|
||||||
|
|
||||||
|
// we assume block_size is power of two
|
||||||
|
size = (size + mem->block_size - 1) & ~(mem->block_size - 1);
|
||||||
|
|
||||||
|
// simple first fit
|
||||||
|
for( cur = mem->first; cur; cur = cur->next ) {
|
||||||
|
if( !cur->alloced && cur->size >= size )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cur == NULL ) {
|
||||||
|
SHOW_FLOW0( 2, "out of memory" );
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( size != cur->size ) {
|
||||||
|
new_entry = mem->unused;
|
||||||
|
|
||||||
|
if( new_entry == NULL ) {
|
||||||
|
SHOW_FLOW0( 2, "out of blocks" );
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem->unused = new_entry->next;
|
||||||
|
|
||||||
|
new_entry->next = cur->next;
|
||||||
|
new_entry->prev = cur;
|
||||||
|
new_entry->alloced = false;
|
||||||
|
new_entry->base = cur->base + size;
|
||||||
|
new_entry->size = cur->size - size;
|
||||||
|
|
||||||
|
if( cur->next )
|
||||||
|
cur->next->prev = new_entry;
|
||||||
|
|
||||||
|
cur->next = new_entry;
|
||||||
|
cur->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur->alloced = true;
|
||||||
|
cur->tag = tag;
|
||||||
|
|
||||||
|
*block = cur - mem->heap + 1;
|
||||||
|
*offset = cur->base;
|
||||||
|
|
||||||
|
release_sem( mem->lock );
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "block_id=%ld, offset=%lx", *block, *offset );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
release_sem( mem->lock );
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// merge "block" with successor
|
||||||
|
static void merge( mem_info *mem, mem_block *block )
|
||||||
|
{
|
||||||
|
mem_block *next;
|
||||||
|
|
||||||
|
next = block->next;
|
||||||
|
|
||||||
|
block->size += next->size;
|
||||||
|
|
||||||
|
if( next->next )
|
||||||
|
next->next->prev = block;
|
||||||
|
|
||||||
|
block->next = next->next;
|
||||||
|
|
||||||
|
next->next = mem->unused;
|
||||||
|
mem->unused = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// internal: free memory block including merge
|
||||||
|
static mem_block *freeblock( mem_info *mem, mem_block *block )
|
||||||
|
{
|
||||||
|
mem_block *prev, *next;
|
||||||
|
|
||||||
|
block->alloced = false;
|
||||||
|
|
||||||
|
prev = block->prev;
|
||||||
|
|
||||||
|
if( prev && !prev->alloced ) {
|
||||||
|
block = prev;
|
||||||
|
merge( mem, prev );
|
||||||
|
}
|
||||||
|
|
||||||
|
next = block->next;
|
||||||
|
|
||||||
|
if( next && !next->alloced )
|
||||||
|
merge( mem, block );
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// free memory
|
||||||
|
// mem - heap handle
|
||||||
|
// block_id - block id
|
||||||
|
// tag - owner tag (must match tag passed to mem_alloc)
|
||||||
|
status_t mem_free( mem_info *mem, uint32 block_id, void *tag )
|
||||||
|
{
|
||||||
|
mem_block *block;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "block_id=%ld, tag=%p", block_id, tag );
|
||||||
|
|
||||||
|
acquire_sem( mem->lock );
|
||||||
|
|
||||||
|
--block_id;
|
||||||
|
|
||||||
|
if( block_id >= mem->heap_entries ) {
|
||||||
|
SHOW_FLOW0( 2, "invalid id" );
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
block = &mem->heap[block_id];
|
||||||
|
if( !block->alloced || block->tag != tag ) {
|
||||||
|
SHOW_FLOW0( 2, "not owner" );
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeblock( mem, block );
|
||||||
|
|
||||||
|
release_sem( mem->lock );
|
||||||
|
|
||||||
|
SHOW_FLOW0( 2, "success" );
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
release_sem( mem->lock );
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// free all memory belonging to owner "tag"
|
||||||
|
void mem_freetag( mem_info *mem, void *tag )
|
||||||
|
{
|
||||||
|
mem_block *cur;
|
||||||
|
|
||||||
|
SHOW_FLOW( 2, "tag=%p", tag );
|
||||||
|
|
||||||
|
acquire_sem( mem->lock );
|
||||||
|
|
||||||
|
for( cur = mem->first; cur; cur = cur->next ) {
|
||||||
|
if( cur->alloced && cur->tag == tag )
|
||||||
|
cur = freeblock( mem, cur );
|
||||||
|
}
|
||||||
|
|
||||||
|
release_sem( mem->lock );
|
||||||
|
|
||||||
|
SHOW_FLOW0( 2, "done" );
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Memory manager used for graphics mem
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MEMMGR_H
|
||||||
|
#define _MEMMGR_H
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
|
// allocated memory block
|
||||||
|
typedef struct mem_block {
|
||||||
|
struct mem_block *prev, *next;
|
||||||
|
uint32 base;
|
||||||
|
uint32 size;
|
||||||
|
void *tag;
|
||||||
|
bool alloced;
|
||||||
|
} mem_block;
|
||||||
|
|
||||||
|
|
||||||
|
// memory heap
|
||||||
|
typedef struct mem_info {
|
||||||
|
mem_block *first;
|
||||||
|
uint32 block_size;
|
||||||
|
sem_id lock;
|
||||||
|
mem_block *heap;
|
||||||
|
mem_block *unused;
|
||||||
|
uint32 heap_entries;
|
||||||
|
} mem_info;
|
||||||
|
|
||||||
|
|
||||||
|
mem_info *mem_init( uint32 start, uint32 len, uint32 block_size, uint32 heap_entries );
|
||||||
|
void mem_destroy( mem_info *mem );
|
||||||
|
status_t mem_alloc( mem_info *mem, uint32 size, void *tag, uint32 *block, uint32 *offset );
|
||||||
|
status_t mem_free( mem_info *mem, uint32 block_id, void *tag );
|
||||||
|
void mem_freetag( mem_info *mem, void *tag );
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2002, Thomas Kurschel
|
||||||
|
|
||||||
|
|
||||||
|
Part of Radeon kernel driver
|
||||||
|
|
||||||
|
Common header file
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RADEON_DRIVER_H
|
||||||
|
#define _RADEON_DRIVER_H
|
||||||
|
|
||||||
|
#include <radeon_interface.h>
|
||||||
|
#include "memmgr.h"
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <GraphicsDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
// logging helpers
|
||||||
|
extern int debug_level_flow;
|
||||||
|
extern int debug_level_info;
|
||||||
|
extern int debug_level_error;
|
||||||
|
|
||||||
|
/*#define DEBUG_WAIT_ON_MSG 1000000
|
||||||
|
#define DEBUG_WAIT_ON_ERROR 1000000*/
|
||||||
|
|
||||||
|
#define DEBUG_MSG_PREFIX "Radeon - "
|
||||||
|
#include <debug_ext.h>
|
||||||
|
|
||||||
|
#ifdef ENABLE_LOGGING
|
||||||
|
#include "log_coll.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_DEVICES 8
|
||||||
|
|
||||||
|
|
||||||
|
// DMA buffer
|
||||||
|
typedef struct DMA_buffer {
|
||||||
|
area_id buffer_area;
|
||||||
|
size_t size;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
area_id GART_area;
|
||||||
|
uint32 *GART_ptr;
|
||||||
|
uint32 GART_phys;
|
||||||
|
area_id unaligned_area;
|
||||||
|
} DMA_buffer;
|
||||||
|
|
||||||
|
// info about graphics RAM
|
||||||
|
typedef struct {
|
||||||
|
int ml;
|
||||||
|
int MB;
|
||||||
|
int Trcd;
|
||||||
|
int Trp;
|
||||||
|
int Twr;
|
||||||
|
int CL;
|
||||||
|
int Tr2w;
|
||||||
|
int loop_latency;
|
||||||
|
int Rloop;
|
||||||
|
} ram_info;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
area_id bios_area;
|
||||||
|
|
||||||
|
uint8 *bios_ptr; // begin of entire BIOS
|
||||||
|
uint8 *rom_ptr; // begin of ROM containing hw info
|
||||||
|
} rom_info;
|
||||||
|
|
||||||
|
|
||||||
|
// timer for VBI emulation
|
||||||
|
typedef struct {
|
||||||
|
timer te; /* timer entry for add_timer() */
|
||||||
|
struct device_info *di; /* pointer to the owning device */
|
||||||
|
bigtime_t when_target; /* when we're supposed to wake up */
|
||||||
|
} timer_info;
|
||||||
|
|
||||||
|
|
||||||
|
// info about one device
|
||||||
|
typedef struct device_info {
|
||||||
|
uint32 is_open;
|
||||||
|
area_id shared_area;
|
||||||
|
shared_info *si;
|
||||||
|
|
||||||
|
area_id virtual_card_area;
|
||||||
|
virtual_card *vc;
|
||||||
|
vuint8 *regs;
|
||||||
|
|
||||||
|
bool has_crtc2;
|
||||||
|
radeon_type asic;
|
||||||
|
display_type_e disp_type[2];
|
||||||
|
fp_info fp_info;
|
||||||
|
|
||||||
|
pll_info pll;
|
||||||
|
ram_info ram;
|
||||||
|
char ram_type[32]; // human-readable name of ram type
|
||||||
|
uint32 local_mem_size;
|
||||||
|
|
||||||
|
rom_info rom;
|
||||||
|
|
||||||
|
DMA_buffer DMABuffer;
|
||||||
|
|
||||||
|
mem_info *local_memmgr;
|
||||||
|
|
||||||
|
// VBI data
|
||||||
|
uint32 interrupt_count;
|
||||||
|
uint32 vbi_count[2];
|
||||||
|
// VBI emulation
|
||||||
|
int32 shutdown_virtual_irq; // true, to shutdown virtual interrupts
|
||||||
|
timer_info ti_a; /* a pool of two timer managment buffers */
|
||||||
|
timer_info ti_b;
|
||||||
|
timer_info *current_timer; /* the timer buffer that's currently in use */
|
||||||
|
|
||||||
|
uint32 dac2_cntl; // original dac2_cntl register content
|
||||||
|
|
||||||
|
pci_info pcii;
|
||||||
|
char name[MAX_RADEON_DEVICE_NAME_LENGTH];
|
||||||
|
} device_info;
|
||||||
|
|
||||||
|
|
||||||
|
// device list and some global data
|
||||||
|
typedef struct {
|
||||||
|
uint32 count;
|
||||||
|
benaphore kernel;
|
||||||
|
char *device_names[MAX_DEVICES+1];
|
||||||
|
device_info di[MAX_DEVICES];
|
||||||
|
} radeon_devices;
|
||||||
|
|
||||||
|
|
||||||
|
extern pci_module_info *pci_bus;
|
||||||
|
extern radeon_devices *devices;
|
||||||
|
|
||||||
|
|
||||||
|
// detect.c
|
||||||
|
bool Radeon_CardDetect( void );
|
||||||
|
void Radeon_ProbeDevices( void );
|
||||||
|
|
||||||
|
|
||||||
|
// init.c
|
||||||
|
status_t Radeon_FirstOpen( device_info *di );
|
||||||
|
void Radeon_LastClose( device_info *di );
|
||||||
|
status_t Radeon_MapDevice( device_info *di, bool mmio_only );
|
||||||
|
void Radeon_UnmapDevice(device_info *di);
|
||||||
|
|
||||||
|
|
||||||
|
// bios.c
|
||||||
|
status_t Radeon_MapBIOS( pci_info *pcii, rom_info *ri );
|
||||||
|
void Radeon_UnmapBIOS( rom_info *ri );
|
||||||
|
status_t Radeon_ReadBIOSData( device_info *di );
|
||||||
|
|
||||||
|
|
||||||
|
// dma.c
|
||||||
|
void Radeon_CleanupDMA( device_info *di );
|
||||||
|
status_t Radeon_InitDMA( device_info *di );
|
||||||
|
|
||||||
|
|
||||||
|
// irq.c
|
||||||
|
status_t Radeon_SetupIRQ( device_info *di, char *buffer );
|
||||||
|
void Radeon_CleanupIRQ( device_info *di );
|
||||||
|
|
||||||
|
|
||||||
|
// agp.c
|
||||||
|
void Radeon_Fix_AGP();
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user