diff mbox

[RFC] drm/exynos: added hdcp driver for contents protection.

Message ID 1356083277-11676-2-git-send-email-chulspro.kim@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Eunchul Kim Dec. 21, 2012, 9:47 a.m. UTC
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@samsung.com>
---
 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

Comments

Alan Cox Dec. 21, 2012, 11:53 a.m. UTC | #1
On Fri, 21 Dec 2012 18:47:57 +0900
Eunchul Kim <chulspro.kim@samsung.com> wrote:

> HDCP stands for High-bandwidth Digital Content Protection.
> This is a newer form of Digital Rights Management(secure DRM)

was.. the master key was leaked long ago 8)

> 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.

For this code to go into the kernel it must do so in GPL form. Can you
confirm Samsung has the necessary IPR and permissions to grant use of
this to all third parties as the GPL requires not just to specific
customers ? (whether they can use it usefully is a different question as
obviously they need the key.

I ask this because there are currently a lot of lawyers busy trying to
sue makers of some HDCP aware devices.

> +
> +	dev_info(dev, "drm hdcp registered successfully.\n");

This sort of stuff ought to be dev_dbg, minor item


> 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@samsung.com>
> + *
> + * 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.
> + */


Two things strike me here - firstly kernel code is GPLv2 or later (or
dual licensed) as per the Signed-off-by: certification. I don't see any
problem in the above but to be sure you realise.

Secondly - VA Linux systems ??? has this been copied from some random
file and not updated correctly or does the old VA Linux Systems really
own bits of this code.

Other questions I'll look at later - in particular
- I don't see how the work queue is locked against the main thread of
  execution within the driver
- There is a general DRI question here about HDCP and interfaces -
  several out of tree drivers do HDCP and perhaps a common API would be
  sensible ?

Alan
Inki Dae Dec. 21, 2012, 5:44 p.m. UTC | #2
2012/12/21 Alan Cox <alan@lxorguk.ukuu.org.uk>

> On Fri, 21 Dec 2012 18:47:57 +0900
> Eunchul Kim <chulspro.kim@samsung.com> wrote:
>
> > HDCP stands for High-bandwidth Digital Content Protection.
> > This is a newer form of Digital Rights Management(secure DRM)
>
> was.. the master key was leaked long ago 8)
>
> > 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.
>
> For this code to go into the kernel it must do so in GPL form. Can you
> confirm Samsung has the necessary IPR and permissions to grant use of
> this to all third parties as the GPL requires not just to specific
> customers ? (whether they can use it usefully is a different question as
> obviously they need the key.
>
> I ask this because there are currently a lot of lawyers busy trying to
> sue makers of some HDCP aware devices.
>
> > +
> > +     dev_info(dev, "drm hdcp registered successfully.\n");
>
> This sort of stuff ought to be dev_dbg, minor item
>
>
> > 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@samsung.com>
> > + *
> > + * 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.
> > + */
>
>
> Two things strike me here - firstly kernel code is GPLv2 or later (or
> dual licensed) as per the Signed-off-by: certification. I don't see any
> problem in the above but to be sure you realise.
>
> Secondly - VA Linux systems ??? has this been copied from some random
> file and not updated correctly or does the old VA Linux Systems really
> own bits of this code.
>
>
And also I found out that other exynos files also haven't been updated
correctly. Definitely this was our mistakes.

Thanks,
Inki Dae



> Other questions I'll look at later - in particular
> - I don't see how the work queue is locked against the main thread of
>   execution within the driver
> - There is a general DRI question here about HDCP and interfaces -
>   several out of tree drivers do HDCP and perhaps a common API would be
>   sensible ?
>
> Alan
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Sean Paul Dec. 21, 2012, 7:29 p.m. UTC | #3
On Fri, Dec 21, 2012 at 4:47 AM, Eunchul Kim <chulspro.kim@samsung.com> 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@samsung.com>
> ---

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@samsung.com>
> + *
> + * 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@samsung.com>
> + *
> + * 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@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Eunchul Kim Dec. 22, 2012, 5:59 a.m. UTC | #4
Thank's for your advice.

Your opinion is very helpful to me.
I will consider your comments, and then I resend it.

Thank's
BR
Eunchul Kim

On 12/21/2012 08:53 PM, Alan Cox wrote:
> On Fri, 21 Dec 2012 18:47:57 +0900
> Eunchul Kim <chulspro.kim@samsung.com> wrote:
>
>> HDCP stands for High-bandwidth Digital Content Protection.
>> This is a newer form of Digital Rights Management(secure DRM)
>
> was.. the master key was leaked long ago 8)

- Okay, I will change description. :)

