On Fri, Dec 21, 2012 at 4:47 AM, Eunchul Kim <chulspro.kim@xxxxxxxxxxx> wrote: > HDCP stands for High-bandwidth Digital Content Protection. > This is a newer form of Digital Rights Management(secure DRM) > that was designed to control digital video and audio content. > Contains an integrated HDCP encryption engine for video/audio content protection. > supports version HDCP v1.1. > Exynos AP supports embedded HDCP key system. > The HDCP key value is fused during fabrication, based on customer's request. > > Exynos HDCP scenario. > - start encryption. > - receive Bcaps, Bksv from peer device. > - check repeater caps from Bcaps. > - send An, Aksv to peer device. > - receive Rj from peer device. > - compare Ri,Rj. If same and not repeater, then start encryption. > - If not same, retry. If repeater, then start second authentication. > - stop encryption. > > Signed-off-by: Eunchul Kim <chulspro.kim@xxxxxxxxxxx> > --- Hey Eunchul, Through some unfortunate duplication of work, I've also been working on HDCP for exynos drm. I uploaded my patch a couple of days ago https://gerrit.chromium.org/gerrit/#/c/39960. A few high level comments on your patch: - Why implement a new driver? You're using the irq and registers from the hdmi IP block, seems to make more sense to do it right in the hdmi driver. There's a fair bit of code gymnastics that you're doing to make hdcp visible to hdmi that just wouldn't be necessary if it was all included. - I'm not sure what your interface to userspace is. I think it makes most sense to implement this as a connector property. Here's a patch I uploaded a couple weeks ago https://gerrit.chromium.org/gerrit/#/c/38845/ A couple more comments inline. > drivers/gpu/drm/exynos/Kconfig | 6 + > drivers/gpu/drm/exynos/Makefile | 1 + > drivers/gpu/drm/exynos/exynos_drm_drv.c | 12 + > drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 + > drivers/gpu/drm/exynos/exynos_hdcp.c | 1164 +++++++++++++++++++++++++++++++ > drivers/gpu/drm/exynos/exynos_hdcp.h | 47 ++ > drivers/gpu/drm/exynos/exynos_hdmi.c | 11 + > drivers/gpu/drm/exynos/exynos_hdmi.h | 7 + > drivers/gpu/drm/exynos/regs-hdmi.h | 177 +++++ > 9 files changed, 1426 insertions(+), 0 deletions(-) > create mode 100644 drivers/gpu/drm/exynos/exynos_hdcp.c > create mode 100644 drivers/gpu/drm/exynos/exynos_hdcp.h > > diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig > index 1d1f1e5..93c2f00 100644 > --- a/drivers/gpu/drm/exynos/Kconfig > +++ b/drivers/gpu/drm/exynos/Kconfig > @@ -34,6 +34,12 @@ config DRM_EXYNOS_HDMI > help > Choose this option if you want to use Exynos HDMI for DRM. > > +config DRM_EXYNOS_HDCP > + bool "Exynos DRM HDCP" > + depends on DRM_EXYNOS_HDMI > + help > + Choose this option if you want to use Exynos HDCP in HDMI for DRM. > + > config DRM_EXYNOS_VIDI > bool "Exynos DRM Virtual Display" > depends on DRM_EXYNOS > diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile > index 639b49e..58d8fb7 100644 > --- a/drivers/gpu/drm/exynos/Makefile > +++ b/drivers/gpu/drm/exynos/Makefile > @@ -14,6 +14,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o > exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ > exynos_ddc.o exynos_hdmiphy.o \ > exynos_drm_hdmi.o > +exynosdrm-$(CONFIG_DRM_EXYNOS_HDCP) += exynos_hdcp.o > exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o > exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o > exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o > diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c > index e0a8e80..0d2ada1 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c > @@ -345,6 +345,11 @@ static int __init exynos_drm_init(void) > #endif > > #ifdef CONFIG_DRM_EXYNOS_HDMI > +#ifdef CONFIG_DRM_EXYNOS_HDCP > + ret = platform_driver_register(&hdcp_driver); > + if (ret < 0) > + goto out_hdcp; > +#endif > ret = platform_driver_register(&hdmi_driver); > if (ret < 0) > goto out_hdmi; > @@ -452,6 +457,10 @@ out_common_hdmi: > out_mixer: > platform_driver_unregister(&hdmi_driver); > out_hdmi: > +#ifdef CONFIG_DRM_EXYNOS_HDCP > + platform_driver_unregister(&hdcp_driver); > +out_hdcp: > +#endif > #endif > > #ifdef CONFIG_DRM_EXYNOS_FIMD > @@ -494,6 +503,9 @@ static void __exit exynos_drm_exit(void) > platform_driver_unregister(&exynos_drm_common_hdmi_driver); > platform_driver_unregister(&mixer_driver); > platform_driver_unregister(&hdmi_driver); > +#ifdef CONFIG_DRM_EXYNOS_HDCP > + platform_driver_unregister(&hdcp_driver); > +#endif > #endif > > #ifdef CONFIG_DRM_EXYNOS_VIDI > diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h > index f5a9774..c591ffc 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h > +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h > @@ -344,6 +344,7 @@ extern int exynos_platform_device_hdmi_register(void); > void exynos_platform_device_hdmi_unregister(void); > > extern struct platform_driver fimd_driver; > +extern struct platform_driver hdcp_driver; > extern struct platform_driver hdmi_driver; > extern struct platform_driver mixer_driver; > extern struct platform_driver exynos_drm_common_hdmi_driver; > diff --git a/drivers/gpu/drm/exynos/exynos_hdcp.c b/drivers/gpu/drm/exynos/exynos_hdcp.c > new file mode 100644 > index 0000000..58a345c > --- /dev/null > +++ b/drivers/gpu/drm/exynos/exynos_hdcp.c > @@ -0,0 +1,1164 @@ > +/* > + * Copyright (C) 2012 Samsung Electronics Co.Ltd > + * Authors: > + * Eunchul Kim <chulspro.kim@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > + > +#include <drm/drmP.h> > +#include <drm/exynos_drm.h> > +#include "exynos_drm_drv.h" > +#include "exynos_drm_hdmi.h" > +#include "exynos_hdmi.h" > +#include "exynos_hdcp.h" > +#include "regs-hdmi.h" > + > +/* > + * HDCP stands for High-bandwidth Digital Content Protection. > + * contains an integrated HDCP encryption engine > + * for video/audio content protection. > + * supports version HDCP v1.1. > + * Exynos supports embedded HDCP key system. > + * The HDCP key value is fused during fabrication, based on customer's request. > + * > + * First part authentication protocol. > + * The HDCP transmitter(Device A) can initiate authentication at any time. > + * Authentication is initiated by the HDCP transmitter by sending an An, Aksv. > + * An(64-bit psedo-random value), Aksv(Transmitter Key Selection Vector). > + * The HDCP Receiver(Device B) generates Rj. > + * The HDCP Receiver responds by sending a response message Bksv. > + * Bksv(Receiver Key Selection Vector). > + * If authentication was successfull, then Ri will be equal to Rj. > + * > + * Second part authentication protocol. > + * The second part of the authentication protocol is required > + * if the HDCP receiver is an HDCP repeater. > + * The HDCP transmitter executes the second part of the protocol only > + * when the REPEATER bit is set, > + * indicating that the attached HDCP receiver is an HDCP repeater. > + * This part of the protocol assembles a list of all downstream > + * KSVs attached to the HDCP repeater through a permitted connection tree, > + * enabling revocation support upstream. > + * > + * Third part authentication protocol. > + * The third part of the authentication protocol occurs during > + * the vertical blanking interval preceding the frame for which it applies. > + * Each of the two HDCP devices calculates new cipher initialization values, > + * Ki and Mi, and a third value Ri. and asynchronous polling every 2 seconds. > + * > + * Exynos scenario. > + * 1. start encryption. > + * 2. receive Bcaps, Bksv from peer device. > + * 3. check repeater caps from Bcaps. > + * 4. send An, Aksv to peer device. > + * 5. receive Rj from peer device. > + * 6. compare Ri, Rj. If same and not repeater, then start encryption. > + * 7. If not same, retry. If repeater, then start second authentication. > + * 8. stop encryption. > + */ > + > +/* > + * TODO > + * - need to fix compare timing. > + * - need to fix dpms start timing. > + */ > + > +#define HDCP_AN_SIZE 8 > +#define HDCP_AKSV_SIZE 5 > +#define HDCP_BKSV_SIZE 5 > +#define HDCP_MAX_KEY_SIZE 16 > +#define HDCP_BCAPS_SIZE 1 > +#define HDCP_BSTATUS_SIZE 2 > +#define HDCP_SHA_1_HASH_SIZE 20 > +#define HDCP_MAX_DEVS 128 > +#define HDCP_KSV_SIZE 5 > + > +#define HDCP_KSV_FIFO_READY (0x1 << 5) > +#define HDCP_MAX_CASCADE_EXCEEDED (0x1 << 3) > +#define HDCP_MAX_DEVS_EXCEEDED (0x1 << 7) > + > +/* offset of HDCP port */ > +#define HDCP_BKSV 0x00 > +#define HDCP_RI 0x08 > +#define HDCP_AKSV 0x10 > +#define HDCP_AN 0x18 > +#define HDCP_SHA1 0x20 > +#define HDCP_BCAPS 0x40 > +#define HDCP_BSTATUS 0x41 > +#define HDCP_KSVFIFO 0x43 > + > +#define HDCP_RI_LEN 2 > +#define HDCP_RJ_LEN 2 > +#define HDCP_DDC_DELAY 25 > +#define HDCP_AKSV_DELAY 100 > +#define HDCP_BKSV_DELAY 100 > +#define HDCP_BCAPS_DELAY 100 > +#define HDCP_LOADKEY_DELAY 120 > +#define HDCP_RESET_DELAY 16 > +#define HDCP_I2C_RETRIES 5 > +#define HDCP_LOADKEY_RETRIES 1000 > +#define HDCP_BKSV_RETRIES 14 > +#define HDCP_REPEATER_RETRIES 50 > +#define HDCP_REPEATER_KSV_RETRIES 10000 > +#define HDCP_ENCRYPTION_RETRIES 10 > + > +enum hdcp_error { > + HDCP_ERR_MAX_CASCADE, > + HDCP_ERR_MAX_DEVS, > + HDCP_ERR_REPEATER_ILLEGAL_DEVICE, > + HDCP_ERR_REPEATER_TIMEOUT, > +}; > + > +enum hdcp_event { > + HDCP_EVENT_STOP = 1 << 0, > + HDCP_EVENT_START = 1 << 1, > + HDCP_EVENT_READ_BKSV_START = 1 << 2, > + HDCP_EVENT_WRITE_AKSV_START = 1 << 4, > + HDCP_EVENT_CHECK_RI_START = 1 << 8, > + HDCP_EVENT_SECOND_AUTH_START = 1 << 16, > +}; > + > +/* > + * A structure of event work information. > + * > + * @work: work structure. > + * @event: event id of hdcp. > + */ > +struct hdcp_event_work { > + struct work_struct work; > + u32 event; > +}; > + > +/* > + * A structure of context. > + * > + * @regs: memory mapped io registers. > + * @ddc_port: hdmi ddc port. > + * @event_work: work information of hdcp event. > + * @wq: work queue struct. > + * @is_repeater: true is repeater, false is sink. > + * @hpd: HPD config value. > + * @hdcp_mutex: mutex for HDCP. > + * @powered : HDCP power state. > + */ > +struct hdcp_context { > + void __iomem *regs; > + struct i2c_client *ddc_port; > + struct hdcp_event_work event_work; > + struct workqueue_struct *wq; > + bool is_repeater; > + atomic_t *hpd; > + struct mutex hdcp_mutex; > + bool powered; > +}; > + > +static struct i2c_client *hdcp_ddc; > + > +static inline u8 hdcp_is_streaming(struct hdcp_context *ctx) > +{ > + u8 hpd = atomic_read(ctx->hpd); > + > + DRM_DEBUG_KMS("%s:hpd[%d]\n", __func__, hpd); > + > + return hpd; > +} > + > +static int hdcp_i2c_recv(struct hdcp_context *ctx, u8 offset, u8 *buf, int len) > +{ > + struct i2c_client *client = ctx->ddc_port; > + int ret, retries = HDCP_I2C_RETRIES; > + > + struct i2c_msg msgs[] = { > + [0] = { > + .addr = client->addr, > + .flags = 0, > + .len = 1, > + .buf = &offset > + }, > + [1] = { > + .addr = client->addr, > + .flags = I2C_M_RD, > + .len = len, > + .buf = buf > + } > + }; > + > + DRM_DEBUG_KMS("%s:offset[0x%x]len[0x%x]\n", __func__, offset, len); > + > + /* > + * The core i2c driver will automatically retry the transfer if the > + * adapter reports EAGAIN. However, we find that bit-banging transfers > + * are susceptible to errors under a heavily loaded machine and > + * generate spurious NAKs and timeouts. Retrying the transfer > + * of the individual block a few times seems to overcome this. > + */ > + do { > + if (!hdcp_is_streaming(ctx)) > + return 0; > + > + ret = i2c_transfer(client->adapter, msgs, 2); > + if (ret == -ENXIO) > + goto err_i2c_recv; > + > + if (ret < 0 || ret != 2) > + DRM_ERROR("failed to recv %d retry.\n", ret); > + else > + break; > + > + msleep(HDCP_DDC_DELAY); > + } while (ret != 2 && --retries); > + > + if (!retries) > + goto err_i2c_recv; > + > + DRM_DEBUG_KMS("%s:success to recv HDCP via I2C.\n", __func__); > + > + return 0; > + > +err_i2c_recv: > + DRM_ERROR("failed to recv HDCP via I2C.\n"); > + return ret; > +} > + > +static int hdcp_i2c_send(struct hdcp_context *ctx, u8 offset, u8 *buf, int len) > +{ > + struct i2c_client *client = ctx->ddc_port; > + int ret, retries = HDCP_I2C_RETRIES; > + u8 msg[len+1]; > + > + DRM_DEBUG_KMS("%s:offset[0x%x]len[0x%x]\n", __func__, offset, len); > + > + msg[0] = offset; > + memcpy(&msg[1], buf, len); > + > + /* > + * The core i2c driver will automatically retry the transfer if the > + * adapter reports EAGAIN. However, we find that bit-banging transfers > + * are susceptible to errors under a heavily loaded machine and > + * generate spurious NAKs and timeouts. Retrying the transfer > + * of the individual block a few times seems to overcome this. > + */ > + do { > + if (!hdcp_is_streaming(ctx)) > + return 0; > + > + ret = i2c_master_send(client, msg, len+1); > + if (ret == -ENXIO) > + goto err_i2c_send; > + > + if (ret < 0 || ret < len + 1) > + DRM_ERROR("failed to send %d retry.\n", ret); > + else > + break; > + > + msleep(HDCP_DDC_DELAY); > + } while (ret != 2 && --retries); > + > + if (!retries) > + goto err_i2c_send; > + > + DRM_DEBUG_KMS("%s:success to send HDCP via I2C.\n", __func__); > + > + return 0; > + > +err_i2c_send: > + DRM_ERROR("failed to send HDCP via I2C.\n"); > + return ret; > +} > + > +static inline u8 hdcp_reg_readb(struct hdcp_context *ctx, u32 reg_id) > +{ > + return readb(ctx->regs + reg_id); > +} > + > +static inline void hdcp_reg_readb_bytes(struct hdcp_context *ctx, u32 reg_id, > + u8 *buf, int bytes) > +{ > + int i; > + > + for (i = 0; i < bytes; ++i) > + buf[i] = readb(ctx->regs + reg_id + i * 4); > +} > + > +static inline void hdcp_reg_writeb(struct hdcp_context *ctx, u32 reg_id, > + u8 value) > +{ > + writeb(value, ctx->regs + reg_id); > +} > + > +static inline void hdcp_reg_writeb_bytes(struct hdcp_context *ctx, > + u32 reg_id, u8 *buf, u32 size) > +{ > + int i; > + > + for (i = 0; i < size; ++i) > + writeb(buf[i], ctx->regs + reg_id + i * 4); > +} > + > +static inline void hdcp_reg_writeb_mask(struct hdcp_context *ctx, > + u32 reg_id, u8 value, u8 mask) > +{ > + u32 old = readb(ctx->regs + reg_id); > + value = (value & mask) | (old & ~mask); > + writeb(value, ctx->regs + reg_id); > +} > + > +static void hdcp_set_int_mask(struct hdcp_context *ctx, u8 mask, bool enable) > +{ > + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); > + > + if (enable) { > + mask |= HDMI_INTC_EN_GLOBAL; > + hdcp_reg_writeb_mask(ctx, HDMI_INTC_CON, ~0, mask); > + } else > + hdcp_reg_writeb_mask(ctx, HDMI_INTC_CON, 0, > + HDMI_INTC_EN_GLOBAL); > +} > + > +static void hdcp_sw_reset(struct hdcp_context *ctx) > +{ > + u8 val; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + val = hdcp_reg_readb(ctx, HDMI_INTC_CON); > + > + hdcp_set_int_mask(ctx, HDMI_INTC_EN_HPD_PLUG, 0); > + hdcp_set_int_mask(ctx, HDMI_INTC_EN_HPD_UNPLUG, 0); > + > + hdcp_reg_writeb_mask(ctx, HDMI_HPD, ~0, HDMI_HPD_SEL_I_HPD); > + hdcp_reg_writeb_mask(ctx, HDMI_HPD, 0, HDMI_SW_HPD_PLUGGED); > + hdcp_reg_writeb_mask(ctx, HDMI_HPD, ~0, HDMI_SW_HPD_PLUGGED); > + hdcp_reg_writeb_mask(ctx, HDMI_HPD, 0, HDMI_HPD_SEL_I_HPD); > + > + if (val & HDMI_INTC_EN_HPD_PLUG) > + hdcp_set_int_mask(ctx, HDMI_INTC_EN_HPD_PLUG, 1); > + > + if (val & HDMI_INTC_EN_HPD_UNPLUG) > + hdcp_set_int_mask(ctx, HDMI_INTC_EN_HPD_UNPLUG, 1); > +} > + > +static void hdcp_encryption(struct hdcp_context *ctx, bool enable) > +{ > + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); > + > + /* hdcp encoder control */ > + if (enable) > + hdcp_reg_writeb_mask(ctx, HDMI_ENC_EN, ~0, > + HDMI_HDCP_ENC_ENABLE); > + else > + hdcp_reg_writeb_mask(ctx, HDMI_ENC_EN, 0, > + HDMI_HDCP_ENC_ENABLE); > +} > + > +static int hdcp_loadkey(struct hdcp_context *ctx) > +{ > + u8 val; > + int retries = HDCP_LOADKEY_RETRIES; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + hdcp_reg_writeb_mask(ctx, HDMI_EFUSE_CTRL, ~0, > + HDMI_EFUSE_CTRL_HDCP_KEY_READ); > + > + do { > + val = hdcp_reg_readb(ctx, HDMI_EFUSE_STATUS); > + if (val & HDMI_EFUSE_ECC_DONE) > + break; > + mdelay(1); > + } while (--retries); > + > + if (!retries) > + goto hdcp_err; > + > + val = hdcp_reg_readb(ctx, HDMI_EFUSE_STATUS); > + > + if (val & HDMI_EFUSE_ECC_FAIL) > + goto hdcp_err; > + > + DRM_DEBUG_KMS("%s:hdcp key read success.\n", __func__); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to read EFUSE val.\n"); > + return -EINVAL; > +} > + > +static void hdcp_poweron(struct hdcp_context *ctx) > +{ > + DRM_DEBUG_KMS("%s\n", __func__); > + > + mutex_lock(&ctx->hdcp_mutex); > + if (ctx->powered) > + goto out; > + > + hdcp_sw_reset(ctx); > + hdcp_encryption(ctx, false); > + > + msleep(HDCP_LOADKEY_DELAY); > + if (hdcp_loadkey(ctx) < 0) { > + DRM_ERROR("failed to load hdcp key.\n"); > + goto out; > + } > + > + hdcp_reg_writeb(ctx, HDMI_GCP_CON, HDMI_GCP_CON_NO_TRAN); > + hdcp_reg_writeb(ctx, HDMI_STATUS_EN, HDMI_INT_EN_ALL); > + hdcp_reg_writeb(ctx, HDMI_HDCP_CTRL1, HDMI_HDCP_CP_DESIRED_EN); > + > + hdcp_set_int_mask(ctx, HDMI_INTC_EN_HDCP, 1); > + > + ctx->powered = true; > + > + DRM_DEBUG_KMS("%s:start encription.\n", __func__); > + > +out: > + mutex_unlock(&ctx->hdcp_mutex); > +} > + > +static void hdcp_poweroff(struct hdcp_context *ctx) > +{ > + u8 val; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + mutex_lock(&ctx->hdcp_mutex); > + if (!ctx->powered) > + goto out; > + > + ctx->powered = false; > + > + hdcp_set_int_mask(ctx, HDMI_INTC_EN_HDCP, 0); > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_CTRL1, 0x0); > + hdcp_reg_writeb_mask(ctx, HDMI_HPD, 0, HDMI_HPD_SEL_I_HPD); > + > + val = HDMI_UPDATE_RI_INT_EN | HDMI_WRITE_INT_EN | > + HDMI_WATCHDOG_INT_EN | HDMI_WTFORACTIVERX_INT_EN; > + hdcp_reg_writeb_mask(ctx, HDMI_STATUS_EN, 0, val); > + hdcp_reg_writeb_mask(ctx, HDMI_STATUS_EN, ~0, val); > + > + hdcp_reg_writeb_mask(ctx, HDMI_SYS_STATUS, ~0, HDMI_INT_EN_ALL); > + > + hdcp_encryption(ctx, false); > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_CHECK_RESULT, HDMI_HDCP_CLR_ALL_RESULTS); > + > + ctx->regs = NULL; > + > + DRM_DEBUG_KMS("%s:stop encription.\n", __func__); > + > +out: > + mutex_unlock(&ctx->hdcp_mutex); > +} > + > +static int hdcp_recv_bcaps(struct hdcp_context *ctx) > +{ > + u8 bcaps = 0; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!hdcp_is_streaming(ctx)) > + goto hdcp_err; > + > + if (hdcp_i2c_recv(ctx, HDCP_BCAPS, &bcaps, > + HDCP_BCAPS_SIZE) < 0) > + goto hdcp_err; > + > + DRM_DEBUG_KMS("%s:Bcaps[0x%x]\n", __func__, bcaps); > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_BCAPS, bcaps); > + > + if (bcaps & HDMI_HDCP_BCAPS_REPEATER) > + ctx->is_repeater = true; > + else > + ctx->is_repeater = false; > + > + DRM_DEBUG_KMS("%s:is_repeater[%s]\n", __func__, > + ctx->is_repeater ? "REPEAT" : "SINK"); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to recv bcaps.\n"); > + return -EIO; > +} > + > +static int hdcp_recv_bksv(struct hdcp_context *ctx) > +{ > + u8 bksv[HDCP_BKSV_SIZE]; > + int i, j; > + u32 one = 0, zero = 0, result = 0; > + int retries = HDCP_BKSV_RETRIES; > + > + if (!hdcp_is_streaming(ctx)) > + goto hdcp_err; > + > + memset(bksv, 0x0, sizeof(bksv)); > + > + do { > + if (hdcp_i2c_recv(ctx, HDCP_BKSV, bksv, > + HDCP_BKSV_SIZE) < 0) > + goto hdcp_err; > + > + for (i = 0; i < HDCP_BKSV_SIZE; i++) > + DRM_DEBUG_KMS("%s:Bksv[%d][0x%x]\n", > + __func__, i, bksv[i]); > + > + for (i = 0; i < HDCP_BKSV_SIZE; i++) { > + for (j = 0; j < 8; j++) { > + result = bksv[i] & (0x1 << j); > + > + if (result == 0) > + zero++; > + else > + one++; > + } > + } > + > + if ((zero == 20) && (one == 20)) { > + DRM_DEBUG_KMS("%s:success.\n", __func__); > + > + hdcp_reg_writeb_bytes(ctx, HDMI_HDCP_BKSV(0), bksv, > + HDCP_BKSV_SIZE); > + break; > + } > + > + DRM_ERROR("invalid bksv retries[%d]\n", retries); > + msleep(HDCP_BKSV_DELAY); > + } while (--retries); > + > + if (!retries) > + goto hdcp_err; > + > + DRM_DEBUG_KMS("%s:retries[%d]\n", __func__, retries); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to recv bksv.\n"); > + return -EIO; > +} > + > +static int hdcp_recv_b_caps_ksv(struct hdcp_context *ctx) > +{ > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!hdcp_is_streaming(ctx)) > + goto hdcp_err; > + > + if (hdcp_recv_bcaps(ctx) < 0) > + goto hdcp_err; > + > + if (hdcp_recv_bksv(ctx) < 0) > + goto hdcp_err; > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to check bksv.\n"); > + return -EIO; > +} > + > +static int hdcp_check_repeater(struct hdcp_context *ctx) > +{ > + int ret = -EINVAL, val, i; > + u32 dev_cnt; > + u8 bcaps = 0; > + u8 status[HDCP_BSTATUS_SIZE]; > + u8 rx_v[HDCP_SHA_1_HASH_SIZE]; > + u8 ksv_list[HDCP_MAX_DEVS * HDCP_KSV_SIZE]; > + int cnt; > + int retries1 = HDCP_REPEATER_RETRIES; > + int retries2; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + memset(status, 0x0, sizeof(status)); > + memset(rx_v, 0x0, sizeof(rx_v)); > + memset(ksv_list, 0x0, sizeof(ksv_list)); > + > + do { > + if (hdcp_recv_bcaps(ctx) < 0) > + goto hdcp_err; > + > + bcaps = hdcp_reg_readb(ctx, HDMI_HDCP_BCAPS); > + if (bcaps & HDCP_KSV_FIFO_READY) { > + DRM_DEBUG_KMS("%s:ksv fifo not ready.\n", __func__); > + break; > + } > + > + msleep(HDCP_BCAPS_DELAY); > + } while (--retries1); > + > + if (!retries1) { > + ret = HDCP_ERR_REPEATER_TIMEOUT; > + goto hdcp_err; > + } > + > + DRM_DEBUG_KMS("%s:ksv fifo ready.\n", __func__); > + > + if (hdcp_i2c_recv(ctx, HDCP_BSTATUS, status, > + HDCP_BSTATUS_SIZE) < 0) > + goto hdcp_err; > + > + if (status[1] & HDCP_MAX_CASCADE_EXCEEDED) { > + ret = HDCP_ERR_MAX_CASCADE; > + goto hdcp_err; > + } else if (status[0] & HDCP_MAX_DEVS_EXCEEDED) { > + ret = HDCP_ERR_MAX_DEVS; > + goto hdcp_err; > + } > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_BSTATUS_0, status[0]); > + hdcp_reg_writeb(ctx, HDMI_HDCP_BSTATUS_1, status[1]); > + > + DRM_DEBUG_KMS("%s:status0[0x%x],status1[0x%x]\n", > + __func__, status[0], status[1]); > + > + dev_cnt = status[0] & 0x7f; > + DRM_DEBUG_KMS("%s:dev_cnt[%d]\n", __func__, dev_cnt); > + > + if (dev_cnt) { > + if (hdcp_i2c_recv(ctx, HDCP_KSVFIFO, ksv_list, > + dev_cnt * HDCP_KSV_SIZE) < 0) > + goto hdcp_err; > + > + cnt = 0; > + do { > + hdcp_reg_writeb_bytes(ctx, HDMI_HDCP_KSV_LIST(0), > + &ksv_list[cnt * 5], HDCP_KSV_SIZE); > + > + val = HDMI_HDCP_KSV_WRITE_DONE; > + if (cnt == dev_cnt - 1) > + val |= HDMI_HDCP_KSV_END; > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_KSV_LIST_CON, val); > + > + if (cnt < dev_cnt - 1) { > + retries2 = HDCP_REPEATER_KSV_RETRIES; > + do { > + val = hdcp_reg_readb(ctx, > + HDMI_HDCP_KSV_LIST_CON); > + if (val & HDMI_HDCP_KSV_READ) > + break; > + } while (--retries2); > + > + if (!retries2) > + DRM_DEBUG_KMS("%s:ksv not readed.\n", > + __func__); > + } > + cnt++; > + } while (cnt < dev_cnt); > + } else > + hdcp_reg_writeb(ctx, HDMI_HDCP_KSV_LIST_CON, > + HDMI_HDCP_KSV_LIST_EMPTY); > + > + if (hdcp_i2c_recv(ctx, HDCP_SHA1, rx_v, > + HDCP_SHA_1_HASH_SIZE) < 0) > + goto hdcp_err; > + > + for (i = 0; i < HDCP_SHA_1_HASH_SIZE; i++) > + DRM_DEBUG_KMS("%s:SHA-1 rx[0x%x]\n", __func__, rx_v[i]); > + > + hdcp_reg_writeb_bytes(ctx, HDMI_HDCP_SHA1(0), rx_v, > + HDCP_SHA_1_HASH_SIZE); > + > + val = hdcp_reg_readb(ctx, HDMI_HDCP_SHA_RESULT); > + if (val & HDMI_HDCP_SHA_VALID_RD) { > + if (val & HDMI_HDCP_SHA_VALID) { > + DRM_DEBUG_KMS("%s:SHA-1 result is ok.\n", __func__); > + hdcp_reg_writeb(ctx, HDMI_HDCP_SHA_RESULT, 0x0); > + } else { > + DRM_DEBUG_KMS("%s:SHA-1 result is not vaild.\n", > + __func__); > + hdcp_reg_writeb(ctx, HDMI_HDCP_SHA_RESULT, 0x0); > + goto hdcp_err; > + } > + } else { > + DRM_DEBUG_KMS("%s:SHA-1 result is not ready.\n", __func__); > + hdcp_reg_writeb(ctx, HDMI_HDCP_SHA_RESULT, 0x0); > + goto hdcp_err; > + } > + > + DRM_DEBUG_KMS("%s:done.\n", __func__); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to check repeater.\n"); > + return ret; > +} > + > +static int hdcp_start_encryption(struct hdcp_context *ctx) > +{ > + u8 val; > + int retries = HDCP_ENCRYPTION_RETRIES; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + do { > + val = hdcp_reg_readb(ctx, HDMI_SYS_STATUS); > + > + if (val & HDMI_AUTHEN_ACK_AUTH) { > + hdcp_encryption(ctx, true); > + break; > + } > + > + mdelay(1); > + } while (--retries); > + > + if (!retries) > + goto hdcp_err; > + > + DRM_DEBUG_KMS("%s:retries[%d]\n", __func__, retries); > + > + return 0; > + > +hdcp_err: > + hdcp_encryption(ctx, false); > + DRM_ERROR("failed to start encription.\n"); > + return -EIO; > +} > + > +static int hdcp_start_second_auth(struct hdcp_context *ctx) > +{ > + int ret = 0; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!hdcp_is_streaming(ctx)) > + goto hdcp_err; > + > + ret = hdcp_check_repeater(ctx); > + > + if (ret) { > + DRM_DEBUG_KMS("%s:ret[%d]\n", __func__, ret); > + > + switch (ret) { > + case HDCP_ERR_REPEATER_ILLEGAL_DEVICE: > + hdcp_reg_writeb(ctx, HDMI_HDCP_CTRL2, 0x1); > + mdelay(1); > + hdcp_reg_writeb(ctx, HDMI_HDCP_CTRL2, 0x0); > + > + DRM_DEBUG_KMS("%s:illegal device.\n", __func__); > + break; > + case HDCP_ERR_REPEATER_TIMEOUT: > + hdcp_reg_writeb_mask(ctx, HDMI_HDCP_CTRL1, ~0, > + HDMI_HDCP_SET_REPEATER_TIMEOUT); > + hdcp_reg_writeb_mask(ctx, HDMI_HDCP_CTRL1, 0, > + HDMI_HDCP_SET_REPEATER_TIMEOUT); > + > + DRM_DEBUG_KMS("%s:timeout.\n", __func__); > + break; > + case HDCP_ERR_MAX_CASCADE: > + DRM_DEBUG_KMS("%s:exceeded MAX_CASCADE.\n", __func__); > + break; > + case HDCP_ERR_MAX_DEVS: > + DRM_DEBUG_KMS("%s:exceeded MAX_DEVS.\n", __func__); > + break; > + default: > + break; > + } > + > + goto hdcp_err; > + } > + > + hdcp_start_encryption(ctx); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to check second authentication.\n"); > + return -EIO; > +} > + > +static int hdcp_send_key(struct hdcp_context *ctx, int size, > + int reg, int offset) > +{ > + u8 buf[HDCP_MAX_KEY_SIZE]; > + int cnt, zero = 0; > + int i; > + > + DRM_DEBUG_KMS("%s:size[%d]reg[0x%x]offset[0x%x]\n", __func__, > + size, reg, offset); > + > + memset(buf, 0x0, sizeof(buf)); > + hdcp_reg_readb_bytes(ctx, reg, buf, size); > + > + for (cnt = 0; cnt < size; cnt++) { > + DRM_DEBUG_KMS("%s:%s:cnt[%d]buf[0x%x]\n", __func__, > + offset == HDCP_AN ? "An" : "Aksv", cnt, buf[cnt]); > + if (buf[cnt] == 0) > + zero++; > + } > + > + if (zero == size) { > + DRM_ERROR("%s: %s is null.\n", __func__, > + offset == HDCP_AN ? "An" : "Aksv"); > + goto hdcp_err; > + } > + > + if (hdcp_i2c_send(ctx, offset, buf, size) < 0) > + goto hdcp_err; > + > + for (i = 1; i < size + 1; i++) > + DRM_DEBUG_KMS("%s: %s %d[0x%x].\n", __func__, > + offset == HDCP_AN ? "An" : "Aksv", i-1, buf[i-1]); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to write %s key.\n", > + offset == HDCP_AN ? "An" : "Aksv"); > + return -EIO; > +} > + > +static int hdcp_send_aksv(struct hdcp_context *ctx) > +{ > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!hdcp_is_streaming(ctx)) > + goto hdcp_err; > + > + if (hdcp_send_key(ctx, HDCP_AN_SIZE, HDMI_HDCP_AN(0), HDCP_AN) < 0) { > + DRM_ERROR("failed to write an.\n"); > + goto hdcp_err; > + } > + > + DRM_DEBUG_KMS("%s:write an is done.\n", __func__); > + > + if (hdcp_send_key(ctx, HDCP_AKSV_SIZE, HDMI_HDCP_AKSV(0), > + HDCP_AKSV) < 0) { > + DRM_ERROR("failed to send aksv.\n"); > + goto hdcp_err; > + } > + > + msleep(HDCP_AKSV_DELAY); > + > + DRM_DEBUG_KMS("%s:write aksv is done.\n", __func__); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to write aksv.\n"); > + return -EIO; > +} > + > +static int hdcp_check_ri_rj(struct hdcp_context *ctx) > +{ > + u8 ri[HDCP_RI_LEN] = {0, 0}; > + u8 rj[HDCP_RJ_LEN] = {0, 0}; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!hdcp_is_streaming(ctx)) > + goto hdcp_err; > + > + ri[0] = hdcp_reg_readb(ctx, HDMI_HDCP_RI_0); > + ri[1] = hdcp_reg_readb(ctx, HDMI_HDCP_RI_1); > + > + if (hdcp_i2c_recv(ctx, HDCP_RI, rj, HDCP_RJ_LEN) < 0) { > + DRM_ERROR("failed to receive rj.\n"); > + goto hdcp_err; > + } > + > + DRM_DEBUG_KMS("%s:ri[0x%x,0x%x]\n", __func__, ri[0] , ri[1]); > + DRM_DEBUG_KMS("%s:rj[0x%x,0x%x]\n", __func__, rj[0] , rj[1]); > + > + if ((ri[0] == rj[0]) && (ri[1] == rj[1]) && (ri[0] | ri[1])) > + hdcp_reg_writeb(ctx, HDMI_HDCP_CHECK_RESULT, > + HDMI_HDCP_RI_MATCH_RESULT_Y); > + else { > + hdcp_reg_writeb(ctx, HDMI_HDCP_CHECK_RESULT, > + HDMI_HDCP_RI_MATCH_RESULT_N); > + > + DRM_DEBUG_KMS("%s:failed to compare ri with rj.\n", __func__); > + return 0; > + } > + > + if (!ctx->is_repeater) > + hdcp_start_encryption(ctx); > + > + DRM_DEBUG_KMS("%s:done.\n", __func__); > + > + return 0; > + > +hdcp_err: > + DRM_ERROR("failed to check ri, rj.\n"); > + return -EIO; > +} > + > +static void hdcp_reset_auth(struct hdcp_context *ctx) > +{ > + u8 val; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!hdcp_is_streaming(ctx)) > + return; > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_CTRL1, 0x0); > + hdcp_reg_writeb(ctx, HDMI_HDCP_CTRL2, 0x0); > + > + hdcp_encryption(ctx, false); > + > + val = HDMI_UPDATE_RI_INT_EN | HDMI_WRITE_INT_EN | > + HDMI_WATCHDOG_INT_EN | HDMI_WTFORACTIVERX_INT_EN; > + hdcp_reg_writeb_mask(ctx, HDMI_STATUS_EN, 0, val); > + > + hdcp_reg_writeb(ctx, HDMI_HDCP_CHECK_RESULT, HDMI_HDCP_CLR_ALL_RESULTS); > + > + /* need some delay (at least 1 frame) */ > + mdelay(HDCP_RESET_DELAY); > + > + hdcp_sw_reset(ctx); > + > + val = HDMI_UPDATE_RI_INT_EN | HDMI_WRITE_INT_EN | > + HDMI_WATCHDOG_INT_EN | HDMI_WTFORACTIVERX_INT_EN; > + hdcp_reg_writeb_mask(ctx, HDMI_STATUS_EN, ~0, val); > + hdcp_reg_writeb_mask(ctx, HDMI_HDCP_CTRL1, ~0, HDMI_HDCP_CP_DESIRED_EN); > + hdcp_reg_writeb_mask(ctx, HDMI_INTC_CON, 0, HDMI_INTC_EN_HDCP); > + > + DRM_DEBUG_KMS("%s:done.\n", __func__); > +} > + > +static void hdcp_event_wq(struct work_struct *work) > +{ > + struct hdcp_context *ctx = container_of((struct hdcp_event_work *)work, > + struct hdcp_context, event_work); > + struct hdcp_event_work *event_work = (struct hdcp_event_work *)work; > + > + DRM_DEBUG_KMS("%s:event[0x%x]\n", __func__, event_work->event); > + > + if (!ctx->powered) > + return; > + > + if (!hdcp_is_streaming(ctx)) > + return; > + > + if (event_work->event & HDCP_EVENT_READ_BKSV_START) { > + if (hdcp_recv_b_caps_ksv(ctx) < 0) > + goto hdcp_err; > + else > + event_work->event &= ~HDCP_EVENT_READ_BKSV_START; > + } > + > + if (event_work->event & HDCP_EVENT_SECOND_AUTH_START) { > + if (hdcp_start_second_auth(ctx) < 0) > + goto hdcp_err; > + else > + event_work->event &= ~HDCP_EVENT_SECOND_AUTH_START; > + } > + > + if (event_work->event & HDCP_EVENT_WRITE_AKSV_START) { > + if (hdcp_send_aksv(ctx) < 0) > + goto hdcp_err; > + else > + event_work->event &= ~HDCP_EVENT_WRITE_AKSV_START; > + } > + > + if (event_work->event & HDCP_EVENT_CHECK_RI_START) { > + if (hdcp_check_ri_rj(ctx) < 0) > + goto hdcp_err; > + else > + event_work->event &= ~HDCP_EVENT_CHECK_RI_START; > + } > + > + return; > + > +hdcp_err: > + hdcp_reset_auth(ctx); > +} > + > +static void hdcp_dpms(void *data, int mode) > +{ > + struct hdcp_context *ctx = data; > + > + DRM_DEBUG_KMS("%s:mode[%d]\n", __func__, mode); > + > + switch (mode) { > + case DRM_MODE_DPMS_ON: > + hdcp_poweron(ctx); > + break; > + case DRM_MODE_DPMS_STANDBY: > + case DRM_MODE_DPMS_SUSPEND: > + case DRM_MODE_DPMS_OFF: > + hdcp_poweroff(ctx); > + break; > + default: > + DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); > + break; > + } > +} > + > +static void hdcp_commit(void *data) > +{ > + struct hdcp_context *ctx = data; > + u32 event = 0; > + u8 flag; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (!ctx->powered) > + return; > + > + if (!hdcp_is_streaming(ctx)) > + return; > + > + flag = hdcp_reg_readb(ctx, HDMI_SYS_STATUS); > + > + DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag); > + > + if (flag & HDMI_WTFORACTIVERX_INT_OCC) { > + event |= HDCP_EVENT_READ_BKSV_START; > + hdcp_reg_writeb_mask(ctx, HDMI_SYS_STATUS, ~0, > + HDMI_WTFORACTIVERX_INT_OCC); > + hdcp_reg_writeb(ctx, HDMI_HDCP_I2C_INT, 0x0); > + } > + > + if (flag & HDMI_WRITE_INT_OCC) { > + event |= HDCP_EVENT_WRITE_AKSV_START; > + hdcp_reg_writeb_mask(ctx, HDMI_SYS_STATUS, ~0, > + HDMI_WRITE_INT_OCC); > + hdcp_reg_writeb(ctx, HDMI_HDCP_AN_INT, 0x0); > + } > + > + if (flag & HDMI_UPDATE_RI_INT_OCC) { > + event |= HDCP_EVENT_CHECK_RI_START; > + hdcp_reg_writeb_mask(ctx, HDMI_SYS_STATUS, ~0, > + HDMI_UPDATE_RI_INT_OCC); > + hdcp_reg_writeb(ctx, HDMI_HDCP_RI_INT, 0x0); > + } > + > + if (flag & HDMI_WATCHDOG_INT_OCC) { > + event |= HDCP_EVENT_SECOND_AUTH_START; > + hdcp_reg_writeb_mask(ctx, HDMI_SYS_STATUS, ~0, > + HDMI_WATCHDOG_INT_OCC); > + hdcp_reg_writeb(ctx, HDMI_HDCP_WDT_INT, 0x0); > + } > + > + if (flag & HDMI_AUTHEN_ACK_AUTH) > + DRM_DEBUG_KMS("%s:authentication success.\n", __func__); > + > + if (!event) { > + DRM_DEBUG_KMS("%s:unknown irq\n", __func__); > + return; > + } > + > + ctx->event_work.event |= event; Event is just mirroring HDMI_SYS_STATUS, I don't think there's any need to define it and a whole set of events when we already have defined bitmasks (ex: HDMI_WRITE_INT_OCC vs. HDCP_EVENT_WRITE_AKSV_START) > + queue_work(ctx->wq, (struct work_struct *)&ctx->event_work); > +} > + > +static struct exynos_hdcp_ops hdmi_ops = { > + /* manager */ > + .dpms = hdcp_dpms, > + .commit = hdcp_commit, Where do you intend on calling this from within the hdmi driver? Ideally this would be checked after an HDCP interrupt, so will you call it from the HDMI interrupt handler? I also don't think "commit" is a particularly good name. Lastly, I'm not sure why you need an hdmi_ops structure for this, can't you just expose the definitions in your hdcp header? > +}; > + > +void exynos_hdcp_attach_ddc_client(struct i2c_client *ddc) > +{ > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (ddc) > + hdcp_ddc = ddc; > +} > + > +int exynos_hdcp_register(void *data, void __iomem *regs, atomic_t *hpd) > +{ > + struct hdcp_context *ctx = data; > + > + DRM_DEBUG_KMS("%s:regs[0x%x]\n", __func__, (int)regs); > + > + if (!hdcp_ddc) { > + DRM_ERROR("failed to get ddc port.\n"); > + return -ENODEV; > + } > + > + ctx->ddc_port = hdcp_ddc; > + ctx->hpd = hpd; > + ctx->regs = regs; > + This is pretty hacky, I think it would be much better to just do hdcp in the hdmi driver. Of course, I'm biased since that's what my implementation did :). > + return 0; > +} > + > +static int __devinit hdcp_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct exynos_drm_hdmi_context *drm_hdcp_ctx; > + struct hdcp_context *ctx; > + int ret = -EINVAL; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + drm_hdcp_ctx = devm_kzalloc(dev, sizeof(struct exynos_drm_hdmi_context), > + GFP_KERNEL); > + if (!drm_hdcp_ctx) { > + DRM_ERROR("failed to allocate common hdmi context.\n"); > + return -ENOMEM; > + } I'm not sure what you intend to do with this structure. > + > + ctx = devm_kzalloc(dev, sizeof(struct hdcp_context), GFP_KERNEL); > + if (!ctx) { > + DRM_ERROR("failed to get ctx memory.\n"); > + ret = -ENOMEM; > + goto err_free; > + } > + > + ctx->wq = create_workqueue("hdcp"); > + if (!ctx->wq) { > + ret = -ENOMEM; > + goto err_workqueue; > + } > + > + INIT_WORK((struct work_struct *)&ctx->event_work, hdcp_event_wq); Again, if you put this in hdmi driver, you could just use its workqueue and avoid spinning a new one up. > + drm_hdcp_ctx->ctx = (void *)ctx; > + > + mutex_init(&ctx->hdcp_mutex); > + > + platform_set_drvdata(pdev, drm_hdcp_ctx); > + > + exynos_hdcp_ops_register(&hdmi_ops); > + > + dev_info(dev, "drm hdcp registered successfully.\n"); > + > + return 0; > + > +err_workqueue: > + devm_kfree(dev, ctx); > +err_free: > + devm_kfree(dev, drm_hdcp_ctx); > + return ret; > +} > + > +static int __devexit hdcp_remove(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct exynos_drm_hdmi_context *drm_hdcp_ctx = > + platform_get_drvdata(pdev); > + struct hdcp_context *ctx = drm_hdcp_ctx->ctx; > + > + DRM_DEBUG_KMS("%s\n", __func__); > + > + devm_kfree(dev, ctx); > + devm_kfree(dev, drm_hdcp_ctx); > + > + return 0; > +} > + > +struct platform_driver hdcp_driver = { > + .probe = hdcp_probe, > + .remove = __devexit_p(hdcp_remove), > + .driver = { > + .name = "exynos-hdcp", > + .owner = THIS_MODULE, > + }, > +}; > diff --git a/drivers/gpu/drm/exynos/exynos_hdcp.h b/drivers/gpu/drm/exynos/exynos_hdcp.h > new file mode 100644 > index 0000000..86d0c79 > --- /dev/null > +++ b/drivers/gpu/drm/exynos/exynos_hdcp.h > @@ -0,0 +1,47 @@ > +/* > + * Copyright (c) 2012 Samsung Electronics Co., Ltd. > + * > + * Authors: > + * Eunchul Kim <chulspro.kim@xxxxxxxxxxx> > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > + * OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#ifndef _EXYNOS_HDCP_H_ > +#define _EXYNOS_HDCP_H_ > + > +#ifdef CONFIG_DRM_EXYNOS_HDCP > +extern void exynos_hdcp_attach_ddc_client(struct i2c_client *ddc); > +extern int exynos_hdcp_register(void *data, void __iomem *regs, atomic_t *hpd); > +#else > +static inline void exynos_hdcp_attach_ddc_client(struct i2c_client *ddc) > +{ > + > +} > + > +static inline int exynos_hdcp_register(void *data, void __iomem *regs, > + atomic_t *hpd) > +{ > + return -ENODEV; > +} > +#endif > + > +#endif /* _EXYNOS_HDCP_H_ */ > + > diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c > index 2c46b6c..f8dd504 100644 > --- a/drivers/gpu/drm/exynos/exynos_hdmi.c > +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c > @@ -42,6 +42,7 @@ > #include "exynos_drm_hdmi.h" > > #include "exynos_hdmi.h" > +#include "exynos_hdcp.h" > > #include <linux/gpio.h> > #include <media/s5p_hdmi.h> > @@ -1253,6 +1254,16 @@ static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix) > #undef DUMPREG > } > > +static struct exynos_hdcp_ops *hdcp_ops; > + > +void exynos_hdcp_ops_register(struct exynos_hdcp_ops *ops) > +{ > + DRM_DEBUG_KMS("%s\n", __func__); > + > + if (ops) > + hdcp_ops = ops; > +} > + > static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) > { > if (hdata->type == HDMI_TYPE13) > diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h > index 1c3b6d8..f4ae937 100644 > --- a/drivers/gpu/drm/exynos/exynos_hdmi.h > +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h > @@ -28,8 +28,15 @@ > #ifndef _EXYNOS_HDMI_H_ > #define _EXYNOS_HDMI_H_ > > +struct exynos_hdcp_ops { > + /* manager */ > + void (*dpms)(void *ctx, int mode); > + void (*commit)(void *ctx); > +}; > + > void hdmi_attach_ddc_client(struct i2c_client *ddc); > void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy); > +void exynos_hdcp_ops_register(struct exynos_hdcp_ops *ops); > > extern struct i2c_driver hdmiphy_driver; > extern struct i2c_driver ddc_driver; > diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h > index ef1b3eb..7b62c4c 100644 > --- a/drivers/gpu/drm/exynos/regs-hdmi.h > +++ b/drivers/gpu/drm/exynos/regs-hdmi.h > @@ -24,6 +24,7 @@ > #define HDMI_CORE_BASE(x) ((x) + 0x00010000) > #define HDMI_I2S_BASE(x) ((x) + 0x00040000) > #define HDMI_TG_BASE(x) ((x) + 0x00050000) > +#define HDMI_EFUSE_BASE(x) ((x) + 0x00060000) > > /* Control registers */ > #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) > @@ -120,10 +121,15 @@ > #define HDMI_INTC_EN_GLOBAL (1 << 6) > #define HDMI_INTC_EN_HPD_PLUG (1 << 3) > #define HDMI_INTC_EN_HPD_UNPLUG (1 << 2) > +#define HDMI_INTC_EN_HDCP (1 << 0) > > /* HDMI_INTC_FLAG */ > #define HDMI_INTC_FLAG_HPD_PLUG (1 << 3) > #define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2) > +#define HDMI_INTC_FLAG_HDCP (1 << 0) > + > +/* HDMI_HDCP_KEY_LOAD */ > +#define HDMI_HDCP_KEY_LOAD_DONE (1 << 0) > > /* HDMI_PHY_RSTOUT */ > #define HDMI_PHY_SW_RSTOUT (1 << 0) > @@ -142,6 +148,27 @@ > #define HDMI_VID_PREAMBLE_DIS (1 << 5) > #define HDMI_GUARD_BAND_DIS (1 << 1) > > +/* STATUS */ > +#define HDMI_AUTHEN_ACK_AUTH (1 << 7) > +#define HDMI_AUTHEN_ACK_NOT (0 << 7) > +#define HDMI_AUD_FIFO_OVF_FULL (1 << 6) > +#define HDMI_AUD_FIFO_OVF_NOT (0 << 6) > +#define HDMI_UPDATE_RI_INT_OCC (1 << 4) > +#define HDMI_UPDATE_RI_INT_NOT (0 << 4) > +#define HDMI_UPDATE_RI_INT_CLEAR (1 << 4) > +#define HDMI_UPDATE_PJ_INT_OCC (1 << 3) > +#define HDMI_UPDATE_PJ_INT_NOT (0 << 3) > +#define HDMI_UPDATE_PJ_INT_CLEAR (1 << 3) > +#define HDMI_WRITE_INT_OCC (1 << 2) > +#define HDMI_WRITE_INT_NOT (0 << 2) > +#define HDMI_WRITE_INT_CLEAR (1 << 2) > +#define HDMI_WATCHDOG_INT_OCC (1 << 1) > +#define HDMI_WATCHDOG_INT_NOT (0 << 1) > +#define HDMI_WATCHDOG_INT_CLEAR (1 << 1) > +#define HDMI_WTFORACTIVERX_INT_OCC (1) > +#define HDMI_WTFORACTIVERX_INT_NOT (0) > +#define HDMI_WTFORACTIVERX_INT_CLEAR (1) > + > /* HDMI_PHY_STATUS */ > #define HDMI_PHY_STATUS_READY (1 << 0) > > @@ -154,6 +181,84 @@ > #define HDMI_TG_EN (1 << 0) > #define HDMI_FIELD_EN (1 << 1) > > +/* STATUS_EN */ > +#define HDMI_AUD_FIFO_OVF_EN (1 << 6) > +#define HDMI_AUD_FIFO_OVF_DIS (0 << 6) > +#define HDMI_UPDATE_RI_INT_EN (1 << 4) > +#define HDMI_UPDATE_RI_INT_DIS (0 << 4) > +#define HDMI_UPDATE_PJ_INT_EN (1 << 3) > +#define HDMI_UPDATE_PJ_INT_DIS (0 << 3) > +#define HDMI_WRITE_INT_EN (1 << 2) > +#define HDMI_WRITE_INT_DIS (0 << 2) > +#define HDMI_WATCHDOG_INT_EN (1 << 1) > +#define HDMI_WATCHDOG_INT_DIS (0 << 1) > +#define HDMI_WTFORACTIVERX_INT_EN (1) > +#define HDMI_WTFORACTIVERX_INT_DIS (0) > +#define HDMI_INT_EN_ALL (HDMI_UPDATE_RI_INT_EN|\ > + HDMI_UPDATE_PJ_INT_DIS|\ > + HDMI_WRITE_INT_EN|\ > + HDMI_WATCHDOG_INT_EN|\ > + HDMI_WTFORACTIVERX_INT_EN) > +#define HDMI_INT_DIS_ALL (~0x1F) > + > +/* HDMI_HPD */ > +#define HDMI_SW_HPD_PLUGGED (1 << 1) > +#define HDMI_SW_HPD_UNPLUGGED (0 << 1) > +#define HDMI_HPD_SEL_I_HPD (1) > +#define HDMI_HPD_SEL_SW_HPD (0) > + > +/* ENC_EN */ > +#define HDMI_HDCP_ENC_ENABLE (1) > +#define HDMI_HDCP_ENC_DISABLE (0) > + > +/* HDCP Register */ > + > +/* HDCP_SHA1_00~19 */ > + > +/* HDCP_KSV_LIST_0~4 */ > + > +/* HDCP_KSV_LIST_CON */ > +#define HDMI_HDCP_KSV_WRITE_DONE (0x1 << 3) > +#define HDMI_HDCP_KSV_LIST_EMPTY (0x1 << 2) > +#define HDMI_HDCP_KSV_END (0x1 << 1) > +#define HDMI_HDCP_KSV_READ (0x1 << 0) > + > +/* HDCP_CTRL1 */ > +#define HDMI_HDCP_EN_PJ_EN (1 << 4) > +#define HDMI_HDCP_EN_PJ_DIS (~(1 << 4)) > +#define HDMI_HDCP_SET_REPEATER_TIMEOUT (1 << 2) > +#define HDMI_HDCP_CLEAR_REPEATER_TIMEOUT (~(1 << 2)) > +#define HDMI_HDCP_CP_DESIRED_EN (1 << 1) > +#define HDMI_HDCP_CP_DESIRED_DIS (~(1 << 1)) > +#define HDMI_HDCP_ENABLE_1_1_FEATURE_EN (1) > +#define HDMI_HDCP_ENABLE_1_1_FEATURE_DIS (~(1)) > + > +/* HDCP_CHECK_RESULT */ > +#define HDMI_HDCP_PI_MATCH_RESULT_Y ((0x1 << 3) | (0x1 << 2)) > +#define HDMI_HDCP_PI_MATCH_RESULT_N ((0x1 << 3) | (0x0 << 2)) > +#define HDMI_HDCP_RI_MATCH_RESULT_Y ((0x1 << 1) | (0x1 << 0)) > +#define HDMI_HDCP_RI_MATCH_RESULT_N ((0x1 << 1) | (0x0 << 0)) > +#define HDMI_HDCP_CLR_ALL_RESULTS (0) > + > +/* HDCP_BKSV0~4 */ > +/* HDCP_AKSV0~4 */ > + > +/* HDCP_BCAPS */ > +#define HDMI_HDCP_BCAPS_REPEATER (1 << 6) > +#define HDMI_HDCP_BCAPS_READY (1 << 5) > +#define HDMI_HDCP_BCAPS_FAST (1 << 4) > +#define HDMI_HDCP_BCAPS_1_1_FEATURES (1 << 1) > +#define HDMI_HDCP_BCAPS_FAST_REAUTH (1) > + > +/* HDCP_BSTATUS_0/1 */ > +/* HDCP_Ri_0/1 */ > +/* HDCP_I2C_INT */ > +/* HDCP_AN_INT */ > +/* HDCP_WATCHDOG_INT */ > +/* HDCP_RI_INT/1 */ > +/* HDCP_Ri_Compare_0 */ > +/* HDCP_Ri_Compare_1 */ > +/* HDCP_Frame_Count */ > > /* HDMI Version 1.4 */ > /* Control registers */ > @@ -421,6 +526,22 @@ > #define HDMI_I2S_MUX_CH HDMI_I2S_BASE(0x054) > #define HDMI_I2S_MUX_CUV HDMI_I2S_BASE(0x058) > > +/* HDMI eFUSE registers */ > +#define HDMI_EFUSE_CTRL HDMI_EFUSE_BASE(0x000) > +#define HDMI_EFUSE_STATUS HDMI_EFUSE_BASE(0x004) > +#define HDMI_EFUSE_ADDR_WIDTH HDMI_EFUSE_BASE(0x008) > +#define HDMI_EFUSE_SIGDEV_ASSERT HDMI_EFUSE_BASE(0x00c) > +#define HDMI_EFUSE_SIGDEV_DE_ASSERT HDMI_EFUSE_BASE(0x010) > +#define HDMI_EFUSE_PRCHG_ASSERT HDMI_EFUSE_BASE(0x014) > +#define HDMI_EFUSE_PRCHG_DE_ASSERT HDMI_EFUSE_BASE(0x018) > +#define HDMI_EFUSE_FSET_ASSERT HDMI_EFUSE_BASE(0x01c) > +#define HDMI_EFUSE_FSET_DE_ASSERT HDMI_EFUSE_BASE(0x020) > +#define HDMI_EFUSE_SENSING HDMI_EFUSE_BASE(0x024) > +#define HDMI_EFUSE_SCK_ASSERT HDMI_EFUSE_BASE(0x028) > +#define HDMI_EFUSE_SCK_DE_ASSERT HDMI_EFUSE_BASE(0x02c) > +#define HDMI_EFUSE_SDOUT_OFFSET HDMI_EFUSE_BASE(0x030) > +#define HDMI_EFUSE_READ_OFFSET HDMI_EFUSE_BASE(0x034) > + > /* I2S bit definition */ > > /* I2S_CLK_CON */ > @@ -570,6 +691,62 @@ > #define HDMI_I2S_CUV_R_DATA_MASK (0x7 << 4) > #define HDMI_I2S_CUV_L_DATA_MASK (0x7) > > +/* GCP_CON */ > +#define HDMI_GCP_CON_EN_1ST_VSYNC (1 << 3) > +#define HDMI_GCP_CON_EN_2ST_VSYNC (1 << 2) > +#define HDMI_GCP_CON_TRANS_EVERY_VSYNC (2) > +#define HDMI_GCP_CON_NO_TRAN (0) > +#define HDMI_GCP_CON_TRANS_ONCE (1) > +#define HDMI_GCP_CON_TRANS_EVERY_VSYNC (2) > + > +/* GCP_BYTE1 */ > +#define HDMI_GCP_BYTE1_MASK (0xFF) > + > +/* GCP_BYTE2 */ > +#define HDMI_GCP_BYTE2_PP_MASK (0xF << 4) > +#define HDMI_GCP_24BPP (1 << 2) > +#define HDMI_GCP_30BPP (1 << 0 | 1 << 2) > +#define HDMI_GCP_36BPP (1 << 1 | 1 << 2) > +#define HDMI_GCP_48BPP (1 << 0 | 1 << 1 | 1 << 2) > + > +/* GCP_BYTE3 */ > +#define HDMI_GCP_BYTE3_MASK (0xFF) > + > +/* HDCP E-FUSE Control Register */ > +/* HDCP_E_FUSE_CTRL */ > +#define HDMI_EFUSE_CTRL_HDCP_KEY_READ (1 << 0) > + > +/* HDCP_E_FUSE_STATUS */ > +#define HDMI_EFUSE_ECC_FAIL (1 << 2) > +#define HDMI_EFUSE_ECC_BUSY (1 << 1) > +#define HDMI_EFUSE_ECC_DONE (1) > + > +/* EFUSE_ADDR_WIDTH */ > +/* EFUSE_SIGDEV_ASSERT */ > +/* EFUSE_SIGDEV_DE-ASSERT */ > +/* EFUSE_PRCHG_ASSERT */ > +/* EFUSE_PRCHG_DE-ASSERT */ > +/* EFUSE_FSET_ASSERT */ > +/* EFUSE_FSET_DE-ASSERT */ > +/* EFUSE_SENSING */ > +/* EFUSE_SCK_ASSERT */ > +/* EFUSE_SCK_DEASSERT */ > +/* EFUSE_SDOUT_OFFSET */ > +/* EFUSE_READ_OFFSET */ > + > +/* HDCP_SHA_RESULT */ > +#define HDMI_HDCP_SHA_VALID_NO_RD (0 << 1) > +#define HDMI_HDCP_SHA_VALID_RD (1 << 1) > +#define HDMI_HDCP_SHA_VALID (1) > +#define HDMI_HDCP_SHA_NO_VALID (0) > + > +/* Audio InfoFrame Register */ > + > +/* AUI_CON */ > +#define HDMI_AUI_CON_NO_TRAN (0 << 0) > +#define HDMI_AUI_CON_TRANS_ONCE (1 << 0) > +#define HDMI_AUI_CON_TRANS_EVERY_VSYNC (2 << 0) > + > /* Timing generator registers */ > /* TG configure/status registers */ > #define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068) > -- > 1.7.0.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/dri-devel _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel