mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-15 02:42:51 -05:00
drm/vc4: hdmi: switch to generic CEC helpers
Switch VC4 driver to using CEC helpers code, simplifying hotplug and registration / cleanup. The existing vc4_hdmi_cec_release() is kept for now. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Reviewed-by: Maxime Ripard <mripard@kernel.org> Tested-by: Dave Stevenson <dave.stevenson@raspberrypi.com> Link: https://lore.kernel.org/r/20250705-drm-hdmi-connector-cec-v7-1-d14fa0c31b74@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
This commit is contained in:
@@ -35,6 +35,7 @@ config DRM_VC4_HDMI_CEC
|
||||
bool "Broadcom VC4 HDMI CEC Support"
|
||||
depends on DRM_VC4
|
||||
select CEC_CORE
|
||||
select DRM_DISPLAY_HDMI_CEC_HELPER
|
||||
help
|
||||
Choose this option if you have a Broadcom VC4 GPU
|
||||
and want to use CEC.
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
#include <drm/display/drm_hdmi_audio_helper.h>
|
||||
#include <drm/display/drm_hdmi_cec_helper.h>
|
||||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
#include <drm/display/drm_scdc_helper.h>
|
||||
@@ -375,14 +376,6 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
|
||||
|
||||
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
|
||||
|
||||
if (status == connector_status_disconnected) {
|
||||
cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
|
||||
return;
|
||||
}
|
||||
|
||||
cec_s_phys_addr(vc4_hdmi->cec_adap,
|
||||
connector->display_info.source_physical_address, false);
|
||||
|
||||
if (status != connector_status_connected)
|
||||
return;
|
||||
|
||||
@@ -2384,8 +2377,8 @@ static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
|
||||
struct vc4_hdmi *vc4_hdmi = priv;
|
||||
|
||||
if (vc4_hdmi->cec_rx_msg.len)
|
||||
cec_received_msg(vc4_hdmi->cec_adap,
|
||||
&vc4_hdmi->cec_rx_msg);
|
||||
drm_connector_hdmi_cec_received_msg(&vc4_hdmi->connector,
|
||||
&vc4_hdmi->cec_rx_msg);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@@ -2395,15 +2388,17 @@ static irqreturn_t vc4_cec_irq_handler_tx_thread(int irq, void *priv)
|
||||
struct vc4_hdmi *vc4_hdmi = priv;
|
||||
|
||||
if (vc4_hdmi->cec_tx_ok) {
|
||||
cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
|
||||
0, 0, 0, 0);
|
||||
drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector,
|
||||
CEC_TX_STATUS_OK,
|
||||
0, 0, 0, 0);
|
||||
} else {
|
||||
/*
|
||||
* This CEC implementation makes 1 retry, so if we
|
||||
* get a NACK, then that means it made 2 attempts.
|
||||
*/
|
||||
cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK,
|
||||
0, 2, 0, 0);
|
||||
drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector,
|
||||
CEC_TX_STATUS_NACK,
|
||||
0, 2, 0, 0);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@@ -2560,9 +2555,9 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
|
||||
static int vc4_hdmi_cec_enable(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *drm = vc4_hdmi->connector.dev;
|
||||
/* clock period in microseconds */
|
||||
const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
|
||||
@@ -2627,9 +2622,9 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
|
||||
static int vc4_hdmi_cec_disable(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *drm = vc4_hdmi->connector.dev;
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
@@ -2663,17 +2658,17 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
static int vc4_hdmi_cec_adap_enable(struct drm_connector *connector, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
return vc4_hdmi_cec_enable(adap);
|
||||
return vc4_hdmi_cec_enable(connector);
|
||||
else
|
||||
return vc4_hdmi_cec_disable(adap);
|
||||
return vc4_hdmi_cec_disable(connector);
|
||||
}
|
||||
|
||||
static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
|
||||
static int vc4_hdmi_cec_adap_log_addr(struct drm_connector *connector, u8 log_addr)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *drm = vc4_hdmi->connector.dev;
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
@@ -2699,10 +2694,10 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
static int vc4_hdmi_cec_adap_transmit(struct drm_connector *connector, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct drm_device *dev = vc4_hdmi->connector.dev;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
@@ -2745,84 +2740,65 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
|
||||
.adap_enable = vc4_hdmi_cec_adap_enable,
|
||||
.adap_log_addr = vc4_hdmi_cec_adap_log_addr,
|
||||
.adap_transmit = vc4_hdmi_cec_adap_transmit,
|
||||
};
|
||||
|
||||
static void vc4_hdmi_cec_release(void *ptr)
|
||||
static int vc4_hdmi_cec_init(struct drm_connector *connector)
|
||||
{
|
||||
struct vc4_hdmi *vc4_hdmi = ptr;
|
||||
|
||||
cec_unregister_adapter(vc4_hdmi->cec_adap);
|
||||
vc4_hdmi->cec_adap = NULL;
|
||||
}
|
||||
|
||||
static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
|
||||
{
|
||||
struct cec_connector_info conn_info;
|
||||
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
|
||||
struct platform_device *pdev = vc4_hdmi->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
if (!of_property_present(dev->of_node, "interrupts")) {
|
||||
dev_warn(dev, "'interrupts' DT property is missing, no CEC\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
|
||||
vc4_hdmi,
|
||||
vc4_hdmi->variant->card_name,
|
||||
CEC_CAP_DEFAULTS |
|
||||
CEC_CAP_CONNECTOR_INFO, 1);
|
||||
ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
|
||||
cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
|
||||
|
||||
if (vc4_hdmi->variant->external_irq_controller) {
|
||||
ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"),
|
||||
vc4_cec_irq_handler_rx_bare,
|
||||
vc4_cec_irq_handler_rx_thread, 0,
|
||||
"vc4 hdmi cec rx", vc4_hdmi);
|
||||
if (ret)
|
||||
goto err_delete_cec_adap;
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"),
|
||||
vc4_cec_irq_handler_tx_bare,
|
||||
vc4_cec_irq_handler_tx_thread, 0,
|
||||
"vc4 hdmi cec tx", vc4_hdmi);
|
||||
if (ret)
|
||||
goto err_delete_cec_adap;
|
||||
return ret;
|
||||
} else {
|
||||
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
|
||||
vc4_cec_irq_handler,
|
||||
vc4_cec_irq_handler_thread, 0,
|
||||
"vc4 hdmi cec", vc4_hdmi);
|
||||
if (ret)
|
||||
goto err_delete_cec_adap;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
|
||||
if (ret < 0)
|
||||
goto err_delete_cec_adap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_hdmi_cec_funcs vc4_hdmi_cec_funcs = {
|
||||
.init = vc4_hdmi_cec_init,
|
||||
.enable = vc4_hdmi_cec_adap_enable,
|
||||
.log_addr = vc4_hdmi_cec_adap_log_addr,
|
||||
.transmit = vc4_hdmi_cec_adap_transmit,
|
||||
};
|
||||
|
||||
static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi)
|
||||
{
|
||||
struct platform_device *pdev = vc4_hdmi->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (!of_property_present(dev->of_node, "interrupts")) {
|
||||
dev_warn(dev, "'interrupts' DT property is missing, no CEC\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: Strictly speaking, we should probably use a DRM-managed
|
||||
* registration there to avoid removing the CEC adapter by the
|
||||
* time the DRM driver doesn't have any user anymore.
|
||||
* NOTE: the CEC adapter will be unregistered by drmm cleanup from
|
||||
* drm_managed_release(), which is called from drm_dev_release()
|
||||
* during device unbind.
|
||||
*
|
||||
* However, the CEC framework already cleans up the CEC adapter
|
||||
* only when the last user has closed its file descriptor, so we
|
||||
* don't need to handle it in DRM.
|
||||
*
|
||||
* By the time the device-managed hook is executed, we will give
|
||||
* up our reference to the CEC adapter and therefore don't
|
||||
* really care when it's actually freed.
|
||||
*
|
||||
* There's still a problematic sequence: if we unregister our
|
||||
* CEC adapter, but the userspace keeps a handle on the CEC
|
||||
* adapter but not the DRM device for some reason. In such a
|
||||
@@ -2833,19 +2809,14 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
|
||||
* the CEC framework already handles this too, by calling
|
||||
* cec_is_registered() in cec_ioctl() and cec_poll().
|
||||
*/
|
||||
ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err_delete_cec_adap:
|
||||
cec_delete_adapter(vc4_hdmi->cec_adap);
|
||||
|
||||
return ret;
|
||||
return drmm_connector_hdmi_cec_register(&vc4_hdmi->connector,
|
||||
&vc4_hdmi_cec_funcs,
|
||||
vc4_hdmi->variant->card_name,
|
||||
1,
|
||||
&pdev->dev);
|
||||
}
|
||||
#else
|
||||
static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
|
||||
static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -3250,7 +3221,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
if (ret)
|
||||
goto err_put_runtime_pm;
|
||||
|
||||
ret = vc4_hdmi_cec_init(vc4_hdmi);
|
||||
ret = vc4_hdmi_cec_register(vc4_hdmi);
|
||||
if (ret)
|
||||
goto err_put_runtime_pm;
|
||||
|
||||
|
||||
@@ -147,7 +147,6 @@ struct vc4_hdmi {
|
||||
*/
|
||||
bool disable_wifi_frequencies;
|
||||
|
||||
struct cec_adapter *cec_adap;
|
||||
struct cec_msg cec_rx_msg;
|
||||
bool cec_tx_ok;
|
||||
bool cec_irq_was_rx;
|
||||
|
||||
Reference in New Issue
Block a user