>
>> 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.
>
> For this code to go into the kernel it must do so in GPL form. Can you
> confirm Samsung has the necessary IPR and permissions to grant use of
> this to all third parties as the GPL requires not just to specific
> customers ? (whether they can use it usefully is a different question as
> obviously they need the key.
>
> I ask this because there are currently a lot of lawyers busy trying to
> sue makers of some HDCP aware devices.

- I will check about your comments. thank's

>
>> +
>> +	dev_info(dev, "drm hdcp registered successfully.\n");
>
> This sort of stuff ought to be dev_dbg, minor item

- changed it.

>
>
>> 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@samsung.com>
>> + *
>> + * 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.
>> + */
>
>
> Two things strike me here - firstly kernel code is GPLv2 or later (or
> dual licensed) as per the Signed-off-by: certification. I don't see any
> problem in the above but to be sure you realise.
>
> Secondly - VA Linux systems ??? has this been copied from some random
> file and not updated correctly or does the old VA Linux Systems really
> own bits of this code.
>

- changed it.

> Other questions I'll look at later - in particular
> - I don't see how the work queue is locked against the main thread of
>    execution within the driver

- I want to avoid that main thread is locked by I2C operation with peer 
device.
   The performance deteriorated when i used callback. so, I used workqueue.
   I designed two kind of mechanism about this. first one is based on 
workqueue.
   second one is based on callback.
   I will consider one more time between synchronization and performance.

> - There is a general DRI question here about HDCP and interfaces -
>    several out of tree drivers do HDCP and perhaps a common API would be
>    sensible ?

- I agree with your opinion.
   So, I discussed with our X video driver engineer for common API 
internally.
   I think, If we provide connector property for HDCP, then DRI can 
control HDCP generally.
   I didn't fully implemented yet.

>
> Alan
>
Eunchul Kim Dec. 22, 2012, 7:39 a.m. UTC | #5
Dear Sean Paul.

Thank's for your comment.

I am in charge of Display Part in Samsung TN.
and I also use exynos drm and I contributed some part to dri-devel(e.g 
IPPs).

Your comment is very helpful to me.
I answer to your comment. please check my answer.

Thank's
BR
Eunchul Kim

On 12/22/2012 04:29 AM, Sean Paul wrote:
> On Fri, Dec 21, 2012 at 4:47 AM, Eunchul Kim <chulspro.kim@samsung.com> 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@samsung.com>
>> ---
>
> Hey Eunchul,
> Through some unfortunate duplication of work, I've also been working
> on HDCP for exynos drm.

- Look's good news. because we can upgrade exynos drm together.

>
> I uploaded my patch a couple of days ago
> https://gerrit.chromium.org/gerrit/#/c/39960.

- I didn't subscribe chromium.org. so, I didn't see your patch.
   I will check your patch for understanding your design about HDCP.
   How can i subscribe your gerrit for the future?
   because we have very many changes in HDMI, FIMD side in our local git.
   I want to synchronize from yours to avoid duplication work.
   I am in charge of all Display Part H/W IP in exynos DRM. please refer it.

>
> A few high level comments on your patch:
>
> - Why implement a new driver? You're using the irq and registers from

- Yes your are right in some part. I think you didn't see the connection 
with HDMI driver and
   HDCP driver. so, you got the question. IMO, HDMI driver is very big 
currently, so, If we add
   HDCP code directly in HDMI driver.
   I can't handle HDCP driver on/off using configuration(defconfig).
   so, I designed different file and make some ops(callback).
   I will check one more time between in-side and out-side with our team.

> 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 understood what your mean. I will consider about your comment.

> - 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/

- connector property is good idea. I also think that.(currently we 
always enable this)
   I will check your patch.
   thank's. It's good information.

>
> 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@samsung.com>
>> + *
>> + * 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)

- changed it.

>
>> +       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?

- Yes, HDMI driver called this ops(callback).

> Ideally this would be checked after an HDCP interrupt, so will you

- HDMI driver call dpms after HDMI plug/unplug interrupt.
   HDMI driver call commit after HDCP interrupt.

> call it from the HDMI interrupt handler? I also don't think "commit"

- Yes. What do you prefer? DRM side using "commit" for execution.
   so, I used it.

> 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?

- I register ops to HDMI driver. and If driver registered in HDMI driver.
   HDMI driver handle HDCP driver. In this case, HDCP working.

>
>> +};
>> +
>> +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 :).

- hmm ~ I respect your opinion. I will consider one more time.

>
>> +       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.

- HDCP ctx have some information of data. I want to get this ctx using 
drm_hdcp_ctx.
   please see the exynos_drm_hdmi.c. this driver handle mixer/vp and hdmi.
   It's same with hdcp controlling.

>
>> +
>> +       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.

- I designed two kind of mechanism about this. frist one is based on 
workqueue.
   second one is based on callback.
   HDCP encryption validation works evey 2 seconds as you know.
   so, I worry about performance issue. so, I choose workqueue.
   I will check one more time between workqueue and callback.
   and what is nice position about HDCP.

>
>> +       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@samsung.com>
>> + *
>> + * 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@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
diff mbox

Patch

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@samsung.com>
+ *
+ * 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;
+	queue_work(ctx->wq, (struct work_struct *)&ctx->event_work);
+}
+
+static struct exynos_hdcp_ops hdmi_ops = {
+	/* manager */
+	.dpms = hdcp_dpms,
+	.commit = hdcp_commit,
+};
+
+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;
+
+	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;
+	}
+
+	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);
+	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@samsung.com>
+ *
+ * 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)