int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev,
                                                u8 perf_req, bool advertise);
 int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev);
+
+void amdgpu_acpi_get_backlight_caps(struct amdgpu_device *adev,
+               struct amdgpu_dm_backlight_caps *caps);
 #else
 static inline int amdgpu_acpi_init(struct amdgpu_device *adev) { return 0; }
 static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { }
 
        struct amdgpu_atif_functions functions;
        struct amdgpu_atif_notification_cfg notification_cfg;
        struct amdgpu_encoder *encoder_for_bl;
+       struct amdgpu_dm_backlight_caps backlight_caps;
 };
 
 /* Call the ATIF method
        return err;
 }
 
+/**
+ * amdgpu_atif_query_backlight_caps - get min and max backlight input signal
+ *
+ * @handle: acpi handle
+ *
+ * Execute the QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS ATIF function
+ * to determine the acceptable range of backlight values
+ *
+ * Backlight_caps.caps_valid will be set to true if the query is successful
+ *
+ * The input signals are in range 0-255
+ *
+ * This function assumes the display with backlight is the first LCD
+ *
+ * Returns 0 on success, error on failure.
+ */
+static int amdgpu_atif_query_backlight_caps(struct amdgpu_atif *atif)
+{
+       union acpi_object *info;
+       struct atif_qbtc_output characteristics;
+       struct atif_qbtc_arguments arguments;
+       struct acpi_buffer params;
+       size_t size;
+       int err = 0;
+
+       arguments.size = sizeof(arguments);
+       arguments.requested_display = ATIF_QBTC_REQUEST_LCD1;
+
+       params.length = sizeof(arguments);
+       params.pointer = (void *)&arguments;
+
+       info = amdgpu_atif_call(atif,
+               ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS,
+               ¶ms);
+       if (!info) {
+               err = -EIO;
+               goto out;
+       }
+
+       size = *(u16 *) info->buffer.pointer;
+       if (size < 10) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       memset(&characteristics, 0, sizeof(characteristics));
+       size = min(sizeof(characteristics), size);
+       memcpy(&characteristics, info->buffer.pointer, size);
+
+       atif->backlight_caps.caps_valid = true;
+       atif->backlight_caps.min_input_signal =
+                       characteristics.min_input_signal;
+       atif->backlight_caps.max_input_signal =
+                       characteristics.max_input_signal;
+out:
+       kfree(info);
+       return err;
+}
+
 /**
  * amdgpu_atif_get_sbios_requests - get requested sbios event
  *
                }
        }
 
+       if (atif->functions.query_backlight_transfer_characteristics) {
+               ret = amdgpu_atif_query_backlight_caps(atif);
+               if (ret) {
+                       DRM_DEBUG_DRIVER("Call to QUERY_BACKLIGHT_TRANSFER_CHARACTERISTICS failed: %d\n",
+                                       ret);
+                       atif->backlight_caps.caps_valid = false;
+               }
+       } else {
+               atif->backlight_caps.caps_valid = false;
+       }
+
 out:
        adev->acpi_nb.notifier_call = amdgpu_acpi_event;
        register_acpi_notifier(&adev->acpi_nb);
        return ret;
 }
 
+void amdgpu_acpi_get_backlight_caps(struct amdgpu_device *adev,
+               struct amdgpu_dm_backlight_caps *caps)
+{
+       if (!adev->atif) {
+               caps->caps_valid = false;
+               return;
+       }
+       caps->caps_valid = adev->atif->backlight_caps.caps_valid;
+       caps->min_input_signal = adev->atif->backlight_caps.min_input_signal;
+       caps->max_input_signal = adev->atif->backlight_caps.max_input_signal;
+}
+
 /**
  * amdgpu_acpi_fini - tear down driver acpi support
  *
 
        return 0;
 }
 
+#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
+#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
+
 #if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
        defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
 
+static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm)
+{
+#if defined(CONFIG_ACPI)
+       struct amdgpu_dm_backlight_caps caps;
+
+       if (dm->backlight_caps.caps_valid)
+               return;
+
+       amdgpu_acpi_get_backlight_caps(dm->adev, &caps);
+       if (caps.caps_valid) {
+               dm->backlight_caps.min_input_signal = caps.min_input_signal;
+               dm->backlight_caps.max_input_signal = caps.max_input_signal;
+               dm->backlight_caps.caps_valid = true;
+       } else {
+               dm->backlight_caps.min_input_signal =
+                               AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+               dm->backlight_caps.max_input_signal =
+                               AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+       }
+#else
+       dm->backlight_min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+       dm->backlight_max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+#endif
+}
+
 static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
 {
        struct amdgpu_display_manager *dm = bl_get_data(bd);
+       struct amdgpu_dm_backlight_caps caps;
+       uint32_t brightness = bd->props.brightness;
 
-       /* backlight_pwm_u16_16 parameter is in unsigned 32 bit, 16 bit integer
-        * and 16 bit fractional, where 1.0 is max backlight value.
-        * bd->props.brightness is 8 bit format and needs to be converted by
-        * scaling via copy lower byte to upper byte of 16 bit value.
-        */
-       uint32_t brightness = bd->props.brightness * 0x101;
-
+       amdgpu_dm_update_backlight_caps(dm);
+       caps = dm->backlight_caps;
        /*
-        * PWM interperts 0 as 100% rather than 0% because of HW
-        * limitation for level 0.  So limiting minimum brightness level
-        * to 1.
+        * The brightness input is in the range 0-255
+        * It needs to be rescaled to be between the
+        * requested min and max input signal
+        *
+        * It also needs to be scaled up by 0x101 to
+        * match the DC interface which has a range of
+        * 0 to 0xffff
         */
