Add HDMI CEC support for the DesignWare HDMI QP controller
Backport the generic DRM HDMI CEC helpers (drm_hdmi_cec_helper and drm_hdmi_cec_notifier_helper) along with the drm_connector CEC state, drm_bridge CEC callbacks, and drm_bridge_connector glue that the adapter registration path requires. Propagate the HDMI physical address to the CEC adapter from the HDMI state helper hotplug path. Implement the CEC adapter in dw-hdmi-qp: register definitions, IRQ handling, transmit and receive paths, logical address programming, and enable/disable. Pass the named "cec" IRQ and the HDMI reference clock rate down from dw_hdmi_qp-rockchip so the timer base is initialised from the actual reference clock. Enable CONFIG_DRM_DW_HDMI_QP_CEC in tart_defconfig.
This commit is contained in:
parent
10cf76215c
commit
9a769db9af
16 changed files with 704 additions and 6 deletions
|
|
@ -6012,6 +6012,7 @@ CONFIG_VIDEO_NOMODESET=y
|
|||
CONFIG_DRM=y
|
||||
#CONFIG_DRM_MIPI_DBI=m
|
||||
CONFIG_DRM_DW_HDMI_QP=y
|
||||
CONFIG_DRM_DW_HDMI_QP_CEC=y
|
||||
CONFIG_DRM_DW_DP=y
|
||||
CONFIG_DRM_MIPI_DSI=y
|
||||
CONFIG_DRM_EDID=y
|
||||
|
|
|
|||
|
|
@ -61,6 +61,14 @@ config DRM_DW_HDMI_QP
|
|||
select DRM_KMS_HELPER
|
||||
select REGMAP_MMIO
|
||||
|
||||
config DRM_DW_HDMI_QP_CEC
|
||||
bool "Synopsys Designware QP CEC interface"
|
||||
depends on DRM_DW_HDMI_QP
|
||||
select DRM_DISPLAY_HDMI_CEC_HELPER
|
||||
help
|
||||
Support the CEC interface which is part of the Synopsys
|
||||
Designware HDMI QP block.
|
||||
|
||||
config DRM_DW_MIPI_DSI
|
||||
tristate
|
||||
select DRM_KMS_HELPER
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <drm/bridge/dw_hdmi_qp.h>
|
||||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_cec_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
#include <drm/display/drm_scdc_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
#include <drm/drm_modes.h>
|
||||
|
||||
#include <sound/hdmi-codec.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "dw-hdmi-qp.h"
|
||||
|
||||
|
|
@ -136,6 +138,18 @@ struct dw_hdmi_qp_i2c {
|
|||
bool is_segment;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
struct dw_hdmi_qp_cec {
|
||||
struct drm_connector *connector;
|
||||
int irq;
|
||||
u32 addresses;
|
||||
struct cec_msg rx_msg;
|
||||
u8 tx_status;
|
||||
bool tx_done;
|
||||
bool rx_done;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dw_hdmi_qp {
|
||||
struct drm_bridge bridge;
|
||||
|
||||
|
|
@ -147,11 +161,16 @@ struct dw_hdmi_qp {
|
|||
void *data;
|
||||
} phy;
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
struct dw_hdmi_qp_cec *cec;
|
||||
#endif
|
||||
|
||||
struct drm_connector *curr_conn;
|
||||
struct delayed_work scramb_work;
|
||||
bool scramb_enabled;
|
||||
|
||||
struct regmap *regm;
|
||||
unsigned long ref_clk_rate;
|
||||
|
||||
unsigned long tmds_char_rate;
|
||||
};
|
||||
|
|
@ -1130,6 +1149,171 @@ static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
static irqreturn_t dw_hdmi_qp_cec_hardirq(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
u32 stat;
|
||||
|
||||
stat = dw_hdmi_qp_read(hdmi, CEC_INT_STATUS);
|
||||
if (!stat)
|
||||
return IRQ_NONE;
|
||||
|
||||
dw_hdmi_qp_write(hdmi, stat, CEC_INT_CLEAR);
|
||||
|
||||
if (stat & CEC_STAT_LINE_ERR) {
|
||||
cec->tx_status = CEC_TX_STATUS_ERROR;
|
||||
cec->tx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
} else if (stat & CEC_STAT_DONE) {
|
||||
cec->tx_status = CEC_TX_STATUS_OK;
|
||||
cec->tx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
} else if (stat & CEC_STAT_NACK) {
|
||||
cec->tx_status = CEC_TX_STATUS_NACK;
|
||||
cec->tx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
if (stat & CEC_STAT_EOM) {
|
||||
unsigned int len, i, val;
|
||||
|
||||
val = dw_hdmi_qp_read(hdmi, CEC_RX_COUNT_STATUS);
|
||||
len = (val & 0xf) + 1;
|
||||
if (len > sizeof(cec->rx_msg.msg))
|
||||
len = sizeof(cec->rx_msg.msg);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
val = dw_hdmi_qp_read(hdmi, CEC_RX_DATA3_0 + i * 4);
|
||||
cec->rx_msg.msg[i * 4] = val & 0xff;
|
||||
cec->rx_msg.msg[i * 4 + 1] = (val >> 8) & 0xff;
|
||||
cec->rx_msg.msg[i * 4 + 2] = (val >> 16) & 0xff;
|
||||
cec->rx_msg.msg[i * 4 + 3] = (val >> 24) & 0xff;
|
||||
}
|
||||
|
||||
dw_hdmi_qp_write(hdmi, 1, CEC_LOCK_CONTROL);
|
||||
cec->rx_msg.len = len;
|
||||
cec->rx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_cec_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
|
||||
if (cec->tx_done) {
|
||||
cec->tx_done = false;
|
||||
drm_connector_hdmi_cec_transmit_attempt_done(cec->connector,
|
||||
cec->tx_status);
|
||||
}
|
||||
|
||||
if (cec->rx_done) {
|
||||
cec->rx_done = false;
|
||||
drm_connector_hdmi_cec_received_msg(cec->connector, &cec->rx_msg);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_init(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
|
||||
cec->connector = connector;
|
||||
dw_hdmi_qp_write(hdmi, 0, CEC_TX_COUNT);
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
dw_hdmi_qp_write(hdmi, 0, CEC_INT_MASK_N);
|
||||
|
||||
return devm_request_threaded_irq(hdmi->dev, cec->irq,
|
||||
dw_hdmi_qp_cec_hardirq,
|
||||
dw_hdmi_qp_cec_thread, IRQF_SHARED,
|
||||
dev_name(hdmi->dev), hdmi);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_log_addr(struct drm_bridge *bridge, u8 logical_addr)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
|
||||
if (logical_addr == CEC_LOG_ADDR_INVALID)
|
||||
cec->addresses = 0;
|
||||
else
|
||||
cec->addresses |= BIT(logical_addr) | CEC_ADDR_BROADCAST;
|
||||
|
||||
dw_hdmi_qp_write(hdmi, cec->addresses, CEC_ADDR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_enable(struct drm_bridge *bridge, bool enable)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
unsigned int irqs;
|
||||
u32 swdisable;
|
||||
|
||||
if (!enable) {
|
||||
dw_hdmi_qp_write(hdmi, 0, CEC_INT_MASK_N);
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
swdisable = dw_hdmi_qp_read(hdmi, GLOBAL_SWDISABLE);
|
||||
dw_hdmi_qp_write(hdmi, swdisable | CEC_SWDISABLE,
|
||||
GLOBAL_SWDISABLE);
|
||||
} else {
|
||||
swdisable = dw_hdmi_qp_read(hdmi, GLOBAL_SWDISABLE);
|
||||
dw_hdmi_qp_write(hdmi, swdisable & ~CEC_SWDISABLE,
|
||||
GLOBAL_SWDISABLE);
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
dw_hdmi_qp_write(hdmi, 1, CEC_LOCK_CONTROL);
|
||||
dw_hdmi_qp_cec_log_addr(bridge, CEC_LOG_ADDR_INVALID);
|
||||
irqs = CEC_STAT_LINE_ERR | CEC_STAT_NACK | CEC_STAT_EOM |
|
||||
CEC_STAT_DONE;
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
dw_hdmi_qp_write(hdmi, irqs, CEC_INT_MASK_N);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts,
|
||||
u32 signal_free_time,
|
||||
struct cec_msg *msg)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
unsigned int i;
|
||||
u32 val = 0;
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
if (!(i % 4))
|
||||
val = msg->msg[i];
|
||||
else if ((i % 4) == 1)
|
||||
val |= msg->msg[i] << 8;
|
||||
else if ((i % 4) == 2)
|
||||
val |= msg->msg[i] << 16;
|
||||
else
|
||||
val |= msg->msg[i] << 24;
|
||||
|
||||
if (i == msg->len - 1 || (i % 4) == 3)
|
||||
dw_hdmi_qp_write(hdmi, val,
|
||||
CEC_TX_DATA3_0 + (i / 4) * 4);
|
||||
}
|
||||
|
||||
dw_hdmi_qp_write(hdmi, msg->len - 1, CEC_TX_COUNT);
|
||||
dw_hdmi_qp_write(hdmi, CEC_CTRL_START, CEC_TX_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define dw_hdmi_qp_cec_init NULL
|
||||
#define dw_hdmi_qp_cec_enable NULL
|
||||
#define dw_hdmi_qp_cec_log_addr NULL
|
||||
#define dw_hdmi_qp_cec_transmit NULL
|
||||
#endif
|
||||
|
||||
static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
|
|
@ -1145,6 +1329,10 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
|||
.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
|
||||
.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
|
||||
.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
|
||||
.hdmi_cec_init = dw_hdmi_qp_cec_init,
|
||||
.hdmi_cec_enable = dw_hdmi_qp_cec_enable,
|
||||
.hdmi_cec_log_addr = dw_hdmi_qp_cec_log_addr,
|
||||
.hdmi_cec_transmit = dw_hdmi_qp_cec_transmit,
|
||||
};
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
|
||||
|
|
@ -1180,7 +1368,7 @@ static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi)
|
|||
{
|
||||
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N);
|
||||
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N);
|
||||
dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0);
|
||||
dw_hdmi_qp_write(hdmi, hdmi->ref_clk_rate, TIMER_BASE_CONFIG0);
|
||||
|
||||
/* Software reset */
|
||||
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||||
|
|
@ -1232,6 +1420,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
|||
|
||||
hdmi->phy.ops = plat_data->phy_ops;
|
||||
hdmi->phy.data = plat_data->phy_data;
|
||||
if (plat_data->ref_clk_rate)
|
||||
hdmi->ref_clk_rate = plat_data->ref_clk_rate;
|
||||
else
|
||||
hdmi->ref_clk_rate = 428571429;
|
||||
|
||||
dw_hdmi_qp_init_hw(hdmi);
|
||||
|
||||
|
|
@ -1260,6 +1452,18 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
|||
hdmi->bridge.hdmi_audio_dev = dev;
|
||||
hdmi->bridge.hdmi_audio_dai_port = 1;
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
if (plat_data->cec_irq) {
|
||||
hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI_CEC_ADAPTER;
|
||||
hdmi->bridge.hdmi_cec_dev = dev;
|
||||
hdmi->bridge.hdmi_cec_adapter_name = dev_name(dev);
|
||||
hdmi->cec = devm_kzalloc(dev, sizeof(*hdmi->cec), GFP_KERNEL);
|
||||
if (!hdmi->cec)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
hdmi->cec->irq = plat_data->cec_irq;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
|||
|
|
@ -488,9 +488,23 @@
|
|||
#define AUDPKT_VBIT_OVR0 0xf24
|
||||
/* CEC Registers */
|
||||
#define CEC_TX_CONTROL 0x1000
|
||||
#define CEC_CTRL_CLEAR BIT(0)
|
||||
#define CEC_CTRL_START BIT(0)
|
||||
#define CEC_STATUS 0x1004
|
||||
#define CEC_STAT_DONE BIT(0)
|
||||
#define CEC_STAT_NACK BIT(1)
|
||||
#define CEC_STAT_ARBLOST BIT(2)
|
||||
#define CEC_STAT_LINE_ERR BIT(3)
|
||||
#define CEC_STAT_RETRANS_FAIL BIT(4)
|
||||
#define CEC_STAT_DISCARD BIT(5)
|
||||
#define CEC_STAT_TX_BUSY BIT(8)
|
||||
#define CEC_STAT_RX_BUSY BIT(9)
|
||||
#define CEC_STAT_DRIVE_ERR BIT(10)
|
||||
#define CEC_STAT_EOM BIT(11)
|
||||
#define CEC_STAT_NOTIFY_ERR BIT(12)
|
||||
#define CEC_CONFIG 0x1008
|
||||
#define CEC_ADDR 0x100c
|
||||
#define CEC_ADDR_BROADCAST BIT(15)
|
||||
#define CEC_TX_COUNT 0x1020
|
||||
#define CEC_TX_DATA3_0 0x1024
|
||||
#define CEC_TX_DATA7_4 0x1028
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ config DRM_DISPLAY_DP_AUX_BUS
|
|||
config DRM_DISPLAY_HELPER
|
||||
tristate
|
||||
depends on DRM
|
||||
select CEC_CORE if DRM_DISPLAY_DP_AUX_CEC || DRM_DISPLAY_HDMI_CEC_HELPER || CEC_NOTIFIER
|
||||
help
|
||||
DRM helpers for display adapters.
|
||||
|
||||
|
||||
if DRM_DISPLAY_HELPER
|
||||
|
||||
config DRM_BRIDGE_CONNECTOR
|
||||
|
|
@ -82,6 +84,16 @@ config DRM_DISPLAY_HDMI_AUDIO_HELPER
|
|||
DRM display helpers for HDMI Audio functionality (generic HDMI Codec
|
||||
implementation).
|
||||
|
||||
config DRM_DISPLAY_HDMI_CEC_HELPER
|
||||
bool
|
||||
help
|
||||
DRM display helpers for HDMI CEC implementation.
|
||||
|
||||
config DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER
|
||||
def_bool CEC_NOTIFIER
|
||||
help
|
||||
DRM display helpers for HDMI CEC notifiers implementation.
|
||||
|
||||
config DRM_DISPLAY_HDMI_HELPER
|
||||
bool
|
||||
help
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DSC_HELPER) += \
|
|||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_AUDIO_HELPER) += \
|
||||
drm_hdmi_audio_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_HELPER) += \
|
||||
drm_hdmi_cec_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER) += \
|
||||
drm_hdmi_cec_notifier_helper.o
|
||||
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \
|
||||
drm_hdmi_helper.o \
|
||||
drm_scdc_helper.o
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#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>
|
||||
|
||||
|
|
@ -98,6 +99,7 @@ struct drm_bridge_connector {
|
|||
* HDMI connector infrastructure, if any (see &DRM_BRIDGE_OP_HDMI).
|
||||
*/
|
||||
struct drm_bridge *bridge_hdmi;
|
||||
struct drm_bridge *bridge_hdmi_cec;
|
||||
};
|
||||
|
||||
#define to_drm_bridge_connector(x) \
|
||||
|
|
@ -534,6 +536,61 @@ static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_aud
|
|||
.shutdown = drm_bridge_connector_audio_shutdown,
|
||||
.mute_stream = drm_bridge_connector_audio_mute_stream,
|
||||
};
|
||||
static int drm_bridge_connector_hdmi_cec_enable(struct drm_connector *connector,
|
||||
bool enable)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
|
||||
|
||||
return bridge->funcs->hdmi_cec_enable(bridge, enable);
|
||||
}
|
||||
|
||||
static int drm_bridge_connector_hdmi_cec_log_addr(
|
||||
struct drm_connector *connector, u8 logical_addr)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
|
||||
|
||||
return bridge->funcs->hdmi_cec_log_addr(bridge, logical_addr);
|
||||
}
|
||||
|
||||
static int drm_bridge_connector_hdmi_cec_transmit(
|
||||
struct drm_connector *connector, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
|
||||
|
||||
return bridge->funcs->hdmi_cec_transmit(bridge, attempts,
|
||||
signal_free_time, msg);
|
||||
}
|
||||
|
||||
static int drm_bridge_connector_hdmi_cec_init(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector =
|
||||
to_drm_bridge_connector(connector);
|
||||
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
|
||||
|
||||
if (!bridge->funcs->hdmi_cec_init)
|
||||
return 0;
|
||||
|
||||
return bridge->funcs->hdmi_cec_init(connector, bridge);
|
||||
}
|
||||
|
||||
static const struct drm_connector_hdmi_cec_funcs
|
||||
drm_bridge_connector_hdmi_cec_funcs = {
|
||||
.init = drm_bridge_connector_hdmi_cec_init,
|
||||
.enable = drm_bridge_connector_hdmi_cec_enable,
|
||||
.log_addr = drm_bridge_connector_hdmi_cec_log_addr,
|
||||
.transmit = drm_bridge_connector_hdmi_cec_transmit,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Bridge Connector Initialisation
|
||||
*/
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Bridge Connector Initialisation
|
||||
|
|
@ -614,6 +671,20 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||
if (bridge->max_bpc)
|
||||
max_bpc = bridge->max_bpc;
|
||||
}
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
|
||||
if (bridge_connector->bridge_hdmi_cec)
|
||||
return ERR_PTR(-EBUSY);
|
||||
bridge_connector->bridge_hdmi_cec = bridge;
|
||||
}
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
|
||||
if (bridge_connector->bridge_hdmi_cec)
|
||||
return ERR_PTR(-EBUSY);
|
||||
if (!bridge->funcs->hdmi_cec_enable ||
|
||||
!bridge->funcs->hdmi_cec_log_addr ||
|
||||
!bridge->funcs->hdmi_cec_transmit)
|
||||
return ERR_PTR(-EINVAL);
|
||||
bridge_connector->bridge_hdmi_cec = bridge;
|
||||
}
|
||||
|
||||
if (!drm_bridge_get_next_bridge(bridge))
|
||||
connector_type = bridge->type;
|
||||
|
|
@ -673,6 +744,19 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
if (bridge_connector->bridge_hdmi_cec) {
|
||||
bridge = bridge_connector->bridge_hdmi_cec;
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER)
|
||||
ret = drm_connector_hdmi_cec_register(connector,
|
||||
&drm_bridge_connector_hdmi_cec_funcs,
|
||||
bridge->hdmi_cec_adapter_name, 0,
|
||||
bridge->hdmi_cec_dev);
|
||||
else
|
||||
ret = drm_connector_hdmi_cec_notifier_register(connector, NULL,
|
||||
bridge->hdmi_cec_dev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
|
||||
|
||||
|
|
|
|||
179
drivers/gpu/drm/display/drm_hdmi_cec_helper.c
Normal file
179
drivers/gpu/drm/display/drm_hdmi_cec_helper.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (c) 2024 Linaro Ltd
|
||||
*/
|
||||
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/display/drm_hdmi_cec_helper.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
struct drm_connector_hdmi_cec_data {
|
||||
struct cec_adapter *adapter;
|
||||
const struct drm_connector_hdmi_cec_funcs *funcs;
|
||||
};
|
||||
|
||||
static int drm_connector_hdmi_cec_adap_enable(struct cec_adapter *adap,
|
||||
bool enable)
|
||||
{
|
||||
struct drm_connector *connector = cec_get_drvdata(adap);
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
return data->funcs->enable(connector, enable);
|
||||
}
|
||||
|
||||
static int drm_connector_hdmi_cec_adap_log_addr(struct cec_adapter *adap,
|
||||
u8 logical_addr)
|
||||
{
|
||||
struct drm_connector *connector = cec_get_drvdata(adap);
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
return data->funcs->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 = cec_get_drvdata(adap);
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
return data->funcs->transmit(connector, attempts, signal_free_time, msg);
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops drm_connector_hdmi_cec_adap_ops = {
|
||||
.adap_enable = drm_connector_hdmi_cec_adap_enable,
|
||||
.adap_log_addr = drm_connector_hdmi_cec_adap_log_addr,
|
||||
.adap_transmit = drm_connector_hdmi_cec_adap_transmit,
|
||||
};
|
||||
|
||||
static void drm_connector_hdmi_cec_adapter_phys_addr_invalidate(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
cec_phys_addr_invalidate(data->adapter);
|
||||
}
|
||||
|
||||
static void drm_connector_hdmi_cec_adapter_phys_addr_set(
|
||||
struct drm_connector *connector, u16 addr)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
cec_s_phys_addr(data->adapter, addr, false);
|
||||
}
|
||||
|
||||
static void drm_connector_hdmi_cec_adapter_unregister(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
cec_delete_adapter(data->adapter);
|
||||
|
||||
if (data->funcs->uninit)
|
||||
data->funcs->uninit(connector);
|
||||
|
||||
kfree(data);
|
||||
connector->cec.data = NULL;
|
||||
}
|
||||
|
||||
static const struct drm_connector_cec_funcs
|
||||
drm_connector_hdmi_cec_adapter_funcs = {
|
||||
.phys_addr_invalidate = drm_connector_hdmi_cec_adapter_phys_addr_invalidate,
|
||||
.phys_addr_set = drm_connector_hdmi_cec_adapter_phys_addr_set,
|
||||
.unregister = drm_connector_hdmi_cec_adapter_unregister,
|
||||
};
|
||||
|
||||
int drm_connector_hdmi_cec_register(
|
||||
struct drm_connector *connector,
|
||||
const struct drm_connector_hdmi_cec_funcs *funcs,
|
||||
const char *name, u8 available_las, struct device *dev)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data;
|
||||
struct cec_connector_info conn_info;
|
||||
struct cec_adapter *cec_adap;
|
||||
int ret;
|
||||
|
||||
if (!funcs->init || !funcs->enable || !funcs->log_addr ||
|
||||
!funcs->transmit)
|
||||
return -EINVAL;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->funcs = funcs;
|
||||
|
||||
cec_adap = cec_allocate_adapter(&drm_connector_hdmi_cec_adap_ops,
|
||||
connector, name,
|
||||
CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO,
|
||||
available_las ? : CEC_MAX_LOG_ADDRS);
|
||||
ret = PTR_ERR_OR_ZERO(cec_adap);
|
||||
if (ret < 0)
|
||||
goto err_free;
|
||||
|
||||
cec_fill_conn_info_from_drm(&conn_info, connector);
|
||||
cec_s_conn_info(cec_adap, &conn_info);
|
||||
|
||||
data->adapter = cec_adap;
|
||||
|
||||
mutex_lock(&connector->cec.mutex);
|
||||
|
||||
connector->cec.data = data;
|
||||
connector->cec.funcs = &drm_connector_hdmi_cec_adapter_funcs;
|
||||
|
||||
ret = funcs->init(connector);
|
||||
if (ret < 0)
|
||||
goto err_delete_adapter;
|
||||
|
||||
ret = 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.data = NULL;
|
||||
mutex_unlock(&connector->cec.mutex);
|
||||
err_free:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_hdmi_cec_register);
|
||||
|
||||
void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector,
|
||||
struct cec_msg *msg)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
cec_received_msg(data->adapter, msg);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg);
|
||||
|
||||
void drm_connector_hdmi_cec_transmit_attempt_done(
|
||||
struct drm_connector *connector, u8 status)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
cec_transmit_attempt_done(data->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)
|
||||
{
|
||||
struct drm_connector_hdmi_cec_data *data = connector->cec.data;
|
||||
|
||||
cec_transmit_done(data->adapter, status, arb_lost_cnt, nack_cnt,
|
||||
low_drive_cnt, error_cnt);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done);
|
||||
59
drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c
Normal file
59
drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (c) 2024 Linaro Ltd
|
||||
*/
|
||||
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/display/drm_hdmi_cec_helper.h>
|
||||
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <media/cec-notifier.h>
|
||||
|
||||
static void drm_connector_hdmi_cec_notifier_phys_addr_invalidate(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
cec_notifier_phys_addr_invalidate(connector->cec.data);
|
||||
}
|
||||
|
||||
static void drm_connector_hdmi_cec_notifier_phys_addr_set(
|
||||
struct drm_connector *connector, u16 addr)
|
||||
{
|
||||
cec_notifier_set_phys_addr(connector->cec.data, addr);
|
||||
}
|
||||
|
||||
static void drm_connector_hdmi_cec_notifier_unregister(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
cec_notifier_conn_unregister(connector->cec.data);
|
||||
connector->cec.data = NULL;
|
||||
}
|
||||
|
||||
static const struct drm_connector_cec_funcs drm_connector_cec_notifier_funcs = {
|
||||
.phys_addr_invalidate = drm_connector_hdmi_cec_notifier_phys_addr_invalidate,
|
||||
.phys_addr_set = drm_connector_hdmi_cec_notifier_phys_addr_set,
|
||||
.unregister = drm_connector_hdmi_cec_notifier_unregister,
|
||||
};
|
||||
|
||||
int drm_connector_hdmi_cec_notifier_register(struct drm_connector *connector,
|
||||
const char *port_name,
|
||||
struct device *dev)
|
||||
{
|
||||
struct cec_connector_info conn_info;
|
||||
struct cec_notifier *notifier;
|
||||
|
||||
cec_fill_conn_info_from_drm(&conn_info, connector);
|
||||
|
||||
notifier = cec_notifier_conn_register(dev, port_name, &conn_info);
|
||||
if (!notifier)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&connector->cec.mutex);
|
||||
connector->cec.data = notifier;
|
||||
connector->cec.funcs = &drm_connector_cec_notifier_funcs;
|
||||
mutex_unlock(&connector->cec.mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_hdmi_cec_notifier_register);
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <drm/drm_print.h>
|
||||
|
||||
#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>
|
||||
|
||||
|
|
@ -789,9 +790,10 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector,
|
|||
const struct drm_edid *drm_edid;
|
||||
|
||||
if (status == connector_status_disconnected) {
|
||||
// TODO: also handle CEC and scramber, HDMI sink disconnected.
|
||||
// TODO: also handle scramber, HDMI sink disconnected.
|
||||
drm_connector_hdmi_audio_plugged_notify(connector, false);
|
||||
drm_edid_connector_update(connector, NULL);
|
||||
drm_connector_cec_phys_addr_invalidate(connector);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -805,8 +807,9 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector,
|
|||
drm_edid_free(drm_edid);
|
||||
|
||||
if (status == connector_status_connected) {
|
||||
// TODO: also handle CEC and scramber, HDMI sink is now connected.
|
||||
// TODO: also handle scramber, HDMI sink is now connected.
|
||||
drm_connector_hdmi_audio_plugged_notify(connector, true);
|
||||
drm_connector_cec_phys_addr_set(connector);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ static int drm_connector_init_only(struct drm_device *dev,
|
|||
INIT_LIST_HEAD(&connector->probed_modes);
|
||||
INIT_LIST_HEAD(&connector->modes);
|
||||
mutex_init(&connector->mutex);
|
||||
mutex_init(&connector->cec.mutex);
|
||||
mutex_init(&connector->eld_mutex);
|
||||
mutex_init(&connector->edid_override_mutex);
|
||||
mutex_init(&connector->hdmi.infoframes.lock);
|
||||
|
|
@ -701,6 +702,34 @@ static void drm_mode_remove(struct drm_connector *connector,
|
|||
drm_mode_destroy(connector->dev, mode);
|
||||
}
|
||||
|
||||
void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector)
|
||||
{
|
||||
mutex_lock(&connector->cec.mutex);
|
||||
if (connector->cec.funcs &&
|
||||
connector->cec.funcs->phys_addr_invalidate)
|
||||
connector->cec.funcs->phys_addr_invalidate(connector);
|
||||
mutex_unlock(&connector->cec.mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_cec_phys_addr_invalidate);
|
||||
|
||||
void drm_connector_cec_phys_addr_set(struct drm_connector *connector)
|
||||
{
|
||||
u16 addr;
|
||||
|
||||
mutex_lock(&connector->cec.mutex);
|
||||
addr = connector->display_info.source_physical_address;
|
||||
if (connector->cec.funcs && connector->cec.funcs->phys_addr_set)
|
||||
connector->cec.funcs->phys_addr_set(connector, addr);
|
||||
mutex_unlock(&connector->cec.mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_cec_phys_addr_set);
|
||||
|
||||
static void drm_connector_cec_unregister(struct drm_connector *connector)
|
||||
{
|
||||
if (connector->cec.funcs && connector->cec.funcs->unregister)
|
||||
connector->cec.funcs->unregister(connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_connector_cleanup - cleans up an initialised connector
|
||||
* @connector: connector to cleanup
|
||||
|
|
@ -720,6 +749,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
|
|||
drm_connector_unregister(connector);
|
||||
|
||||
platform_device_unregister(connector->hdmi_audio.codec_pdev);
|
||||
drm_connector_cec_unregister(connector);
|
||||
|
||||
if (connector->privacy_screen) {
|
||||
drm_privacy_screen_put(connector->privacy_screen);
|
||||
|
|
@ -759,6 +789,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
|
|||
|
||||
mutex_destroy(&connector->hdmi_audio.lock);
|
||||
mutex_destroy(&connector->hdmi.infoframes.lock);
|
||||
mutex_destroy(&connector->cec.mutex);
|
||||
mutex_destroy(&connector->mutex);
|
||||
|
||||
memset(connector, 0, sizeof(*connector));
|
||||
|
|
|
|||
|
|
@ -436,15 +436,15 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
|||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
const struct rockchip_hdmi_qp_cfg *cfg;
|
||||
struct dw_hdmi_qp_plat_data plat_data;
|
||||
struct dw_hdmi_qp_plat_data plat_data = {};
|
||||
struct drm_device *drm = data;
|
||||
//struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct rockchip_hdmi_qp *hdmi;
|
||||
struct resource *res;
|
||||
struct clk_bulk_data *clks;
|
||||
//int ret, irq, i;
|
||||
struct clk *ref_clk;
|
||||
int ret, hpd_irq, i;
|
||||
int cec_irq;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
return -ENODEV;
|
||||
|
|
@ -519,6 +519,11 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
|||
dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ref_clk = clk_get(hdmi->dev, "ref");
|
||||
if (IS_ERR(ref_clk))
|
||||
return PTR_ERR(ref_clk);
|
||||
plat_data.ref_clk_rate = clk_get_rate(ref_clk);
|
||||
clk_put(ref_clk);
|
||||
|
||||
hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
|
||||
GPIOD_OUT_HIGH);
|
||||
|
|
@ -538,6 +543,10 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
|||
|
||||
cfg->ctrl_ops->io_init(hdmi);
|
||||
|
||||
cec_irq = platform_get_irq_byname(pdev, "cec");
|
||||
if (cec_irq < 0)
|
||||
return cec_irq;
|
||||
plat_data.cec_irq = cec_irq;
|
||||
INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
|
||||
|
||||
plat_data.main_irq = platform_get_irq_byname(pdev, "main");
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ struct dw_hdmi_qp_plat_data {
|
|||
const struct dw_hdmi_qp_phy_ops *phy_ops;
|
||||
void *phy_data;
|
||||
int main_irq;
|
||||
int cec_irq;
|
||||
unsigned long ref_clk_rate;
|
||||
};
|
||||
|
||||
struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||||
|
|
|
|||
49
include/drm/display/drm_hdmi_cec_helper.h
Normal file
49
include/drm/display/drm_hdmi_cec_helper.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef DRM_DISPLAY_HDMI_CEC_HELPER
|
||||
#define DRM_DISPLAY_HDMI_CEC_HELPER
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_connector;
|
||||
struct cec_msg;
|
||||
struct device;
|
||||
|
||||
struct drm_connector_hdmi_cec_funcs {
|
||||
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_funcs *funcs,
|
||||
const char *name, u8 available_las, struct device *dev);
|
||||
|
||||
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 *connector, u8 status);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER)
|
||||
int drm_connector_hdmi_cec_notifier_register(struct drm_connector *connector,
|
||||
const char *port_name,
|
||||
struct device *dev);
|
||||
#else
|
||||
static inline int drm_connector_hdmi_cec_notifier_register(
|
||||
struct drm_connector *connector,
|
||||
const char *port_name,
|
||||
struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -41,6 +41,7 @@ struct drm_display_info;
|
|||
struct drm_minor;
|
||||
struct drm_panel;
|
||||
struct edid;
|
||||
struct cec_msg;
|
||||
struct hdmi_codec_daifmt;
|
||||
struct hdmi_codec_params;
|
||||
struct i2c_adapter;
|
||||
|
|
@ -758,6 +759,19 @@ struct drm_bridge_funcs {
|
|||
struct drm_bridge *bridge,
|
||||
bool enable, int direction);
|
||||
|
||||
/**
|
||||
* @hdmi_cec_init: initialize the bridge CEC hardware for a connector.
|
||||
*/
|
||||
int (*hdmi_cec_init)(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge);
|
||||
/** @hdmi_cec_enable: enable or disable CEC. */
|
||||
int (*hdmi_cec_enable)(struct drm_bridge *bridge, bool enable);
|
||||
/** @hdmi_cec_log_addr: program a CEC logical address. */
|
||||
int (*hdmi_cec_log_addr)(struct drm_bridge *bridge, u8 logical_addr);
|
||||
/** @hdmi_cec_transmit: start a CEC message transmission. */
|
||||
int (*hdmi_cec_transmit)(struct drm_bridge *bridge, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg);
|
||||
|
||||
/**
|
||||
* @debugfs_init:
|
||||
*
|
||||
|
|
@ -843,6 +857,8 @@ enum drm_bridge_ops {
|
|||
* drivers.
|
||||
*/
|
||||
DRM_BRIDGE_OP_HDMI = BIT(4),
|
||||
DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER = BIT(5),
|
||||
DRM_BRIDGE_OP_HDMI_CEC_ADAPTER = BIT(6),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -962,6 +978,11 @@ struct drm_bridge {
|
|||
* @hdmi_audio_dai_port: sound DAI port, -1 if it is not enabled
|
||||
*/
|
||||
int hdmi_audio_dai_port;
|
||||
|
||||
/** @hdmi_cec_dev: device to use as the CEC adapter parent. */
|
||||
struct device *hdmi_cec_dev;
|
||||
/** @hdmi_cec_adapter_name: name of the CEC adapter. */
|
||||
const char *hdmi_cec_adapter_name;
|
||||
};
|
||||
|
||||
static inline struct drm_bridge *
|
||||
|
|
|
|||
|
|
@ -1191,6 +1191,15 @@ struct drm_connector_hdmi_audio_funcs {
|
|||
bool enable, int direction);
|
||||
};
|
||||
|
||||
void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector);
|
||||
void drm_connector_cec_phys_addr_set(struct drm_connector *connector);
|
||||
|
||||
struct drm_connector_cec_funcs {
|
||||
void (*phys_addr_invalidate)(struct drm_connector *connector);
|
||||
void (*phys_addr_set)(struct drm_connector *connector, u16 addr);
|
||||
void (*unregister)(struct drm_connector *connector);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions
|
||||
*/
|
||||
|
|
@ -1832,6 +1841,12 @@ struct drm_connector_hdmi {
|
|||
} infoframes;
|
||||
};
|
||||
|
||||
struct drm_connector_cec {
|
||||
struct mutex mutex;
|
||||
const struct drm_connector_cec_funcs *funcs;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_connector - central DRM connector control structure
|
||||
*
|
||||
|
|
@ -2253,6 +2268,9 @@ struct drm_connector {
|
|||
* @hdmi_audio: HDMI codec properties and non-DRM state.
|
||||
*/
|
||||
struct drm_connector_hdmi_audio hdmi_audio;
|
||||
|
||||
/** @cec: CEC-related data. */
|
||||
struct drm_connector_cec cec;
|
||||
};
|
||||
|
||||
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue