Message ID | 20240211055011.3583-2-mario.limonciello@amd.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel+bounces-60807-ouuuleilei=gmail.com@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:50ea:b0:106:860b:bbdd with SMTP id r10csp1960827dyd; Sun, 11 Feb 2024 06:56:12 -0800 (PST) X-Google-Smtp-Source: AGHT+IFz8vcMkmvu1GoeTA035/XysJ5cLnHl4W8aD/euBVNoa8+L/iYRKD7kRbDy7Z8lLSA+hA0A X-Received: by 2002:a17:906:3552:b0:a3b:fe76:d666 with SMTP id s18-20020a170906355200b00a3bfe76d666mr3188862eja.0.1707663372252; Sun, 11 Feb 2024 06:56:12 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCUofbAJnbnx4uaVcvgwngqWdc63Z97MITJz9Ryj5u8OWKkLPiOjDMkaWVlZB7RFVGRtgNT3Hdh3MLiwPfgGMZQWGvNLPw== Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id fi8-20020a170906da0800b00a318f0a6a33si3066247ejb.372.2024.02.11.06.56.12 for <ouuuleilei@gmail.com> (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 11 Feb 2024 06:56:12 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-60807-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@amd.com header.s=selector1 header.b=kbvZyFkf; arc=fail (signature failed); spf=pass (google.com: domain of linux-kernel+bounces-60807-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-60807-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amd.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id AF61C1F219F6 for <ouuuleilei@gmail.com>; Sun, 11 Feb 2024 14:56:11 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DD9125CDEA; Sun, 11 Feb 2024 14:55:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="kbvZyFkf" Received: from NAM11-DM6-obe.outbound.protection.outlook.com (mail-dm6nam11on2048.outbound.protection.outlook.com [40.107.223.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BC48F5B668; Sun, 11 Feb 2024 14:55:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.223.48 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707663305; cv=fail; b=Q5GdLgsANt6GClMs27L2GBV1uWOENFY7xyS/aBMP4YAm5O1m6OJHr/nTX+RiAfk5frFzJWTjfcKdKMB2VgUmtta3eR/CZplxLmQPZPDQ4QzCGDGuqEAKo0smhdIRpVg2MsYrSz2Wuj8IjdbZ6EP80PFeU8saUtoHxXItb2BtmB0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707663305; c=relaxed/simple; bh=m6A03YCtZUgTv1Dr9k8y11+Py1yAJzHFE/wMU/nqYrI=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LRHROunDAW872+P0EsGr4FVcdq2dwq0azSOqHl+2NbtJQyHiBx5SFZWfHLMC4t6Lhsubu59EkLf0zy1tPOl9VENm0nKt6f+rgL5wv2V6uZRQ6IkYxiGV6TS1k3zBOKZEiSFhg+OtfKNR9govgbuofZyT6bryw48JVp19U1FEzjM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=kbvZyFkf; arc=fail smtp.client-ip=40.107.223.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AJlm84cG+Qiz7fTfjx+s6L/rop1lOkUae38FxQTx4S0wCdt7oHpXXATha01ptfNmkUgqwl3tZqwLWzFWTCMQ2yBnz1lHi6HabDDFUuyBavNQumn9gn8HrHoUUqNt8oyw+SGrBU4nykU3DnSSkGVL/jILC3TbUllNB7v38QFwOS9qc3CryE5fllfGiRykeXtaVYsV+eoXdUIJXWJUXtae7MOWizmCiBN2Jqgy4YaLoTOIqz3Aif9CWGiAwmLJE5WZ6hDaRpFizkdEUcjUkxUd1AcdUFc5MhC7VdiWThM75StJfZ/ewBRRhuzNyXh9CjkYNC9upcWqXjP7fvI7xzacuQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=qt4IJtDz9lJXEWpJox7SKHGWQTRgQ/1HuPjj515rsMA=; b=V1OEeigsyM9q/3ZP9MlPMDqfq025zSYFQgO45O904KKKHitrh8U859M8e3qFEYbv3xs/UFXteSGIL13P9zWNvGLevAPCuB9AHNRY72ODuY2vDMWWMAAQOBhvsneIYwhLzRC9G6f1ZMYRTLcxCaYzrefiMfAO7+CwhGJtYo4yTZLdWmz5H+w/UOYdv5efl98gGXgL/NwFaZseeDrGZzBbvB/kr3UMLVHNbiVD5R24qiQl2DGwNLnv72xWuv7o0KcnxRpXfUmJ/cP1bFGekNwSgbyYNbJbT8Pjy+H5V8lROXPX/8ocHXpaalDpsARb7ZpZ9WY6iUal/JfQ4r68g4LGpQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=lists.freedesktop.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=qt4IJtDz9lJXEWpJox7SKHGWQTRgQ/1HuPjj515rsMA=; b=kbvZyFkfeh5q40dW5W4IdXBnzAmtkWoVbVQUFHw2jveI1CeE+WQCytF3t8EgFywYMSw8FnMfrrazLcnrn6TE0AwCKCRBZVgQGWzy+m34I5gwBlM3EY2lChicy1mXQREIVaAWdMzeJqA/KXP6sCyotePYBVQAVwSWeTWbtLVQHYs= Received: from BL1PR13CA0270.namprd13.prod.outlook.com (2603:10b6:208:2ba::35) by PH7PR12MB9127.namprd12.prod.outlook.com (2603:10b6:510:2f6::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7292.12; Sun, 11 Feb 2024 14:54:58 +0000 Received: from BL02EPF0001A0FB.namprd03.prod.outlook.com (2603:10b6:208:2ba:cafe::d6) by BL1PR13CA0270.outlook.office365.com (2603:10b6:208:2ba::35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7292.10 via Frontend Transport; Sun, 11 Feb 2024 14:54:58 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by BL02EPF0001A0FB.mail.protection.outlook.com (10.167.242.102) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.7249.19 via Frontend Transport; Sun, 11 Feb 2024 14:54:57 +0000 Received: from AUS-P9-MLIMONCI.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Sun, 11 Feb 2024 08:54:57 -0600 From: Mario Limonciello <mario.limonciello@amd.com> To: <amd-gfx@lists.freedesktop.org>, "open list:DRM DRIVERS" <dri-devel@lists.freedesktop.org> CC: "open list:ACPI" <linux-acpi@vger.kernel.org>, open list <linux-kernel@vger.kernel.org>, Melissa Wen <mwen@igalia.com>, Mark Pearson <mpearson-lenovo@squebb.ca>, Mario Limonciello <mario.limonciello@amd.com> Subject: [PATCH v5 1/3] drm: Add support to get EDID from ACPI Date: Sat, 10 Feb 2024 23:50:09 -0600 Message-ID: <20240211055011.3583-2-mario.limonciello@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240211055011.3583-1-mario.limonciello@amd.com> References: <20240211055011.3583-1-mario.limonciello@amd.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: <linux-kernel.vger.kernel.org> List-Subscribe: <mailto:linux-kernel+subscribe@vger.kernel.org> List-Unsubscribe: <mailto:linux-kernel+unsubscribe@vger.kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: SATLEXMB04.amd.com (10.181.40.145) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: BL02EPF0001A0FB:EE_|PH7PR12MB9127:EE_ X-MS-Office365-Filtering-Correlation-Id: c792d1f3-ffab-49a5-e5a6-08dc2b116a74 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: EdiyyLUlXHoFNFGxglZXwHxayjgusrnM15c78NmCAHBr3/Ggca5u1mVrpd6CXIJWq8nx2lvLLQMdJQPCqk5ja5PoetUAdCB36k8JEP+f410hZDBWH1jh2SVzKofrffjRg9d2uMSq7Q+SB0189Y3nqtsQrP+383fWzeUog9SjBFrb0K1kXN8xRyKsi3ELiJtEvZ7NSPAldSO5fx30NDylUTxQow5Y7NfckYeDaBT+fjGVycfbTWLU5VtTMmSqvdSIsO43vlHSk0lVeiECh+mbuMhpbmy8M/54xTDnL+okvb0uU3uZvFtToicdxIWlMfdE7Tt3uvFuHjGzob32l+0hv1HrHsR1tZPWWUlPfmLILSUdP9pA26VrYxf3J9lvqALtnnSX95nj5jMpYmFzmRrrdhrGN3IYyV24RbHLD7uhY8j/CcAGT/YfeokNIIkKZa/NB6EzC+STDD+PdrYlFFu1FuMaOQMspvoCXrf6HRoeupmladWfa17ty/LR2+tGOY9tGo1qnk8enwHLeVX0Ffl82uoIAu5F3Se3/p/RjGVsAxPegGShv2tU5OJtKTgwnN5X5gpnXFaSvm2js95aJN4goam+m3qOqJZUnTJZx9gvri0= X-Forefront-Antispam-Report: CIP:165.204.84.17;CTRY:US;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:SATLEXMB04.amd.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230031)(4636009)(136003)(396003)(39860400002)(346002)(376002)(230922051799003)(64100799003)(1800799012)(451199024)(186009)(82310400011)(40470700004)(36840700001)(46966006)(7696005)(478600001)(2616005)(41300700001)(66899024)(5660300002)(8936002)(70206006)(4326008)(70586007)(44832011)(2906002)(8676002)(110136005)(6666004)(316002)(54906003)(26005)(83380400001)(16526019)(1076003)(82740400003)(36756003)(86362001)(336012)(356005)(81166007)(426003);DIR:OUT;SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Feb 2024 14:54:57.9783 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: c792d1f3-ffab-49a5-e5a6-08dc2b116a74 X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d;Ip=[165.204.84.17];Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: BL02EPF0001A0FB.namprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB9127 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1790614828242008034 X-GMAIL-MSGID: 1790614828242008034 |
Series |
Add support for getting EDID over ACPI to DRM
|
|
Commit Message
Mario Limonciello
Feb. 11, 2024, 5:50 a.m. UTC
Some manufacturers have intentionally put an EDID that differs from
the EDID on the internal panel on laptops. Drivers that prefer to
fetch this EDID can set a bit on the drm_connector to indicate that
the DRM EDID helpers should try to fetch it and it is preferred if
it's present.
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
---
v1->v2:
* Split code from previous amdgpu specific helper to generic drm helper.
v2->v3:
* Add an extra select to fix a variety of randconfig errors found from
LKP robot.
v3->v4:
* Return struct drm_edid
v4->v5:
* Rename to drm_edid_read_acpi
* Drop selects
---
drivers/gpu/drm/Kconfig | 7 +++
drivers/gpu/drm/drm_edid.c | 113 +++++++++++++++++++++++++++++++++---
include/drm/drm_connector.h | 6 ++
include/drm/drm_edid.h | 1 +
4 files changed, 119 insertions(+), 8 deletions(-)
Comments
On 2/10/2024 23:50, Mario Limonciello wrote: > Some manufacturers have intentionally put an EDID that differs from > the EDID on the internal panel on laptops. Drivers that prefer to > fetch this EDID can set a bit on the drm_connector to indicate that > the DRM EDID helpers should try to fetch it and it is preferred if > it's present. > > Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> > --- > v1->v2: > * Split code from previous amdgpu specific helper to generic drm helper. > v2->v3: > * Add an extra select to fix a variety of randconfig errors found from > LKP robot. > v3->v4: > * Return struct drm_edid > v4->v5: > * Rename to drm_edid_read_acpi > * Drop selects > --- > drivers/gpu/drm/Kconfig | 7 +++ > drivers/gpu/drm/drm_edid.c | 113 +++++++++++++++++++++++++++++++++--- > include/drm/drm_connector.h | 6 ++ > include/drm/drm_edid.h | 1 + > 4 files changed, 119 insertions(+), 8 deletions(-) > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 2520db0b776e..a49740c528b9 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -103,6 +103,13 @@ config DRM_KMS_HELPER > help > CRTC helpers for KMS drivers. > > +config DRM_ACPI_HELPER > + tristate "ACPI support in DRM" > + depends on DRM > + depends on (ACPI_VIDEO || ACPI_VIDEO=n) > + help > + ACPI helpers for DRM drivers. > + Unfortunately in my wider testing this still fails with a few combinations. This combination fails to link: CONFIG_ACPI_VIDEO=m CONFIG_DRM_ACPI_HELPER=y CONFIG_DRM=y This combination links but doesn't work because the IS_REACHABLE() fails (-EOPNOTSUPP): CONFIG_ACPI_VIDEO=m CONFIG_DRM_ACPI_HELPER=M CONFIG_DRM=y I'm tempted to split off all of drm_edid to it's own module instead of CONFIG_DRM_ACPI_HELPER which has a depends on (ACPI_VIDEO || ACPI_VIDEIO=n). Or Daniel, any better ideas? > config DRM_DEBUG_DP_MST_TOPOLOGY_REFS > bool "Enable refcount backtrace history in the DP MST helpers" > depends on STACKTRACE_SUPPORT > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c > index 69c68804023f..096c278b6f66 100644 > --- a/drivers/gpu/drm/drm_edid.c > +++ b/drivers/gpu/drm/drm_edid.c > @@ -28,6 +28,7 @@ > * DEALINGS IN THE SOFTWARE. > */ > > +#include <acpi/video.h> > #include <linux/bitfield.h> > #include <linux/cec.h> > #include <linux/hdmi.h> > @@ -2188,6 +2189,62 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) > return ret == xfers ? 0 : -1; > } > > +/** > + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC > + * @data: struct drm_connector > + * @buf: EDID data buffer to be filled > + * @block: 128 byte EDID block to start fetching from > + * @len: EDID data buffer length to fetch > + * > + * Try to fetch EDID information by calling acpi_video_get_edid() function. > + * > + * Return: 0 on success or error code on failure. > + */ > +static int > +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len) > +{ > + struct drm_connector *connector = data; > + struct drm_device *ddev = connector->dev; > + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev); > + unsigned char start = block * EDID_LENGTH; > + void *edid; > + int r; > + > + if (!acpidev) > + return -ENODEV; > + > + switch (connector->connector_type) { > + case DRM_MODE_CONNECTOR_LVDS: > + case DRM_MODE_CONNECTOR_eDP: > + break; > + default: > + return -EINVAL; > + } > + > + /* fetch the entire edid from BIOS */ > + if (IS_REACHABLE(CONFIG_DRM_ACPI_HELPER)) { > + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, &edid); > + if (r < 0) { > + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r); > + return -EINVAL; > + } > + } else { > + r = -EOPNOTSUPP; > + } > + if (len > r || start > r || start + len > r) { > + r = -EINVAL; > + goto cleanup; > + } > + > + memcpy(buf, edid + start, len); > + r = 0; > + > +cleanup: > + kfree(edid); > + > + return r; > +} > + > static void connector_bad_edid(struct drm_connector *connector, > const struct edid *edid, int num_blocks) > { > @@ -2621,7 +2678,8 @@ EXPORT_SYMBOL(drm_probe_ddc); > * @connector: connector we're probing > * @adapter: I2C adapter to use for DDC > * > - * Poke the given I2C channel to grab EDID data if possible. If found, > + * If the connector allows it, try to fetch EDID data using ACPI. If not found > + * poke the given I2C channel to grab EDID data if possible. If found, > * attach it to the connector. > * > * Return: Pointer to valid EDID or NULL if we couldn't find any. > @@ -2629,20 +2687,50 @@ EXPORT_SYMBOL(drm_probe_ddc); > struct edid *drm_get_edid(struct drm_connector *connector, > struct i2c_adapter *adapter) > { > - struct edid *edid; > + struct edid *edid = NULL; > > if (connector->force == DRM_FORCE_OFF) > return NULL; > > - if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) > - return NULL; > + if (connector->acpi_edid_allowed) > + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector, NULL); > + > + if (!edid) { > + if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) > + return NULL; > + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); > + } > > - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); > drm_connector_update_edid_property(connector, edid); > return edid; > } > EXPORT_SYMBOL(drm_get_edid); > > +/** > + * drm_edid_read_acpi - get EDID data, if available > + * @connector: connector we're probing > + * > + * Use the BIOS to attempt to grab EDID data if possible. > + * > + * The returned pointer must be freed using drm_edid_free(). > + * > + * Return: Pointer to valid EDID or NULL if we couldn't find any. > + */ > +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector) > +{ > + const struct drm_edid *drm_edid; > + > + if (connector->force == DRM_FORCE_OFF) > + return NULL; > + > + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector); > + > + /* Note: Do *not* call connector updates here. */ > + > + return drm_edid; > +} > +EXPORT_SYMBOL(drm_edid_read_acpi); > + > /** > * drm_edid_read_custom - Read EDID data using given EDID block read function > * @connector: Connector to use > @@ -2727,10 +2815,11 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, > EXPORT_SYMBOL(drm_edid_read_ddc); > > /** > - * drm_edid_read - Read EDID data using connector's I2C adapter > + * drm_edid_read - Read EDID data using BIOS or connector's I2C adapter > * @connector: Connector to use > * > - * Read EDID using the connector's I2C adapter. > + * Read EDID from BIOS if allowed by connector or by using the connector's > + * I2C adapter. > * > * The EDID may be overridden using debugfs override_edid or firmware EDID > * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority > @@ -2742,10 +2831,18 @@ EXPORT_SYMBOL(drm_edid_read_ddc); > */ > const struct drm_edid *drm_edid_read(struct drm_connector *connector) > { > + const struct drm_edid *drm_edid = NULL; > + > if (drm_WARN_ON(connector->dev, !connector->ddc)) > return NULL; > > - return drm_edid_read_ddc(connector, connector->ddc); > + if (connector->acpi_edid_allowed) > + drm_edid = drm_edid_read_acpi(connector); > + > + if (!drm_edid) > + drm_edid = drm_edid_read_ddc(connector, connector->ddc); > + > + return drm_edid; > } > EXPORT_SYMBOL(drm_edid_read); > > diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h > index fe88d7fc6b8f..74ed47f37a69 100644 > --- a/include/drm/drm_connector.h > +++ b/include/drm/drm_connector.h > @@ -1886,6 +1886,12 @@ struct drm_connector { > > /** @hdr_sink_metadata: HDR Metadata Information read from sink */ > struct hdr_sink_metadata hdr_sink_metadata; > + > + /** > + * @acpi_edid_allowed: Get the EDID from the BIOS, if available. > + * This is only applicable to eDP and LVDS displays. > + */ > + bool acpi_edid_allowed; > }; > > #define obj_to_connector(x) container_of(x, struct drm_connector, base) > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > index 518d1b8106c7..38b5e1b5c773 100644 > --- a/include/drm/drm_edid.h > +++ b/include/drm/drm_edid.h > @@ -463,5 +463,6 @@ bool drm_edid_is_digital(const struct drm_edid *drm_edid); > > const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, > int ext_id, int *ext_index); > +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector); > > #endif /* __DRM_EDID_H__ */
On 2/12/2024 10:31, Mario Limonciello wrote: > On 2/10/2024 23:50, Mario Limonciello wrote: >> Some manufacturers have intentionally put an EDID that differs from >> the EDID on the internal panel on laptops. Drivers that prefer to >> fetch this EDID can set a bit on the drm_connector to indicate that >> the DRM EDID helpers should try to fetch it and it is preferred if >> it's present. >> >> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> >> --- >> v1->v2: >> * Split code from previous amdgpu specific helper to generic drm >> helper. >> v2->v3: >> * Add an extra select to fix a variety of randconfig errors found from >> LKP robot. >> v3->v4: >> * Return struct drm_edid >> v4->v5: >> * Rename to drm_edid_read_acpi >> * Drop selects >> --- >> drivers/gpu/drm/Kconfig | 7 +++ >> drivers/gpu/drm/drm_edid.c | 113 +++++++++++++++++++++++++++++++++--- >> include/drm/drm_connector.h | 6 ++ >> include/drm/drm_edid.h | 1 + >> 4 files changed, 119 insertions(+), 8 deletions(-) >> >> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig >> index 2520db0b776e..a49740c528b9 100644 >> --- a/drivers/gpu/drm/Kconfig >> +++ b/drivers/gpu/drm/Kconfig >> @@ -103,6 +103,13 @@ config DRM_KMS_HELPER >> help >> CRTC helpers for KMS drivers. >> +config DRM_ACPI_HELPER >> + tristate "ACPI support in DRM" >> + depends on DRM >> + depends on (ACPI_VIDEO || ACPI_VIDEO=n) >> + help >> + ACPI helpers for DRM drivers. >> + > > Unfortunately in my wider testing this still fails with a few combinations. > > This combination fails to link: > > CONFIG_ACPI_VIDEO=m > CONFIG_DRM_ACPI_HELPER=y > CONFIG_DRM=y > > This combination links but doesn't work because the IS_REACHABLE() fails > (-EOPNOTSUPP): > > CONFIG_ACPI_VIDEO=m > CONFIG_DRM_ACPI_HELPER=M > CONFIG_DRM=y > > I'm tempted to split off all of drm_edid to it's own module instead of > CONFIG_DRM_ACPI_HELPER which has a depends on (ACPI_VIDEO || > ACPI_VIDEIO=n). > > Or Daniel, any better ideas? I've come up with a solution that undoes all the select mess in preceding patches. This patch will be swapped around to --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -8,6 +8,7 @@ menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA + depends on (ACPI_VIDEO || ACPI_VIDEO=n) select DRM_PANEL_ORIENTATION_QUIRKS select DRM_KMS_HELPER if DRM_FBDEV_EMULATION select FB_CORE if DRM_FBDEV_EMULATION I'll wait a little for any other feedback and then post the updated series. None of the other patches change in any material way. > >> config DRM_DEBUG_DP_MST_TOPOLOGY_REFS >> bool "Enable refcount backtrace history in the DP MST helpers" >> depends on STACKTRACE_SUPPORT >> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c >> index 69c68804023f..096c278b6f66 100644 >> --- a/drivers/gpu/drm/drm_edid.c >> +++ b/drivers/gpu/drm/drm_edid.c >> @@ -28,6 +28,7 @@ >> * DEALINGS IN THE SOFTWARE. >> */ >> +#include <acpi/video.h> >> #include <linux/bitfield.h> >> #include <linux/cec.h> >> #include <linux/hdmi.h> >> @@ -2188,6 +2189,62 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, >> unsigned int block, size_t len) >> return ret == xfers ? 0 : -1; >> } >> +/** >> + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC >> + * @data: struct drm_connector >> + * @buf: EDID data buffer to be filled >> + * @block: 128 byte EDID block to start fetching from >> + * @len: EDID data buffer length to fetch >> + * >> + * Try to fetch EDID information by calling acpi_video_get_edid() >> function. >> + * >> + * Return: 0 on success or error code on failure. >> + */ >> +static int >> +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, >> size_t len) >> +{ >> + struct drm_connector *connector = data; >> + struct drm_device *ddev = connector->dev; >> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev); >> + unsigned char start = block * EDID_LENGTH; >> + void *edid; >> + int r; >> + >> + if (!acpidev) >> + return -ENODEV; >> + >> + switch (connector->connector_type) { >> + case DRM_MODE_CONNECTOR_LVDS: >> + case DRM_MODE_CONNECTOR_eDP: >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + /* fetch the entire edid from BIOS */ >> + if (IS_REACHABLE(CONFIG_DRM_ACPI_HELPER)) { >> + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, >> &edid); >> + if (r < 0) { >> + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r); >> + return -EINVAL; >> + } >> + } else { >> + r = -EOPNOTSUPP; >> + } >> + if (len > r || start > r || start + len > r) { >> + r = -EINVAL; >> + goto cleanup; >> + } >> + >> + memcpy(buf, edid + start, len); >> + r = 0; >> + >> +cleanup: >> + kfree(edid); >> + >> + return r; >> +} >> + >> static void connector_bad_edid(struct drm_connector *connector, >> const struct edid *edid, int num_blocks) >> { >> @@ -2621,7 +2678,8 @@ EXPORT_SYMBOL(drm_probe_ddc); >> * @connector: connector we're probing >> * @adapter: I2C adapter to use for DDC >> * >> - * Poke the given I2C channel to grab EDID data if possible. If found, >> + * If the connector allows it, try to fetch EDID data using ACPI. If >> not found >> + * poke the given I2C channel to grab EDID data if possible. If found, >> * attach it to the connector. >> * >> * Return: Pointer to valid EDID or NULL if we couldn't find any. >> @@ -2629,20 +2687,50 @@ EXPORT_SYMBOL(drm_probe_ddc); >> struct edid *drm_get_edid(struct drm_connector *connector, >> struct i2c_adapter *adapter) >> { >> - struct edid *edid; >> + struct edid *edid = NULL; >> if (connector->force == DRM_FORCE_OFF) >> return NULL; >> - if (connector->force == DRM_FORCE_UNSPECIFIED && >> !drm_probe_ddc(adapter)) >> - return NULL; >> + if (connector->acpi_edid_allowed) >> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, >> connector, NULL); >> + >> + if (!edid) { >> + if (connector->force == DRM_FORCE_UNSPECIFIED && >> !drm_probe_ddc(adapter)) >> + return NULL; >> + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, >> adapter, NULL); >> + } >> - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, >> adapter, NULL); >> drm_connector_update_edid_property(connector, edid); >> return edid; >> } >> EXPORT_SYMBOL(drm_get_edid); >> +/** >> + * drm_edid_read_acpi - get EDID data, if available >> + * @connector: connector we're probing >> + * >> + * Use the BIOS to attempt to grab EDID data if possible. >> + * >> + * The returned pointer must be freed using drm_edid_free(). >> + * >> + * Return: Pointer to valid EDID or NULL if we couldn't find any. >> + */ >> +const struct drm_edid *drm_edid_read_acpi(struct drm_connector >> *connector) >> +{ >> + const struct drm_edid *drm_edid; >> + >> + if (connector->force == DRM_FORCE_OFF) >> + return NULL; >> + >> + drm_edid = drm_edid_read_custom(connector, >> drm_do_probe_acpi_edid, connector); >> + >> + /* Note: Do *not* call connector updates here. */ >> + >> + return drm_edid; >> +} >> +EXPORT_SYMBOL(drm_edid_read_acpi); >> + >> /** >> * drm_edid_read_custom - Read EDID data using given EDID block read >> function >> * @connector: Connector to use >> @@ -2727,10 +2815,11 @@ const struct drm_edid >> *drm_edid_read_ddc(struct drm_connector *connector, >> EXPORT_SYMBOL(drm_edid_read_ddc); >> /** >> - * drm_edid_read - Read EDID data using connector's I2C adapter >> + * drm_edid_read - Read EDID data using BIOS or connector's I2C adapter >> * @connector: Connector to use >> * >> - * Read EDID using the connector's I2C adapter. >> + * Read EDID from BIOS if allowed by connector or by using the >> connector's >> + * I2C adapter. >> * >> * The EDID may be overridden using debugfs override_edid or >> firmware EDID >> * (drm_edid_load_firmware() and drm.edid_firmware parameter), in >> this priority >> @@ -2742,10 +2831,18 @@ EXPORT_SYMBOL(drm_edid_read_ddc); >> */ >> const struct drm_edid *drm_edid_read(struct drm_connector *connector) >> { >> + const struct drm_edid *drm_edid = NULL; >> + >> if (drm_WARN_ON(connector->dev, !connector->ddc)) >> return NULL; >> - return drm_edid_read_ddc(connector, connector->ddc); >> + if (connector->acpi_edid_allowed) >> + drm_edid = drm_edid_read_acpi(connector); >> + >> + if (!drm_edid) >> + drm_edid = drm_edid_read_ddc(connector, connector->ddc); >> + >> + return drm_edid; >> } >> EXPORT_SYMBOL(drm_edid_read); >> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h >> index fe88d7fc6b8f..74ed47f37a69 100644 >> --- a/include/drm/drm_connector.h >> +++ b/include/drm/drm_connector.h >> @@ -1886,6 +1886,12 @@ struct drm_connector { >> /** @hdr_sink_metadata: HDR Metadata Information read from sink */ >> struct hdr_sink_metadata hdr_sink_metadata; >> + >> + /** >> + * @acpi_edid_allowed: Get the EDID from the BIOS, if available. >> + * This is only applicable to eDP and LVDS displays. >> + */ >> + bool acpi_edid_allowed; >> }; >> #define obj_to_connector(x) container_of(x, struct drm_connector, base) >> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h >> index 518d1b8106c7..38b5e1b5c773 100644 >> --- a/include/drm/drm_edid.h >> +++ b/include/drm/drm_edid.h >> @@ -463,5 +463,6 @@ bool drm_edid_is_digital(const struct drm_edid >> *drm_edid); >> const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, >> int ext_id, int *ext_index); >> +const struct drm_edid *drm_edid_read_acpi(struct drm_connector >> *connector); >> #endif /* __DRM_EDID_H__ */ >
On Sat, 10 Feb 2024, Mario Limonciello <mario.limonciello@amd.com> wrote: > Some manufacturers have intentionally put an EDID that differs from > the EDID on the internal panel on laptops. Drivers that prefer to > fetch this EDID can set a bit on the drm_connector to indicate that > the DRM EDID helpers should try to fetch it and it is preferred if > it's present. > > Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> > --- > v1->v2: > * Split code from previous amdgpu specific helper to generic drm helper. > v2->v3: > * Add an extra select to fix a variety of randconfig errors found from > LKP robot. > v3->v4: > * Return struct drm_edid > v4->v5: > * Rename to drm_edid_read_acpi > * Drop selects > --- > drivers/gpu/drm/Kconfig | 7 +++ > drivers/gpu/drm/drm_edid.c | 113 +++++++++++++++++++++++++++++++++--- > include/drm/drm_connector.h | 6 ++ > include/drm/drm_edid.h | 1 + > 4 files changed, 119 insertions(+), 8 deletions(-) > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 2520db0b776e..a49740c528b9 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -103,6 +103,13 @@ config DRM_KMS_HELPER > help > CRTC helpers for KMS drivers. > > +config DRM_ACPI_HELPER > + tristate "ACPI support in DRM" > + depends on DRM > + depends on (ACPI_VIDEO || ACPI_VIDEO=n) > + help > + ACPI helpers for DRM drivers. > + > config DRM_DEBUG_DP_MST_TOPOLOGY_REFS > bool "Enable refcount backtrace history in the DP MST helpers" > depends on STACKTRACE_SUPPORT > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c > index 69c68804023f..096c278b6f66 100644 > --- a/drivers/gpu/drm/drm_edid.c > +++ b/drivers/gpu/drm/drm_edid.c > @@ -28,6 +28,7 @@ > * DEALINGS IN THE SOFTWARE. > */ > > +#include <acpi/video.h> > #include <linux/bitfield.h> > #include <linux/cec.h> > #include <linux/hdmi.h> > @@ -2188,6 +2189,62 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) > return ret == xfers ? 0 : -1; > } > > +/** > + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC > + * @data: struct drm_connector > + * @buf: EDID data buffer to be filled > + * @block: 128 byte EDID block to start fetching from > + * @len: EDID data buffer length to fetch > + * > + * Try to fetch EDID information by calling acpi_video_get_edid() function. > + * > + * Return: 0 on success or error code on failure. > + */ > +static int > +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len) > +{ > + struct drm_connector *connector = data; > + struct drm_device *ddev = connector->dev; > + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev); > + unsigned char start = block * EDID_LENGTH; > + void *edid; > + int r; > + > + if (!acpidev) > + return -ENODEV; > + > + switch (connector->connector_type) { > + case DRM_MODE_CONNECTOR_LVDS: > + case DRM_MODE_CONNECTOR_eDP: > + break; > + default: > + return -EINVAL; > + } > + > + /* fetch the entire edid from BIOS */ > + if (IS_REACHABLE(CONFIG_DRM_ACPI_HELPER)) { > + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, &edid); > + if (r < 0) { > + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r); > + return -EINVAL; > + } > + } else { > + r = -EOPNOTSUPP; > + } > + if (len > r || start > r || start + len > r) { > + r = -EINVAL; > + goto cleanup; > + } > + > + memcpy(buf, edid + start, len); > + r = 0; > + > +cleanup: > + kfree(edid); > + > + return r; > +} > + > static void connector_bad_edid(struct drm_connector *connector, > const struct edid *edid, int num_blocks) > { > @@ -2621,7 +2678,8 @@ EXPORT_SYMBOL(drm_probe_ddc); > * @connector: connector we're probing > * @adapter: I2C adapter to use for DDC > * > - * Poke the given I2C channel to grab EDID data if possible. If found, > + * If the connector allows it, try to fetch EDID data using ACPI. If not found > + * poke the given I2C channel to grab EDID data if possible. If found, > * attach it to the connector. > * > * Return: Pointer to valid EDID or NULL if we couldn't find any. > @@ -2629,20 +2687,50 @@ EXPORT_SYMBOL(drm_probe_ddc); > struct edid *drm_get_edid(struct drm_connector *connector, > struct i2c_adapter *adapter) > { > - struct edid *edid; > + struct edid *edid = NULL; > > if (connector->force == DRM_FORCE_OFF) > return NULL; > > - if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) > - return NULL; > + if (connector->acpi_edid_allowed) > + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector, NULL); > + > + if (!edid) { > + if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) > + return NULL; > + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); > + } Hmm. So do you want the presence of ACPI EDID to determine whether the display is there or not? Note how the override and firmware EDID mechanisms only override the EDID *contents*. They are orthogonal from connector forcing. So you can override the EDID, but still rely on the DDC probe to determine if the display is there. The question is, how likely is it that the ACPI EDID is used not just because the actual EDID is bogus, but also because the display just doesn't respond to DDC at all? If possible, I'd like ACPI EDID to follow the override/firmware EDID semantics on this. > > - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); > drm_connector_update_edid_property(connector, edid); > return edid; > } > EXPORT_SYMBOL(drm_get_edid); > > +/** > + * drm_edid_read_acpi - get EDID data, if available > + * @connector: connector we're probing > + * > + * Use the BIOS to attempt to grab EDID data if possible. > + * > + * The returned pointer must be freed using drm_edid_free(). > + * > + * Return: Pointer to valid EDID or NULL if we couldn't find any. > + */ > +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector) > +{ > + const struct drm_edid *drm_edid; > + > + if (connector->force == DRM_FORCE_OFF) > + return NULL; If the caller handled all the connector->force stuff, this could be dropped. > + > + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector); > + > + /* Note: Do *not* call connector updates here. */ > + > + return drm_edid; > +} > +EXPORT_SYMBOL(drm_edid_read_acpi); > + > /** > * drm_edid_read_custom - Read EDID data using given EDID block read function > * @connector: Connector to use > @@ -2727,10 +2815,11 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, > EXPORT_SYMBOL(drm_edid_read_ddc); > > /** > - * drm_edid_read - Read EDID data using connector's I2C adapter > + * drm_edid_read - Read EDID data using BIOS or connector's I2C adapter > * @connector: Connector to use > * > - * Read EDID using the connector's I2C adapter. > + * Read EDID from BIOS if allowed by connector or by using the connector's > + * I2C adapter. > * > * The EDID may be overridden using debugfs override_edid or firmware EDID > * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority > @@ -2742,10 +2831,18 @@ EXPORT_SYMBOL(drm_edid_read_ddc); > */ > const struct drm_edid *drm_edid_read(struct drm_connector *connector) > { > + const struct drm_edid *drm_edid = NULL; > + > if (drm_WARN_ON(connector->dev, !connector->ddc)) > return NULL; > > - return drm_edid_read_ddc(connector, connector->ddc); > + if (connector->acpi_edid_allowed) > + drm_edid = drm_edid_read_acpi(connector); > + > + if (!drm_edid) > + drm_edid = drm_edid_read_ddc(connector, connector->ddc); This should be in drm_edid_read_ddc() not drm_edid_read(). Please let's not make the two behave any different. It would be really weird if one had an ACPI check and the other not. > + > + return drm_edid; > } > EXPORT_SYMBOL(drm_edid_read); > > diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h > index fe88d7fc6b8f..74ed47f37a69 100644 > --- a/include/drm/drm_connector.h > +++ b/include/drm/drm_connector.h > @@ -1886,6 +1886,12 @@ struct drm_connector { > > /** @hdr_sink_metadata: HDR Metadata Information read from sink */ > struct hdr_sink_metadata hdr_sink_metadata; > + > + /** > + * @acpi_edid_allowed: Get the EDID from the BIOS, if available. > + * This is only applicable to eDP and LVDS displays. > + */ > + bool acpi_edid_allowed; > }; > > #define obj_to_connector(x) container_of(x, struct drm_connector, base) > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > index 518d1b8106c7..38b5e1b5c773 100644 > --- a/include/drm/drm_edid.h > +++ b/include/drm/drm_edid.h > @@ -463,5 +463,6 @@ bool drm_edid_is_digital(const struct drm_edid *drm_edid); > > const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, > int ext_id, int *ext_index); > +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector); > > #endif /* __DRM_EDID_H__ */
On 2/15/2024 08:01, Jani Nikula wrote: > On Sat, 10 Feb 2024, Mario Limonciello <mario.limonciello@amd.com> wrote: >> Some manufacturers have intentionally put an EDID that differs from >> the EDID on the internal panel on laptops. Drivers that prefer to >> fetch this EDID can set a bit on the drm_connector to indicate that >> the DRM EDID helpers should try to fetch it and it is preferred if >> it's present. >> >> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> >> --- >> v1->v2: >> * Split code from previous amdgpu specific helper to generic drm helper. >> v2->v3: >> * Add an extra select to fix a variety of randconfig errors found from >> LKP robot. >> v3->v4: >> * Return struct drm_edid >> v4->v5: >> * Rename to drm_edid_read_acpi >> * Drop selects >> --- >> drivers/gpu/drm/Kconfig | 7 +++ >> drivers/gpu/drm/drm_edid.c | 113 +++++++++++++++++++++++++++++++++--- >> include/drm/drm_connector.h | 6 ++ >> include/drm/drm_edid.h | 1 + >> 4 files changed, 119 insertions(+), 8 deletions(-) >> >> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig >> index 2520db0b776e..a49740c528b9 100644 >> --- a/drivers/gpu/drm/Kconfig >> +++ b/drivers/gpu/drm/Kconfig >> @@ -103,6 +103,13 @@ config DRM_KMS_HELPER >> help >> CRTC helpers for KMS drivers. >> >> +config DRM_ACPI_HELPER >> + tristate "ACPI support in DRM" >> + depends on DRM >> + depends on (ACPI_VIDEO || ACPI_VIDEO=n) >> + help >> + ACPI helpers for DRM drivers. >> + >> config DRM_DEBUG_DP_MST_TOPOLOGY_REFS >> bool "Enable refcount backtrace history in the DP MST helpers" >> depends on STACKTRACE_SUPPORT >> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c >> index 69c68804023f..096c278b6f66 100644 >> --- a/drivers/gpu/drm/drm_edid.c >> +++ b/drivers/gpu/drm/drm_edid.c >> @@ -28,6 +28,7 @@ >> * DEALINGS IN THE SOFTWARE. >> */ >> >> +#include <acpi/video.h> >> #include <linux/bitfield.h> >> #include <linux/cec.h> >> #include <linux/hdmi.h> >> @@ -2188,6 +2189,62 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) >> return ret == xfers ? 0 : -1; >> } >> >> +/** >> + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC >> + * @data: struct drm_connector >> + * @buf: EDID data buffer to be filled >> + * @block: 128 byte EDID block to start fetching from >> + * @len: EDID data buffer length to fetch >> + * >> + * Try to fetch EDID information by calling acpi_video_get_edid() function. >> + * >> + * Return: 0 on success or error code on failure. >> + */ >> +static int >> +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len) >> +{ >> + struct drm_connector *connector = data; >> + struct drm_device *ddev = connector->dev; >> + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev); >> + unsigned char start = block * EDID_LENGTH; >> + void *edid; >> + int r; >> + >> + if (!acpidev) >> + return -ENODEV; >> + >> + switch (connector->connector_type) { >> + case DRM_MODE_CONNECTOR_LVDS: >> + case DRM_MODE_CONNECTOR_eDP: >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + /* fetch the entire edid from BIOS */ >> + if (IS_REACHABLE(CONFIG_DRM_ACPI_HELPER)) { >> + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, &edid); >> + if (r < 0) { >> + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r); >> + return -EINVAL; >> + } >> + } else { >> + r = -EOPNOTSUPP; >> + } >> + if (len > r || start > r || start + len > r) { >> + r = -EINVAL; >> + goto cleanup; >> + } >> + >> + memcpy(buf, edid + start, len); >> + r = 0; >> + >> +cleanup: >> + kfree(edid); >> + >> + return r; >> +} >> + >> static void connector_bad_edid(struct drm_connector *connector, >> const struct edid *edid, int num_blocks) >> { >> @@ -2621,7 +2678,8 @@ EXPORT_SYMBOL(drm_probe_ddc); >> * @connector: connector we're probing >> * @adapter: I2C adapter to use for DDC >> * >> - * Poke the given I2C channel to grab EDID data if possible. If found, >> + * If the connector allows it, try to fetch EDID data using ACPI. If not found >> + * poke the given I2C channel to grab EDID data if possible. If found, >> * attach it to the connector. >> * >> * Return: Pointer to valid EDID or NULL if we couldn't find any. >> @@ -2629,20 +2687,50 @@ EXPORT_SYMBOL(drm_probe_ddc); >> struct edid *drm_get_edid(struct drm_connector *connector, >> struct i2c_adapter *adapter) >> { >> - struct edid *edid; >> + struct edid *edid = NULL; >> >> if (connector->force == DRM_FORCE_OFF) >> return NULL; >> >> - if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) >> - return NULL; >> + if (connector->acpi_edid_allowed) >> + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector, NULL); >> + >> + if (!edid) { >> + if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) >> + return NULL; >> + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); >> + } > > Hmm. So do you want the presence of ACPI EDID to determine whether the > display is there or not? > > Note how the override and firmware EDID mechanisms only override the > EDID *contents*. They are orthogonal from connector forcing. So you can > override the EDID, but still rely on the DDC probe to determine if the > display is there. > > The question is, how likely is it that the ACPI EDID is used not just > because the actual EDID is bogus, but also because the display just > doesn't respond to DDC at all? > The cases that prompted this the panel "responds" to DDC fine, but the EDID data doesn't match what is expected to be used which leads to other unexpected functional problems. > If possible, I'd like ACPI EDID to follow the override/firmware EDID > semantics on this. > So basically probe with DDC but if the driver specifies on the connector to use ACPI if present then try that (similar to how override is only used if specified by the user)? >> >> - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); >> drm_connector_update_edid_property(connector, edid); >> return edid; >> } >> EXPORT_SYMBOL(drm_get_edid); >> >> +/** >> + * drm_edid_read_acpi - get EDID data, if available >> + * @connector: connector we're probing >> + * >> + * Use the BIOS to attempt to grab EDID data if possible. >> + * >> + * The returned pointer must be freed using drm_edid_free(). >> + * >> + * Return: Pointer to valid EDID or NULL if we couldn't find any. >> + */ >> +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector) >> +{ >> + const struct drm_edid *drm_edid; >> + >> + if (connector->force == DRM_FORCE_OFF) >> + return NULL; > > If the caller handled all the connector->force stuff, this could be > dropped. > >> + >> + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector); >> + >> + /* Note: Do *not* call connector updates here. */ >> + >> + return drm_edid; >> +} >> +EXPORT_SYMBOL(drm_edid_read_acpi); >> + >> /** >> * drm_edid_read_custom - Read EDID data using given EDID block read function >> * @connector: Connector to use >> @@ -2727,10 +2815,11 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, >> EXPORT_SYMBOL(drm_edid_read_ddc); >> >> /** >> - * drm_edid_read - Read EDID data using connector's I2C adapter >> + * drm_edid_read - Read EDID data using BIOS or connector's I2C adapter >> * @connector: Connector to use >> * >> - * Read EDID using the connector's I2C adapter. >> + * Read EDID from BIOS if allowed by connector or by using the connector's >> + * I2C adapter. >> * >> * The EDID may be overridden using debugfs override_edid or firmware EDID >> * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority >> @@ -2742,10 +2831,18 @@ EXPORT_SYMBOL(drm_edid_read_ddc); >> */ >> const struct drm_edid *drm_edid_read(struct drm_connector *connector) >> { >> + const struct drm_edid *drm_edid = NULL; >> + >> if (drm_WARN_ON(connector->dev, !connector->ddc)) >> return NULL; >> >> - return drm_edid_read_ddc(connector, connector->ddc); >> + if (connector->acpi_edid_allowed) >> + drm_edid = drm_edid_read_acpi(connector); >> + >> + if (!drm_edid) >> + drm_edid = drm_edid_read_ddc(connector, connector->ddc); > > This should be in drm_edid_read_ddc() not drm_edid_read(). Please let's > not make the two behave any different. It would be really weird if one > had an ACPI check and the other not. OK, I'll move things around. > >> + >> + return drm_edid; >> } >> EXPORT_SYMBOL(drm_edid_read); >> >> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h >> index fe88d7fc6b8f..74ed47f37a69 100644 >> --- a/include/drm/drm_connector.h >> +++ b/include/drm/drm_connector.h >> @@ -1886,6 +1886,12 @@ struct drm_connector { >> >> /** @hdr_sink_metadata: HDR Metadata Information read from sink */ >> struct hdr_sink_metadata hdr_sink_metadata; >> + >> + /** >> + * @acpi_edid_allowed: Get the EDID from the BIOS, if available. >> + * This is only applicable to eDP and LVDS displays. >> + */ >> + bool acpi_edid_allowed; >> }; >> >> #define obj_to_connector(x) container_of(x, struct drm_connector, base) >> diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h >> index 518d1b8106c7..38b5e1b5c773 100644 >> --- a/include/drm/drm_edid.h >> +++ b/include/drm/drm_edid.h >> @@ -463,5 +463,6 @@ bool drm_edid_is_digital(const struct drm_edid *drm_edid); >> >> const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, >> int ext_id, int *ext_index); >> +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector); >> >> #endif /* __DRM_EDID_H__ */ >
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 2520db0b776e..a49740c528b9 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -103,6 +103,13 @@ config DRM_KMS_HELPER help CRTC helpers for KMS drivers. +config DRM_ACPI_HELPER + tristate "ACPI support in DRM" + depends on DRM + depends on (ACPI_VIDEO || ACPI_VIDEO=n) + help + ACPI helpers for DRM drivers. + config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" depends on STACKTRACE_SUPPORT diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 69c68804023f..096c278b6f66 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -28,6 +28,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include <acpi/video.h> #include <linux/bitfield.h> #include <linux/cec.h> #include <linux/hdmi.h> @@ -2188,6 +2189,62 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) return ret == xfers ? 0 : -1; } +/** + * drm_do_probe_acpi_edid() - get EDID information via ACPI _DDC + * @data: struct drm_connector + * @buf: EDID data buffer to be filled + * @block: 128 byte EDID block to start fetching from + * @len: EDID data buffer length to fetch + * + * Try to fetch EDID information by calling acpi_video_get_edid() function. + * + * Return: 0 on success or error code on failure. + */ +static int +drm_do_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len) +{ + struct drm_connector *connector = data; + struct drm_device *ddev = connector->dev; + struct acpi_device *acpidev = ACPI_COMPANION(ddev->dev); + unsigned char start = block * EDID_LENGTH; + void *edid; + int r; + + if (!acpidev) + return -ENODEV; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_eDP: + break; + default: + return -EINVAL; + } + + /* fetch the entire edid from BIOS */ + if (IS_REACHABLE(CONFIG_DRM_ACPI_HELPER)) { + r = acpi_video_get_edid(acpidev, ACPI_VIDEO_DISPLAY_LCD, -1, &edid); + if (r < 0) { + DRM_DEBUG_KMS("Failed to get EDID from ACPI: %d\n", r); + return -EINVAL; + } + } else { + r = -EOPNOTSUPP; + } + if (len > r || start > r || start + len > r) { + r = -EINVAL; + goto cleanup; + } + + memcpy(buf, edid + start, len); + r = 0; + +cleanup: + kfree(edid); + + return r; +} + static void connector_bad_edid(struct drm_connector *connector, const struct edid *edid, int num_blocks) { @@ -2621,7 +2678,8 @@ EXPORT_SYMBOL(drm_probe_ddc); * @connector: connector we're probing * @adapter: I2C adapter to use for DDC * - * Poke the given I2C channel to grab EDID data if possible. If found, + * If the connector allows it, try to fetch EDID data using ACPI. If not found + * poke the given I2C channel to grab EDID data if possible. If found, * attach it to the connector. * * Return: Pointer to valid EDID or NULL if we couldn't find any. @@ -2629,20 +2687,50 @@ EXPORT_SYMBOL(drm_probe_ddc); struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { - struct edid *edid; + struct edid *edid = NULL; if (connector->force == DRM_FORCE_OFF) return NULL; - if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) - return NULL; + if (connector->acpi_edid_allowed) + edid = _drm_do_get_edid(connector, drm_do_probe_acpi_edid, connector, NULL); + + if (!edid) { + if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) + return NULL; + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); + } - edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); drm_connector_update_edid_property(connector, edid); return edid; } EXPORT_SYMBOL(drm_get_edid); +/** + * drm_edid_read_acpi - get EDID data, if available + * @connector: connector we're probing + * + * Use the BIOS to attempt to grab EDID data if possible. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to valid EDID or NULL if we couldn't find any. + */ +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector) +{ + const struct drm_edid *drm_edid; + + if (connector->force == DRM_FORCE_OFF) + return NULL; + + drm_edid = drm_edid_read_custom(connector, drm_do_probe_acpi_edid, connector); + + /* Note: Do *not* call connector updates here. */ + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_read_acpi); + /** * drm_edid_read_custom - Read EDID data using given EDID block read function * @connector: Connector to use @@ -2727,10 +2815,11 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, EXPORT_SYMBOL(drm_edid_read_ddc); /** - * drm_edid_read - Read EDID data using connector's I2C adapter + * drm_edid_read - Read EDID data using BIOS or connector's I2C adapter * @connector: Connector to use * - * Read EDID using the connector's I2C adapter. + * Read EDID from BIOS if allowed by connector or by using the connector's + * I2C adapter. * * The EDID may be overridden using debugfs override_edid or firmware EDID * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority @@ -2742,10 +2831,18 @@ EXPORT_SYMBOL(drm_edid_read_ddc); */ const struct drm_edid *drm_edid_read(struct drm_connector *connector) { + const struct drm_edid *drm_edid = NULL; + if (drm_WARN_ON(connector->dev, !connector->ddc)) return NULL; - return drm_edid_read_ddc(connector, connector->ddc); + if (connector->acpi_edid_allowed) + drm_edid = drm_edid_read_acpi(connector); + + if (!drm_edid) + drm_edid = drm_edid_read_ddc(connector, connector->ddc); + + return drm_edid; } EXPORT_SYMBOL(drm_edid_read); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index fe88d7fc6b8f..74ed47f37a69 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1886,6 +1886,12 @@ struct drm_connector { /** @hdr_sink_metadata: HDR Metadata Information read from sink */ struct hdr_sink_metadata hdr_sink_metadata; + + /** + * @acpi_edid_allowed: Get the EDID from the BIOS, if available. + * This is only applicable to eDP and LVDS displays. + */ + bool acpi_edid_allowed; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 518d1b8106c7..38b5e1b5c773 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -463,5 +463,6 @@ bool drm_edid_is_digital(const struct drm_edid *drm_edid); const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, int ext_id, int *ext_index); +const struct drm_edid *drm_edid_read_acpi(struct drm_connector *connector); #endif /* __DRM_EDID_H__ */