Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6a57a50 authored by jilai wang's avatar jilai wang Committed by Rob Clark
Browse files

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: default avatarJilai Wang <jilaiw@codeaurora.org>
Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 2d3584eb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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 \
+42 −3
Original line number Diff line number Diff line
@@ -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;
+31 −0
Original line number Diff line number Diff line
@@ -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__ */
+0 −1
Original line number Diff line number Diff line
@@ -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;
Loading