diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index bf396a0f6a50..c9f9647aece3 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -721,3 +721,182 @@ bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor) return false; } } + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; + struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4; + struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5; +}; + +union fan_info { + struct _ATOM_PPLIB_FANTABLE fan; + struct _ATOM_PPLIB_FANTABLE2 fan2; +}; + +static int r600_parse_clk_voltage_dep_table(struct radeon_clock_voltage_dependency_table *radeon_table, + ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table) +{ + u32 size = atom_table->ucNumEntries * + sizeof(struct radeon_clock_voltage_dependency_entry); + int i; + + radeon_table->entries = kzalloc(size, GFP_KERNEL); + if (!radeon_table->entries) + return -ENOMEM; + + for (i = 0; i < atom_table->ucNumEntries; i++) { + radeon_table->entries[i].clk = le16_to_cpu(atom_table->entries[i].usClockLow) | + (atom_table->entries[i].ucClockHigh << 16); + radeon_table->entries[i].v = le16_to_cpu(atom_table->entries[i].usVoltage); + } + radeon_table->count = atom_table->ucNumEntries; + + return 0; +} + +int r600_parse_extended_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + union power_info *power_info; + union fan_info *fan_info; + ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + int ret, i; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + /* fan table */ + if (power_info->pplib.usTableSize >= sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { + if (power_info->pplib3.usFanTableOffset) { + fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib3.usFanTableOffset)); + rdev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst; + rdev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin); + rdev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed); + rdev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh); + rdev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin); + rdev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed); + rdev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh); + if (fan_info->fan.ucFanTableFormat >= 2) + rdev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax); + else + rdev->pm.dpm.fan.t_max = 10900; + rdev->pm.dpm.fan.cycle_delay = 100000; + rdev->pm.dpm.fan.ucode_fan_control = true; + } + } + + /* clock dependancy tables */ + if (power_info->pplib.usTableSize >= sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) { + if (power_info->pplib4.usVddcDependencyOnSCLKOffset) { + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset)); + ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + dep_table); + if (ret) + return ret; + } + if (power_info->pplib4.usVddciDependencyOnMCLKOffset) { + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset)); + ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + dep_table); + if (ret) { + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + return ret; + } + } + if (power_info->pplib4.usVddcDependencyOnMCLKOffset) { + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset)); + ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + dep_table); + if (ret) { + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + return ret; + } + } + if (power_info->pplib4.usMaxClockVoltageOnDCOffset) { + ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v = + (ATOM_PPLIB_Clock_Voltage_Limit_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset)); + if (clk_v->ucNumEntries) { + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk = + le16_to_cpu(clk_v->entries[0].usSclkLow) | + (clk_v->entries[0].ucSclkHigh << 16); + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk = + le16_to_cpu(clk_v->entries[0].usMclkLow) | + (clk_v->entries[0].ucMclkHigh << 16); + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc = + le16_to_cpu(clk_v->entries[0].usVddc); + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci = + le16_to_cpu(clk_v->entries[0].usVddci); + } + } + } + + /* cac data */ + if (power_info->pplib.usTableSize >= sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) { + rdev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit); + rdev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit); + rdev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit); + if (rdev->pm.dpm.tdp_od_limit) + rdev->pm.dpm.power_control = true; + else + rdev->pm.dpm.power_control = false; + rdev->pm.dpm.tdp_adjustment = 0; + rdev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold); + rdev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage); + rdev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope); + if (power_info->pplib5.usCACLeakageTableOffset) { + ATOM_PPLIB_CAC_Leakage_Table *cac_table = + (ATOM_PPLIB_CAC_Leakage_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset)); + u32 size = cac_table->ucNumEntries * sizeof(struct radeon_cac_leakage_table); + rdev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL); + if (!rdev->pm.dpm.dyn_state.cac_leakage_table.entries) { + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries); + return -ENOMEM; + } + for (i = 0; i < cac_table->ucNumEntries; i++) { + rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc = + le16_to_cpu(cac_table->entries[i].usVddc); + rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage = + le32_to_cpu(cac_table->entries[i].ulLeakageValue); + } + rdev->pm.dpm.dyn_state.cac_leakage_table.count = cac_table->ucNumEntries; + } + } + + return 0; +} + +void r600_free_extended_power_table(struct radeon_device *rdev) +{ + if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries) + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + if (rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries) + kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + if (rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries) + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries); + if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries) + kfree(rdev->pm.dpm.dyn_state.cac_leakage_table.entries); +} diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h index bd33aa1df036..c6b9e30f20c2 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.h +++ b/drivers/gpu/drm/radeon/r600_dpm.h @@ -215,4 +215,7 @@ int r600_set_thermal_temperature_range(struct radeon_device *rdev, int min_temp, int max_temp); bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor); +int r600_parse_extended_power_table(struct radeon_device *rdev); +void r600_free_extended_power_table(struct radeon_device *rdev); + #endif diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 84c459d447e8..170d72be998d 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1217,6 +1217,66 @@ struct radeon_dpm_thermal { bool high_to_low; }; +struct radeon_clock_and_voltage_limits { + u32 sclk; + u32 mclk; + u32 vddc; + u32 vddci; +}; + +struct radeon_clock_array { + u32 count; + u32 *values; +}; + +struct radeon_clock_voltage_dependency_entry { + u32 clk; + u16 v; +}; + +struct radeon_clock_voltage_dependency_table { + u32 count; + struct radeon_clock_voltage_dependency_entry *entries; +}; + +struct radeon_cac_leakage_entry { + u16 vddc; + u32 leakage; +}; + +struct radeon_cac_leakage_table { + u32 count; + struct radeon_cac_leakage_entry *entries; +}; + +struct radeon_dpm_dynamic_state { + struct radeon_clock_voltage_dependency_table vddc_dependency_on_sclk; + struct radeon_clock_voltage_dependency_table vddci_dependency_on_mclk; + struct radeon_clock_voltage_dependency_table vddc_dependency_on_mclk; + struct radeon_clock_array valid_sclk_values; + struct radeon_clock_array valid_mclk_values; + struct radeon_clock_and_voltage_limits max_clock_voltage_on_dc; + struct radeon_clock_and_voltage_limits max_clock_voltage_on_ac; + u32 mclk_sclk_ratio; + u32 sclk_mclk_delta; + u16 vddc_vddci_delta; + u16 min_vddc_for_pcie_gen2; + struct radeon_cac_leakage_table cac_leakage_table; +}; + +struct radeon_dpm_fan { + u16 t_min; + u16 t_med; + u16 t_high; + u16 pwm_min; + u16 pwm_med; + u16 pwm_high; + u8 t_hyst; + u32 cycle_delay; + u16 t_max; + bool ucode_fan_control; +}; + struct radeon_dpm { struct radeon_ps *ps; /* number of valid power states */ @@ -1239,6 +1299,16 @@ struct radeon_dpm { int new_active_crtc_count; u32 current_active_crtcs; int current_active_crtc_count; + struct radeon_dpm_dynamic_state dyn_state; + struct radeon_dpm_fan fan; + u32 tdp_limit; + u32 near_tdp_limit; + u32 sq_ramping_threshold; + u32 cac_leakage; + u16 tdp_od_limit; + u32 tdp_adjustment; + u16 load_line_slope; + bool power_control; /* special states active */ bool thermal_active; bool uvd_active;