From nobody Tue Feb 10 01:30:54 2026 Received: from mail-lf1-f53.google.com (mail-lf1-f53.google.com [209.85.167.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 886AC25A65D for ; Sun, 26 Jan 2025 13:29:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737898160; cv=none; b=TnU/DqHRxqFjS0vDpn+GK1TlyuR9n8tn0A3QtMlN/AyAhxfkZEgTnW1TisEebwYrJ/yTiVkvI3bj95aE3vd8W++s6XE0bZWcvSgr3T3Qd/2MVbZbd+VR1TbIeJqhV9iez80LBvvAT/BU805SEU9tcdcVxQ+c/ALofO4Nvb8YwqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737898160; c=relaxed/simple; bh=OcWcKf/InZviT08C0ZhI5Jo5qPbyiNWwLd10i7D2BeE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oCAOdUXNxIBNl37aKotagcNwXw+ujKRN+oWCU8e67xNxlOkbw2m/fuLN7E8QE77+r8Qx8OOMsDOVaNqttgv6+LqxzJLPYcE2fanMimrf5AtQ6Dr5VubB1lV/A2dZFYIj29PLk2QNLfgBqMKIhc/8bYlFt0GW++nM7otW91c5cj4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=GeVUzN2p; arc=none smtp.client-ip=209.85.167.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="GeVUzN2p" Received: by mail-lf1-f53.google.com with SMTP id 2adb3069b0e04-53f22fd6832so3994858e87.1 for ; Sun, 26 Jan 2025 05:29:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1737898155; x=1738502955; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=mtOuCADvqxiQ8qskiTg6RnzWChalNaUWo6+lKfRYGWM=; b=GeVUzN2p5nbvJhym/s0zZpGkaZ7vi3Xz/iyPrs1JclH/btDl1hoAjcluH2Iuua+wS1 dJGQo95JMNoBDO/lZ/Uc/n8CPB7/FKIrhs2iVRqBnLhrbKvd+FSgZMT0MgJEO4JGk59b DwnSwrWbR+yzdDej6i8v2C1zwjTInZ5xYkRTCNI4aUfXHvMjPUPfiLOSOAjrDQKJjQnr RJ9b9r5O2BkFbkj61cYgLshy9qK/rB4BhrngOaPjw0xdODeSZcBvwNBRn2OUXOEznq5k xSXWEwckGPAY8dCVikjbQ/xD9wYQiO7Vcx27RQlV9iEfAY38dMO6gbFk1ztxadYo8N4n 8V/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737898155; x=1738502955; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mtOuCADvqxiQ8qskiTg6RnzWChalNaUWo6+lKfRYGWM=; b=Lai4UjvgFP2ZnuKCvx1KJ+gA5MWl9jqqY0XebdZb/LL6oQhJF8jZ3+17EECaOIUQz2 5DlaajRYOh7T5bJkIWGOH39a3YMBGebMq9wdZIuigxlc4iydL0XDMD+1i1dIzqGzxDwq dmJ/1epj/Bf0E9Edp9FrPjcW1PcBAzzZS4BAcQEwzLfe0Cv6bI/8K0KToT4yOLdM4l38 lkKulbokVOiOechg9jiuQ/ltwtRg5FA3d4eu4MNdGfsPwYVYmKkBHOaSO3EvknHsBLWp rruu/niPOGUnK1ysb6eXFrfXW2q4x2EP5A4uXSa1ColnFCxzJvxqyIfdm8RfHzm9XDcm cilw== X-Forwarded-Encrypted: i=1; AJvYcCU/tnx5zKCsif70hOOgj1+o2UhOmrPdJBuRhOKag7MLFVp/Ud6XxDdRgvCjpPLRkC7Nw6sb2UB6uIr1EKQ=@vger.kernel.org X-Gm-Message-State: AOJu0YzEDP2QhC+G6g5pannzqayftdhg4LUTNJIx1j8wZSKCvD2ZcAY1 rwtb3xRDwHkEh1+eGe54Jq+jdtgNc8ZsnM8IawbVHhedMr8ys8c8Dy9oSB9VT50= X-Gm-Gg: ASbGncsXjYasA/9MxUXINMutT+ViBmAD6Q4Lvfhdu2BLUC9Jem1tPn5QINnUgz3DjDD lPTAFOeg3J6RqVcT0wdpp/2z6RgTdWhtVpaJltAs3Ftii7rTWh36GRXcUwrxrogV43N9tAsR4ds Uj8cnkITD2v9Uu2jTHum3jelsp8CZYEESksrKAlKOdwQU8lSxJwM1pO7q99V5FG9JltrDPy19BD +rbrEpasKKwRznT5U2RMRNmd4+zHeEFWfH5WP+s0tzJ+KeGRUAITvc8LwCh5Q8/c1AY7iCFZY0u i4ONxSCkTlkv X-Google-Smtp-Source: AGHT+IFOVh39CoIeKAxiwxGLv30P4T5xgVBKdF914T+dQZYy4EUSVW/1GagTDS2LlI1AwCUC6vC8Yw== X-Received: by 2002:a19:5519:0:b0:540:1b2d:8ef3 with SMTP id 2adb3069b0e04-5439c28854fmr10983563e87.52.1737898155497; Sun, 26 Jan 2025 05:29:15 -0800 (PST) Received: from umbar.lan ([192.130.178.90]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-543cbbd4cf8sm770488e87.201.2025.01.26.05.29.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jan 2025 05:29:14 -0800 (PST) From: Dmitry Baryshkov Date: Sun, 26 Jan 2025 15:29:07 +0200 Subject: [PATCH v3 02/10] drm/display: add CEC helpers code Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250126-drm-hdmi-connector-cec-v3-2-5b5b2d4956da@linaro.org> References: <20250126-drm-hdmi-connector-cec-v3-0-5b5b2d4956da@linaro.org> In-Reply-To: <20250126-drm-hdmi-connector-cec-v3-0-5b5b2d4956da@linaro.org> To: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Dave Stevenson , =?utf-8?q?Ma=C3=ADra_Canal?= , Raspberry Pi Kernel Maintenance , Andrzej Hajda , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=10695; i=dmitry.baryshkov@linaro.org; h=from:subject:message-id; bh=OcWcKf/InZviT08C0ZhI5Jo5qPbyiNWwLd10i7D2BeE=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBnljijFPF1RqGbrj/r74mBNTqY1s66/05q/E0iq VnBUdiy+0GJATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCZ5Y4owAKCRCLPIo+Aiko 1e4sB/wPzL9K3r1QUEY7NhESKMiCvc/+0HlGVb4HZXsMysrdeJTY9sY9SWpZpI1LxC+//yGvtJb H22w+cqi5ShIgmDqz0s06NOvBQOE/6dxX0x07ey7nEG7r9wLvHmKZhUQY4vHLdcttNSR+X9Qo0E 4BH8O9m5K2+lHQnAc0BBxcY46lC3KzDWbaEAimxKF7SFgNku478Jrik5qFkrfnXxrBdpewXCGMR Oi5sJvfxeNADyKSneN/0aJsOBssrXTyue6BhEqGPvI2T9nkvpL1HLSoEXny3NjGeXMURMf29vdV uEkkyyaa6IM11JTsp5nbU/GlFOPZqMF5fWVZWg2gQHswJ2zn X-Developer-Key: i=dmitry.baryshkov@linaro.org; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A Add generic CEC helpers to be used by HDMI drivers. Both notifier and and adapter are supported for registration. Once registered, the driver can call common set of functions to update physical address, to invalidate it or to unregister CEC data. Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/Kconfig | 5 + drivers/gpu/drm/display/Makefile | 2 + drivers/gpu/drm/display/drm_hdmi_cec_helper.c | 209 ++++++++++++++++++++++= ++++ include/drm/display/drm_hdmi_cec_helper.h | 61 ++++++++ 4 files changed, 277 insertions(+) diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kcon= fig index 8d22b7627d41f7bc015decf24ae02a05bc00f055..49da9b768acf3e5f84f2cefae4b= b042cfd57a50c 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -82,6 +82,11 @@ config DRM_DISPLAY_HDMI_AUDIO_HELPER DRM display helpers for HDMI Audio functionality (generic HDMI Codec implementation). =20 +config DRM_DISPLAY_HDMI_CEC_HELPER + bool + help + DRM display helpers for HDMI CEC implementation. + config DRM_DISPLAY_HDMI_HELPER bool help diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Mak= efile index b17879b957d5401721396e247fa346387cf6c48a..2cd078e2b81c1a9e6b336c4187b= 444bcb8a50e51 100644 --- a/drivers/gpu/drm/display/Makefile +++ b/drivers/gpu/drm/display/Makefile @@ -16,6 +16,8 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DSC_HELPER) +=3D \ drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) +=3D drm_hdcp_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_AUDIO_HELPER) +=3D \ drm_hdmi_audio_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_HELPER) +=3D \ + drm_hdmi_cec_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) +=3D \ drm_hdmi_helper.o \ drm_scdc_helper.o diff --git a/drivers/gpu/drm/display/drm_hdmi_cec_helper.c b/drivers/gpu/dr= m/display/drm_hdmi_cec_helper.c new file mode 100644 index 0000000000000000000000000000000000000000..a6ed5f0fc3835b013a83308f528= 5ea0819c5702c --- /dev/null +++ b/drivers/gpu/drm/display/drm_hdmi_cec_helper.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2024 Linaro Ltd + */ + +#include +#include +#include + +#include + +#include +#include + +void drm_connector_hdmi_cec_unregister(struct drm_connector *connector) +{ + cec_unregister_adapter(connector->cec.adapter); + connector->cec.adapter =3D NULL; + + cec_notifier_conn_unregister(connector->cec.notifier); + connector->cec.notifier =3D NULL; + + connector->cec.funcs =3D NULL; +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_unregister); + +static const struct drm_connector_cec_funcs drm_connector_hdmi_cec_funcs = =3D { + .unregister =3D drm_connector_hdmi_cec_unregister, +}; + +int drm_connector_hdmi_cec_notifier_register(struct drm_connector *connect= or, + const char *port_name, + struct device *dev) +{ + struct cec_connector_info conn_info; + struct cec_notifier *notifier; + int ret; + + mutex_lock(&connector->cec.mutex); + + if (connector->cec.funcs) { + ret =3D -EBUSY; + goto err_unlock; + } + + cec_fill_conn_info_from_drm(&conn_info, connector); + + notifier =3D cec_notifier_conn_register(dev, port_name, &conn_info); + if (!notifier) { + ret =3D -ENOMEM; + goto err_unlock; + } + + connector->cec.notifier =3D notifier; + connector->cec.funcs =3D &drm_connector_hdmi_cec_funcs; + + mutex_unlock(&connector->cec.mutex); + + return 0; + +err_unlock: + mutex_unlock(&connector->cec.mutex); + + return ret; +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_notifier_register); + +#define to_hdmi_cec_adapter_ops(ops) \ + container_of(ops, struct drm_connector_hdmi_cec_adapter_ops, base) + +static int drm_connector_hdmi_cec_adap_enable(struct cec_adapter *adap, bo= ol enable) +{ + struct drm_connector *connector =3D cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_adapter_ops *ops =3D + to_hdmi_cec_adapter_ops(connector->cec.funcs); + + return ops->enable(connector, enable); +} + +static int drm_connector_hdmi_cec_adap_log_addr(struct cec_adapter *adap, = u8 logical_addr) +{ + struct drm_connector *connector =3D cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_adapter_ops *ops =3D + to_hdmi_cec_adapter_ops(connector->cec.funcs); + + return ops->log_addr(connector, logical_addr); +} + +static int drm_connector_hdmi_cec_adap_transmit(struct cec_adapter *adap, = u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct drm_connector *connector =3D cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_adapter_ops *ops =3D + to_hdmi_cec_adapter_ops(connector->cec.funcs); + + return ops->transmit(connector, attempts, signal_free_time, msg); +} + +static const struct cec_adap_ops drm_connector_hdmi_cec_adap_ops =3D { + .adap_enable =3D drm_connector_hdmi_cec_adap_enable, + .adap_log_addr =3D drm_connector_hdmi_cec_adap_log_addr, + .adap_transmit =3D drm_connector_hdmi_cec_adap_transmit, +}; + +int drm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_adapter_ops *ops, + const char *name, + u8 available_las, + struct device *dev) +{ + struct cec_connector_info conn_info; + struct cec_adapter *cec_adap; + int ret; + + if (!ops->base.unregister || + !ops->init || !ops->enable || !ops->log_addr || !ops->transmit) + return -EINVAL; + + mutex_lock(&connector->cec.mutex); + + if (connector->cec.funcs) { + ret =3D -EBUSY; + goto err_unlock; + } + + cec_adap =3D cec_allocate_adapter(&drm_connector_hdmi_cec_adap_ops, conne= ctor, name, + CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO, + available_las ? : CEC_MAX_LOG_ADDRS); + ret =3D PTR_ERR_OR_ZERO(cec_adap); + if (ret < 0) + goto err_unlock; + + cec_fill_conn_info_from_drm(&conn_info, connector); + cec_s_conn_info(cec_adap, &conn_info); + + connector->cec.adapter =3D cec_adap; + connector->cec.funcs =3D &ops->base; + + ret =3D ops->init(connector); + if (ret < 0) + goto err_delete_adapter; + + ret =3D cec_register_adapter(cec_adap, dev); + if (ret < 0) + goto err_delete_adapter; + + mutex_unlock(&connector->cec.mutex); + + return 0; + +err_delete_adapter: + cec_delete_adapter(cec_adap); + + connector->cec.adapter =3D NULL; + +err_unlock: + mutex_unlock(&connector->cec.mutex); + + return ret; +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_register); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg) +{ + cec_received_msg(connector->cec.adapter, msg); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *co= nnector, + u8 status) +{ + cec_transmit_attempt_done(connector->cec.adapter, status); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_attempt_done); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt) +{ + cec_transmit_done(connector->cec.adapter, status, + arb_lost_cnt, nack_cnt, low_drive_cnt, error_cnt); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done); + +void drm_connector_hdmi_cec_phys_addr_invalidate(struct drm_connector *con= nector) +{ + mutex_lock(&connector->cec.mutex); + + cec_phys_addr_invalidate(connector->cec.adapter); + cec_notifier_phys_addr_invalidate(connector->cec.notifier); + + mutex_unlock(&connector->cec.mutex); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_phys_addr_invalidate); + +void drm_connector_hdmi_cec_phys_addr_set(struct drm_connector *connector) +{ + mutex_lock(&connector->cec.mutex); + + cec_s_phys_addr(connector->cec.adapter, + connector->display_info.source_physical_address, false); + cec_notifier_set_phys_addr(connector->cec.notifier, + connector->display_info.source_physical_address); + + mutex_unlock(&connector->cec.mutex); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_phys_addr_set); diff --git a/include/drm/display/drm_hdmi_cec_helper.h b/include/drm/displa= y/drm_hdmi_cec_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..cd6274e4ee9b3e41a2d85289c4a= 420b854340e19 --- /dev/null +++ b/include/drm/display/drm_hdmi_cec_helper.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_DISPLAY_HDMI_CEC_HELPER +#define DRM_DISPLAY_HDMI_CEC_HELPER + +#include + +#include + +struct drm_connector; + +struct cec_msg; +struct device; + +struct drm_connector_hdmi_cec_adapter_ops { + struct drm_connector_cec_funcs base; + + int (*init)(struct drm_connector *connector); + void (*uninit)(struct drm_connector *connector); + + int (*enable)(struct drm_connector *connector, bool enable); + int (*log_addr)(struct drm_connector *connector, u8 logical_addr); + int (*transmit)(struct drm_connector *connector, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); +}; + +int drm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_adapter_ops *ops, + const char *name, + u8 available_las, + struct device *dev); + +int drm_connector_hdmi_cec_notifier_register(struct drm_connector *connect= or, + const char *port_name, + struct device *dev); + +void drm_connector_hdmi_cec_unregister(struct drm_connector *connector); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *co= nnector, + u8 status); +/* + * These functions are used by the state helper, so we end up linking to t= he + * same module. Define stubs to simplify the code. + */ +#ifdef CONFIG_DRM_DISPLAY_HDMI_CEC_HELPER +void drm_connector_hdmi_cec_phys_addr_invalidate(struct drm_connector *con= nector); +void drm_connector_hdmi_cec_phys_addr_set(struct drm_connector *connector); +#else +static inline void drm_connector_hdmi_cec_phys_addr_invalidate(struct drm_= connector *connector) {} +static inline void drm_connector_hdmi_cec_phys_addr_set(struct drm_connect= or *connector) {} +#endif + +#endif --=20 2.39.5