Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -238,11 +238,28 @@ struct opregion_asle_ext {
#define SWSCI_SBCB_POST_VBE_PM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)
#define SWSCI_SBCB_ENABLE_DISABLE_AUDIO SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)
-#define ACPI_OTHER_OUTPUT (0<<8)
-#define ACPI_VGA_OUTPUT (1<<8)
-#define ACPI_TV_OUTPUT (2<<8)
-#define ACPI_DIGITAL_OUTPUT (3<<8)
-#define ACPI_LVDS_OUTPUT (4<<8)
+/*
+ * ACPI Specification, Revision 5.0, Appendix B.3.2 _DOD (Enumerate All Devices
+ * Attached to the Display Adapter).
+ */
+#define ACPI_DISPLAY_INDEX_SHIFT 0
+#define ACPI_DISPLAY_INDEX_MASK (0xf << 0)
+#define ACPI_DISPLAY_PORT_ATTACHMENT_SHIFT 4
+#define ACPI_DISPLAY_PORT_ATTACHMENT_MASK (0xf << 4)
+#define ACPI_DISPLAY_TYPE_SHIFT 8
+#define ACPI_DISPLAY_TYPE_MASK (0xf << 8)
+#define ACPI_DISPLAY_TYPE_OTHER (0 << 8)
+#define ACPI_DISPLAY_TYPE_VGA (1 << 8)
+#define ACPI_DISPLAY_TYPE_TV (2 << 8)
+#define ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL (3 << 8)
+#define ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL (4 << 8)
+#define ACPI_VENDOR_SPECIFIC_SHIFT 12
+#define ACPI_VENDOR_SPECIFIC_MASK (0xf << 12)
+#define ACPI_BIOS_CAN_DETECT (1 << 16)
+#define ACPI_DEPENDS_ON_VGA (1 << 17)
+#define ACPI_PIPE_ID_SHIFT 18
+#define ACPI_PIPE_ID_MASK (7 << 18)
+#define ACPI_DEVICE_ID_SCHEME (1 << 31)
#define MAX_DSLP 1500
@@ -733,7 +750,7 @@ end:
blind_set:
i = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- int output_type = ACPI_OTHER_OUTPUT;
+ int output_type = ACPI_DISPLAY_TYPE_OTHER;
if (i >= max_outputs) {
DRM_DEBUG_KMS("More than %u outputs in connector list\n",
max_outputs);
@@ -742,23 +759,23 @@ blind_set:
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_VGA:
case DRM_MODE_CONNECTOR_DVIA:
- output_type = ACPI_VGA_OUTPUT;
+ output_type = ACPI_DISPLAY_TYPE_VGA;
break;
case DRM_MODE_CONNECTOR_Composite:
case DRM_MODE_CONNECTOR_SVIDEO:
case DRM_MODE_CONNECTOR_Component:
case DRM_MODE_CONNECTOR_9PinDIN:
- output_type = ACPI_TV_OUTPUT;
+ output_type = ACPI_DISPLAY_TYPE_TV;
break;
case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_DVID:
case DRM_MODE_CONNECTOR_DisplayPort:
case DRM_MODE_CONNECTOR_HDMIA:
case DRM_MODE_CONNECTOR_HDMIB:
- output_type = ACPI_DIGITAL_OUTPUT;
+ output_type = ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL;
break;
case DRM_MODE_CONNECTOR_LVDS:
- output_type = ACPI_LVDS_OUTPUT;
+ output_type = ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL;
break;
}
temp = get_did(opregion, i);
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -682,6 +682,36 @@ static void set_did(struct intel_opregion *opregion, int i, u32 val)
}
}
+static u32 acpi_display_type(struct drm_connector *connector)
+{
+ u32 display_type = ACPI_DISPLAY_TYPE_OTHER;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+ case DRM_MODE_CONNECTOR_DVIA:
+ display_type = ACPI_DISPLAY_TYPE_VGA;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ case DRM_MODE_CONNECTOR_9PinDIN:
+ display_type = ACPI_DISPLAY_TYPE_TV;
+ break;
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_DVID:
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ case DRM_MODE_CONNECTOR_HDMIA:
+ case DRM_MODE_CONNECTOR_HDMIB:
+ display_type = ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL;
+ break;
+ case DRM_MODE_CONNECTOR_LVDS:
+ display_type = ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL;
+ break;
+ }
+
+ return display_type;
+}
+
static void intel_didl_outputs(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -750,36 +780,16 @@ end:
blind_set:
i = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- int output_type = ACPI_DISPLAY_TYPE_OTHER;
+ int display_type = acpi_display_type(connector);
+
if (i >= max_outputs) {
DRM_DEBUG_KMS("More than %u outputs in connector list\n",
max_outputs);
return;
}
- switch (connector->connector_type) {
- case DRM_MODE_CONNECTOR_VGA:
- case DRM_MODE_CONNECTOR_DVIA:
- output_type = ACPI_DISPLAY_TYPE_VGA;
- break;
- case DRM_MODE_CONNECTOR_Composite:
- case DRM_MODE_CONNECTOR_SVIDEO:
- case DRM_MODE_CONNECTOR_Component:
- case DRM_MODE_CONNECTOR_9PinDIN:
- output_type = ACPI_DISPLAY_TYPE_TV;
- break;
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_DVID:
- case DRM_MODE_CONNECTOR_DisplayPort:
- case DRM_MODE_CONNECTOR_HDMIA:
- case DRM_MODE_CONNECTOR_HDMIB:
- output_type = ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL;
- break;
- case DRM_MODE_CONNECTOR_LVDS:
- output_type = ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL;
- break;
- }
+
temp = get_did(opregion, i);
- set_did(opregion, i, temp | (1 << 31) | output_type | i);
+ set_did(opregion, i, temp | (1 << 31) | display_type | i);
i++;
}
goto end;
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -684,7 +684,7 @@ static void set_did(struct intel_opregion *opregion, int i, u32 val)
static u32 acpi_display_type(struct drm_connector *connector)
{
- u32 display_type = ACPI_DISPLAY_TYPE_OTHER;
+ u32 display_type;
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_VGA:
@@ -695,6 +695,7 @@ static u32 acpi_display_type(struct drm_connector *connector)
case DRM_MODE_CONNECTOR_SVIDEO:
case DRM_MODE_CONNECTOR_Component:
case DRM_MODE_CONNECTOR_9PinDIN:
+ case DRM_MODE_CONNECTOR_TV:
display_type = ACPI_DISPLAY_TYPE_TV;
break;
case DRM_MODE_CONNECTOR_DVII:
@@ -705,8 +706,18 @@ static u32 acpi_display_type(struct drm_connector *connector)
display_type = ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL;
break;
case DRM_MODE_CONNECTOR_LVDS:
+ case DRM_MODE_CONNECTOR_eDP:
+ case DRM_MODE_CONNECTOR_DSI:
display_type = ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL;
break;
+ case DRM_MODE_CONNECTOR_Unknown:
+ case DRM_MODE_CONNECTOR_VIRTUAL:
+ display_type = ACPI_DISPLAY_TYPE_OTHER;
+ break;
+ default:
+ MISSING_CASE(connector->connector_type);
+ display_type = ACPI_DISPLAY_TYPE_OTHER;
+ break;
}
return display_type;
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -237,6 +237,9 @@ struct intel_connector {
*/
struct intel_encoder *encoder;
+ /* ACPI device id for ACPI and driver cooperation */
+ u32 acpi_device_id;
+
/* Reads out the current hw, returning true if the connector is enabled
* and active (i.e. dpms ON state). */
bool (*get_hw_state)(struct intel_connector *);
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index adba17bfe5ba..1af9ed5c1d0a 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -682,11 +682,11 @@ static void set_did(struct intel_opregion *opregion, int i, u32 val)
}
}
-static u32 acpi_display_type(struct drm_connector *connector)
+static u32 acpi_display_type(struct intel_connector *connector)
{
u32 display_type;
- switch (connector->connector_type) {
+ switch (connector->base.connector_type) {
case DRM_MODE_CONNECTOR_VGA:
case DRM_MODE_CONNECTOR_DVIA:
display_type = ACPI_DISPLAY_TYPE_VGA;
@@ -715,7 +715,7 @@ static u32 acpi_display_type(struct drm_connector *connector)
display_type = ACPI_DISPLAY_TYPE_OTHER;
break;
default:
- MISSING_CASE(connector->connector_type);
+ MISSING_CASE(connector->base.connector_type);
display_type = ACPI_DISPLAY_TYPE_OTHER;
break;
}
@@ -727,33 +727,9 @@ static void intel_didl_outputs(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
- struct drm_connector *connector;
- acpi_handle handle;
- struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL;
- unsigned long long device_id;
- acpi_status status;
- u32 temp, max_outputs;
- int i = 0;
-
- handle = ACPI_HANDLE(&dev->pdev->dev);
- if (!handle || acpi_bus_get_device(handle, &acpi_dev))
- return;
-
- if (acpi_is_video_device(handle))
- acpi_video_bus = acpi_dev;
- else {
- list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
- if (acpi_is_video_device(acpi_cdev->handle)) {
- acpi_video_bus = acpi_cdev;
- break;
- }
- }
- }
-
- if (!acpi_video_bus) {
- DRM_DEBUG_KMS("No ACPI video bus found\n");
- return;
- }
+ struct intel_connector *connector;
+ int i = 0, max_outputs;
+ int display_index[16] = {};
/*
* In theory, did2, the extended didl, gets added at opregion version
@@ -765,45 +741,32 @@ static void intel_didl_outputs(struct drm_device *dev)
max_outputs = ARRAY_SIZE(opregion->acpi->didl) +
ARRAY_SIZE(opregion->acpi->did2);
- list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
- if (i >= max_outputs) {
- DRM_DEBUG_KMS("More than %u outputs detected via ACPI\n",
- max_outputs);
- return;
- }
- status = acpi_evaluate_integer(acpi_cdev->handle, "_ADR",
- NULL, &device_id);
- if (ACPI_SUCCESS(status)) {
- if (!device_id)
- goto blind_set;
- set_did(opregion, i++, (u32)(device_id & 0x0f0f));
- }
+ for_each_intel_connector(dev, connector) {
+ u32 device_id, type;
+
+ device_id = acpi_display_type(connector);
+ device_id |= ACPI_DEVICE_ID_SCHEME;
+
+ /* Use display type specific display index. */
+ type = (device_id & ACPI_DISPLAY_TYPE_MASK)
+ >> ACPI_DISPLAY_TYPE_SHIFT;
+ device_id |= display_index[type]++ << ACPI_DISPLAY_INDEX_SHIFT;
+
+ connector->acpi_device_id = device_id;
+ if (i < max_outputs)
+ set_did(opregion, i, device_id);
+ i++;
}
-end:
DRM_DEBUG_KMS("%d outputs detected\n", i);
+ if (i > max_outputs)
+ DRM_ERROR("More than %d outputs in connector list\n",
+ max_outputs);
+
/* If fewer than max outputs, the list must be null terminated */
if (i < max_outputs)
set_did(opregion, i, 0);
- return;
-
-blind_set:
- i = 0;
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- int display_type = acpi_display_type(connector);
-
- if (i >= max_outputs) {
- DRM_DEBUG_KMS("More than %u outputs in connector list\n",
- max_outputs);
- return;
- }
-
- temp = get_did(opregion, i);
- set_did(opregion, i, temp | (1 << 31) | display_type | i);
- i++;
- }
- goto end;
}
static void intel_setup_cadls(struct drm_device *dev)
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3407,6 +3407,7 @@ extern int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
bool enable);
extern int intel_opregion_notify_adapter(struct drm_device *dev,
pci_power_t state);
+void intel_opregion_update_cadl(struct drm_device *dev);
#else
static inline int intel_opregion_setup(struct drm_device *dev) { return 0; }
static inline void intel_opregion_init(struct drm_device *dev) { return; }
@@ -3422,6 +3423,7 @@ intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
{
return 0;
}
+void intel_opregion_update_cadl(struct drm_device *dev) { return; }
#endif
/* intel_acpi.c */
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 51f913fb199d..76f48c44782f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13646,6 +13646,8 @@ static int intel_atomic_commit(struct drm_device *dev,
drm_atomic_state_free(state);
+ intel_opregion_update_cadl(dev);
+
/* As one of the primary mmio accessors, KMS has a high likelihood
* of triggering bugs in unclaimed access. After we finish
* modesetting, see if an error has been flagged, and if so
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index 1af9ed5c1d0a..d1ff264a82e6 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -650,24 +650,6 @@ static struct notifier_block intel_opregion_notifier = {
* (version 3)
*/
-static u32 get_did(struct intel_opregion *opregion, int i)
-{
- u32 did;
-
- if (i < ARRAY_SIZE(opregion->acpi->didl)) {
- did = opregion->acpi->didl[i];
- } else {
- i -= ARRAY_SIZE(opregion->acpi->didl);
-
- if (WARN_ON(i >= ARRAY_SIZE(opregion->acpi->did2)))
- return 0;
-
- did = opregion->acpi->did2[i];
- }
-
- return did;
-}
-
static void set_did(struct intel_opregion *opregion, int i, u32 val)
{
if (i < ARRAY_SIZE(opregion->acpi->didl)) {
@@ -682,6 +664,14 @@ static void set_did(struct intel_opregion *opregion, int i, u32 val)
}
}
+static void set_cad(struct intel_opregion *opregion, int i, u32 val)
+{
+ if (WARN_ON(i >= ARRAY_SIZE(opregion->acpi->cadl)))
+ return;
+
+ opregion->acpi->cadl[i] = val;
+}
+
static u32 acpi_display_type(struct intel_connector *connector)
{
u32 display_type;
@@ -769,23 +759,37 @@ static void intel_didl_outputs(struct drm_device *dev)
set_did(opregion, i, 0);
}
-static void intel_setup_cadls(struct drm_device *dev)
+/* Update CADL to reflect active outputs. */
+void intel_opregion_update_cadl(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
- int i = 0;
- u32 disp_id;
-
- /* Initialize the CADL field by duplicating the DIDL values.
- * Technically, this is not always correct as display outputs may exist,
- * but not active. This initialization is necessary for some Clevo
- * laptops that check this field before processing the brightness and
- * display switching hotkeys. Just like DIDL, CADL is NULL-terminated if
- * there are less than eight devices. */
- do {
- disp_id = get_did(opregion, i);
- opregion->acpi->cadl[i] = disp_id;
- } while (++i < 8 && disp_id != 0);
+ struct intel_crtc *crtc;
+ int i = 0, max_active = ARRAY_SIZE(opregion->acpi->cadl);
+
+ for_each_intel_crtc(dev, crtc) {
+ struct intel_encoder *encoder;
+
+ if (!crtc->active)
+ continue;
+
+ for_each_encoder_on_crtc(dev, &crtc->base, encoder) {
+ struct intel_connector *connector;
+
+ for_each_connector_on_encoder(dev, &encoder->base, connector) {
+ if (i >= max_active) {
+ DRM_DEBUG_KMS("too many outputs active\n");
+ return;
+ }
+
+ set_cad(opregion, i++, connector->acpi_device_id);
+ }
+ }
+ }
+
+ /* If fewer than max active outputs, the list must be null terminated */
+ if (i < max_active)
+ set_cad(opregion, i, 0);
}
void intel_opregion_init(struct drm_device *dev)
@@ -798,7 +802,7 @@ void intel_opregion_init(struct drm_device *dev)
if (opregion->acpi) {
intel_didl_outputs(dev);
- intel_setup_cadls(dev);
+ intel_opregion_update_cadl(dev);
/* Notify BIOS we are ready to handle ACPI video ext notifs.
* Right now, all the events are handled by the ACPI video module.
[usr@arch linux-4.5]$ patch -p1 --dry-run < drm-i915-opregion-proper-handling-of-DIDL-and-CADL.patch
checking file drivers/gpu/drm/i915/intel_opregion.c
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 succeeded at 665 (offset -17 lines).
Hunk #2 FAILED at 780.
1 out of 2 hunks FAILED
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 FAILED at 684.
Hunk #2 FAILED at 695.
Hunk #3 FAILED at 705.
3 out of 3 hunks FAILED
checking file drivers/gpu/drm/i915/intel_drv.h
Hunk #1 succeeded at 202 (offset -35 lines).
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 FAILED at 682.
Hunk #2 FAILED at 715.
Hunk #3 succeeded at 669 (offset -58 lines).
Hunk #4 FAILED at 741.
3 out of 4 hunks FAILED
checking file drivers/gpu/drm/i915/i915_drv.h
Hunk #1 succeeded at 3403 (offset -4 lines).
Hunk #2 succeeded at 3419 (offset -4 lines).
checking file drivers/gpu/drm/i915/intel_display.c
Hunk #1 succeeded at 13613 (offset -33 lines).
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 succeeded at 633 (offset -17 lines).
Hunk #2 FAILED at 664.
Hunk #3 succeeded at 750 with fuzz 1 (offset -1 lines).
Hunk #4 succeeded at 793 (offset -1 lines).
1 out of 4 hunks FAILED
[usr@arch linux-4.4]$ patch -p1 --dry-run < drm-i915-opregion-proper-handling-of-DIDL-and-CADL.patch
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 succeeded at 227 (offset -11 lines).
Hunk #2 succeeded at 739 (offset -11 lines).
Hunk #3 succeeded at 748 (offset -11 lines).
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 succeeded at 654 (offset -28 lines).
Hunk #2 FAILED at 780.
1 out of 2 hunks FAILED
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 FAILED at 684.
Hunk #2 FAILED at 695.
Hunk #3 FAILED at 705.
3 out of 3 hunks FAILED
checking file drivers/gpu/drm/i915/intel_drv.h
Hunk #1 succeeded at 204 (offset -33 lines).
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 FAILED at 682.
Hunk #2 FAILED at 715.
Hunk #3 FAILED at 727.
Hunk #4 FAILED at 765.
4 out of 4 hunks FAILED
checking file drivers/gpu/drm/i915/i915_drv.h
Hunk #1 succeeded at 3322 (offset -85 lines).
Hunk #2 succeeded at 3338 (offset -85 lines).
checking file drivers/gpu/drm/i915/intel_display.c
Hunk #1 FAILED at 13646.
1 out of 1 hunk FAILED
checking file drivers/gpu/drm/i915/intel_opregion.c
Hunk #1 succeeded at 622 (offset -28 lines).
Hunk #2 FAILED at 664.
Hunk #3 succeeded at 739 with fuzz 1 (offset -12 lines).
Hunk #4 succeeded at 782 (offset -12 lines).
1 out of 4 hunks FAILED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment