From 1da2dde8c210e5d9a9aa4d1d79459a2e4dd7f4c7 Mon Sep 17 00:00:00 2001 From: shadow303 Date: Mon, 15 Mar 2004 00:57:31 +0000 Subject: [PATCH] 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 --- .../kernel/drivers/graphics/radeon/Jamfile | 37 ++ .../kernel/drivers/graphics/radeon/agp.c | 213 +++++++ .../kernel/drivers/graphics/radeon/bios.c | 572 ++++++++++++++++++ .../kernel/drivers/graphics/radeon/detect.c | 375 ++++++++++++ .../kernel/drivers/graphics/radeon/dma.c | 328 ++++++++++ .../kernel/drivers/graphics/radeon/driver.c | 275 +++++++++ .../drivers/graphics/radeon/global_data.c | 18 + .../kernel/drivers/graphics/radeon/init.c | 312 ++++++++++ .../kernel/drivers/graphics/radeon/irq.c | 220 +++++++ .../kernel/drivers/graphics/radeon/memmgr.c | 255 ++++++++ .../kernel/drivers/graphics/radeon/memmgr.h | 43 ++ .../drivers/graphics/radeon/radeon_driver.h | 166 +++++ 12 files changed, 2814 insertions(+) create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/Jamfile create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/agp.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/bios.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/detect.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/dma.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/driver.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/global_data.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/init.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/irq.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/memmgr.c create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/memmgr.h create mode 100644 src/add-ons/kernel/drivers/graphics/radeon/radeon_driver.h diff --git a/src/add-ons/kernel/drivers/graphics/radeon/Jamfile b/src/add-ons/kernel/drivers/graphics/radeon/Jamfile new file mode 100644 index 0000000000..6f5ff16825 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/Jamfile @@ -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 : + 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 = radeon.driver ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : radeon.driver ; +} + diff --git a/src/add-ons/kernel/drivers/graphics/radeon/agp.c b/src/add-ons/kernel/drivers/graphics/radeon/agp.c new file mode 100644 index 0000000000..89bebf3e58 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/agp.c @@ -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 ); + } + } +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/bios.c b/src/add-ons/kernel/drivers/graphics/radeon/bios.c new file mode 100644 index 0000000000..ba088965c8 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/bios.c @@ -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 +#include +#include +#include +#include +#include +#include +//#include "../common/utils.h" + +#include +#include + +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; +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/detect.c b/src/add-ons/kernel/drivers/graphics/radeon/detect.c new file mode 100644 index 0000000000..3ef3f58934 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/detect.c @@ -0,0 +1,375 @@ +/* + Copyright (c) 2002, Thomas Kurschel + + + Part of Radeon kernel driver + + Graphics card detection +*/ + + +#include "radeon_driver.h" + +#include + + +// 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 ); +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/dma.c b/src/add-ons/kernel/drivers/graphics/radeon/dma.c new file mode 100644 index 0000000000..7977148f6c --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/dma.c @@ -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 +#include +#include +#include +#include +#include +#include + + +#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 ); +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/driver.c b/src/add-ons/kernel/drivers/graphics/radeon/driver.c new file mode 100644 index 0000000000..a49d1e0fbe --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/driver.c @@ -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 +#include +#include +#include +#include +#include +#include + +// 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; +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/global_data.c b/src/add-ons/kernel/drivers/graphics/radeon/global_data.c new file mode 100644 index 0000000000..987e06bb41 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/global_data.c @@ -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; diff --git a/src/add-ons/kernel/drivers/graphics/radeon/init.c b/src/add-ons/kernel/drivers/graphics/radeon/init.c new file mode 100644 index 0000000000..be057eb187 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/init.c @@ -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 +#include +#include +#include + +// 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 ); +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/irq.c b/src/add-ons/kernel/drivers/graphics/radeon/irq.c new file mode 100644 index 0000000000..395fdeedad --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/irq.c @@ -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 + +#include +#include + + +// 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; +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/memmgr.c b/src/add-ons/kernel/drivers/graphics/radeon/memmgr.c new file mode 100644 index 0000000000..d27bc500eb --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/memmgr.c @@ -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 +#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" ); +} diff --git a/src/add-ons/kernel/drivers/graphics/radeon/memmgr.h b/src/add-ons/kernel/drivers/graphics/radeon/memmgr.h new file mode 100644 index 0000000000..6faf7e0823 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/memmgr.h @@ -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 + + +// 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 diff --git a/src/add-ons/kernel/drivers/graphics/radeon/radeon_driver.h b/src/add-ons/kernel/drivers/graphics/radeon/radeon_driver.h new file mode 100644 index 0000000000..55085d53a1 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/radeon/radeon_driver.h @@ -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 +#include "memmgr.h" + +#include +#include + + +// 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 + +#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