diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c | 292 |
1 files changed, 290 insertions, 2 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index 8e995148f56e..6fa0feac27f8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -24,6 +24,7 @@ #include <linux/list.h> #include <linux/slab.h> #include <linux/pci.h> +#include <linux/acpi.h> #include <drm/drmP.h> #include <linux/firmware.h> #include <drm/amdgpu_drm.h> @@ -32,7 +33,6 @@ #include "atom.h" #include "amdgpu_ucode.h" - struct amdgpu_cgs_device { struct cgs_device base; struct amdgpu_device *adev; @@ -703,6 +703,9 @@ static int amdgpu_cgs_get_firmware_info(void *cgs_device, case CHIP_TONGA: strcpy(fw_name, "amdgpu/tonga_smc.bin"); break; + case CHIP_FIJI: + strcpy(fw_name, "amdgpu/fiji_smc.bin"); + break; default: DRM_ERROR("SMC firmware not supported\n"); return -EINVAL; @@ -736,6 +739,288 @@ static int amdgpu_cgs_get_firmware_info(void *cgs_device, return 0; } +static int amdgpu_cgs_query_system_info(void *cgs_device, + struct cgs_system_info *sys_info) +{ + CGS_FUNC_ADEV; + + if (NULL == sys_info) + return -ENODEV; + + if (sizeof(struct cgs_system_info) != sys_info->size) + return -ENODEV; + + switch (sys_info->info_id) { + case CGS_SYSTEM_INFO_ADAPTER_BDF_ID: + sys_info->value = adev->pdev->devfn | (adev->pdev->bus->number << 8); + break; + case CGS_SYSTEM_INFO_PCIE_GEN_INFO: + sys_info->value = adev->pm.pcie_gen_mask; + break; + case CGS_SYSTEM_INFO_PCIE_MLW: + sys_info->value = adev->pm.pcie_mlw_mask; + break; + default: + return -ENODEV; + } + + return 0; +} + +static int amdgpu_cgs_get_active_displays_info(void *cgs_device, + struct cgs_display_info *info) +{ + CGS_FUNC_ADEV; + struct amdgpu_crtc *amdgpu_crtc; + struct drm_device *ddev = adev->ddev; + struct drm_crtc *crtc; + uint32_t line_time_us, vblank_lines; + + if (info == NULL) + return -EINVAL; + + if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { + list_for_each_entry(crtc, + &ddev->mode_config.crtc_list, head) { + amdgpu_crtc = to_amdgpu_crtc(crtc); + if (crtc->enabled) { + info->active_display_mask |= (1 << amdgpu_crtc->crtc_id); + info->display_count++; + } + if (info->mode_info != NULL && + crtc->enabled && amdgpu_crtc->enabled && + amdgpu_crtc->hw_mode.clock) { + line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) / + amdgpu_crtc->hw_mode.clock; + vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end - + amdgpu_crtc->hw_mode.crtc_vdisplay + + (amdgpu_crtc->v_border * 2); + info->mode_info->vblank_time_us = vblank_lines * line_time_us; + info->mode_info->refresh_rate = drm_mode_vrefresh(&amdgpu_crtc->hw_mode); + info->mode_info->ref_clock = adev->clock.spll.reference_freq; + info->mode_info++; + } + } + } + + return 0; +} + +/** \brief evaluate acpi namespace object, handle or pathname must be valid + * \param cgs_device + * \param info input/output arguments for the control method + * \return status + */ + +#if defined(CONFIG_ACPI) +static int amdgpu_cgs_acpi_eval_object(void *cgs_device, + struct cgs_acpi_method_info *info) +{ + CGS_FUNC_ADEV; + acpi_handle handle; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *params = NULL; + union acpi_object *obj = NULL; + uint8_t name[5] = {'\0'}; + struct cgs_acpi_method_argument *argument = NULL; + uint32_t i, count; + acpi_status status; + int result; + uint32_t func_no = 0xFFFFFFFF; + + handle = ACPI_HANDLE(&adev->pdev->dev); + if (!handle) + return -ENODEV; + + memset(&input, 0, sizeof(struct acpi_object_list)); + + /* validate input info */ + if (info->size != sizeof(struct cgs_acpi_method_info)) + return -EINVAL; + + input.count = info->input_count; + if (info->input_count > 0) { + if (info->pinput_argument == NULL) + return -EINVAL; + argument = info->pinput_argument; + func_no = argument->value; + for (i = 0; i < info->input_count; i++) { + if (((argument->type == ACPI_TYPE_STRING) || + (argument->type == ACPI_TYPE_BUFFER)) + && (argument->pointer == NULL)) + return -EINVAL; + argument++; + } + } + + if (info->output_count > 0) { + if (info->poutput_argument == NULL) + return -EINVAL; + argument = info->poutput_argument; + for (i = 0; i < info->output_count; i++) { + if (((argument->type == ACPI_TYPE_STRING) || + (argument->type == ACPI_TYPE_BUFFER)) + && (argument->pointer == NULL)) + return -EINVAL; + argument++; + } + } + + /* The path name passed to acpi_evaluate_object should be null terminated */ + if ((info->field & CGS_ACPI_FIELD_METHOD_NAME) != 0) { + strncpy(name, (char *)&(info->name), sizeof(uint32_t)); + name[4] = '\0'; + } + + /* parse input parameters */ + if (input.count > 0) { + input.pointer = params = + kzalloc(sizeof(union acpi_object) * input.count, GFP_KERNEL); + if (params == NULL) + return -EINVAL; + + argument = info->pinput_argument; + + for (i = 0; i < input.count; i++) { + params->type = argument->type; + switch (params->type) { + case ACPI_TYPE_INTEGER: + params->integer.value = argument->value; + break; + case ACPI_TYPE_STRING: + params->string.length = argument->method_length; + params->string.pointer = argument->pointer; + break; + case ACPI_TYPE_BUFFER: + params->buffer.length = argument->method_length; + params->buffer.pointer = argument->pointer; + break; + default: + break; + } + params++; + argument++; + } + } + + /* parse output info */ + count = info->output_count; + argument = info->poutput_argument; + + /* evaluate the acpi method */ + status = acpi_evaluate_object(handle, name, &input, &output); + + if (ACPI_FAILURE(status)) { + result = -EIO; + goto error; + } + + /* return the output info */ + obj = output.pointer; + + if (count > 1) { + if ((obj->type != ACPI_TYPE_PACKAGE) || + (obj->package.count != count)) { + result = -EIO; + goto error; + } + params = obj->package.elements; + } else + params = obj; + + if (params == NULL) { + result = -EIO; + goto error; + } + + for (i = 0; i < count; i++) { + if (argument->type != params->type) { + result = -EIO; + goto error; + } + switch (params->type) { + case ACPI_TYPE_INTEGER: + argument->value = params->integer.value; + break; + case ACPI_TYPE_STRING: + if ((params->string.length != argument->data_length) || + (params->string.pointer == NULL)) { + result = -EIO; + goto error; + } + strncpy(argument->pointer, + params->string.pointer, + params->string.length); + break; + case ACPI_TYPE_BUFFER: + if (params->buffer.pointer == NULL) { + result = -EIO; + goto error; + } + memcpy(argument->pointer, + params->buffer.pointer, + argument->data_length); + break; + default: + break; + } + argument++; + params++; + } + +error: + if (obj != NULL) + kfree(obj); + kfree((void *)input.pointer); + return result; +} +#else +static int amdgpu_cgs_acpi_eval_object(void *cgs_device, + struct cgs_acpi_method_info *info) +{ + return -EIO; +} +#endif + +int amdgpu_cgs_call_acpi_method(void *cgs_device, + uint32_t acpi_method, + uint32_t acpi_function, + void *pinput, void *poutput, + uint32_t output_count, + uint32_t input_size, + uint32_t output_size) +{ + struct cgs_acpi_method_argument acpi_input[2] = { {0}, {0} }; + struct cgs_acpi_method_argument acpi_output = {0}; + struct cgs_acpi_method_info info = {0}; + + acpi_input[0].type = CGS_ACPI_TYPE_INTEGER; + acpi_input[0].method_length = sizeof(uint32_t); + acpi_input[0].data_length = sizeof(uint32_t); + acpi_input[0].value = acpi_function; + + acpi_input[1].type = CGS_ACPI_TYPE_BUFFER; + acpi_input[1].method_length = CGS_ACPI_MAX_BUFFER_SIZE; + acpi_input[1].data_length = input_size; + acpi_input[1].pointer = pinput; + + acpi_output.type = CGS_ACPI_TYPE_BUFFER; + acpi_output.method_length = CGS_ACPI_MAX_BUFFER_SIZE; + acpi_output.data_length = output_size; + acpi_output.pointer = poutput; + + info.size = sizeof(struct cgs_acpi_method_info); + info.field = CGS_ACPI_FIELD_METHOD_NAME | CGS_ACPI_FIELD_INPUT_ARGUMENT_COUNT; + info.input_count = 2; + info.name = acpi_method; + info.pinput_argument = acpi_input; + info.output_count = output_count; + info.poutput_argument = &acpi_output; + + return amdgpu_cgs_acpi_eval_object(cgs_device, &info); +} + static const struct cgs_ops amdgpu_cgs_ops = { amdgpu_cgs_gpu_mem_info, amdgpu_cgs_gmap_kmem, @@ -768,7 +1053,10 @@ static const struct cgs_ops amdgpu_cgs_ops = { amdgpu_cgs_set_camera_voltages, amdgpu_cgs_get_firmware_info, amdgpu_cgs_set_powergating_state, - amdgpu_cgs_set_clockgating_state + amdgpu_cgs_set_clockgating_state, + amdgpu_cgs_get_active_displays_info, + amdgpu_cgs_call_acpi_method, + amdgpu_cgs_query_system_info, }; static const struct cgs_os_ops amdgpu_cgs_os_ops = { |