diff --git a/sys/dev/drm2/radeon/radeon.h b/sys/dev/drm2/radeon/radeon.h index 4a63cf647332b..3063fa728feca 100644 --- a/sys/dev/drm2/radeon/radeon.h +++ b/sys/dev/drm2/radeon/radeon.h @@ -203,6 +203,8 @@ void radeon_pm_fini(struct radeon_device *rdev); void radeon_pm_compute_clocks(struct radeon_device *rdev); void radeon_pm_suspend(struct radeon_device *rdev); void radeon_pm_resume(struct radeon_device *rdev); +extern int radeon_pm_sysctl_init(struct drm_device *dev, + struct sysctl_ctx_list *ctx, struct sysctl_oid *top); void radeon_combios_get_power_modes(struct radeon_device *rdev); void radeon_atombios_get_power_modes(struct radeon_device *rdev); void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type); diff --git a/sys/dev/drm2/radeon/radeon_drv.c b/sys/dev/drm2/radeon/radeon_drv.c index 39506319c026b..74c3c646119f4 100644 --- a/sys/dev/drm2/radeon/radeon_drv.c +++ b/sys/dev/drm2/radeon/radeon_drv.c @@ -85,6 +85,8 @@ extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos); extern struct drm_ioctl_desc radeon_ioctls_kms[]; extern int radeon_max_kms_ioctl; +extern int radeon_pm_sysctl_init(struct drm_device *dev, + struct sysctl_ctx_list *ctx, struct sysctl_oid *top); #ifdef FREEBSD_WIP int radeon_mmap(struct file *filp, struct vm_area_struct *vma); #endif /* FREEBSD_WIP */ @@ -203,6 +205,7 @@ static struct drm_driver kms_driver; static int radeon_sysctl_init(struct drm_device *dev, struct sysctl_ctx_list *ctx, struct sysctl_oid *top) { + radeon_pm_sysctl_init(dev, ctx, top); return drm_add_busid_modesetting(dev, ctx, top); } diff --git a/sys/dev/drm2/radeon/radeon_pm.c b/sys/dev/drm2/radeon/radeon_pm.c index 18d800334be4f..5c0626403324e 100644 --- a/sys/dev/drm2/radeon/radeon_pm.c +++ b/sys/dev/drm2/radeon/radeon_pm.c @@ -336,36 +336,31 @@ static void radeon_pm_print_states(struct radeon_device *rdev) } } -#ifdef FREEBSD_WIP -static ssize_t radeon_get_pm_profile(struct device *dev, - struct device_attribute *attr, +static int radeon_get_pm_profile(struct radeon_device *rdev, char *buf) { - struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); - struct radeon_device *rdev = ddev->dev_private; int cp = rdev->pm.profile; - return snprintf(buf, PAGE_SIZE, "%s\n", + return snprintf(buf, 32, "%s", (cp == PM_PROFILE_AUTO) ? "auto" : (cp == PM_PROFILE_LOW) ? "low" : (cp == PM_PROFILE_MID) ? "mid" : (cp == PM_PROFILE_HIGH) ? "high" : "default"); } -static ssize_t radeon_set_pm_profile(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) +static int radeon_set_pm_profile(struct radeon_device *rdev, + const char *buf) { - struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); - struct radeon_device *rdev = ddev->dev_private; + int error = 0; sx_xlock(&rdev->pm.mutex); if (rdev->pm.pm_method == PM_METHOD_PROFILE) { if (strncmp("default", buf, strlen("default")) == 0) rdev->pm.profile = PM_PROFILE_DEFAULT; +#ifdef FREEBSD_WIP else if (strncmp("auto", buf, strlen("auto")) == 0) rdev->pm.profile = PM_PROFILE_AUTO; +#endif /* FREEBSD_WIP */ else if (strncmp("low", buf, strlen("low")) == 0) rdev->pm.profile = PM_PROFILE_LOW; else if (strncmp("mid", buf, strlen("mid")) == 0) @@ -373,48 +368,44 @@ static ssize_t radeon_set_pm_profile(struct device *dev, else if (strncmp("high", buf, strlen("high")) == 0) rdev->pm.profile = PM_PROFILE_HIGH; else { - count = -EINVAL; + error = -EINVAL; goto fail; } radeon_pm_update_profile(rdev); radeon_pm_set_clocks(rdev); } else - count = -EINVAL; + error = -EINVAL; fail: sx_xunlock(&rdev->pm.mutex); - return count; + return error; } -static ssize_t radeon_get_pm_method(struct device *dev, - struct device_attribute *attr, +static int radeon_get_pm_method(struct radeon_device *rdev, char *buf) { - struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); - struct radeon_device *rdev = ddev->dev_private; int pm = rdev->pm.pm_method; - return snprintf(buf, PAGE_SIZE, "%s\n", + return snprintf(buf, 32, "%s\n", (pm == PM_METHOD_DYNPM) ? "dynpm" : "profile"); } -static ssize_t radeon_set_pm_method(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) +static int radeon_set_pm_method(struct radeon_device *rdev, + const char *buf) { - struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); - struct radeon_device *rdev = ddev->dev_private; - + int error = 0; +#ifdef FREEBSD_WIP if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { sx_xlock(&rdev->pm.mutex); rdev->pm.pm_method = PM_METHOD_DYNPM; rdev->pm.dynpm_state = DYNPM_STATE_PAUSED; rdev->pm.dynpm_planned_action = DYNPM_ACTION_DEFAULT; sx_xunlock(&rdev->pm.mutex); - } else if (strncmp("profile", buf, strlen("profile")) == 0) { + } else +#endif /* FREEBSD_WIP */ + if (strncmp("profile", buf, strlen("profile")) == 0) { sx_xlock(&rdev->pm.mutex); /* disable dynpm */ rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; @@ -425,17 +416,57 @@ static ssize_t radeon_set_pm_method(struct device *dev, cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); #endif /* FREEBSD_WIP */ } else { - count = -EINVAL; + error = -EINVAL; goto fail; } radeon_pm_compute_clocks(rdev); fail: - return count; + return error; } -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 int +power_profile_handler(SYSCTL_HANDLER_ARGS) +{ + char newname[32]; + struct drm_device *dev; + struct radeon_device *rdev; + int error; + + dev = arg1; + rdev = dev->dev_private; + if (rdev == NULL) + return (EBUSY); + + radeon_get_pm_profile(rdev, newname); + error = sysctl_handle_string(oidp, newname, sizeof(newname), req); + if (error != 0 || req->newptr == NULL) + return (error); + error = radeon_set_pm_profile(rdev, newname); + return (-error); +} + +static int +power_method_handler(SYSCTL_HANDLER_ARGS) +{ + char newname[32]; + struct drm_device *dev; + struct radeon_device *rdev; + int error; + + dev = arg1; + rdev = dev->dev_private; + if (rdev == NULL) + return (EBUSY); + + radeon_get_pm_method(rdev, newname); + error = sysctl_handle_string(oidp, newname, sizeof(newname), req); + if (error != 0 || req->newptr == NULL) + return (error); + error = radeon_set_pm_method(rdev, newname); + return (-error); +} +#ifdef FREEBSD_WIP static ssize_t radeon_hwmon_show_temp(struct device *dev, struct device_attribute *attr, char *buf) @@ -648,14 +679,14 @@ int radeon_pm_init(struct radeon_device *rdev) /* where's the best place to put these? */ #ifdef FREEBSD_WIP ret = device_create_file(rdev->dev, &dev_attr_power_profile); -#endif /* FREEBSD_WIP */ if (ret) DRM_ERROR("failed to create device file for power profile\n"); +#endif /* FREEBSD_WIP */ #ifdef FREEBSD_WIP ret = device_create_file(rdev->dev, &dev_attr_power_method); -#endif /* FREEBSD_WIP */ if (ret) DRM_ERROR("failed to create device file for power method\n"); +#endif /* FREEBSD_WIP */ if (radeon_debugfs_pm_init(rdev)) { DRM_ERROR("Failed to register debugfs file for PM!\n"); @@ -918,3 +949,107 @@ static int radeon_debugfs_pm_init(struct radeon_device *rdev) return 0; #endif } + +static int +radeon_temp_handler(SYSCTL_HANDLER_ARGS) +{ + struct drm_device *dev; + struct radeon_device *rdev; + int error; + int temp; + + dev = arg1; + rdev = dev->dev_private; + if (rdev == NULL) + return (EBUSY); + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_RV6XX: + temp = rv6xx_get_temp(rdev); + break; + case THERMAL_TYPE_RV770: + temp = rv770_get_temp(rdev); + break; + case THERMAL_TYPE_EVERGREEN: + case THERMAL_TYPE_NI: + temp = evergreen_get_temp(rdev); + break; + case THERMAL_TYPE_SUMO: + temp = sumo_get_temp(rdev); + break; + case THERMAL_TYPE_SI: + temp = si_get_temp(rdev); + break; + default: + temp = 0; + break; + } + + /* Convert from millidegrees Celsius to decidegrees Kelvin. */ + temp = temp / 100 + 2732; + error = sysctl_handle_int(oidp, &temp, 0, req); + return (error); +} + +static int +radeon_pm_info_handler(SYSCTL_HANDLER_ARGS) +{ + struct sbuf m; + struct drm_device *dev; + struct radeon_device *rdev; + int error; + + dev = arg1; + rdev = dev->dev_private; + if (rdev == NULL) + return (EBUSY); + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + sbuf_new_for_sysctl(&m, NULL, 128, req); + + sbuf_printf(&m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); + /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ + if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) + sbuf_printf(&m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); + else + sbuf_printf(&m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); + sbuf_printf(&m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); + if (rdev->asic->pm.get_memory_clock) + sbuf_printf(&m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); + if (rdev->pm.current_vddc) + sbuf_printf(&m, "voltage: %u mV\n", rdev->pm.current_vddc); + if (rdev->asic->pm.get_pcie_lanes) + sbuf_printf(&m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); + + if (error == 0) + error = sbuf_finish(&m); + sbuf_delete(&m); + return (error); +} + +int +radeon_pm_sysctl_init(struct drm_device *dev, struct sysctl_ctx_list *ctx, + struct sysctl_oid *top) +{ + + (void)SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(top), OID_AUTO, + "power_profile", CTLTYPE_STRING | CTLFLAG_RW, dev, 0, + power_profile_handler, "A", NULL); + + (void)SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(top), OID_AUTO, + "power_method", CTLTYPE_STRING | CTLFLAG_RW, dev, 0, + power_method_handler, "A", NULL); + + (void)SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(top), OID_AUTO, + "pm_info", CTLTYPE_STRING | CTLFLAG_RD, dev, 0, + radeon_pm_info_handler, "A", NULL); + + (void)SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(top), OID_AUTO, + "temperature", CTLTYPE_INT | CTLFLAG_RD, dev, 0, + radeon_temp_handler, "IK", NULL); + + return (0); +} +