Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/gpu/drm/vc4/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
137 changes: 54 additions & 83 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -387,14 +388,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;

Expand Down Expand Up @@ -2416,8 +2409,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;
}
Expand All @@ -2427,15 +2420,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;
}
Expand Down Expand Up @@ -2592,9 +2587,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;
Expand Down Expand Up @@ -2659,9 +2654,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;
Expand Down Expand Up @@ -2695,17 +2690,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;
Expand All @@ -2731,10 +2726,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;
Expand Down Expand Up @@ -2777,84 +2772,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)
{
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)
static int vc4_hdmi_cec_init(struct drm_connector *connector)
{
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
Expand All @@ -2865,19 +2841,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;
}
Expand Down Expand Up @@ -3295,7 +3266,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;

Expand Down
1 change: 0 additions & 1 deletion drivers/gpu/drm/vc4/vc4_hdmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,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;
Expand Down
Loading