mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-20 10:44:23 +08:00
drm/msm/hdmi: add hdmi hdcp support (V3)
Add HDMI HDCP support including HDCP PartI/II/III authentication. V1: Initial Change V2: Address Bjorn&Rob's comments Refactor the authentication process to use single work instead of multiple work for different authentication stages. V3: Update to align with qcom SCM api. Signed-off-by: Jilai Wang <jilaiw@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
parent
2d3584eb87
commit
c6a57a50ad
@ -9,6 +9,7 @@ config DRM_MSM
|
||||
select DRM_PANEL
|
||||
select SHMEM
|
||||
select TMPFS
|
||||
select QCOM_SCM
|
||||
default y
|
||||
help
|
||||
DRM/KMS driver for MSM/snapdragon.
|
||||
|
@ -10,6 +10,7 @@ msm-y := \
|
||||
hdmi/hdmi_audio.o \
|
||||
hdmi/hdmi_bridge.o \
|
||||
hdmi/hdmi_connector.o \
|
||||
hdmi/hdmi_hdcp.o \
|
||||
hdmi/hdmi_i2c.o \
|
||||
hdmi/hdmi_phy_8960.o \
|
||||
hdmi/hdmi_phy_8x60.o \
|
||||
|
@ -22,7 +22,9 @@
|
||||
void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
|
||||
{
|
||||
uint32_t ctrl = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hdmi->reg_lock, flags);
|
||||
if (power_on) {
|
||||
ctrl |= HDMI_CTRL_ENABLE;
|
||||
if (!hdmi->hdmi_mode) {
|
||||
@ -37,6 +39,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
|
||||
}
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
|
||||
spin_unlock_irqrestore(&hdmi->reg_lock, flags);
|
||||
DBG("HDMI Core: %s, HDMI_CTRL=0x%08x",
|
||||
power_on ? "Enable" : "Disable", ctrl);
|
||||
}
|
||||
@ -51,6 +54,10 @@ static irqreturn_t hdmi_irq(int irq, void *dev_id)
|
||||
/* Process DDC: */
|
||||
hdmi_i2c_irq(hdmi->i2c);
|
||||
|
||||
/* Process HDCP: */
|
||||
if (hdmi->hdcp_ctrl)
|
||||
hdmi_hdcp_irq(hdmi->hdcp_ctrl);
|
||||
|
||||
/* TODO audio.. */
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -60,6 +67,15 @@ static void hdmi_destroy(struct hdmi *hdmi)
|
||||
{
|
||||
struct hdmi_phy *phy = hdmi->phy;
|
||||
|
||||
/*
|
||||
* at this point, hpd has been disabled,
|
||||
* after flush workq, it's safe to deinit hdcp
|
||||
*/
|
||||
if (hdmi->workq) {
|
||||
flush_workqueue(hdmi->workq);
|
||||
destroy_workqueue(hdmi->workq);
|
||||
}
|
||||
hdmi_hdcp_destroy(hdmi);
|
||||
if (phy)
|
||||
phy->funcs->destroy(phy);
|
||||
|
||||
@ -77,6 +93,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
|
||||
{
|
||||
struct hdmi_platform_config *config = pdev->dev.platform_data;
|
||||
struct hdmi *hdmi = NULL;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
|
||||
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
||||
@ -87,6 +104,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
|
||||
|
||||
hdmi->pdev = pdev;
|
||||
hdmi->config = config;
|
||||
spin_lock_init(&hdmi->reg_lock);
|
||||
|
||||
/* not sure about which phy maps to which msm.. probably I miss some */
|
||||
if (config->phy_init)
|
||||
@ -107,6 +125,18 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* HDCP needs physical address of hdmi register */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
config->mmio_name);
|
||||
hdmi->mmio_phy_addr = res->start;
|
||||
|
||||
hdmi->qfprom_mmio = msm_ioremap(pdev,
|
||||
config->qfprom_mmio_name, "HDMI_QFPROM");
|
||||
if (IS_ERR(hdmi->qfprom_mmio)) {
|
||||
dev_info(&pdev->dev, "can't find qfprom resource\n");
|
||||
hdmi->qfprom_mmio = NULL;
|
||||
}
|
||||
|
||||
hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) *
|
||||
config->hpd_reg_cnt, GFP_KERNEL);
|
||||
if (!hdmi->hpd_regs) {
|
||||
@ -189,6 +219,8 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
|
||||
hdmi->pwr_clks[i] = clk;
|
||||
}
|
||||
|
||||
hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0);
|
||||
|
||||
hdmi->i2c = hdmi_i2c_init(hdmi);
|
||||
if (IS_ERR(hdmi->i2c)) {
|
||||
ret = PTR_ERR(hdmi->i2c);
|
||||
@ -197,6 +229,12 @@ static struct hdmi *hdmi_init(struct platform_device *pdev)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi);
|
||||
if (IS_ERR(hdmi->hdcp_ctrl)) {
|
||||
dev_warn(&pdev->dev, "failed to init hdcp: disabled\n");
|
||||
hdmi->hdcp_ctrl = NULL;
|
||||
}
|
||||
|
||||
return hdmi;
|
||||
|
||||
fail:
|
||||
@ -376,6 +414,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
}
|
||||
|
||||
hdmi_cfg->mmio_name = "core_physical";
|
||||
hdmi_cfg->qfprom_mmio_name = "qfprom_physical";
|
||||
hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk");
|
||||
hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data");
|
||||
hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd");
|
||||
@ -391,7 +430,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
if (cpu_is_apq8064()) {
|
||||
static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
|
||||
config.phy_init = hdmi_phy_8960_init;
|
||||
config.mmio_name = "hdmi_msm_hdmi_addr";
|
||||
config.hpd_reg_names = hpd_reg_names;
|
||||
config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
|
||||
config.hpd_clk_names = hpd_clk_names;
|
||||
@ -404,7 +442,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
} else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
|
||||
static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
|
||||
config.phy_init = hdmi_phy_8960_init;
|
||||
config.mmio_name = "hdmi_msm_hdmi_addr";
|
||||
config.hpd_reg_names = hpd_reg_names;
|
||||
config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
|
||||
config.hpd_clk_names = hpd_clk_names;
|
||||
@ -419,7 +456,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
"8901_hdmi_mvs", "8901_mpp0"
|
||||
};
|
||||
config.phy_init = hdmi_phy_8x60_init;
|
||||
config.mmio_name = "hdmi_msm_hdmi_addr";
|
||||
config.hpd_reg_names = hpd_reg_names;
|
||||
config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
|
||||
config.hpd_clk_names = hpd_clk_names;
|
||||
@ -430,6 +466,9 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
config.mux_en_gpio = -1;
|
||||
config.mux_sel_gpio = -1;
|
||||
}
|
||||
config.mmio_name = "hdmi_msm_hdmi_addr";
|
||||
config.qfprom_mmio_name = "hdmi_msm_qfprom_addr";
|
||||
|
||||
hdmi_cfg = &config;
|
||||
#endif
|
||||
dev->platform_data = hdmi_cfg;
|
||||
|
@ -37,6 +37,8 @@ struct hdmi_audio {
|
||||
int rate;
|
||||
};
|
||||
|
||||
struct hdmi_hdcp_ctrl;
|
||||
|
||||
struct hdmi {
|
||||
struct drm_device *dev;
|
||||
struct platform_device *pdev;
|
||||
@ -51,6 +53,8 @@ struct hdmi {
|
||||
unsigned long int pixclock;
|
||||
|
||||
void __iomem *mmio;
|
||||
void __iomem *qfprom_mmio;
|
||||
phys_addr_t mmio_phy_addr;
|
||||
|
||||
struct regulator **hpd_regs;
|
||||
struct regulator **pwr_regs;
|
||||
@ -68,12 +72,25 @@ struct hdmi {
|
||||
bool hdmi_mode; /* are we in hdmi mode? */
|
||||
|
||||
int irq;
|
||||
struct workqueue_struct *workq;
|
||||
|
||||
struct hdmi_hdcp_ctrl *hdcp_ctrl;
|
||||
|
||||
/*
|
||||
* spinlock to protect registers shared by different execution
|
||||
* REG_HDMI_CTRL
|
||||
* REG_HDMI_DDC_ARBITRATION
|
||||
* REG_HDMI_HDCP_INT_CTRL
|
||||
* REG_HDMI_HPD_CTRL
|
||||
*/
|
||||
spinlock_t reg_lock;
|
||||
};
|
||||
|
||||
/* platform config data (ie. from DT, or pdata) */
|
||||
struct hdmi_platform_config {
|
||||
struct hdmi_phy *(*phy_init)(struct hdmi *hdmi);
|
||||
const char *mmio_name;
|
||||
const char *qfprom_mmio_name;
|
||||
|
||||
/* regulators that need to be on for hpd: */
|
||||
const char **hpd_reg_names;
|
||||
@ -109,6 +126,11 @@ static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg)
|
||||
return msm_readl(hdmi->mmio + reg);
|
||||
}
|
||||
|
||||
static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg)
|
||||
{
|
||||
return msm_readl(hdmi->qfprom_mmio + reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* The phy appears to be different, for example between 8960 and 8x60,
|
||||
* so split the phy related functions out and load the correct one at
|
||||
@ -163,4 +185,13 @@ void hdmi_i2c_irq(struct i2c_adapter *i2c);
|
||||
void hdmi_i2c_destroy(struct i2c_adapter *i2c);
|
||||
struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi);
|
||||
|
||||
/*
|
||||
* hdcp
|
||||
*/
|
||||
struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi);
|
||||
void hdmi_hdcp_destroy(struct hdmi *hdmi);
|
||||
void hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl);
|
||||
void hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl);
|
||||
void hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl);
|
||||
|
||||
#endif /* __HDMI_CONNECTOR_H__ */
|
||||
|
@ -203,7 +203,6 @@ int hdmi_audio_update(struct hdmi *hdmi)
|
||||
audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4);
|
||||
audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE;
|
||||
} else {
|
||||
hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
|
||||
acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_CONT;
|
||||
acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SEND;
|
||||
vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE;
|
||||
|
@ -102,6 +102,9 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
|
||||
phy->funcs->powerup(phy, hdmi->pixclock);
|
||||
hdmi_set_mode(hdmi, true);
|
||||
|
||||
if (hdmi->hdcp_ctrl)
|
||||
hdmi_hdcp_on(hdmi->hdcp_ctrl);
|
||||
}
|
||||
|
||||
static void hdmi_bridge_enable(struct drm_bridge *bridge)
|
||||
@ -118,6 +121,9 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge)
|
||||
struct hdmi *hdmi = hdmi_bridge->hdmi;
|
||||
struct hdmi_phy *phy = hdmi->phy;
|
||||
|
||||
if (hdmi->hdcp_ctrl)
|
||||
hdmi_hdcp_off(hdmi->hdcp_ctrl);
|
||||
|
||||
DBG("power down");
|
||||
hdmi_set_mode(hdmi, false);
|
||||
phy->funcs->powerdown(phy);
|
||||
@ -142,8 +148,6 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
|
||||
hdmi->pixclock = mode->clock * 1000;
|
||||
|
||||
hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1;
|
||||
|
||||
hstart = mode->htotal - mode->hsync_start;
|
||||
hend = mode->htotal - mode->hsync_start + mode->hdisplay;
|
||||
|
||||
|
@ -141,6 +141,7 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector)
|
||||
struct hdmi_phy *phy = hdmi->phy;
|
||||
uint32_t hpd_ctrl;
|
||||
int i, ret;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < config->hpd_reg_cnt; i++) {
|
||||
ret = regulator_enable(hdmi->hpd_regs[i]);
|
||||
@ -192,6 +193,7 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector)
|
||||
HDMI_HPD_INT_CTRL_INT_EN);
|
||||
|
||||
/* set timeout to 4.1ms (max) for hardware debounce */
|
||||
spin_lock_irqsave(&hdmi->reg_lock, flags);
|
||||
hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
|
||||
hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
|
||||
|
||||
@ -200,6 +202,7 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector)
|
||||
~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
|
||||
HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
|
||||
spin_unlock_irqrestore(&hdmi->reg_lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -250,7 +253,6 @@ hotplug_work(struct work_struct *work)
|
||||
void hdmi_connector_irq(struct drm_connector *connector)
|
||||
{
|
||||
struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
|
||||
struct msm_drm_private *priv = connector->dev->dev_private;
|
||||
struct hdmi *hdmi = hdmi_connector->hdmi;
|
||||
uint32_t hpd_int_status, hpd_int_ctrl;
|
||||
|
||||
@ -274,7 +276,7 @@ void hdmi_connector_irq(struct drm_connector *connector)
|
||||
hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
|
||||
|
||||
queue_work(priv->wq, &hdmi_connector->hpd_work);
|
||||
queue_work(hdmi->workq, &hdmi_connector->hpd_work);
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,6 +352,7 @@ static int hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
|
||||
hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
|
||||
|
||||
hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
|
||||
if (edid) {
|
||||
|
1437
drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
Normal file
1437
drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user