* introduce mc control calls

* malloc storage for mc state info
* redo pll range struct
* change to ATOM_ENCODER_MODE for connector info
* redo pll calculations to match AtomBIOS requirements
* some structure changes
* no longer init already posted AtomBIOS as it
  causes an infinite loop of AtomBIOS calls


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42644 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Alexander von Gluck IV
2011-08-19 23:07:45 +00:00
parent efa3bc3eca
commit d3e8b64208
12 changed files with 423 additions and 700 deletions
+34 -14
View File
@@ -51,15 +51,6 @@
#define RHD_POWER_UNKNOWN 3 /* initial state */
// info about PLL on graphics card
struct pll_info {
uint32 reference_frequency;
uint32 max_frequency;
uint32 min_frequency;
uint32 divisor_register;
};
struct ring_buffer {
struct lock lock;
uint32 register_base;
@@ -129,7 +120,6 @@ struct radeon_shared_info {
uint16 device_chipset;
char device_identifier[32];
struct pll_info pll_info;
};
//----------------- ioctl() interface ----------------
@@ -171,10 +161,40 @@ struct radeon_free_graphics_memory {
#define R6XX_CONFIG_APER_SIZE 0x5430 // r600>
#define OLD_CONFIG_APER_SIZE 0x0108 // <r600
#define R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914
#define R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c
#define R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114
#define R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c
#define D1CRTC_CONTROL 0x6080
#define CRTC_EN (1 << 0)
#define D1CRTC_STATUS 0x609c
#define D1CRTC_UPDATE_LOCK 0x60E8
#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110
#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118
#define D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914 // r700>
#define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c // r700>
#define D2CRTC_CONTROL 0x6880
#define D2CRTC_STATUS 0x689c
#define D2CRTC_UPDATE_LOCK 0x68E8
#define D2GRPH_PRIMARY_SURFACE_ADDRESS 0x6910
#define D2GRPH_SECONDARY_SURFACE_ADDRESS 0x6918
#define D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114 // r700>
#define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c // r700>
#define D1VGA_CONTROL 0x0330
#define DVGA_CONTROL_MODE_ENABLE (1 << 0)
#define DVGA_CONTROL_TIMING_SELECT (1 << 8)
#define DVGA_CONTROL_SYNC_POLARITY_SELECT (1 << 9)
#define DVGA_CONTROL_OVERSCAN_TIMING_SELECT (1 << 10)
#define DVGA_CONTROL_OVERSCAN_COLOR_EN (1 << 16)
#define DVGA_CONTROL_ROTATE (1 << 24)
#define D2VGA_CONTROL 0x0338
#define VGA_HDP_CONTROL 0x328
#define VGA_MEM_PAGE_SELECT_EN (1 << 0)
#define VGA_MEMORY_DISABLE (1 << 4)
#define VGA_RBBM_LOCK_DISABLE (1 << 8)
#define VGA_SOFT_RESET (1 << 16)
#define VGA_MEMORY_BASE_ADDRESS 0x0310
#define VGA_RENDER_CONTROL 0x0300
#define VGA_VSTATUS_CNTL_MASK 0x00030000
// cursor
#define RADEON_CURSOR_CONTROL 0x70080
@@ -108,6 +108,8 @@ init_common(int device, bool isClone)
memset(gInfo, 0, sizeof(accelerant_info));
gInfo->mc_info = (gpu_mc_info *)malloc(sizeof(gpu_mc_info));
for (uint32 id = 0; id < MAX_DISPLAY; id++) {
gDisplay[id] = (display_info *)malloc(sizeof(display_info));
if (gDisplay[id] == NULL)
@@ -130,6 +132,7 @@ init_common(int device, bool isClone)
if (ioctl(device, RADEON_GET_PRIVATE_DATA, &data,
sizeof(radeon_get_private_data)) != 0) {
free(gInfo->mc_info);
free(gInfo);
return B_ERROR;
}
@@ -140,6 +143,7 @@ init_common(int device, bool isClone)
data.shared_info_area);
status_t status = sharedCloner.InitCheck();
if (status < B_OK) {
free(gInfo->mc_info);
free(gInfo);
TRACE("%s, failed to create shared area\n", __func__);
return status;
@@ -151,6 +155,7 @@ init_common(int device, bool isClone)
gInfo->shared_info->registers_area);
status = regsCloner.InitCheck();
if (status < B_OK) {
free(gInfo->mc_info);
free(gInfo);
TRACE("%s, failed to create mmio area\n", __func__);
return status;
@@ -171,12 +176,6 @@ init_common(int device, bool isClone)
sharedCloner.Keep();
regsCloner.Keep();
// Define Radeon PLL default ranges
gInfo->shared_info->pll_info.reference_frequency
= RHD_PLL_REFERENCE_DEFAULT;
gInfo->shared_info->pll_info.min_frequency = RHD_PLL_MIN_DEFAULT;
gInfo->shared_info->pll_info.max_frequency = RHD_PLL_MAX_DEFAULT;
return B_OK;
}
@@ -196,6 +195,7 @@ uninit_common(void)
if (gInfo->is_clone)
close(gInfo->device);
free(gInfo->mc_info);
free(gInfo);
}
+57 -36
View File
@@ -27,6 +27,16 @@
// Maximum displays (more then two requires AtomBIOS)
typedef struct {
uint32 d1vga_control;
uint32 d2vga_control;
uint32 vga_render_control;
uint32 vga_hdp_control;
uint32 d1crtc_control;
uint32 d2crtc_control;
} gpu_mc_info;
struct accelerant_info {
vuint8 *regs;
area_id regs_area;
@@ -46,6 +56,8 @@ struct accelerant_info {
int device;
bool is_clone;
gpu_mc_info *mc_info; // used for last known mc state
// LVDS panel mode passed from the bios/startup.
display_mode lvds_panel_mode;
};
@@ -91,6 +103,41 @@ struct register_info {
};
struct pll_info {
/* reference frequency */
uint32 reference_freq;
/* fixed dividers */
uint32 reference_div;
uint32 post_div;
/* pll in/out limits */
uint32 pll_in_min;
uint32 pll_in_max;
uint32 pll_out_min;
uint32 pll_out_max;
uint32 lcd_pll_out_min;
uint32 lcd_pll_out_max;
uint32 best_vco;
/* divider limits */
uint32 min_ref_div;
uint32 max_ref_div;
uint32 min_post_div;
uint32 max_post_div;
uint32 min_feedback_div;
uint32 max_feedback_div;
uint32 min_frac_feedback_div;
uint32 max_frac_feedback_div;
/* flags for the current clock */
uint32 flags;
/* pll id */
uint32 id;
};
typedef struct {
bool active;
uint32 connection_type;
@@ -101,24 +148,19 @@ typedef struct {
uint32 vfreq_min;
uint32 hfreq_max;
uint32 hfreq_min;
pll_info pll;
} display_info;
// display_info connection_type
#define CONNECTION_DAC 0x0001
#define CONNECTION_TMDS 0x0002
#define CONNECTION_LVDS 0x0004
// register MMIO modes
#define OUT 0x1 // direct MMIO calls
#define CRT 0x2 // crt controler calls
#define VGA 0x3 // vga calls
#define OUT 0x1 // Direct MMIO calls
#define CRT 0x2 // Crt controller calls
#define VGA 0x3 // Vga calls
#define PLL 0x4 // PLL calls
#define MC 0x5 // Memory Controler calls
#define MC 0x5 // Memory controller calls
extern accelerant_info *gInfo;
//extern void *gAtomBIOS;
extern atom_context *gAtomContext;
extern display_info *gDisplay[MAX_DISPLAY];
@@ -139,22 +181,6 @@ _write32(uint32 offset, uint32 value)
}
inline uint32
_read32PLL(uint16 offset)
{
_write32(CLOCK_CNTL_INDEX, offset & PLL_ADDR);
return _read32(CLOCK_CNTL_DATA);
}
inline void
_write32PLL(uint16 offset, uint32 data)
{
_write32(CLOCK_CNTL_INDEX, (offset & PLL_ADDR) | PLL_WR_EN);
_write32(CLOCK_CNTL_DATA, data);
}
inline uint32
Read32(uint32 subsystem, uint32 offset)
{
@@ -162,13 +188,11 @@ Read32(uint32 subsystem, uint32 offset)
default:
case OUT:
case VGA:
case MC:
return _read32(offset);
case CRT:
return _read32(offset);
case PLL:
return _read32(offset);
//return _read32PLL(offset);
case MC:
return _read32(offset);
};
}
@@ -180,15 +204,12 @@ Write32(uint32 subsystem, uint32 offset, uint32 value)
default:
case OUT:
case VGA:
case MC:
_write32(offset, value);
return;
case CRT:
_write32(offset, value);
return;
case PLL:
_write32(offset, value);
//_write32PLL(offset, value);
return;
case MC:
_write32(offset, value);
return;
};
}
+55 -6
View File
@@ -59,6 +59,51 @@ radeon_bios_init_scratch()
}
bool
radeon_bios_isposted()
{
// aka, is primary graphics card that POST loaded
radeon_shared_info &info = *gInfo->shared_info;
uint32 reg;
if (info.device_chipset == (RADEON_R1000 | 0x50)) {
// palms
reg = Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC0_REGISTER_OFFSET)
| Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC1_REGISTER_OFFSET);
if (reg & EVERGREEN_CRTC_MASTER_EN)
return true;
} else if (info.device_chipset >= RADEON_R1000) {
// evergreen or higher
reg = Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC0_REGISTER_OFFSET)
| Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC1_REGISTER_OFFSET)
| Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC2_REGISTER_OFFSET)
| Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC3_REGISTER_OFFSET)
| Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC4_REGISTER_OFFSET)
| Read32(OUT, EVERGREEN_CRTC_CONTROL
+ EVERGREEN_CRTC5_REGISTER_OFFSET);
if (reg & EVERGREEN_CRTC_MASTER_EN)
return true;
} else if (info.device_chipset > RADEON_R580) {
// avivio through r700
reg = Read32(OUT, AVIVO_D1CRTC_CONTROL) |
Read32(OUT, AVIVO_D2CRTC_CONTROL);
if (reg & AVIVO_CRTC_EN) {
return true;
}
}
return false;
}
status_t
radeon_init_bios(uint8* bios)
{
@@ -114,12 +159,16 @@ radeon_init_bios(uint8* bios)
radeon_bios_init_scratch();
atom_allocate_fb_scratch(gAtomContext);
// TODO : this is only *required* on cards <= r500
// is it ok to run on cards > r500 before asic_init?
radeon_gpu_reset();
atom_asic_init(gAtomContext);
// Post card
// post card atombios if needed
if (!radeon_bios_isposted()) {
TRACE("%s: init AtomBIOS for this card as it is not not posted\n",
__func__);
// radeon_gpu_reset(); // <= r500 only?
atom_asic_init(gAtomContext);
} else {
TRACE("%s: AtomBIOS is already posted\n",
__func__);
}
return B_OK;
}
+1
View File
@@ -15,6 +15,7 @@
status_t radeon_init_bios(uint8* bios);
bool radeon_bios_isposted();
status_t radeon_dump_bios();
+42 -15
View File
@@ -112,11 +112,11 @@ init_registers(register_info* regs, uint8 crtid)
// Surface Address high only used on r770+
regs->grphPrimarySurfaceAddrHigh
= crtid == 1 ? R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH
: R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH;
= crtid == 1 ? D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH
: D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH;
regs->grphSecondarySurfaceAddrHigh
= crtid == 1 ? R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH
: R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH;
= crtid == 1 ? D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH
: D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH;
regs->grphPitch
= crtid == 1 ? D2GRPH_PITCH : D1GRPH_PITCH;
@@ -233,7 +233,7 @@ detect_displays()
for (uint32 id = 0; id < 2; id++) {
if (DACSense(id)) {
gDisplay[index]->active = true;
gDisplay[index]->connection_type = CONNECTION_DAC;
gDisplay[index]->connection_type = ATOM_ENCODER_MODE_CRT;
gDisplay[index]->connection_id = id;
init_registers(gDisplay[index]->regs, index);
if (detect_crt_ranges(index) == B_OK)
@@ -250,7 +250,8 @@ detect_displays()
for (uint32 id = 0; id < 1; id++) {
if (TMDSSense(id)) {
gDisplay[index]->active = true;
gDisplay[index]->connection_type = CONNECTION_TMDS;
gDisplay[index]->connection_type = ATOM_ENCODER_MODE_DVI;
// or ATOM_ENCODER_MODE_HDMI?
gDisplay[index]->connection_id = id;
init_registers(gDisplay[index]->regs, index);
if (detect_crt_ranges(index) == B_OK)
@@ -266,7 +267,7 @@ detect_displays()
// No monitors? Lets assume LVDS for now
if (index == 0) {
gDisplay[index]->active = true;
gDisplay[index]->connection_type = CONNECTION_LVDS;
gDisplay[index]->connection_type = ATOM_ENCODER_MODE_LVDS;
gDisplay[index]->connection_id = 1;
// 0 : LVDSA ; 1 : LVDSB / TDMSB
init_registers(gDisplay[index]->regs, index);
@@ -285,14 +286,40 @@ debug_displays()
id, gDisplay[id]->active ? "true" : "false");
if (gDisplay[id]->active) {
if (gDisplay[id]->connection_type == CONNECTION_DAC)
TRACE(" + connection: DAC\n");
else if (gDisplay[id]->connection_type == CONNECTION_TMDS)
TRACE(" + connection: TMDS\n");
else if (gDisplay[id]->connection_type == CONNECTION_LVDS)
TRACE(" + connection: LVDS\n");
else
TRACE(" + connection: UNKNOWN\n");
switch (gDisplay[id]->connection_type) {
case ATOM_ENCODER_MODE_DP:
TRACE(" + connection: DP\n");
break;
case ATOM_ENCODER_MODE_LVDS:
TRACE(" + connection: LVDS\n");
break;
case ATOM_ENCODER_MODE_DVI:
TRACE(" + connection: DVI\n");
break;
case ATOM_ENCODER_MODE_HDMI:
TRACE(" + connection: HDMI\n");
break;
case ATOM_ENCODER_MODE_SDVO:
TRACE(" + connection: SDVO\n");
break;
case ATOM_ENCODER_MODE_DP_AUDIO:
TRACE(" + connection: DP AUDIO\n");
break;
case ATOM_ENCODER_MODE_TV:
TRACE(" + connection: TV\n");
break;
case ATOM_ENCODER_MODE_CV:
TRACE(" + connection: CV\n");
break;
case ATOM_ENCODER_MODE_CRT:
TRACE(" + connection: CRT\n");
break;
case ATOM_ENCODER_MODE_DVO:
TRACE(" + connection: DVO\n");
break;
default:
TRACE(" + connection: UNKNOWN\n");
}
TRACE(" + connection index: % " B_PRIu8 "\n",
gDisplay[id]->connection_id);
+66 -9
View File
@@ -38,9 +38,10 @@ radeon_gpu_reset()
TRACE("%s: GPU software reset in progress...\n", __func__);
// TODO : mc stop
// Halt memory controller
radeon_gpu_mc_halt();
if (radeon_gpu_mc_idle() > 0) {
if (radeon_gpu_mc_idlecheck() > 0) {
ERROR("%s: Timeout waiting for MC to idle!\n", __func__);
}
@@ -152,14 +153,66 @@ radeon_gpu_reset()
snooze(50);
}
// TODO : mc resume
// Resume memory controller
radeon_gpu_mc_resume();
return B_OK;
}
void
radeon_gpu_mc_halt()
{
// Backup current memory controller state
gInfo->mc_info->d1vga_control = Read32(OUT, D1VGA_CONTROL);
gInfo->mc_info->d2vga_control = Read32(OUT, D2VGA_CONTROL);
gInfo->mc_info->vga_render_control = Read32(OUT, VGA_RENDER_CONTROL);
gInfo->mc_info->vga_hdp_control = Read32(OUT, VGA_HDP_CONTROL);
gInfo->mc_info->d1crtc_control = Read32(OUT, D1CRTC_CONTROL);
gInfo->mc_info->d2crtc_control = Read32(OUT, D2CRTC_CONTROL);
// halt all memory controller actions
Write32(OUT, D2CRTC_UPDATE_LOCK, 0);
Write32(OUT, VGA_RENDER_CONTROL, 0);
Write32(OUT, D1CRTC_UPDATE_LOCK, 1);
Write32(OUT, D2CRTC_UPDATE_LOCK, 1);
Write32(OUT, D1CRTC_CONTROL, 0);
Write32(OUT, D2CRTC_CONTROL, 0);
Write32(OUT, D1CRTC_UPDATE_LOCK, 0);
Write32(OUT, D2CRTC_UPDATE_LOCK, 0);
Write32(OUT, D1VGA_CONTROL, 0);
Write32(OUT, D2VGA_CONTROL, 0);
}
void
radeon_gpu_mc_resume()
{
// TODO : do surface addresses disappear on mc halt?
//Write32(OUT, D1GRPH_PRIMARY_SURFACE_ADDRESS, rdev->mc.vram_start);
//Write32(OUT, D1GRPH_SECONDARY_SURFACE_ADDRESS, rdev->mc.vram_start);
//Write32(OUT, D2GRPH_PRIMARY_SURFACE_ADDRESS, rdev->mc.vram_start);
//Write32(OUT, D2GRPH_SECONDARY_SURFACE_ADDRESS, rdev->mc.vram_start);
//Write32(OUT, VGA_MEMORY_BASE_ADDRESS, rdev->mc.vram_start);
// Rnlock host access
Write32(OUT, VGA_HDP_CONTROL, gInfo->mc_info->vga_hdp_control);
snooze(1);
// Restore memory controller state
Write32(OUT, D1VGA_CONTROL, gInfo->mc_info->d1vga_control);
Write32(OUT, D2VGA_CONTROL, gInfo->mc_info->d2vga_control);
Write32(OUT, D1CRTC_UPDATE_LOCK, 1);
Write32(OUT, D2CRTC_UPDATE_LOCK, 1);
Write32(OUT, D1CRTC_CONTROL, gInfo->mc_info->d1crtc_control);
Write32(OUT, D2CRTC_CONTROL, gInfo->mc_info->d2crtc_control);
Write32(OUT, D1CRTC_UPDATE_LOCK, 0);
Write32(OUT, D2CRTC_UPDATE_LOCK, 0);
Write32(OUT, VGA_RENDER_CONTROL, gInfo->mc_info->vga_render_control);
}
uint32
radeon_gpu_mc_idle()
radeon_gpu_mc_idlecheck()
{
uint32 idleStatus;
if (!((idleStatus = Read32(MC, SRBM_STATUS)) &
@@ -176,13 +229,15 @@ radeon_gpu_mc_setup()
{
uint32 fb_location_int = gInfo->shared_info->frame_buffer_int;
uint32 fb_location = Read32(OUT, R6XX_MC_VM_FB_LOCATION);
uint32 fb_location = Read32(OUT, R600_MC_VM_FB_LOCATION);
uint16 fb_size = (fb_location >> 16) - (fb_location & 0xFFFF);
uint32 fb_location_tmp = fb_location_int >> 24;
fb_location_tmp |= (fb_location_tmp + fb_size) << 16;
uint32 fb_offset_tmp = (fb_location_int >> 8) & 0xff0000;
uint32 idleState = radeon_gpu_mc_idle();
radeon_gpu_mc_halt();
uint32 idleState = radeon_gpu_mc_idlecheck();
if (idleState > 0) {
TRACE("%s: Cannot modify non-idle MC! idleState: 0x%" B_PRIX32 "\n",
__func__, idleState);
@@ -194,8 +249,10 @@ radeon_gpu_mc_setup()
__func__, fb_location, fb_location_tmp, fb_size);
// The MC Write32 will handle cards needing a special MC read/write register
Write32(MC, R6XX_MC_VM_FB_LOCATION, fb_location_tmp);
Write32(MC, R6XX_HDP_NONSURFACE_BASE, fb_offset_tmp);
Write32(MC, R600_MC_VM_FB_LOCATION, fb_location_tmp);
Write32(MC, R600_HDP_NONSURFACE_BASE, fb_offset_tmp);
radeon_gpu_mc_resume();
return B_OK;
}
+6 -1
View File
@@ -9,6 +9,9 @@
#define RADEON_HD_GPU_H
#include "accelerant.h"
// GPU Control registers. These are combined as
// the registers exist on all models, some flags
// are different though and are commented as such
@@ -160,7 +163,9 @@
status_t radeon_gpu_reset();
uint32 radeon_gpu_mc_idle();
void radeon_gpu_mc_halt();
void radeon_gpu_mc_resume();
uint32 radeon_gpu_mc_idlecheck();
status_t radeon_gpu_mc_setup();
status_t radeon_gpu_irq_setup();
+17 -13
View File
@@ -17,6 +17,7 @@
#include "utility.h"
#include "mode.h"
#include "display.h"
#include "pll.h"
#include <stdio.h>
#include <string.h>
@@ -108,8 +109,8 @@ radeon_set_display_mode(display_mode *mode)
continue;
}
//pll_set(gDisplay[id]->connection_id,
// mode->timing.pixel_clock, id);
pll_set(gDisplay[id]->connection_id,
mode->timing.pixel_clock, id);
// Program CRT Controller
display_crtc_set_dtd(id, mode);
@@ -119,13 +120,14 @@ radeon_set_display_mode(display_mode *mode)
// Program connector controllers
switch (gDisplay[id]->connection_type) {
case CONNECTION_DAC:
case ATOM_ENCODER_MODE_CRT:
DACSet(gDisplay[id]->connection_id, id);
break;
case CONNECTION_TMDS:
case ATOM_ENCODER_MODE_DVI:
case ATOM_ENCODER_MODE_HDMI:
TMDSSet(gDisplay[id]->connection_id, mode);
break;
case CONNECTION_LVDS:
case ATOM_ENCODER_MODE_LVDS:
LVDSSet(gDisplay[id]->connection_id, mode);
break;
}
@@ -134,17 +136,19 @@ radeon_set_display_mode(display_mode *mode)
display_crtc_blank(id, ATOM_DISABLE);
display_crtc_power(id, ATOM_ENABLE);
PLLPower(gDisplay[id]->connection_id, RHD_POWER_ON);
//PLLPower(gDisplay[id]->connection_id, RHD_POWER_ON);
// Power connector controllers
switch (gDisplay[id]->connection_type) {
case CONNECTION_DAC:
case ATOM_ENCODER_MODE_CRT:
DACPower(gDisplay[id]->connection_id, RHD_POWER_ON);
break;
case CONNECTION_TMDS:
case ATOM_ENCODER_MODE_DVI:
case ATOM_ENCODER_MODE_HDMI:
TMDSPower(gDisplay[id]->connection_id, RHD_POWER_ON);
break;
case CONNECTION_LVDS:
case ATOM_ENCODER_MODE_LVDS:
LVDSSet(gDisplay[id]->connection_id, mode);
LVDSPower(gDisplay[id]->connection_id, RHD_POWER_ON);
break;
}
@@ -197,16 +201,16 @@ radeon_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high)
*(uint32)mode->timing.v_total;
uint32 low = (totalClocks * 48L) / 1000L;
if (low < gInfo->shared_info->pll_info.min_frequency)
low = gInfo->shared_info->pll_info.min_frequency;
else if (low > gInfo->shared_info->pll_info.max_frequency)
if (low < PLL_MIN_DEFAULT)
low = PLL_MIN_DEFAULT;
else if (low > PLL_MAX_DEFAULT)
return B_ERROR;
*_low = low;
}
if (_high != NULL)
*_high = gInfo->shared_info->pll_info.max_frequency;
*_high = PLL_MAX_DEFAULT;
//*_low = 48L;
//*_high = 100 * 1000000L;
+121 -571
View File
@@ -3,7 +3,7 @@
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck, kallisti5@unixzen.com
* Alexander von Gluck, kallisti5@unixzen.com
*/
@@ -39,194 +39,129 @@ union set_pixel_clock {
};
/* From hardcoded values. */
static struct PLL_Control RV610PLLControl[] =
{
{ 0x0049, 0x159F8704 },
{ 0x006C, 0x159B8704 },
{ 0xFFFF, 0x159EC704 }
};
/* Some tables are provided by atombios,
* it's just that they are hidden away deliberately and not exposed */
static struct PLL_Control RV670PLLControl[] =
{
{ 0x004A, 0x159FC704 },
{ 0x0067, 0x159BC704 },
{ 0x00C4, 0x159EC704 },
{ 0x00F4, 0x1593A704 },
{ 0x0136, 0x1595A704 },
{ 0x01A4, 0x1596A704 },
{ 0x022C, 0x159CE504 },
{ 0xFFFF, 0x1591E404 }
};
static uint32
PLLControlTable(struct PLL_Control *table, uint16 feedbackDivider)
pll_compute_post_divider(uint32 targetClock)
{
int i;
radeon_shared_info &info = *gInfo->shared_info;
for (i = 0; table[i].feedbackDivider < 0xFFFF ; i++) {
if (table[i].feedbackDivider >= feedbackDivider)
break;
// if RADEON_PLL_USE_POST_DIV
// return pll->post_div;
uint32 vco;
if (info.device_chipset < (RADEON_R700 | 0x70)) {
if (0) // TODO : RADEON_PLL_IS_LCD
vco = PLL_MIN_DEFAULT; // pll->lcd_pll_out_min;
else
vco = PLL_MIN_DEFAULT; // pll->pll_out_min;
} else {
if (0) // TODO : RADEON_PLL_IS_LCD
vco = PLL_MAX_DEFAULT; // pll->lcd_pll_out_max;
else
vco = PLL_MAX_DEFAULT; // pll->pll_out_min;
}
return table[i].control;
uint32 postDivider = vco / targetClock;
uint32 tmp = vco % targetClock;
if (info.device_chipset < (RADEON_R700 | 0x70)) {
if (tmp)
postDivider++;
} else {
if (!tmp)
postDivider--;
}
if (postDivider > POST_DIV_LIMIT)
postDivider = POST_DIV_LIMIT;
else if (postDivider < POST_DIV_MIN)
postDivider = POST_DIV_MIN;
return postDivider;
}
status_t
PLLCalculate(uint32 pixelClock, uint16 *reference, uint16 *feedback,
uint16 *post)
pll_compute(uint32 pixelClock, uint32 *dotclockOut, uint32 *referenceOut,
uint32 *feedbackOut, uint32 *feedbackFracOut, uint32 *postOut)
{
// Freaking phase-locked loops, how do they work?
uint32 targetClock = pixelClock / 10;
uint32 postDivider = pll_compute_post_divider(targetClock);
uint32 referenceDivider = REF_DIV_MIN;
uint32 feedbackDivider = 0;
uint32 feedbackDividerFrac = 0;
float ratio = ((float) pixelClock)
/ ((float) gInfo->shared_info->pll_info.reference_frequency);
// if RADEON_PLL_USE_REF_DIV
// ref_div = pll->reference_div;
uint32 bestDiff = 0xFFFFFFFF;
uint32 postDiv;
uint32 referenceDiv;
uint32 feedbackDiv;
// if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
// avivo_get_fb_div(pll, targetClock, postDivider, referenceDivider,
// &feedbackDivider, &feedbackDividerFrac);
// feedbackDividerFrac = (100 * feedbackDividerFrac) / pll->reference_freq;
// if (frac_fb_div >= 5) {
// frac_fb_div -= 5;
// frac_fb_div = frac_fb_div / 10;
// frac_fb_div++;
// }
// if (frac_fb_div >= 10) {
// fb_div++;
// frac_fb_div = 0;
// }
// } else {
while (referenceDivider <= REF_DIV_LIMIT) {
// get feedback divider
uint32 retroEncabulator = postDivider * referenceDivider;
for (postDiv = 2; postDiv < POST_DIV_LIMIT; postDiv++) {
uint32 vcoOut = pixelClock * postDiv;
retroEncabulator *= targetClock;
feedbackDivider = retroEncabulator / PLL_REFERENCE_DEFAULT;
feedbackDividerFrac = retroEncabulator % PLL_REFERENCE_DEFAULT;
/* we are conservative and avoid the limits */
if (vcoOut <= gInfo->shared_info->pll_info.min_frequency)
continue;
if (vcoOut >= gInfo->shared_info->pll_info.max_frequency)
break;
if (feedbackDivider > FB_DIV_LIMIT)
feedbackDivider = FB_DIV_LIMIT;
else if (feedbackDivider < FB_DIV_MIN)
feedbackDivider = FB_DIV_MIN;
for (referenceDiv = 1; referenceDiv <= REF_DIV_LIMIT; referenceDiv++) {
feedbackDiv = (uint32)((ratio * postDiv * referenceDiv) + 0.5);
if (feedbackDividerFrac >= (PLL_REFERENCE_DEFAULT / 2))
feedbackDivider++;
if (feedbackDiv >= FB_DIV_LIMIT)
break;
if (feedbackDiv > (500 + (13 * referenceDiv))) // rv6x0 limit
break;
uint32 diff = abs(pixelClock - (feedbackDiv
* gInfo->shared_info->pll_info.reference_frequency)
/ (postDiv * referenceDiv));
if (diff < bestDiff) {
*feedback = feedbackDiv;
*reference = referenceDiv;
*post = postDiv;
bestDiff = diff;
feedbackDividerFrac = 0;
if (referenceDivider == 0 || postDivider == 0 || targetClock == 0) {
TRACE("%s: Caught division by zero\n",
__func__);
return B_ERROR;
}
uint32 tmp = (PLL_REFERENCE_DEFAULT * feedbackDivider)
/ (postDivider * referenceDivider);
tmp = (tmp * 10000) / targetClock;
if (bestDiff == 0)
if (tmp > (10000 + MAX_TOLERANCE))
referenceDivider++;
else if (tmp >= (10000 - MAX_TOLERANCE))
break;
else
referenceDivider++;
}
// }
if (bestDiff == 0)
break;
if (referenceDivider == 0 || postDivider == 0) {
TRACE("%s: Caught division by zero of post or reference divider\n",
__func__);
return B_ERROR;
}
if (bestDiff != 0xFFFFFFFF) {
TRACE("%s: Successful PLL Calculation: %dkHz = "
"(((%i / 0x%X) * 0x%X) / 0x%X) (%dkHz off)\n", __func__,
(int) pixelClock,
(unsigned int) gInfo->shared_info->pll_info.reference_frequency,
*reference, *feedback, *post, (int) bestDiff);
return B_OK;
}
*dotclockOut = ((PLL_REFERENCE_DEFAULT * feedbackDivider * 10)
+ (PLL_REFERENCE_DEFAULT * feedbackDividerFrac))
/ (referenceDivider * postDivider * 10);
// Shouldn't ever happen
TRACE("%s: Failed to get a valid PLL setting for %dkHz\n",
__func__, (int) pixelClock);
return B_ERROR;
}
*feedbackOut = feedbackDivider;
*feedbackFracOut = feedbackDividerFrac;
*referenceOut = referenceDivider;
*postOut = postDivider;
status_t
PLLPower(uint8 pllIndex, int command)
{
uint16 pllControlReg = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL;
bool hasDccg = DCCGCLKAvailable(pllIndex);
TRACE("%s: card has DCCG = %c\n", __func__, hasDccg ? 'y' : 'n');
switch (command) {
case RHD_POWER_ON:
{
TRACE("%s: PLL %d Power On\n", __func__, pllIndex);
if (hasDccg)
DCCGCLKSet(pllIndex, RV620_DCCGCLK_RESET);
Write32Mask(PLL, pllControlReg, 0, 0x02);
// Power On
snooze(2);
PLLCalibrate(pllIndex);
if (hasDccg)
DCCGCLKSet(pllIndex, RV620_DCCGCLK_GRAB);
return B_OK;
}
case RHD_POWER_RESET:
{
TRACE("%s: PLL %d Power Reset\n", __func__, pllIndex);
if (hasDccg)
DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE);
Write32Mask(PLL, pllControlReg, 0x01, 0x01);
// Reset
snooze(2);
Write32Mask(PLL, pllControlReg, 0, 0x02);
// Power On
snooze(2);
return B_OK;
}
case RHD_POWER_SHUTDOWN:
default:
TRACE("%s: PLL %d Power Shutdown\n", __func__, pllIndex);
radeon_shared_info &info = *gInfo->shared_info;
if (hasDccg)
DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE);
Write32Mask(PLL, pllControlReg, 0x01, 0x01);
// Reset
snooze(2);
if (info.device_chipset >= (RADEON_R600 | 0x20)) {
uint16 pllDiffPostReg
= pllIndex == 1 ? RV620_EXT2_DIFF_POST_DIV_CNTL
: RV620_EXT1_DIFF_POST_DIV_CNTL;
uint16 pllDiffDriverEnable
= pllIndex == 1 ? (uint16)RV62_EXT2_DIFF_DRIVER_ENABLE
: (uint16)RV62_EXT1_DIFF_DRIVER_ENABLE;
// Sometimes we have to keep an unused PLL running. X Bug #18016
if ((Read32(PLL, pllDiffPostReg)
& pllDiffDriverEnable) == 0) {
Write32Mask(PLL, pllControlReg, 0x02, 0x02);
// Power Down
} else {
TRACE("%s: PHYA differential clock driver not disabled\n",
__func__);
}
snooze(200);
Write32Mask(PLL, pllControlReg, 0x2000, 0x2000);
// Reset anti-glitch?
} else {
Write32Mask(PLL, pllControlReg, 0x02, 0x02);
// Power Down
snooze(200);
}
}
TRACE("%s: pixel clock: %" B_PRIu32 " gives:"
" feedbackDivider = %" B_PRIu32 ".%" B_PRIu32
"; referenceDivider = %" B_PRIu32 "; postDivider = %" B_PRIu32
"; dotClock = %" B_PRIu32 "\n", __func__, pixelClock, feedbackDivider,
feedbackDividerFrac, referenceDivider, postDivider, *dotclockOut);
return B_OK;
}
@@ -235,30 +170,29 @@ PLLPower(uint8 pllIndex, int command)
status_t
pll_set(uint8 pll_id, uint32 pixelClock, uint8 crtc_id)
{
uint16 reference = 0;
uint16 feedback = 0;
uint16 post = 0;
uint32 dotclock = 0;
uint32 reference = 0;
uint32 feedback = 0;
uint32 feedbackFrac = 0;
uint32 post = 0;
PLLCalculate(pixelClock, &reference, &feedback, &post);
pll_compute(pixelClock, &dotclock, &reference, &feedback,
&feedbackFrac, &post);
int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
union set_pixel_clock args;
memset(&args, 0, sizeof(args));
//uint8 frev;
//uint8 crev;
//atom_parse_cmd_header(gAtomContext, index, &frev, &crev);
uint8 frev = 1;
uint8 crev = 1;
uint8 frev;
uint8 crev;
atom_parse_cmd_header(gAtomContext, index, &frev, &crev);
switch (crev) {
case 1:
args.v1.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
args.v1.usRefDiv = B_HOST_TO_LENDIAN_INT16(reference);
args.v1.usFbDiv = B_HOST_TO_LENDIAN_INT16(feedback);
// args.v1.ucFracFbDiv = frac_fb_div;
args.v1.ucFracFbDiv = 0;
args.v1.ucFracFbDiv = feedbackFrac;
args.v1.ucPostDiv = post;
args.v1.ucPpll = pll_id;
args.v1.ucCRTC = crtc_id;
@@ -268,419 +202,35 @@ pll_set(uint8 pll_id, uint32 pixelClock, uint8 crtc_id)
args.v2.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
args.v2.usRefDiv = B_HOST_TO_LENDIAN_INT16(reference);
args.v2.usFbDiv = B_HOST_TO_LENDIAN_INT16(feedback);
// args.v2.ucFracFbDiv = frac_fb_div;
args.v2.ucFracFbDiv = feedbackFrac;
args.v2.ucPostDiv = post;
args.v2.ucPpll = pll_id;
args.v2.ucCRTC = crtc_id;
args.v2.ucRefDivSrc = 1;
break;
#if 0
case 3:
args.v3.usPixelClock = B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
args.v3.usRefDiv = B_HOST_TO_LENDIAN_INT16(reference);
args.v3.usFbDiv = B_HOST_TO_LENDIAN_INT16(feedback);
// args.v3.ucFracFbDiv = frac_fb_div;
args.v3.ucFracFbDiv = feedbackFrac;
args.v3.ucPostDiv = post;
args.v3.ucPpll = pll_id;
args.v3.ucMiscInfo = (pll_id << 2);
if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC;
args.v3.ucTransmitterId = encoder_id;
args.v3.ucEncoderMode = encoder_mode;
// if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
// args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC;
args.v3.ucTransmitterId = crtc_id;
// TODO : transmitter id is now CRTC id?
args.v3.ucEncoderMode = gDisplay[crtc_id]->connection_type;
break;
#endif
default:
TRACE("%s: TODO: table version %d %d\n", __func__, frev, crev);
TRACE("%s: ERROR: table version %d.%d TODO\n", __func__,
frev, crev);
return B_ERROR;
}
TRACE("%s: setting pixel clock %" B_PRIu32 "\n", __func__, pixelClock);
atom_execute_table(gAtomContext, index, (uint32 *)&args);
#if 0
if (info.device_chipset >= (RADEON_R600 | 0x20)) {
TRACE("%s : setting pixel clock %d on r620+\n", __func__,
(int)pixelClock);
PLLSetLowR620(pllIndex, pixelClock, reference,
feedback, post);
} else if (info.device_chipset < (RADEON_R600 | 0x20)) {
TRACE("%s : setting pixel clock %d on r600-r610\n", __func__,
(int)pixelClock);
PLLSetLowLegacy(pllIndex, pixelClock, reference,
feedback, post);
}
#endif
return B_OK;
}
void
PLLSetLowLegacy(uint8 pllIndex, uint32 pixelClock, uint16 reference,
uint16 feedback, uint16 post)
{
uint32 feedbackTemp = feedback << 16;
uint32 referenceTemp = reference;
/* Internal PLL Registers */
uint16 pllCntl = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL;
uint16 pllIntSSCntl
= pllIndex == 1 ? P2PLL_INT_SS_CNTL : P1PLL_INT_SS_CNTL;
/* External PLL Registers */
uint16 pllExtCntl
= pllIndex == 1 ? EXT2_PPLL_CNTL : EXT1_PPLL_CNTL;
uint16 pllExtUpdateCntl
= pllIndex == 1 ? EXT2_PPLL_UPDATE_CNTL : EXT1_PPLL_UPDATE_CNTL;
uint16 pllExtUpdateLock
= pllIndex == 1 ? EXT2_PPLL_UPDATE_LOCK : EXT1_PPLL_UPDATE_LOCK;
uint16 pllExtPostDiv
= pllIndex == 1 ? EXT2_PPLL_POST_DIV : EXT1_PPLL_POST_DIV;
uint16 pllExtPostDivSrc
= pllIndex == 1 ? EXT2_PPLL_POST_DIV_SRC : EXT1_PPLL_POST_DIV_SRC;
uint16 pllExtFeedbackDiv
= pllIndex == 1 ? EXT2_PPLL_FB_DIV : EXT1_PPLL_FB_DIV;
uint16 pllExtRefDiv
= pllIndex == 1 ? EXT2_PPLL_REF_DIV : EXT1_PPLL_REF_DIV;
uint16 pllExtRefDivSrc
= pllIndex == 1 ? EXT2_PPLL_REF_DIV_SRC : EXT1_PPLL_REF_DIV_SRC;
radeon_shared_info &info = *gInfo->shared_info;
if (info.device_chipset <= RADEON_R600)
feedbackTemp |= 0x00000030;
else {
if (feedback <= 0x24)
feedbackTemp |= 0x00000030;
else if (feedback <= 0x3F)
feedbackTemp |= 0x00000020;
}
uint32 postTemp = Read32(PLL, pllExtPostDiv) & ~0x0000007F;
postTemp |= post & 0x0000007F;
uint32 control;
if (info.device_chipset == RADEON_R600)
control = 0x01130704;
else {
control = PLLControlTable(RV610PLLControl, feedback);
if (!control)
control = Read32(PLL, pllExtCntl);
}
Write32Mask(PLL, pllIntSSCntl, 0, 0x00000001);
// Disable Spread Spectrum
Write32(PLL, pllExtRefDivSrc, 0x01); /* XTAL */
Write32(PLL, pllExtPostDivSrc, 0x00); /* source = reference */
Write32(PLL, pllExtUpdateLock, 0x01); /* lock */
Write32(PLL, pllExtRefDiv, referenceTemp);
Write32(PLL, pllExtFeedbackDiv, feedbackTemp);
Write32(PLL, pllExtPostDiv, postTemp);
Write32(PLL, pllExtCntl, control);
Write32Mask(PLL, pllExtUpdateCntl, 0x00010000, 0x00010000);
// No autoreset
Write32Mask(PLL, pllCntl, 0, 0x04);
// Don't bypass calibration
/* We need to reset the anti glitch logic */
Write32Mask(PLL, pllCntl, 0, 0x00000002);
// Power up
/* reset anti glitch logic */
Write32Mask(PLL, pllCntl, 0x00002000, 0x00002000);
snooze(2);
Write32Mask(PLL, pllCntl, 0, 0x00002000);
/* powerdown and reset */
Write32Mask(PLL, pllCntl, 0x00000003, 0x00000003);
snooze(2);
Write32(PLL, pllExtUpdateLock, 0);
// Unlock
Write32Mask(PLL, pllExtUpdateCntl, 0, 0x01);
// Done updating
Write32Mask(PLL, pllCntl, 0, 0x02);
// Power up PLL
snooze(2);
PLLCalibrate(pllIndex);
Write32(PLL, pllExtPostDivSrc, 0x01);
// Set source as PLL
// TODO : for now we assume crt 0, needs refactoring
PLLCRTCGrab(pllIndex, 0);
}
void
PLLSetLowR620(uint8 pllIndex, uint32 pixelClock, uint16 reference,
uint16 feedback, uint16 post)
{
radeon_shared_info &info = *gInfo->shared_info;
bool hasDccg = DCCGCLKAvailable(pllIndex);
TRACE("%s: card has DCCG = %c\n", __func__, hasDccg ? 'y' : 'n');
if (hasDccg)
DCCGCLKSet(pllIndex, RV620_DCCGCLK_RESET);
/* Internal PLL Registers */
uint16 pllCntl = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL;
uint16 pllIntSSCntl
= pllIndex == 1 ? P2PLL_INT_SS_CNTL : P1PLL_INT_SS_CNTL;
/* External PLL Registers */
uint16 pllExtCntl
= pllIndex == 1 ? EXT2_PPLL_CNTL : EXT1_PPLL_CNTL;
//uint16 pllExtUpdateCntl
// = pllIndex == 1 ? EXT2_PPLL_UPDATE_CNTL : EXT1_PPLL_UPDATE_CNTL;
uint16 pllExtUpdateLock
= pllIndex == 1 ? EXT2_PPLL_UPDATE_LOCK : EXT1_PPLL_UPDATE_LOCK;
uint16 pllExtPostDiv
= pllIndex == 1 ? EXT2_PPLL_POST_DIV : EXT1_PPLL_POST_DIV;
uint16 pllExtPostDivSrc
= pllIndex == 1 ? EXT2_PPLL_POST_DIV_SRC : EXT1_PPLL_POST_DIV_SRC;
uint16 pllExtPostDivSym
= pllIndex == 1 ? EXT2_SYM_PPLL_POST_DIV : EXT1_SYM_PPLL_POST_DIV;
uint16 pllExtFeedbackDiv
= pllIndex == 1 ? EXT2_PPLL_FB_DIV : EXT1_PPLL_FB_DIV;
uint16 pllExtRefDiv
= pllIndex == 1 ? EXT2_PPLL_REF_DIV : EXT1_PPLL_REF_DIV;
//uint16 pllExtRefDivSrc
// = pllIndex == 1 ? EXT2_PPLL_REF_DIV_SRC : EXT1_PPLL_REF_DIV_SRC;
uint16 pllExtDispClkCntl
= pllIndex == 1 ? P2PLL_DISP_CLK_CNTL : P1PLL_DISP_CLK_CNTL;
Write32Mask(PLL, pllIntSSCntl, 0, 0x00000001);
// Disable Spread Spectrum
uint32 referenceDivider = reference;
uint32 feedbackDivider = Read32(PLL, pllExtFeedbackDiv) & ~0x07FF003F;
feedbackDivider |= ((feedback << 16) | 0x0030) & 0x07FF003F;
uint32 postDivider = Read32(PLL, pllExtPostDiv) & ~0x0000007F;
postDivider |= post & 0x0000007F;
uint32 control;
if (info.device_chipset >= (RADEON_R600 | 0x70))
control = PLLControlTable(RV670PLLControl, feedback);
else
control = PLLControlTable(RV610PLLControl, feedback);
uint8 symPostDiv = post & 0x0000007F;
/* switch to external */
Write32(PLL, pllExtPostDivSrc, 0);
Write32Mask(PLL, pllExtDispClkCntl, 0x00000200, 0x00000300);
Write32Mask(PLL, pllExtPostDiv, 0, 0x00000100);
Write32Mask(PLL, pllCntl, 0x00000001, 0x00000001);
// reset
snooze(2);
Write32Mask(PLL, pllCntl, 0x00000002, 0x00000002);
// power down
snooze(10);
Write32Mask(PLL, pllCntl, 0x00002000, 0x00002000);
// reset antiglitch
Write32(PLL, pllExtCntl, control);
Write32Mask(PLL, pllExtDispClkCntl, 2, 0x0000003F);
// Scalar Divider 2
Write32(PLL, pllExtUpdateLock, 1);
// Lock PLL
/* Write PLL clocks */
Write32(PLL, pllExtPostDivSrc, 0x00000001);
Write32(PLL, pllExtRefDiv, referenceDivider);
Write32(PLL, pllExtFeedbackDiv, feedbackDivider);
Write32Mask(PLL, pllExtPostDiv, postDivider, 0x0000007F);
Write32Mask(PLL, pllExtPostDivSym, symPostDiv, 0x0000007F);
snooze(10);
Write32(PLL, pllExtUpdateLock, 0);
// Unlock PLL
Write32Mask(PLL, pllCntl, 0, 0x00000002);
// power up
snooze(10);
Write32Mask(PLL, pllCntl, 0, 0x00002000);
// undo reset antiglitch
PLLCalibrate(pllIndex);
/* Switch back to PLL */
Write32Mask(PLL, pllExtDispClkCntl, 0, 0x00000300);
Write32Mask(PLL, pllExtPostDivSym, 0x00000100, 0x00000100);
Write32(PLL, pllExtPostDivSrc, 0x00000001);
Write32Mask(PLL, pllCntl, 0, 0x80000000);
// needed and undocumented
// TODO : for now we assume crt 0, needs refactoring
PLLCRTCGrab(pllIndex, 0);
if (hasDccg)
DCCGCLKSet(pllIndex, RV620_DCCGCLK_GRAB);
TRACE("%s: PLLSet exit\n", __func__);
}
status_t
PLLCalibrate(uint8 pllIndex)
{
uint16 pllControlReg = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL;
Write32Mask(PLL, pllControlReg, 1, 0x01);
// PLL Reset
snooze(2);
Write32Mask(PLL, pllControlReg, 0, 0x01);
// PLL Set
int i;
for (i = 0; i < PLL_CALIBRATE_WAIT; i++) {
if (((Read32(PLL, pllControlReg) >> 20) & 0x03) == 0x03)
break;
}
if (i >= PLL_CALIBRATE_WAIT) {
if (Read32(PLL, pllControlReg) & 0x00100000) /* Calibration done? */
TRACE("%s: Calibration Failed\n", __func__);
if (Read32(PLL, pllControlReg) & 0x00200000) /* PLL locked? */
TRACE("%s: Locking Failed\n", __func__);
TRACE("%s: We encountered a problem calibrating the PLL.\n", __func__);
return B_ERROR;
} else
TRACE("%s: pll calibrated and locked in %d loops\n", __func__, i);
return B_OK;
}
void
PLLCRTCGrab(uint8 pllIndex, uint8 crtid)
{
bool pll2IsCurrent;
if (crtid == 0) {
pll2IsCurrent = Read32(PLL, PCLK_CRTC1_CNTL) & 0x00010000;
Write32Mask(PLL, PCLK_CRTC1_CNTL, pllIndex == 0 ? 0x00010000 : 0,
0x00010000);
} else {
pll2IsCurrent = Read32(PLL, PCLK_CRTC2_CNTL) & 0x00010000;
Write32Mask(PLL, PCLK_CRTC2_CNTL, pllIndex == 0 ? 0x00010000 : 0,
0x00010000);
}
/* if the current pll is not active, then poke it just enough to flip
* owners */
if (!pll2IsCurrent) {
uint32 stored = Read32(PLL, P1PLL_CNTL);
if (stored & 0x03) {
Write32Mask(PLL, P1PLL_CNTL, 0, 0x03);
snooze(10);
Write32Mask(PLL, P1PLL_CNTL, stored, 0x03);
}
} else {
uint32 stored = Read32(PLL, P2PLL_CNTL);
if (stored & 0x03) {
Write32Mask(PLL, P2PLL_CNTL, 0, 0x03);
snooze(10);
Write32Mask(PLL, P2PLL_CNTL, stored, 0x03);
}
}
}
// See if card has a DCCG available that we need to lock to
// the PLL clock. No one seems really sure what DCCG is.
bool
DCCGCLKAvailable(uint8 pllIndex)
{
radeon_shared_info &info = *gInfo->shared_info;
if (info.device_chipset < (RADEON_R600 | 0x20))
return false;
uint32 dccg = Read32(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03;
if (dccg & 0x02)
return true;
if ((pllIndex == 0) && (dccg == 0))
return true;
if ((pllIndex == 1) && (dccg == 1))
return true;
return false;
}
void
DCCGCLKSet(uint8 pllIndex, int set)
{
uint32 buffer;
switch(set) {
case RV620_DCCGCLK_GRAB:
if (pllIndex == 0)
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003);
else if (pllIndex == 1)
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003);
else
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
break;
case RV620_DCCGCLK_RELEASE:
buffer = Read32(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03;
if ((pllIndex == 0) && (buffer == 0)) {
/* set to other PLL or external */
buffer = Read32(PLL, P2PLL_CNTL);
// if powered and not in reset, and calibrated and locked
if (!(buffer & 0x03) && ((buffer & 0x00300000) == 0x00300000))
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003);
else
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
} else if ((pllIndex == 1) && (buffer == 1)) {
/* set to other PLL or external */
buffer = Read32(PLL, P1PLL_CNTL);
// if powered and not in reset, and calibrated and locked
if (!(buffer & 0x03) && ((buffer & 0x00300000) == 0x00300000))
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003);
else
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
} // no other action needed
break;
case RV620_DCCGCLK_RESET:
buffer = Read32(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03;
if (((pllIndex == 0) && (buffer == 0))
|| ((pllIndex == 1) && (buffer == 1)))
Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
break;
default:
break;
}
}
+11 -28
View File
@@ -9,42 +9,25 @@
#define RADEON_HD_PLL_H
#define RHD_PLL_MIN_DEFAULT 16000
#define RHD_PLL_MAX_DEFAULT 400000
#define RHD_PLL_REFERENCE_DEFAULT 27000
#define MAX_TOLERANCE 10
// xorg default is 0x100000 which seems a little much.
#define PLL_CALIBRATE_WAIT 0x010000
#define PLL_MIN_DEFAULT 16000
#define PLL_MAX_DEFAULT 400000
#define PLL_REFERENCE_DEFAULT 27000
/* limited by the number of bits available */
#define FB_DIV_MIN 4
#define FB_DIV_LIMIT 2048
#define REF_DIV_MIN 2
#define REF_DIV_LIMIT 1024
#define POST_DIV_LIMIT 128
// DCCGClk Operation Modes
#define RV620_DCCGCLK_RESET 0
#define RV620_DCCGCLK_GRAB 1
#define RV620_DCCGCLK_RELEASE 2
#define POST_DIV_MIN 2
#define POST_DIV_LIMIT 127
struct PLL_Control {
uint16 feedbackDivider; // 0xFFFF is the endmarker
uint32 control;
};
status_t PLLCalculate(uint32 pixelClock, uint16 *reference, uint16 *feedback,
uint16 *post);
status_t pll_compute(uint32 pixelClock, uint32 *dotclockOut,
uint32 *referenceOut, uint32 *feedbackOut, uint32 *feedbackFracOut,
uint32 *postOut);
status_t pll_set(uint8 pll_id, uint32 pixelClock, uint8 crtc_id);
void PLLSetLowLegacy(uint8 pllIndex, uint32 pixelClock, uint16 reference,
uint16 feedback, uint16 post);
void PLLSetLowR620(uint8 pllIndex, uint32 pixelClock, uint16 reference,
uint16 feedback, uint16 post);
status_t PLLPower(uint8 pllIndex, int command);
status_t PLLCalibrate(uint8 pllIndex);
void PLLCRTCGrab(uint8 pllIndex, uint8 crtid);
bool DCCGCLKAvailable(uint8 pllIndex);
void DCCGCLKSet(uint8 pllIndex, int set);
#endif /* RADEON_HD_PLL_H */
@@ -107,7 +107,7 @@ const struct supported_device {
// From here on AMD no longer used numeric identifiers
// R1000 series (HD54xx - HD59xx)
// R1000 series (HD54xx - HD63xx)
// Codename: Evergreen
// Cedar
{0x68e1, RADEON_R1000 | 0x00, false, "Radeon HD 5430"},
@@ -128,6 +128,12 @@ const struct supported_device {
{0x6898, RADEON_R1000 | 0x30, false, "Radeon HD 5870"},
// Hemlock
{0x689c, RADEON_R1000 | 0x40, false, "Radeon HD 5900"},
// Fusion APUS
// Palms
{0x9804, RADEON_R1000 | 0x50, true, "Radeon HD 6250"},
{0x9805, RADEON_R1000 | 0x50, true, "Radeon HD 6290"},
{0x9802, RADEON_R1000 | 0x50, true, "Radeon HD 6310"},
{0x9803, RADEON_R1000 | 0x50, true, "Radeon HD 6310"},
// R2000 series (HD64xx - HD69xx)
// Codename: Nothern Islands