[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)

2015-02-11 Thread jil...@codeaurora.org
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)

2015-02-11 Thread Bjorn Andersson
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)

2015-02-10 Thread Bjorn Andersson
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)

2015-01-30 Thread Bjorn Andersson
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)

2015-01-30 Thread Bjorn Andersson
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)

2015-01-13 Thread Jilai Wang
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");