drm/radeon: properly validate the atpx interface
authorAlex Deucher <alexander.deucher@amd.com>
Tue, 19 Feb 2013 17:55:52 +0000 (12:55 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 20 Feb 2013 13:51:21 +0000 (08:51 -0500)
Some bioses don't set the function mask correctly
which caused required functions to be disabled.

Fixes:
https://bugzilla.kernel.org/show_bug.cgi?id=53111

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
drivers/gpu/drm/radeon/radeon_atpx_handler.c

index 15f5ded..d96070b 100644 (file)
@@ -43,6 +43,12 @@ struct atpx_verify_interface {
        u32 function_bits;      /* supported functions bit vector */
 } __packed;
 
+struct atpx_px_params {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u32 valid_flags;        /* which flags are valid */
+       u32 flags;              /* flags */
+} __packed;
+
 struct atpx_power_control {
        u16 size;
        u8 dgpu_state;
@@ -123,9 +129,61 @@ static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mas
 }
 
 /**
+ * radeon_atpx_validate_functions - validate ATPX functions
+ *
+ * @atpx: radeon atpx struct
+ *
+ * Validate that required functions are enabled (all asics).
+ * returns 0 on success, error on failure.
+ */
+static int radeon_atpx_validate(struct radeon_atpx *atpx)
+{
+       /* make sure required functions are enabled */
+       /* dGPU power control is required */
+       atpx->functions.power_cntl = true;
+
+       if (atpx->functions.px_params) {
+               union acpi_object *info;
+               struct atpx_px_params output;
+               size_t size;
+               u32 valid_bits;
+
+               info = radeon_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL);
+               if (!info)
+                       return -EIO;
+
+               memset(&output, 0, sizeof(output));
+
+               size = *(u16 *) info->buffer.pointer;
+               if (size < 10) {
+                       printk("ATPX buffer is too small: %zu\n", size);
+                       kfree(info);
+                       return -EINVAL;
+               }
+               size = min(sizeof(output), size);
+
+               memcpy(&output, info->buffer.pointer, size);
+
+               valid_bits = output.flags & output.valid_flags;
+               /* if separate mux flag is set, mux controls are required */
+               if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) {
+                       atpx->functions.i2c_mux_cntl = true;
+                       atpx->functions.disp_mux_cntl = true;
+               }
+               /* if any outputs are muxed, mux controls are required */
+               if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED |
+                                 ATPX_TV_SIGNAL_MUXED |
+                                 ATPX_DFP_SIGNAL_MUXED))
+                       atpx->functions.disp_mux_cntl = true;
+
+               kfree(info);
+       }
+       return 0;
+}
+
+/**
  * radeon_atpx_verify_interface - verify ATPX
  *
- * @handle: acpi handle
  * @atpx: radeon atpx struct
  *
  * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function
@@ -406,8 +464,19 @@ static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
  */
 static int radeon_atpx_init(void)
 {
+       int r;
+
        /* set up the ATPX handle */
-       return radeon_atpx_verify_interface(&radeon_atpx_priv.atpx);
+       r = radeon_atpx_verify_interface(&radeon_atpx_priv.atpx);
+       if (r)
+               return r;
+
+       /* validate the atpx setup */
+       r = radeon_atpx_validate(&radeon_atpx_priv.atpx);
+       if (r)
+               return r;
+
+       return 0;
 }
 
 /**