diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index ef0e0e0..27fdc99 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -845,6 +845,11 @@ struct radeon_pm { u32 default_mclk; u16 default_vddc; u16 default_vddci; + u32 maximum_sclk; + u32 maximum_mclk; + u16 maximum_vddc; + u16 maximum_vddci; + bool allow_overclocking; struct radeon_i2c_chan *i2c_bus; /* selected pm method */ enum radeon_pm_method pm_method; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index aaa19dc..3b31d39 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -168,12 +168,12 @@ static void radeon_set_power_state(struct radeon_device *rdev) if (radeon_gui_idle(rdev)) { sclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. clock_info[rdev->pm.requested_clock_mode_index].sclk; - if (sclk > rdev->pm.default_sclk) + if (!rdev->pm.allow_overclocking && sclk > rdev->pm.default_sclk) sclk = rdev->pm.default_sclk; mclk = rdev->pm.power_state[rdev->pm.requested_power_state_index]. clock_info[rdev->pm.requested_clock_mode_index].mclk; - if (mclk > rdev->pm.default_mclk) + if (!rdev->pm.allow_overclocking && mclk > rdev->pm.default_mclk) mclk = rdev->pm.default_mclk; /* upvolt before raising clocks, downvolt after lowering clocks */ @@ -424,8 +424,222 @@ fail: return count; } +static ssize_t radeon_get_pm_table(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + int c = 0, i, j, n; + struct radeon_power_state *power_state; + struct radeon_pm_clock_info *clock_info; + static const char *profile_text[PM_PROFILE_MAX] = { + "DEFAULT", + "LOW_SH", + "MID_SH", + "HIGH_SH", + "LOW_MH", + "MID_MH", + "HIGH_MH" + }; + + c += scnprintf(buf + c, PAGE_SIZE - c, + "# power_state,clock_mode " + "dynpm_skip(boolean) engine(kHz) mem(kHz) vddc(mV) vddci ...\n"); + + for (i = 0; i < rdev->pm.num_power_states; i++) { + power_state = &(rdev->pm.power_state[i]); + + for (j = 0; j < power_state->num_clock_modes; j++) { + clock_info = &(power_state->clock_info[j]); + + c += scnprintf(buf + c, PAGE_SIZE - c, + "%u,%u %u %u %u %hu %hu\t", + i, + j, + clock_info->flags & RADEON_PM_MODE_NO_DISPLAY, + clock_info->sclk * 10, + clock_info->mclk * 10, + clock_info->voltage.voltage, + clock_info->voltage.vddci); + + /* XXX: mutex? */ + if (i == rdev->pm.current_power_state_index && + j == rdev->pm.current_clock_mode_index) + c += scnprintf(buf + c, PAGE_SIZE - c, + "[*] "); + + if (i == rdev->pm.default_power_state_index) + c += scnprintf(buf + c, PAGE_SIZE - c, + "[Boot] "); + + if (power_state->type != 0) + c += scnprintf(buf + c, PAGE_SIZE - c, + "[%s] ", + radeon_pm_state_type_name[power_state->type]); + + if (power_state->flags & RADEON_PM_STATE_SINGLE_DISPLAY_ONLY) + c += scnprintf(buf + c, PAGE_SIZE - c, + "[Single display only] "); + + for (n = 0; n < PM_PROFILE_MAX; n++) + if (i == rdev->pm.profiles[n].dpms_on_ps_idx && + j == rdev->pm.profiles[n].dpms_on_cm_idx) + c += scnprintf(buf + c, PAGE_SIZE - c, + "%s ", profile_text[n]); + + for (n = 0; n < PM_PROFILE_MAX; n++) + if (i == rdev->pm.profiles[n].dpms_off_ps_idx && + j == rdev->pm.profiles[n].dpms_off_cm_idx) + c += scnprintf(buf + c, PAGE_SIZE - c, + "%s_DPMS ", profile_text[n]); + + c += scnprintf(buf + c, PAGE_SIZE - c, "\n"); + } + } + + if (c + 1 == PAGE_SIZE) + DRM_ERROR("power_table likely truncated\n"); + + return c; +} + +static ssize_t radeon_set_pm_table(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + struct radeon_pm_clock_info *clock_info; + char linebuf[256]; + int pos = 0, lsize, ret; + u32 ps, cm, nodisplay, sclk, mclk; + u16 vddc, vddci; + + mutex_lock(&rdev->pm.mutex); + + while (count - pos > 0) { + /* find the linesize including the trailing \n (if any) */ + for (lsize = 0; lsize < count - pos; lsize++) { + if (buf[pos + lsize] == '\n') { + lsize += 1; + break; + } + } + ret = min((int)sizeof(linebuf) - 1, lsize); + memcpy(linebuf, buf + pos, ret); + linebuf[ret] = 0; + pos += lsize; + + if (!lsize || linebuf[0] == '\n' || linebuf[0] == '#') + continue; + + ret = sscanf(linebuf, "%u,%u %u %u %u %hu %hu", &ps, &cm, + &nodisplay, &sclk, &mclk, &vddc, &vddci); + if (ret != 7 || + ps >= rdev->pm.num_power_states || + cm >= rdev->pm.power_state[ps].num_clock_modes) { + mutex_unlock(&rdev->pm.mutex); + return -EINVAL; + } + + sclk /= 10; + mclk /= 10; + + if (!rdev->pm.allow_overclocking && + (sclk > rdev->pm.maximum_sclk || + mclk > rdev->pm.maximum_mclk || + vddc > rdev->pm.maximum_vddc || + vddci > rdev->pm.maximum_vddci)) { + mutex_unlock(&rdev->pm.mutex); + DRM_INFO("radeon: Overclocking is disabled by default. " + "It could break the hardware. Enable with: \"echo 1 > " + "/sys/class/drm/cardX/device/allow_overclocking\"\n"); + return -EINVAL; + } + + clock_info = &(rdev->pm.power_state[ps].clock_info[cm]); + + if (nodisplay && !(clock_info->flags & RADEON_PM_MODE_NO_DISPLAY)) { + DRM_INFO("radeon: clock_info[%d,%d]: adding the " + "RADEON_PM_MODE_NO_DISPLAY flag\n", + ps, cm); + clock_info->flags |= RADEON_PM_MODE_NO_DISPLAY; + } + if (!nodisplay && (clock_info->flags & RADEON_PM_MODE_NO_DISPLAY)) { + DRM_INFO("radeon: clock_info[%d,%d]: removing the " + "RADEON_PM_MODE_NO_DISPLAY flag\n", + ps, cm); + clock_info->flags &= ~RADEON_PM_MODE_NO_DISPLAY; + } + if (sclk && sclk != clock_info->sclk) { + DRM_INFO("radeon: clock_info[%d,%d]: changing the engine clock " + "from %d to %d.\n", + ps, cm, clock_info->sclk * 10, sclk * 10); + clock_info->sclk = sclk; + } + if (mclk && mclk != clock_info->mclk) { + DRM_INFO("radeon: clock_info[%d,%d]: changing the memory clock " + "from %d to %d.\n", + ps, cm, clock_info->mclk * 10, mclk * 10); + clock_info->mclk = mclk; + } + if (vddc && vddc != clock_info->voltage.voltage) { + DRM_INFO("radeon: clock_info[%d,%d]: changing the voltage " + "from %d to %d.\n", + ps, cm, clock_info->voltage.voltage, vddc); + clock_info->voltage.voltage = vddc; + } + if (vddci && vddci != clock_info->voltage.vddci) { + DRM_INFO("radeon: clock_info[%d,%d]: changing the vddci " + "from %d to %d.\n", + ps, cm, clock_info->voltage.vddci, vddci); + clock_info->voltage.vddci = vddci; + } + } + + mutex_unlock(&rdev->pm.mutex); + return count; +} + +static ssize_t radeon_get_pm_allow_overclocking(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + rdev->pm.allow_overclocking ? "1" : "0"); +} + +static ssize_t radeon_set_pm_allow_overclocking(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + + if (count && buf[0] == '1') { + if (!rdev->pm.allow_overclocking) { + DRM_INFO("radeon: disabling the upper limits for " + "clock rates and voltage!\n"); + rdev->pm.allow_overclocking = true; + } + } else if (count && buf[0] == '0') + rdev->pm.allow_overclocking = false; + else + return -EINVAL; + + return count; +} + static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); +static DEVICE_ATTR(power_table, S_IRUGO | S_IWUSR, radeon_get_pm_table, radeon_set_pm_table); +static DEVICE_ATTR(allow_overclocking, S_IRUGO | S_IWUSR, + radeon_get_pm_allow_overclocking, radeon_set_pm_allow_overclocking); static ssize_t radeon_hwmon_show_temp(struct device *dev, struct device_attribute *attr, @@ -565,6 +779,48 @@ void radeon_pm_resume(struct radeon_device *rdev) radeon_pm_compute_clocks(rdev); } +static void radeon_pm_init_overclocking(struct radeon_device *rdev) +{ + int i, j; + struct radeon_power_state *power_state; + struct radeon_pm_clock_info *clock_info; + u32 max_sclk, max_mclk; + u16 max_vddc = 0, max_vddci = 0; + + max_sclk = rdev->pm.default_sclk; + max_mclk = rdev->pm.default_mclk; + + for (i = 0; i < rdev->pm.num_power_states; i++) { + power_state = &(rdev->pm.power_state[i]); + + for (j = 0; j < power_state->num_clock_modes; j++) { + clock_info = &(power_state->clock_info[j]); + + if (max_sclk < clock_info->sclk) + max_sclk = clock_info->sclk; + if (max_mclk < clock_info->mclk) + max_mclk = clock_info->mclk; + if (max_vddc < clock_info->voltage.voltage) + max_vddc = clock_info->voltage.voltage; + if (max_vddci < clock_info->voltage.vddci) + max_vddci = clock_info->voltage.vddci; + } + } + + if (max_sclk > rdev->pm.default_sclk) + DRM_ERROR("radeon: clock rates have been limited " + "to the boot defaults for safety\n"); + + DRM_DEBUG_DRIVER("max_sclk=%u max_mclk=%u max_vddc=%u max_vddci=%u\n", + max_sclk, max_mclk, max_vddc, max_vddci); + + rdev->pm.maximum_sclk = max_sclk; + rdev->pm.maximum_mclk = max_mclk; + rdev->pm.maximum_vddc = max_vddc; + rdev->pm.maximum_vddci = max_vddci; + rdev->pm.allow_overclocking = false; +} + int radeon_pm_init(struct radeon_device *rdev) { int ret; @@ -587,6 +843,7 @@ int radeon_pm_init(struct radeon_device *rdev) radeon_atombios_get_power_modes(rdev); else radeon_combios_get_power_modes(rdev); + radeon_pm_init_overclocking(rdev); radeon_pm_print_states(rdev); radeon_pm_init_profile(rdev); /* set up the default clocks if the MC ucode is loaded */ @@ -616,6 +873,12 @@ int radeon_pm_init(struct radeon_device *rdev) ret = device_create_file(rdev->dev, &dev_attr_power_method); if (ret) DRM_ERROR("failed to create device file for power method\n"); + ret = device_create_file(rdev->dev, &dev_attr_power_table); + if (ret) + DRM_ERROR("failed to create device file for power table\n"); + ret = device_create_file(rdev->dev, &dev_attr_allow_overclocking); + if (ret) + DRM_ERROR("failed to create device file for allow_overclocking\n"); #ifdef CONFIG_ACPI rdev->acpi_nb.notifier_call = radeon_acpi_event;