-       if (bd->props.brightness < 1)
-               brightness = 0x101;
+       brightness =
+               brightness
+               * 0x101
+               * (caps.max_input_signal - caps.min_input_signal)
+               / AMDGPU_MAX_BL_LEVEL
+               + caps.min_input_signal * 0x101;
 
        if (dc_link_set_backlight_level(dm->backlight_link,
                        brightness, 0, 0))
        char bl_name[16];
        struct backlight_properties props = { 0 };
 
+       amdgpu_dm_update_backlight_caps(dm);
+
        props.max_brightness = AMDGPU_MAX_BL_LEVEL;
        props.brightness = AMDGPU_MAX_BL_LEVEL;
        props.type = BACKLIGHT_RAW;
 
        uint64_t gpu_addr;
 };
 
+/**
+ * struct amdgpu_dm_backlight_caps - Usable range of backlight values from ACPI
+ * @min_input_signal: minimum possible input in range 0-255
+ * @max_input_signal: maximum possible input in range 0-255
+ * @caps_valid: true if these values are from the ACPI interface
+ */
+struct amdgpu_dm_backlight_caps {
+       int min_input_signal;
+       int max_input_signal;
+       bool caps_valid;
+};
+
 /**
  * struct amdgpu_display_manager - Central amdgpu display manager device
  *
        struct backlight_device *backlight_dev;
 
        const struct dc_link *backlight_link;
+       struct amdgpu_dm_backlight_caps backlight_caps;
 
        struct mod_freesync *freesync_module;
 
 
        u8 backlight_level;     /* panel backlight level (0-255) */
 } __packed;
 
+struct atif_qbtc_arguments {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u8 requested_display;   /* which display is requested */
+} __packed;
+
+#define ATIF_QBTC_MAX_DATA_POINTS 99
+
+struct atif_qbtc_data_point {
+       u8 luminance;           /* luminance in percent */
+       u8 ipnut_signal;        /* input signal in range 0-255 */
+} __packed;
+
+struct atif_qbtc_output {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u16 flags;              /* all zeroes */
+       u8 error_code;          /* error code */
+       u8 ac_level;            /* default brightness on AC power */
+       u8 dc_level;            /* default brightness on DC power */
+       u8 min_input_signal;    /* max input signal in range 0-255 */
+       u8 max_input_signal;    /* min input signal in range 0-255 */
+       u8 number_of_points;    /* number of data points */
+       struct atif_qbtc_data_point data_points[ATIF_QBTC_MAX_DATA_POINTS];
+} __packed;
+
 #define ATIF_NOTIFY_MASK       0x3
 #define ATIF_NOTIFY_NONE       0
 #define ATIF_NOTIFY_81         1