Skip to content

Commit

Permalink
drm/msm/hdmi: add hdmi hdcp support (V3)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jilai wang authored and robclark committed Aug 15, 2015
1 parent 2d3584e commit c6a57a5
Show file tree
Hide file tree
Showing 8 changed files with 1,523 additions and 8 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/msm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/msm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
45 changes: 42 additions & 3 deletions drivers/gpu/drm/msm/hdmi/hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
}
Expand All @@ -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;
Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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:
Expand Down Expand Up @@ -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");
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions drivers/gpu/drm/msm/hdmi/hdmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ struct hdmi_audio {
int rate;
};

struct hdmi_hdcp_ctrl;

struct hdmi {
struct drm_device *dev;
struct platform_device *pdev;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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__ */
1 change: 0 additions & 1 deletion drivers/gpu/drm/msm/hdmi/hdmi_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 6 additions & 2 deletions drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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;

Expand Down
7 changes: 5 additions & 2 deletions drivers/gpu/drm/msm/hdmi/hdmi_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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);

Expand All @@ -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;

Expand Down Expand Up @@ -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;

Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit c6a57a5

Please sign in to comment.