drm/amdgpu: implement new cgs interface for acpi function
authorRex Zhu <Rex.Zhu@amd.com>
Tue, 15 Sep 2015 06:44:44 +0000 (14:44 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 21 Dec 2015 21:42:06 +0000 (16:42 -0500)
Add a new driver internal interface for accessing ACPI
methods.  These will be used by various new components
including powerplay.

Signed-off-by: Rex Zhu <Rex.Zhu@amd.com>
Reviewed-by: Jammy Zhou <Jammy.Zhou@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
drivers/gpu/drm/amd/include/cgs_common.h

index 8e99514..f901cdc 100644 (file)
@@ -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;
@@ -736,6 +736,221 @@ static int amdgpu_cgs_get_firmware_info(void *cgs_device,
        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 +983,8 @@ 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_call_acpi_method,
 };
 
 static const struct cgs_os_ops amdgpu_cgs_os_ops = {
index 992dcd8..8bf6ee5 100644 (file)
@@ -129,6 +129,39 @@ struct cgs_firmware_info {
 
 typedef unsigned long cgs_handle_t;
 
+#define CGS_ACPI_METHOD_ATCS          0x53435441
+#define CGS_ACPI_METHOD_ATIF          0x46495441
+#define CGS_ACPI_METHOD_ATPX          0x58505441
+#define CGS_ACPI_FIELD_METHOD_NAME                      0x00000001
+#define CGS_ACPI_FIELD_INPUT_ARGUMENT_COUNT             0x00000002
+#define CGS_ACPI_MAX_BUFFER_SIZE     256
+#define CGS_ACPI_TYPE_ANY                      0x00
+#define CGS_ACPI_TYPE_INTEGER               0x01
+#define CGS_ACPI_TYPE_STRING                0x02
+#define CGS_ACPI_TYPE_BUFFER                0x03
+#define CGS_ACPI_TYPE_PACKAGE               0x04
+
+struct cgs_acpi_method_argument {
+       uint32_t type;
+       uint32_t method_length;
+       uint32_t data_length;
+       union{
+               uint32_t value;
+               void *pointer;
+       };
+};
+
+struct cgs_acpi_method_info {
+       uint32_t size;
+       uint32_t field;
+       uint32_t input_count;
+       uint32_t name;
+       struct cgs_acpi_method_argument *pinput_argument;
+       uint32_t output_count;
+       struct cgs_acpi_method_argument *poutput_argument;
+       uint32_t padding[9];
+};
+
 /**
  * cgs_gpu_mem_info() - Return information about memory heaps
  * @cgs_device: opaque device handle
@@ -493,6 +526,13 @@ typedef int(*cgs_set_clockgating_state)(void *cgs_device,
                                  enum amd_ip_block_type block_type,
                                  enum amd_clockgating_state state);
 
+typedef int (*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_ops {
        /* memory management calls (similar to KFD interface) */
        cgs_gpu_mem_info_t gpu_mem_info;
@@ -533,7 +573,8 @@ struct cgs_ops {
        /* cg pg interface*/
        cgs_set_powergating_state set_powergating_state;
        cgs_set_clockgating_state set_clockgating_state;
-       /* ACPI (TODO) */
+       /* ACPI */
+       cgs_call_acpi_method call_acpi_method;
 };
 
 struct cgs_os_ops; /* To be define in OS-specific CGS header */
@@ -620,5 +661,7 @@ struct cgs_device
        CGS_CALL(set_powergating_state, dev, block_type, state)
 #define cgs_set_clockgating_state(dev, block_type, state)      \
        CGS_CALL(set_clockgating_state, dev, block_type, state)
+#define cgs_call_acpi_method(dev, acpi_method, acpi_function, pintput, poutput, output_count, input_size, output_size) \
+       CGS_CALL(call_acpi_method, dev, acpi_method, acpi_function, pintput, poutput, output_count, input_size, output_size)
 
 #endif /* _CGS_COMMON_H */