[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
There is one issue to use i2c_smbus_XX functions: i2c_smbus_read_i2c_block_data has limitation with the maximum count I2C_SMBUS_BLOCK_MAX. But in function hdmi_hdcp_recv_ksv_fifo, since the downstream ksv_fifo size will exceed this limitation and must be read in a single transaction, we can't use this function then. > On Fri, Jan 30, 2015 at 2:39 PM, Bjorn Andersson wrote: >> On Fri, Jan 30, 2015 at 1:51 PM, Bjorn Andersson wrote: >>> On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang >>> wrote: Add HDMI HDCP support including HDCP PartI/II/III authentication. V1: Initial Change V2: Address Bjorn's comments Refactor the authentication process to use single work instead of multiple work for different authentication stages. >>> Looks cleaner and the SCM parts look good now. >>> >>> But the ddc communication still makes me wonder, see below. >>> >> >> Rob made me aware about the fact that the hdmi driver is both >> implementing a driver for the i2c controller and now for the hdcp >> client and hence doesn't have an i2c_client handle. >> >> So unless this is redesigned to be split in a separate i2c client >> driver we probably have to have the ddc functions like this. >> >> I'm fine with this as is for now. >> > > After digging some more in the i2c stack I found others using > i2c_new_dummy() to create a "dummy" i2c client from and adaptor and an > address. > By introducing that we could make hdmi->i2c an actual i2c_client and > use the client api in here. > > Regards, > Bjorn >
[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
On Wed, Feb 11, 2015 at 10:59 AM, wrote: > There is one issue to use i2c_smbus_XX functions: > i2c_smbus_read_i2c_block_data has limitation with the maximum count > I2C_SMBUS_BLOCK_MAX. > But in function hdmi_hdcp_recv_ksv_fifo, since the downstream ksv_fifo > size will exceed this limitation and must be read in a single transaction, > we can't use this function then. > That's interesting, because we have the same problem in the Synaptics driver, where we want to read more than 32 bytes with a method that follows the smbus flow. Wolfram, do you have any input on making i2c_smbus* functions work for larger accesses? Or should we just conclude that it's not "smbus" and open code this in the various places? Regards, Bjorn
[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
On Fri, Jan 30, 2015 at 2:39 PM, Bjorn Andersson wrote: > On Fri, Jan 30, 2015 at 1:51 PM, Bjorn Andersson wrote: >> On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang >> wrote: >>> Add HDMI HDCP support including HDCP PartI/II/III authentication. >>> V1: Initial Change >>> V2: Address Bjorn's comments >>> Refactor the authentication process to use single work instead >>> of multiple work for different authentication stages. >>> >> Looks cleaner and the SCM parts look good now. >> >> But the ddc communication still makes me wonder, see below. >> > > Rob made me aware about the fact that the hdmi driver is both > implementing a driver for the i2c controller and now for the hdcp > client and hence doesn't have an i2c_client handle. > > So unless this is redesigned to be split in a separate i2c client > driver we probably have to have the ddc functions like this. > > I'm fine with this as is for now. > After digging some more in the i2c stack I found others using i2c_new_dummy() to create a "dummy" i2c client from and adaptor and an address. By introducing that we could make hdmi->i2c an actual i2c_client and use the client api in here. Regards, Bjorn
[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
On Fri, Jan 30, 2015 at 1:51 PM, Bjorn Andersson wrote: > On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang wrote: >> Add HDMI HDCP support including HDCP PartI/II/III authentication. >> V1: Initial Change >> V2: Address Bjorn's comments >> Refactor the authentication process to use single work instead >> of multiple work for different authentication stages. >> > Looks cleaner and the SCM parts look good now. > > But the ddc communication still makes me wonder, see below. > Rob made me aware about the fact that the hdmi driver is both implementing a driver for the i2c controller and now for the hdcp client and hence doesn't have an i2c_client handle. So unless this is redesigned to be split in a separate i2c client driver we probably have to have the ddc functions like this. I'm fine with this as is for now. Regards, Bjorn
[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang wrote: > Add HDMI HDCP support including HDCP PartI/II/III authentication. > V1: Initial Change > V2: Address Bjorn's comments > Refactor the authentication process to use single work instead > of multiple work for different authentication stages. > Looks cleaner and the SCM parts look good now. But the ddc communication still makes me wonder, see below. [..] > diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c > b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c [..] > + > +static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, > + u8 *data, u16 data_len) > +{ > + int rc; > + int retry = 5; > + struct i2c_msg msgs[] = { > + { > + .addr = addr >> 1, > + .flags = 0, > + .len= 1, > + .buf= , > + }, { > + .addr = addr >> 1, > + .flags = I2C_M_RD, > + .len= data_len, > + .buf= data, > + } > + }; > + > + DBG("Start DDC read"); > +retry: > + rc = i2c_transfer(hdmi->i2c, msgs, 2); > + > + retry--; > + if (rc == 2) > + rc = 0; > + else if (retry > 0) > + goto retry; > + else > + rc = -EIO; > + > + DBG("End DDC read %d", rc); > + > + return rc; > +} Looking back and forth at this, to me this really looks like i2c_smbus_read_i2c_block_data(). > + > +#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32 This matches I2C_SMBUS_BLOCK_MAX, indicating further that there's something in common here. > + > +static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, > + u8 *data, u16 data_len) > +{ > + int rc; > + int retry = 10; > + u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM]; > + struct i2c_msg msgs[] = { > + { > + .addr = addr >> 1, > + .flags = 0, > + .len= 1, > + } > + }; > + > + DBG("Start DDC write"); > + if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) { > + pr_err("%s: write size too big\n", __func__); > + return -ERANGE; > + } > + > + buf[0] = offset; > + memcpy([1], data, data_len); > + msgs[0].buf = buf; > + msgs[0].len = data_len + 1; > +retry: > + rc = i2c_transfer(hdmi->i2c, msgs, 1); > + > + retry--; > + if (rc == 1) > + rc = 0; > + else if (retry > 0) > + goto retry; > + else > + rc = -EIO; > + > + DBG("End DDC write %d", rc); > + > + return rc; > +} And this looks like i2c_smbus_write_i2c_block_data() > + [..] > + > +static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) > +{ > + int rc = 0; > + struct hdmi *hdmi = hdcp_ctrl->hdmi; > + u32 link0_aksv_0, link0_aksv_1; > + u32 link0_an[2]; > + u8 aksv[5]; > + > + /* Read An0 and An1 */ > + link0_an[0] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA5); > + link0_an[1] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA6); > + > + /* Read AKSV */ > + link0_aksv_0 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA3); > + link0_aksv_1 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4); > + > + DBG("Link ASKV=%08x%08x", link0_aksv_0, link0_aksv_1); > + /* Copy An and AKSV to byte arrays for transmission */ > + aksv[0] = link0_aksv_0& 0xFF; > + aksv[1] = (link0_aksv_0 >> 8) & 0xFF; > + aksv[2] = (link0_aksv_0 >> 16) & 0xFF; > + aksv[3] = (link0_aksv_0 >> 24) & 0xFF; > + aksv[4] = link0_aksv_1& 0xFF; > + > + /* Write An to offset 0x18 */ > + rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x18, (u8 *)link0_an, > + (u16)sizeof(link0_an)); rc = i2c_smbus_write_i2c_block_data(hdmi->i2c, 0x18, sizeof(link0_an), link0_an); if (rc < 0) { > + if (rc) { > + pr_err("%s:An write failed\n", __func__); > + return rc; > + } > + DBG("Link0-An=%08x%08x", link0_an[0], link0_an[1]); > + > + /* Write AKSV to offset 0x10 */ > + rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x10, aksv, 5); rc = i2c_smbus_write_i2c_block_data(hdmi->i2c, 0x10, sizeof(aksv), aksv); if (rc < 0) { > + if (rc) { > + pr_err("%s:AKSV write failed\n", __func__); > + return rc; > + } > + DBG("Link0-AKSV=%02x%08x", link0_aksv_1 & 0xFF, link0_aksv_0); > + > + return 0; > +} > + > +static int hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) > +{ > + int rc = 0; > + struct hdmi *hdmi = hdcp_ctrl->hdmi; > + u8 bksv[5]; > + u32 reg[2], data[2]; > + > + /* Read BKSV at offset 0x00 */ > + rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x00, bksv, 5); >
[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
Add HDMI HDCP support including HDCP PartI/II/III authentication. V1: Initial Change V2: Address Bjorn's comments Refactor the authentication process to use single work instead of multiple work for different authentication stages. Signed-off-by: Jilai Wang --- drivers/gpu/drm/msm/Makefile |1 + drivers/gpu/drm/msm/hdmi/hdmi.c | 45 +- drivers/gpu/drm/msm/hdmi/hdmi.h | 31 + drivers/gpu/drm/msm/hdmi/hdmi_audio.c |1 - drivers/gpu/drm/msm/hdmi/hdmi_bridge.c|8 +- drivers/gpu/drm/msm/hdmi/hdmi_connector.c |7 +- drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c | 1445 + 7 files changed, 1530 insertions(+), 8 deletions(-) create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index e5464a0..2b32d4d 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -12,6 +12,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 \ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index b2e610e..7713155 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -22,7 +22,9 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; + unsigned long flags; + spin_lock_irqsave(>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(>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); @@ -82,6 +98,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(>dev, sizeof(*hdmi), GFP_KERNEL); @@ -92,6 +109,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) hdmi->pdev = pdev; hdmi->config = config; + spin_lock_init(>reg_lock); /* not sure about which phy maps to which msm.. probably I miss some */ if (config->phy_init) @@ -112,6 +130,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(>dev, "can't find qfprom resource\n"); + hdmi->qfprom_mmio = NULL; + } + hdmi->hpd_regs = devm_kzalloc(>dev, sizeof(hdmi->hpd_regs[0]) * config->hpd_reg_cnt, GFP_KERNEL); if (!hdmi->hpd_regs) { @@ -194,6 +224,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); @@ -202,6 +234,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(>dev, "failed to init hdcp: disabled\n"); + hdmi->hdcp_ctrl = NULL; + } + return hdmi; fail: @@ -384,6 +422,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");