diff mbox

[RFC] drm/exynos: hdmi: move hdmiphy related code to hdmiphy driver

Message ID 1361971327-7459-1-git-send-email-rahul.sharma@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rahul Sharma Feb. 27, 2013, 1:22 p.m. UTC
Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
should be instantiated independently.

In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
other. It is preferred to isolate them and maintained independently.

This implementations is tested for:
1) Resolutions supported by exynos4 and 5 hdmi.
2) Runtime PM and S2R scenarions for exynos5.

This patch is dependent on 
http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html

Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
---
It is based on exynos-drm-next-todo branch at
git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git

 drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
 drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
 drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
 drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
 drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
 drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
 drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
 drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
 8 files changed, 738 insertions(+), 368 deletions(-)
 create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h

Comments

Sean Paul Feb. 27, 2013, 4:17 p.m. UTC | #1
On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and

s/Physicaly/Physically/

> should be instantiated independently.
>
> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
> other. It is preferred to isolate them and maintained independently.
>
> This implementations is tested for:
> 1) Resolutions supported by exynos4 and 5 hdmi.
> 2) Runtime PM and S2R scenarions for exynos5.
>

I don't like the idea of spawning off yet another driver in here. It
adds more globals, more suspend/resume ordering issues, and more
implicit dependencies. I understand, however, that this is the Chosen
Way for the exynos driver, so I will save my rant.

I've made some comments below.

> This patch is dependent on
> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>
> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> ---
> It is based on exynos-drm-next-todo branch at
> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>
>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>  8 files changed, 738 insertions(+), 368 deletions(-)
>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> index 3da5c2d..03acb6b 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> @@ -334,6 +334,11 @@ static int __init exynos_drm_init(void)
>         ret = platform_driver_register(&hdmi_driver);
>         if (ret < 0)
>                 goto out_hdmi;
> +
> +       ret = exynos_hdmiphy_driver_register();

Not sure why you need to obfuscate the type of driver here. All the
other ones are directly registered as platform drivers, I don't see
the harm in just doing the i2c_add_driver directly here.

> +       if (ret < 0)
> +               goto out_hdmiphy;
> +
>         ret = platform_driver_register(&mixer_driver);
>         if (ret < 0)
>                 goto out_mixer;


I think this ordering is how you plan on enforcing the suspend/resume
order for hdmi/mixer/hdmiphy. In that case, however, I don't think it
makes sense to suspend/resume hdmiphy in between mixer and hdmi.
Ideally, hdmiphy should go down first and come up last.


> @@ -436,6 +441,8 @@ out_common_hdmi_dev:
>  out_common_hdmi:
>         platform_driver_unregister(&mixer_driver);
>  out_mixer:
> +       exynos_hdmiphy_driver_unregister();
> +out_hdmiphy:
>         platform_driver_unregister(&hdmi_driver);
>  out_hdmi:
>  #endif
> @@ -479,6 +486,7 @@ static void __exit exynos_drm_exit(void)
>         exynos_platform_device_hdmi_unregister();
>         platform_driver_unregister(&exynos_drm_common_hdmi_driver);
>         platform_driver_unregister(&mixer_driver);
> +       exynos_hdmiphy_driver_unregister();
>         platform_driver_unregister(&hdmi_driver);
>  #endif
>
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> index 4606fac..17c53e3 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> @@ -325,6 +325,12 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
>  extern int exynos_platform_device_hdmi_register(void);
>
>  /*
> + * these functions registers/unregisters exynos drm hdmiphy driver.
> + */
> +extern int exynos_hdmiphy_driver_register(void);
> +extern void exynos_hdmiphy_driver_unregister(void);
> +
> +/*
>   * this function unregisters exynos drm hdmi platform device if it exists.
>   */
>  void exynos_platform_device_hdmi_unregister(void);
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
> index 5dc956b..45ef331 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
> @@ -32,19 +32,22 @@
>  /* platform device pointer for common drm hdmi device. */
>  static struct platform_device *exynos_drm_hdmi_pdev;
>
> -/* Common hdmi subdrv needs to access the hdmi and mixer though context.
> -* These should be initialied by the repective drivers */
> +/* Common hdmi subdrv needs to access the hdmi, hdmiphy and mixer though
> +*  context. These should be initialied by the repective drivers */
>  static struct exynos_drm_hdmi_context *hdmi_ctx;
> +static struct exynos_drm_hdmi_context *hdmiphy_ctx;
>  static struct exynos_drm_hdmi_context *mixer_ctx;
>
>  /* these callback points shoud be set by specific drivers. */
>  static struct exynos_hdmi_ops *hdmi_ops;
> +static struct exynos_hdmiphy_ops *hdmiphy_ops;
>  static struct exynos_mixer_ops *mixer_ops;
>

This stuff really sets my teeth on edge. I can't stand these global
variables and the set-here, set-there, set-here dance that is done on
probe.

IMO, this and the suspend/resume ordering should be enough motivation
to avoid separating everything out into their own drivers.

Instead of adding more kruft to this list, we should be working to remove it.

/rant

>  struct drm_hdmi_context {
>         struct exynos_drm_subdrv        subdrv;
>         struct exynos_drm_hdmi_context  *hdmi_ctx;
>         struct exynos_drm_hdmi_context  *mixer_ctx;
> +       struct exynos_drm_hdmi_context  *hdmiphy_ctx;
>
>         bool    enabled[MIXER_WIN_NR];
>  };
> @@ -74,6 +77,12 @@ void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
>                 hdmi_ctx = ctx;
>  }
>
> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx)
> +{
> +       if (ctx)
> +               hdmiphy_ctx = ctx;
> +}
> +
>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
>  {
>         if (ctx)
> @@ -88,6 +97,14 @@ void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
>                 hdmi_ops = ops;
>  }
>
> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops)
> +{
> +       DRM_DEBUG_KMS("%s\n", __FILE__);

Listing __FILE__ in these DRM_DEBUG_KMS traces is pretty useless. I'm
sure we can determine the file from the function name. It would be
slightly more useful to list __LINE__, but even more useful if you
actually printed state relevant to the current function.

> +
> +       if (ops)
> +               hdmiphy_ops = ops;
> +}
> +
>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
>  {
>         DRM_DEBUG_KMS("%s\n", __FILE__);
> @@ -121,7 +138,7 @@ struct edid *drm_hdmi_get_edid(struct device *dev,
>         return NULL;
>  }
>
> -static int drm_hdmi_check_timing(struct device *dev, void *timing)
> +static int drm_hdmi_check_timing(struct device *dev, void *mode)
>  {
>         struct drm_hdmi_context *ctx = to_context(dev);
>         int ret = 0;
> @@ -129,18 +146,24 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing)
>         DRM_DEBUG_KMS("%s\n", __FILE__);
>
>         /*
> -       * Both, mixer and hdmi should be able to handle the requested mode.
> -       * If any of the two fails, return mode as BAD.
> +       * Mixer, hdmi and hdmiphy should be able to handle the requested mode.
> +       * If any of the them fails, return mode as BAD.
>         */
>
>         if (mixer_ops && mixer_ops->check_timing)
> -               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
> +               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, mode);
>
>         if (ret)
>                 return ret;
>
>         if (hdmi_ops && hdmi_ops->check_timing)
> -               return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
> +               ret = hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, mode);
> +
> +       if (ret)
> +               return ret;
> +
> +       if (hdmiphy_ops && hdmiphy_ops->check_timing)
> +               return hdmiphy_ops->check_timing(ctx->hdmiphy_ctx->ctx, mode);
>
>         return 0;
>  }
> @@ -254,6 +277,9 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
>
>         if (hdmi_ops && hdmi_ops->mode_set)
>                 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
> +
> +       if (hdmiphy_ops && hdmiphy_ops->mode_set)
> +               hdmiphy_ops->mode_set(ctx->hdmiphy_ctx->ctx, mode);
>  }
>
>  static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
> @@ -273,6 +299,15 @@ static void drm_hdmi_commit(struct device *subdrv_dev)
>
>         DRM_DEBUG_KMS("%s\n", __FILE__);
>
> +       if (hdmiphy_ops && hdmiphy_ops->prepare)
> +               hdmiphy_ops->prepare(ctx->hdmiphy_ctx->ctx);
> +
> +       if (hdmi_ops && hdmi_ops->prepare)
> +               hdmi_ops->prepare(ctx->hdmi_ctx->ctx);
> +

Why are you calling these prepare functions in commit? Isn't that
precisely the reason drm has a prepare callback?

> +       if (hdmiphy_ops && hdmiphy_ops->config_apply)
> +               hdmiphy_ops->config_apply(ctx->hdmiphy_ctx->ctx);
> +
>         if (hdmi_ops && hdmi_ops->commit)
>                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
>  }
> @@ -288,6 +323,9 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
>
>         if (hdmi_ops && hdmi_ops->dpms)
>                 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
> +
> +       if (hdmiphy_ops && hdmiphy_ops->dpms)
> +               hdmiphy_ops->dpms(ctx->hdmiphy_ctx->ctx, mode);

I think you have to be more careful about the ordering of things here
since you'll want to power on in a different order than you power off.

>  }
>
>  static void drm_hdmi_apply(struct device *subdrv_dev)
> @@ -393,6 +431,11 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>                 return -EFAULT;
>         }
>
> +       if (!hdmiphy_ctx) {
> +               DRM_ERROR("hdmiphy context not initialized.\n");
> +               return -EFAULT;
> +       }
> +
>         if (!mixer_ctx) {
>                 DRM_ERROR("mixer context not initialized.\n");
>                 return -EFAULT;
> @@ -406,6 +449,7 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>         }
>
>         ctx->hdmi_ctx = hdmi_ctx;
> +       ctx->hdmiphy_ctx = hdmiphy_ctx;
>         ctx->mixer_ctx = mixer_ctx;
>
>         ctx->hdmi_ctx->drm_dev = drm_dev;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
> index fd2ff9f..10db62e 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
> @@ -39,10 +39,19 @@ struct exynos_hdmi_ops {
>         void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>         void (*get_max_resol)(void *ctx, unsigned int *width,
>                                 unsigned int *height);
> +       void (*prepare)(void *ctx);
>         void (*commit)(void *ctx);
>         void (*dpms)(void *ctx, int mode);
>  };
>
> +struct exynos_hdmiphy_ops {
> +       int (*check_timing)(void *ctx, struct drm_display_mode *mode);
> +       void (*mode_set)(void *ctx, struct drm_display_mode *mode);
> +       void (*prepare)(void *ctx);
> +       int (*config_apply)(void *ctx);
> +       void (*dpms)(void *ctx, int mode);
> +};
> +
>  struct exynos_mixer_ops {
>         /* manager */
>         int (*iommu_on)(void *ctx, bool enable);
> @@ -61,7 +70,9 @@ struct exynos_mixer_ops {
>  };
>
>  void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx);
>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
>  void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops);
>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
>  #endif
> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
> index a5ca2cc..7ee350d 100644
> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
> @@ -194,7 +194,6 @@ struct hdmi_context {
>         int                             internal_irq;
>
>         struct i2c_client               *ddc_port;
> -       struct i2c_client               *hdmiphy_port;
>
>         /* current hdmiphy conf regs */
>         struct hdmi_conf_regs           mode_conf;
> @@ -206,180 +205,6 @@ struct hdmi_context {
>         enum hdmi_type                  type;
>  };
>
> -struct hdmiphy_config {
> -       int pixel_clock;
> -       u8 conf[32];
> -};
> -
> -/* list of phy config settings */
> -static const struct hdmiphy_config hdmiphy_v13_configs[] = {
> -       {
> -               .pixel_clock = 27000000,
> -               .conf = {
> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
> -               },
> -       },
> -       {
> -               .pixel_clock = 27027000,
> -               .conf = {
> -                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
> -               },
> -       },
> -       {
> -               .pixel_clock = 74176000,
> -               .conf = {
> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
> -                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
> -                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
> -               },
> -       },
> -       {
> -               .pixel_clock = 74250000,
> -               .conf = {
> -                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
> -                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
> -                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
> -               },
> -       },
> -       {
> -               .pixel_clock = 148500000,
> -               .conf = {
> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
> -                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
> -                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
> -               },
> -       },
> -};
> -
> -static const struct hdmiphy_config hdmiphy_v14_configs[] = {
> -       {
> -               .pixel_clock = 25200000,
> -               .conf = {
> -                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
> -                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 27000000,
> -               .conf = {
> -                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
> -                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> -                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 27027000,
> -               .conf = {
> -                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
> -                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
> -               },
> -       },
> -       {
> -               .pixel_clock = 36000000,
> -               .conf = {
> -                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 40000000,
> -               .conf = {
> -                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
> -                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 65000000,
> -               .conf = {
> -                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
> -                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 74176000,
> -               .conf = {
> -                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
> -                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 74250000,
> -               .conf = {
> -                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
> -               },
> -       },
> -       {
> -               .pixel_clock = 83500000,
> -               .conf = {
> -                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
> -                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 106500000,
> -               .conf = {
> -                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
> -                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 108000000,
> -               .conf = {
> -                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 146250000,
> -               .conf = {
> -                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
> -                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
> -               },
> -       },
> -       {
> -               .pixel_clock = 148500000,
> -               .conf = {
> -                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> -                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
> -               },
> -       },
> -};
> -
>  struct hdmi_infoframe {
>         enum HDMI_PACKET_TYPE type;
>         u8 ver;
> @@ -774,46 +599,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
>         return raw_edid;
>  }
>
> -static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
> -{
> -       const struct hdmiphy_config *confs;
> -       int count, i;
> -
> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> -
> -       if (hdata->type == HDMI_TYPE13) {
> -               confs = hdmiphy_v13_configs;
> -               count = ARRAY_SIZE(hdmiphy_v13_configs);
> -       } else if (hdata->type == HDMI_TYPE14) {
> -               confs = hdmiphy_v14_configs;
> -               count = ARRAY_SIZE(hdmiphy_v14_configs);
> -       } else
> -               return -EINVAL;
> -
> -       for (i = 0; i < count; i++)
> -               if (confs[i].pixel_clock == pixel_clock)
> -                       return i;
> -
> -       DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
> -       return -EINVAL;
> -}
> -
> -static int hdmi_check_timing(void *ctx, struct drm_display_mode *mode)
> -{
> -       struct hdmi_context *hdata = ctx;
> -       int ret;
> -
> -       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
> -               __func__, mode->hdisplay, mode->vdisplay,
> -               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
> -               false, mode->clock * 1000);
> -
> -       ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
> -       if (ret < 0)
> -               return ret;
> -       return 0;
> -}
> -
>  static void hdmi_set_acr(u32 freq, u8 *acr)
>  {
>         u32 n, cts;
> @@ -1309,20 +1094,12 @@ static void hdmi_timing_apply(struct hdmi_context *hdata)
>
>  static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>  {
> -       u8 buffer[2];
>         u32 reg;
>
>         clk_disable(hdata->res.sclk_hdmi);
>         clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
>         clk_enable(hdata->res.sclk_hdmi);
>
> -       /* operation mode */
> -       buffer[0] = 0x1f;
> -       buffer[1] = 0x00;
> -
> -       if (hdata->hdmiphy_port)
> -               i2c_master_send(hdata->hdmiphy_port, buffer, 2);
> -
>         if (hdata->type == HDMI_TYPE13)
>                 reg = HDMI_V13_PHY_RSTOUT;
>         else
> @@ -1335,102 +1112,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>         usleep_range(10000, 12000);
>  }
>
> -static void hdmiphy_poweron(struct hdmi_context *hdata)
> -{
> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> -
> -       if (hdata->type == HDMI_TYPE14)
> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
> -                       HDMI_PHY_POWER_OFF_EN);
> -}
> -
> -static void hdmiphy_poweroff(struct hdmi_context *hdata)
> -{
> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> -
> -       if (hdata->type == HDMI_TYPE14)
> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
> -                       HDMI_PHY_POWER_OFF_EN);

I assume these register writes are replaced with i2c operations in the
new phy driver?

> -}
> -
> -static void hdmiphy_conf_apply(struct hdmi_context *hdata)
> -{
> -       const u8 *hdmiphy_data;
> -       u8 buffer[32];
> -       u8 operation[2];
> -       u8 read_buffer[32] = {0, };
> -       int ret;
> -       int i;
> -
> -       if (!hdata->hdmiphy_port) {
> -               DRM_ERROR("hdmiphy is not attached\n");
> -               return;
> -       }
> -
> -       /* pixel clock */
> -       i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
> -       if (i < 0) {
> -               DRM_ERROR("failed to find hdmiphy conf\n");
> -               return;
> -       }
> -
> -       if (hdata->type == HDMI_TYPE13) {
> -               hdmiphy_data = hdmiphy_v13_configs[i].conf;
> -       } else {
> -               hdmiphy_data = hdmiphy_v14_configs[i].conf;
> -       }
> -
> -       memcpy(buffer, hdmiphy_data, 32);
> -       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
> -       if (ret != 32) {
> -               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
> -               return;
> -       }
> -
> -       usleep_range(10000, 12000);
> -
> -       /* operation mode */
> -       operation[0] = 0x1f;
> -       operation[1] = 0x80;
> -
> -       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
> -       if (ret != 2) {
> -               DRM_ERROR("failed to enable hdmiphy\n");
> -               return;
> -       }
> -
> -       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
> -       if (ret < 0) {
> -               DRM_ERROR("failed to read hdmiphy config\n");
> -               return;
> -       }
> -
> -       for (i = 0; i < ret; i++)
> -               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
> -                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
> -}
> -
> -static void hdmi_conf_apply(struct hdmi_context *hdata)
> -{
> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> -
> -       hdmiphy_conf_reset(hdata);
> -       hdmiphy_conf_apply(hdata);
> -
> -       mutex_lock(&hdata->hdmi_mutex);
> -       hdmi_conf_reset(hdata);
> -       hdmi_conf_init(hdata);
> -       mutex_unlock(&hdata->hdmi_mutex);
> -
> -       hdmi_audio_init(hdata);
> -
> -       /* setting core registers */
> -       hdmi_timing_apply(hdata);
> -       hdmi_audio_control(hdata, true);
> -
> -       hdmi_regs_dump(hdata, "start");
> -}
> -
>  static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
>  {
>         int i;
> @@ -1448,7 +1129,6 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
>
>         hdata->mode_conf.cea_video_id =
>                 drm_match_cea_mode((struct drm_display_mode *)m);
> -       hdata->mode_conf.pixel_clock = m->clock * 1000;

I don't think you use pixel_clock in mode_conf any longer, please
remove it from the struct.

>
>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>         hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
> @@ -1544,7 +1224,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
>
>         hdata->mode_conf.cea_video_id =
>                 drm_match_cea_mode((struct drm_display_mode *)m);
> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>
>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>         hdmi_set_reg(core->v_line, 2, m->vtotal);
> @@ -1670,13 +1349,32 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width,
>         *height = MAX_HEIGHT;
>  }
>
> +static void hdmi_commit_prepare(void *ctx)

I don't know why the "_commit" is there, just hdmi_prepare will be fine.

> +{
> +       struct hdmi_context *hdata = ctx;
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       hdmiphy_conf_reset(hdata);

There's still phy-specific kruft laying around the driver. There are
now a bunch of implicit dependencies between the hdmi driver and the
hdmiphy driver.

Another example is the sclk_hdmiphy clock, which is still controlled
by the hdmi driver, it should be controlled by the phy.

> +}
> +
>  static void hdmi_commit(void *ctx)
>  {
>         struct hdmi_context *hdata = ctx;
>
>         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>
> -       hdmi_conf_apply(hdata);
> +       mutex_lock(&hdata->hdmi_mutex);
> +       hdmi_conf_reset(hdata);
> +       hdmi_conf_init(hdata);
> +       mutex_unlock(&hdata->hdmi_mutex);
> +
> +       hdmi_audio_init(hdata);
> +
> +       /* setting core registers */
> +       hdmi_timing_apply(hdata);
> +       hdmi_audio_control(hdata, true);
> +
> +       hdmi_regs_dump(hdata, "start");
>  }
>
>  static void hdmi_poweron(struct hdmi_context *hdata)
> @@ -1699,8 +1397,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
>         clk_enable(res->hdmiphy);
>         clk_enable(res->hdmi);
>         clk_enable(res->sclk_hdmi);
> -
> -       hdmiphy_poweron(hdata);
>  }
>
>  static void hdmi_poweroff(struct hdmi_context *hdata)
> @@ -1719,7 +1415,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
>          * its reset state seems to meet the condition.
>          */
>         hdmiphy_conf_reset(hdata);
> -       hdmiphy_poweroff(hdata);
>
>         clk_disable(res->sclk_hdmi);
>         clk_disable(res->hdmi);
> @@ -1761,11 +1456,11 @@ static struct exynos_hdmi_ops hdmi_ops = {
>         /* display */
>         .is_connected   = hdmi_is_connected,
>         .get_edid       = hdmi_get_edid,
> -       .check_timing   = hdmi_check_timing,
>
>         /* manager */
>         .mode_set       = hdmi_mode_set,
>         .get_max_resol  = hdmi_get_max_resol,
> +       .prepare                = hdmi_commit_prepare,
>         .commit         = hdmi_commit,
>         .dpms           = hdmi_dpms,
>  };
> @@ -1878,7 +1573,7 @@ fail:
>         return -ENODEV;
>  }
>
> -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
> +static struct i2c_client *hdmi_ddc;
>
>  void hdmi_attach_ddc_client(struct i2c_client *ddc)
>  {
> @@ -1886,12 +1581,6 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
>                 hdmi_ddc = ddc;
>  }
>
> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
> -{
> -       if (hdmiphy)
> -               hdmi_hdmiphy = hdmiphy;
> -}
> -
>  #ifdef CONFIG_OF
>  static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
>                                         (struct device *dev)
> @@ -2051,27 +1740,18 @@ static int hdmi_probe(struct platform_device *pdev)
>
>         hdata->ddc_port = hdmi_ddc;
>
> -       /* hdmiphy i2c driver */
> -       if (i2c_add_driver(&hdmiphy_driver)) {
> -               DRM_ERROR("failed to register hdmiphy i2c driver\n");
> -               ret = -ENOENT;
> -               goto err_ddc;
> -       }
> -
> -       hdata->hdmiphy_port = hdmi_hdmiphy;
> -
>         hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
>         if (hdata->external_irq < 0) {
>                 DRM_ERROR("failed to get GPIO external irq\n");
>                 ret = hdata->external_irq;
> -               goto err_hdmiphy;
> +               goto err_ddc;
>         }
>
>         hdata->internal_irq = platform_get_irq(pdev, 0);
>         if (hdata->internal_irq < 0) {
>                 DRM_ERROR("failed to get platform internal irq\n");
>                 ret = hdata->internal_irq;
> -               goto err_hdmiphy;
> +               goto err_ddc;
>         }
>
>         hdata->hpd = gpio_get_value(hdata->hpd_gpio);
> @@ -2082,7 +1762,7 @@ static int hdmi_probe(struct platform_device *pdev)
>                         "hdmi_external", drm_hdmi_ctx);
>         if (ret) {
>                 DRM_ERROR("failed to register hdmi external interrupt\n");
> -               goto err_hdmiphy;
> +               goto err_ddc;
>         }
>
>         ret = request_threaded_irq(hdata->internal_irq, NULL,
> @@ -2105,8 +1785,6 @@ static int hdmi_probe(struct platform_device *pdev)
>
>  err_free_irq:
>         free_irq(hdata->external_irq, drm_hdmi_ctx);
> -err_hdmiphy:
> -       i2c_del_driver(&hdmiphy_driver);
>  err_ddc:
>         i2c_del_driver(&ddc_driver);
>         return ret;
> @@ -2125,9 +1803,6 @@ static int hdmi_remove(struct platform_device *pdev)
>         free_irq(hdata->internal_irq, hdata);
>         free_irq(hdata->external_irq, hdata);
>
> -
> -       /* hdmiphy i2c driver */
> -       i2c_del_driver(&hdmiphy_driver);
>         /* DDC i2c driver */
>         i2c_del_driver(&ddc_driver);
>
> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
> index 0ddf395..6595d7b 100644
> --- a/drivers/gpu/drm/exynos/exynos_hdmi.h
> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
> @@ -15,7 +15,6 @@
>  #define _EXYNOS_HDMI_H_
>
>  void hdmi_attach_ddc_client(struct i2c_client *ddc);
> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
>
>  extern struct i2c_driver hdmiphy_driver;
>  extern struct i2c_driver ddc_driver;
> diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
> index ea49d13..0d47302 100644
> --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
> +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
> @@ -16,33 +16,437 @@
>  #include <linux/kernel.h>
>  #include <linux/i2c.h>
>  #include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/clk.h>
>
>  #include "exynos_drm_drv.h"
> +#include "exynos_drm_hdmi.h"
>  #include "exynos_hdmi.h"
>
> +#include "regs-hdmiphy.h"
>
> -static int hdmiphy_probe(struct i2c_client *client,
> -       const struct i2c_device_id *id)
> +#define HDMIPHY_REG_COUNT              (32)

No need to include parentheses.

> +
> +enum hdmiphy_type {
> +       HDMIPHY_EXYNOS4210,
> +       HDMIPHY_EXYNOS4212,
> +};
> +
> +struct hdmiphy_context {
> +       struct device           *dev;
> +       bool                    powered;
> +       void                    *parent_ctx;
> +       enum hdmiphy_type               type;
> +       const struct hdmiphy_config     *conf;
> +       struct clk              *hdmiphy;
> +};
> +
> +struct hdmiphy_config {
> +       int pixel_clock;
> +       u8 conf[HDMIPHY_REG_COUNT];
> +};
> +
> +/* list of all required phy config settings */
> +static const struct hdmiphy_config hdmiphy_4212_configs[] = {
> +       {
> +               .pixel_clock = 25200000,
> +               .conf = {
> +                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
> +                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 27000000,
> +               .conf = {
> +                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
> +                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> +                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 27027000,
> +               .conf = {
> +                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
> +                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
> +               },
> +       },
> +       {
> +               .pixel_clock = 36000000,
> +               .conf = {
> +                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 40000000,
> +               .conf = {
> +                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
> +                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 65000000,
> +               .conf = {
> +                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
> +                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 74176000,
> +               .conf = {
> +                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
> +                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 74250000,
> +               .conf = {
> +                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
> +               },
> +       },
> +       {
> +               .pixel_clock = 83500000,
> +               .conf = {
> +                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
> +                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 106500000,
> +               .conf = {
> +                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
> +                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 108000000,
> +               .conf = {
> +                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 146250000,
> +               .conf = {
> +                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
> +                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
> +               },
> +       },
> +       {
> +               .pixel_clock = 148500000,
> +               .conf = {
> +                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
> +                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
> +               },
> +       },
> +};
> +
> +static const struct hdmiphy_config hdmiphy_4210_configs[] = {
> +       {
> +               .pixel_clock = 27000000,
> +               .conf = {
> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
> +               },
> +       },
> +       {
> +               .pixel_clock = 27027000,
> +               .conf = {
> +                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
> +               },
> +       },
> +       {
> +               .pixel_clock = 74176000,
> +               .conf = {
> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
> +                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
> +                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
> +               },
> +       },
> +       {
> +               .pixel_clock = 74250000,
> +               .conf = {
> +                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
> +                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
> +                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
> +               },
> +       },
> +       {
> +               .pixel_clock = 148500000,
> +               .conf = {
> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
> +                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
> +                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
> +               },
> +       },
> +};
> +
> +static const struct hdmiphy_config *hdmiphy_find_conf(void *ctx,
> +                       const struct drm_display_mode *mode)
> +{
> +       struct hdmiphy_context *hdata = ctx;
> +       const struct hdmiphy_config *confs;
> +       int count, i;
> +
> +       switch (hdata->type) {
> +       case HDMIPHY_EXYNOS4212:
> +               confs = hdmiphy_4212_configs;
> +               count = ARRAY_SIZE(hdmiphy_4212_configs);
> +               break;
> +       case HDMIPHY_EXYNOS4210:
> +               confs = hdmiphy_4210_configs;
> +               count = ARRAY_SIZE(hdmiphy_4210_configs);
> +               break;
> +       default:
> +               return NULL;

Should probably DRM_ERROR here.

> +       }
> +
> +       for (i = 0; i < count; i++)
> +               if (confs[i].pixel_clock == (mode->clock * 1000))
> +                       return &confs[i];
> +
> +       return NULL;
> +}
> +
> +static int hdmiphy_check_timing(void *ctx, struct drm_display_mode *mode)
>  {
> -       hdmi_attach_hdmiphy_client(client);
> +       const struct hdmiphy_config *conf;
> +       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
> +               __func__, mode->hdisplay, mode->vdisplay,
> +               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
> +               ? true : false, mode->clock * 1000);

No need to print __func__ here, DRM_DEBUG_KMS will do that for you. I
think you should also parse the flags to a string instead of a bool
cast to an int.

>
> -       dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
> -               "into i2c adapter successfully\n");
> +       conf = hdmiphy_find_conf(ctx, mode);
> +       if (!conf) {
> +               DRM_DEBUG_KMS("Display Mode is not supported.\n");
> +               return -EINVAL;
> +       }
>
>         return 0;
>  }
>
> -static int hdmiphy_remove(struct i2c_client *client)
> +static void hdmiphy_mode_set(void *ctx, struct drm_display_mode *mode)
> +{
> +       struct hdmiphy_context *hdata = ctx;
> +
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);

No need to print __func__ here, DRM_DEBUG_KMS will do that for you.
This comment stand for the rest of the calls, as well.

> +
> +       hdata->conf = hdmiphy_find_conf(ctx, mode);
> +}
> +
> +static void hdmiphy_config_prepare(void *ctx)
> +{
> +       struct hdmiphy_context *hdata = ctx;
> +       const struct i2c_client *client = to_i2c_client(hdata->dev);
> +       u8 buffer[2];
> +
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       /* operation mode */
> +       buffer[0] = HDMIPHY_MODE_SET_DONE;
> +       buffer[1] = 0x00;
> +
> +       if (client)
> +               i2c_master_send(client, buffer, 2);
> +}
> +
> +static int hdmiphy_config_apply(void *ctx)
>  {
> -       dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
> -               "from i2c adapter successfully\n");
> +       struct hdmiphy_context *hdata = ctx;
> +       struct i2c_client *client = to_i2c_client(hdata->dev);
> +       const u8 *hdmiphy_data;
> +       u8 buffer[HDMIPHY_REG_COUNT];
> +       u8 operation[2];
> +       u8 read_buffer[HDMIPHY_REG_COUNT] = {0, };
> +       int ret;
> +       int i;
> +
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       /* pixel clock */
> +       if (hdata->conf && client)
> +               hdmiphy_data = hdata->conf->conf;
> +       else
> +               return -EINVAL;
> +
> +       memcpy(buffer, hdmiphy_data, HDMIPHY_REG_COUNT);
> +
> +       ret = i2c_master_send(client, buffer, HDMIPHY_REG_COUNT);
> +       if (ret != HDMIPHY_REG_COUNT) {
> +               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
> +               return ret;
> +       }
> +
> +       usleep_range(10000, 12000);
> +
> +       /* operation mode */
> +       operation[0] = HDMIPHY_MODE_SET_DONE;
> +       operation[1] = HDMIPHY_MODE_EN;
> +
> +       ret = i2c_master_send(client, operation, 2);
> +       if (ret != 2) {
> +               DRM_ERROR("failed to enable hdmiphy\n");
> +               return ret;
> +       }
> +
> +       ret = i2c_master_recv(client, read_buffer, HDMIPHY_REG_COUNT);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to read hdmiphy config\n");
> +               return ret;
> +       }
> +
> +       for (i = 0; i < ret; i++)
> +               DRM_DEBUG_KMS("hdmiphy[0x%02x] wr[0x%02x], rd[0x%02x]\n",
> +                               i, buffer[i], read_buffer[i]);
> +       return 0;
> +}
> +
> +static void hdmiphy_dpms(void *ctx, int mode)
> +{
> +       struct hdmiphy_context *hdata = ctx;
> +
> +       DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
> +
> +       switch (mode) {
> +       case DRM_MODE_DPMS_ON:
> +               if (pm_runtime_suspended(hdata->dev))
> +                       pm_runtime_get_sync(hdata->dev);
> +               break;
> +       case DRM_MODE_DPMS_STANDBY:
> +       case DRM_MODE_DPMS_SUSPEND:
> +       case DRM_MODE_DPMS_OFF:
> +               if (!pm_runtime_suspended(hdata->dev))
> +                       pm_runtime_put_sync(hdata->dev);
> +               break;
> +       default:
> +               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
> +               break;
> +       }
> +}
> +
> +static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
> +                              u8 reg, u8 mask, u8 val)
> +{
> +       int ret;
> +       u8 buffer[2];
> +
> +       buffer[0] = reg;
> +       buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
> +       reg_cache[reg] = buffer[1];
> +
> +       ret = i2c_master_send(client, buffer, 2);
> +       if (ret != 2)
> +               return -EIO;
>
>         return 0;
>  }
>
> +static int hdmiphy_4412_turn_on(struct i2c_client *client, bool on)

This name is misleading. Please change it to something that doesn't
convey a power state, such as hdmiphy_4412_power

> +{
> +       u8 reg_cache[HDMIPHY_REG_COUNT] = { 0 };
> +       u8 buffer[2];
> +       int ret;
> +
> +       DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
> +
> +       /* Cache all hdmi-phy registers to make the code below faster */
> +       buffer[0] = 0x0;
> +       ret = i2c_master_send(client, buffer, 1);
> +       if (ret != 1) {
> +               ret = -EIO;
> +               goto exit;
> +       }
> +       ret = i2c_master_recv(client, reg_cache, HDMIPHY_REG_COUNT);
> +       if (ret != HDMIPHY_REG_COUNT) {
> +               ret = -EIO;
> +               goto exit;
> +       }
> +
> +       /* Change to/from configuration from/to operation mode */
> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_MODE_SET_DONE,
> +                               HDMIPHY_MODE_EN, on ? ~0 : 0);
> +       if (ret)
> +               goto exit;
> +
> +       /*
> +        * Turn off "oscpad" if !on; it turns on again in
> +        * during phy-configuration.
> +        */
> +       if (!on)
> +               ret = hdmiphy_update_bits(client, reg_cache,
> +                       HDMIPHY_4212_OSC_PAD_CON, HDMIPHY_OSC_PAD_EN, 0);
> +               if (ret)
> +                       goto exit;
> +
> +       /* Disable powerdown if on; enable if !on */

This comment really isn't useful, it's just describing the code.

> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
> +                       HDMIPHY_PDEN, on ? 0 : ~0);
> +       if (ret)
> +               goto exit;
> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
> +                       HDMIPHY_PD_ALL, on ? 0 : ~0);
> +       if (ret)
> +               goto exit;
> +
> +       /* Disable pixel clock generator block if !on */
> +       if (!on)
> +               ret = hdmiphy_update_bits(client, reg_cache,
> +                       HDMIPHY_4212_PCG_CON, HDMIPHY_PCG_RESET_EN, 0);
> +               if (ret)
> +                       goto exit;
> +
> +exit:
> +       /* Don't expect any errors so just do a single warn */
> +       WARN_ON(ret);
> +
> +       return ret;
> +}
> +
> +static struct exynos_hdmiphy_ops hdmiphy_ops = {
> +       .check_timing   = hdmiphy_check_timing,
> +       .mode_set               = hdmiphy_mode_set,
> +       .prepare                = hdmiphy_config_prepare,
> +       .config_apply   = hdmiphy_config_apply,
> +       .dpms           = hdmiphy_dpms,
> +};
> +
>  static const struct i2c_device_id hdmiphy_id[] = {
> -       { "s5p_hdmiphy", 0 },
> -       { "exynos5-hdmiphy", 0 },
> +       { "s5p_hdmiphy", HDMIPHY_EXYNOS4210 },
> +       { "exynos5-hdmiphy", HDMIPHY_EXYNOS4212 },
>         { },
>  };
>
> @@ -50,21 +454,183 @@ static const struct i2c_device_id hdmiphy_id[] = {
>  static struct of_device_id hdmiphy_match_types[] = {
>         {
>                 .compatible = "samsung,exynos5-hdmiphy",
> +               .data   = (void *)HDMIPHY_EXYNOS4212,
>         }, {
>                 /* end node */
>         }
>  };
>  #endif
>
> +static int hdmiphy_probe(struct i2c_client *client,
> +       const struct i2c_device_id *id)
> +{
> +       struct device *dev = &client->dev;
> +       struct hdmiphy_context *hdata;
> +       struct exynos_drm_hdmi_context *drm_hdmi_ctx;
> +
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
> +                                       GFP_KERNEL);
> +       if (!drm_hdmi_ctx) {
> +               DRM_ERROR("failed to allocate common hdmi context.\n");
> +               return -ENOMEM;
> +       }
> +
> +       hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
> +       if (!hdata) {
> +               DRM_ERROR("failed to allocate hdmiphy context.\n");
> +               return -ENOMEM;
> +       }
> +
> +       if (dev->of_node) {
> +               const struct of_device_id *match;
> +               match = of_match_node(of_match_ptr(hdmiphy_match_types),
> +                                       dev->of_node);
> +               if (match == NULL)
> +                       return -ENODEV;
> +               hdata->type = (enum hdmiphy_type)match->data;
> +       } else {
> +               hdata->type = (enum hdmiphy_type)id->driver_data;
> +       }
> +
> +       drm_hdmi_ctx->ctx = (void *)hdata;
> +       hdata->parent_ctx = (void *)drm_hdmi_ctx;
> +       hdata->dev = dev;
> +
> +       hdata->hdmiphy = devm_clk_get(dev, "hdmiphy");

I don't see this call removed in the hdmi driver, am I missing something?

> +       if (IS_ERR_OR_NULL(hdata->hdmiphy)) {
> +               DRM_ERROR("failed to get clock 'hdmiphy'\n");
> +               return PTR_ERR(hdata->hdmiphy);
> +       }
> +
> +       i2c_set_clientdata(client, hdata);
> +
> +       /* Attach HDMI-PHY Driver to common hdmi. */
> +       exynos_hdmiphy_drv_attach(drm_hdmi_ctx);
> +
> +       /* register specific callbacks to common hdmi. */
> +       exynos_hdmiphy_ops_register(&hdmiphy_ops);
> +
> +       pm_runtime_enable(dev);
> +
> +       dev_info(&client->adapter->dev,
> +               "attached s5p_hdmiphy into i2c adapter successfully\n");
> +
> +       return 0;
> +}
> +
> +static int hdmiphy_remove(struct i2c_client *client)
> +{
> +       dev_info(&client->adapter->dev,
> +               "detached s5p_hdmiphy from i2c adapter successfully\n");
> +
> +       return 0;
> +}
> +
> +static void hdmiphy_poweroff(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
> +
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       if (hdata->type == HDMIPHY_EXYNOS4212)
> +               hdmiphy_4412_turn_on(client, 0);
> +
> +       clk_disable(hdata->hdmiphy);
> +}
> +
> +static void hdmiphy_poweron(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
> +
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       clk_enable(hdata->hdmiphy);
> +
> +       if (hdata->type == HDMIPHY_EXYNOS4212)
> +               hdmiphy_4412_turn_on(client, 1);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int hdmiphy_suspend(struct device *dev)
> +{
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       if (pm_runtime_suspended(dev)) {
> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
> +               __func__);
> +               return 0;
> +       }
> +
> +       hdmiphy_poweroff(dev);
> +       return 0;
> +}
> +
> +static int hdmiphy_resume(struct device *dev)
> +{
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       if (pm_runtime_suspended(dev)) {
> +               /* dpms callback should resume the mixer. */
> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
> +               __func__);
> +               return 0;
> +       }
> +
> +       hdmiphy_poweron(dev);
> +       return 0;
> +}
> +#endif
> +
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static int hdmiphy_runtime_suspend(struct device *dev)
> +{
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       hdmiphy_poweroff(dev);
> +       return 0;
> +}
> +
> +static int hdmiphy_runtime_resume(struct device *dev)
> +{
> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
> +
> +       hdmiphy_poweron(dev);
> +       return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops hdmiphy_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(hdmiphy_suspend, hdmiphy_resume)
> +       SET_RUNTIME_PM_OPS(hdmiphy_runtime_suspend,
> +               hdmiphy_runtime_resume, NULL)
> +};
> +
>  struct i2c_driver hdmiphy_driver = {
>         .driver = {
>                 .name   = "exynos-hdmiphy",
>                 .owner  = THIS_MODULE,
>                 .of_match_table = of_match_ptr(hdmiphy_match_types),
> +               .pm     = &hdmiphy_pm_ops,
>         },
>         .id_table = hdmiphy_id,
>         .probe          = hdmiphy_probe,
>         .remove         = hdmiphy_remove,
>         .command                = NULL,
>  };
> +
> +extern int exynos_hdmiphy_driver_register(void)
> +{
> +       return i2c_add_driver(&hdmiphy_driver);
> +}
> +
> +extern void exynos_hdmiphy_driver_unregister(void)
> +{
> +       i2c_del_driver(&hdmiphy_driver);
> +}
> +
>  EXPORT_SYMBOL(hdmiphy_driver);
> diff --git a/drivers/gpu/drm/exynos/regs-hdmiphy.h b/drivers/gpu/drm/exynos/regs-hdmiphy.h
> new file mode 100644
> index 0000000..8f906c6
> --- /dev/null
> +++ b/drivers/gpu/drm/exynos/regs-hdmiphy.h
> @@ -0,0 +1,61 @@
> +/*
> + *
> + *  regs-hdmiphy.h
> + *
> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com/
> + *
> + * HDMI-PHY register header file for Samsung TVOUT driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#ifndef SAMSUNG_REGS_HDMIPHY_H
> +#define SAMSUNG_REGS_HDMIPHY_H
> +
> +/*
> + * Register part
> +*/
> +
> +/* HDMI PHY Version Common */
> +#define HDMIPHY_MODE_SET_DONE          (0x1f)
> +
> +/* HDMI PHY Version 4212 */
> +#define HDMIPHY_4212_PCG_CON           (0x04)
> +#define HDMIPHY_4212_OSC_PAD_CON       (0x0b)
> +#define HDMIPHY_4212_PD_CON            (0x1d)
> +
> +/*
> + * Bit definition part
> + */
> +
> +/* HDMIPHY_MODE_SET_DONE */
> +#define HDMIPHY_MODE_EN                        (1 << 7)
> +
> +/* HDMIPHY_4212_PCG_CON */
> +#define HDMIPHY_PCG_RESET_EN           (1 << 3)
> +
> +/* HDMIPHY_4212_OSC_PAD_CON */
> +#define HDMIPHY_OSC_PAD_EN             (3 << 6)
> +
> +/* HDMIPHY_4212_PD_CON */
> +#define HDMIPHY_PDEN                   (1 << 7)
> +
> +#define HDMIPHY_PLL_PD                 (1 << 6)
> +#define HDMIPHY_CLKSER_PD              (1 << 5)
> +#define HDMIPHY_CLKDRV_PD              (1 << 4)
> +
> +#define HDMIPHY_DRV_PD                 (1 << 2)
> +#define HDMIPHY_SER_PD                 (1 << 1)
> +#define HDMIPHY_ICLK_PD                        (1 << 0)
> +
> +#define HDMIPHY_PD_ALL                 (HDMIPHY_PLL_PD |\
> +                                       HDMIPHY_CLKSER_PD |\
> +                                       HDMIPHY_CLKDRV_PD|\
> +                                       HDMIPHY_DRV_PD|\
> +                                       HDMIPHY_SER_PD|\
> +                                       HDMIPHY_ICLK_PD)
> +
> +#endif /* SAMSUNG_REGS_HDMIPHY_H */
> --
> 1.8.0
>
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rahul Sharma March 4, 2013, 2:05 p.m. UTC | #2
Thanks Sean,

On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>
> s/Physicaly/Physically/
>
>> should be instantiated independently.
>>
>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>> other. It is preferred to isolate them and maintained independently.
>>
>> This implementations is tested for:
>> 1) Resolutions supported by exynos4 and 5 hdmi.
>> 2) Runtime PM and S2R scenarions for exynos5.
>>
>
> I don't like the idea of spawning off yet another driver in here. It
> adds more globals, more suspend/resume ordering issues, and more
> implicit dependencies. I understand, however, that this is the Chosen
> Way for the exynos driver, so I will save my rant.
>

I agree to it. splitting phy to a new driver will complicate the power related
scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
config values, mapping (i2c/platform bus) etc. Handling this diversity
inside hdmi driver is complicating it with unrelated changes.

I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
we should re-factor this by explicitly calling power related callbacks
of mixer, phy,
hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
device. AFAIR something like this is already in place in chrome-kernel.

> I've made some comments below.
>
>> This patch is dependent on
>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>
>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>> ---
>> It is based on exynos-drm-next-todo branch at
>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>
>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> index 3da5c2d..03acb6b 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> @@ -334,6 +334,11 @@ static int __init exynos_drm_init(void)
>>         ret = platform_driver_register(&hdmi_driver);
>>         if (ret < 0)
>>                 goto out_hdmi;
>> +
>> +       ret = exynos_hdmiphy_driver_register();
>
> Not sure why you need to obfuscate the type of driver here. All the
> other ones are directly registered as platform drivers, I don't see
> the harm in just doing the i2c_add_driver directly here.
>

AIMB, Phy is likely to get mapped as a platform device in later soc's.
I want to make driver registration (inside exynos_drm_drv.c) independent
of this changes. We can handle this inside exynos_hdmiphy_driver_register().

>> +       if (ret < 0)
>> +               goto out_hdmiphy;
>> +
>>         ret = platform_driver_register(&mixer_driver);
>>         if (ret < 0)
>>                 goto out_mixer;
>
>
> I think this ordering is how you plan on enforcing the suspend/resume
> order for hdmi/mixer/hdmiphy. In that case, however, I don't think it
> makes sense to suspend/resume hdmiphy in between mixer and hdmi.
> Ideally, hdmiphy should go down first and come up last.
>

I just wanted to keep, hdmi and hdmiphy things together. But what you said
above makes sense. I will do that change.

>
>> @@ -436,6 +441,8 @@ out_common_hdmi_dev:
>>  out_common_hdmi:
>>         platform_driver_unregister(&mixer_driver);
>>  out_mixer:
>> +       exynos_hdmiphy_driver_unregister();
>> +out_hdmiphy:
>>         platform_driver_unregister(&hdmi_driver);
>>  out_hdmi:
>>  #endif
>> @@ -479,6 +486,7 @@ static void __exit exynos_drm_exit(void)
>>         exynos_platform_device_hdmi_unregister();
>>         platform_driver_unregister(&exynos_drm_common_hdmi_driver);
>>         platform_driver_unregister(&mixer_driver);
>> +       exynos_hdmiphy_driver_unregister();
>>         platform_driver_unregister(&hdmi_driver);
>>  #endif
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> index 4606fac..17c53e3 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> @@ -325,6 +325,12 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
>>  extern int exynos_platform_device_hdmi_register(void);
>>
>>  /*
>> + * these functions registers/unregisters exynos drm hdmiphy driver.
>> + */
>> +extern int exynos_hdmiphy_driver_register(void);
>> +extern void exynos_hdmiphy_driver_unregister(void);
>> +
>> +/*
>>   * this function unregisters exynos drm hdmi platform device if it exists.
>>   */
>>  void exynos_platform_device_hdmi_unregister(void);
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>> index 5dc956b..45ef331 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>> @@ -32,19 +32,22 @@
>>  /* platform device pointer for common drm hdmi device. */
>>  static struct platform_device *exynos_drm_hdmi_pdev;
>>
>> -/* Common hdmi subdrv needs to access the hdmi and mixer though context.
>> -* These should be initialied by the repective drivers */
>> +/* Common hdmi subdrv needs to access the hdmi, hdmiphy and mixer though
>> +*  context. These should be initialied by the repective drivers */
>>  static struct exynos_drm_hdmi_context *hdmi_ctx;
>> +static struct exynos_drm_hdmi_context *hdmiphy_ctx;
>>  static struct exynos_drm_hdmi_context *mixer_ctx;
>>
>>  /* these callback points shoud be set by specific drivers. */
>>  static struct exynos_hdmi_ops *hdmi_ops;
>> +static struct exynos_hdmiphy_ops *hdmiphy_ops;
>>  static struct exynos_mixer_ops *mixer_ops;
>>
>
> This stuff really sets my teeth on edge. I can't stand these global
> variables and the set-here, set-there, set-here dance that is done on
> probe.
>
> IMO, this and the suspend/resume ordering should be enough motivation
> to avoid separating everything out into their own drivers.
>
> Instead of adding more kruft to this list, we should be working to remove it.
>
> /rant
>

Let me think over it. Please suggest me something if you have any idea to
correct this.

>>  struct drm_hdmi_context {
>>         struct exynos_drm_subdrv        subdrv;
>>         struct exynos_drm_hdmi_context  *hdmi_ctx;
>>         struct exynos_drm_hdmi_context  *mixer_ctx;
>> +       struct exynos_drm_hdmi_context  *hdmiphy_ctx;
>>
>>         bool    enabled[MIXER_WIN_NR];
>>  };
>> @@ -74,6 +77,12 @@ void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>                 hdmi_ctx = ctx;
>>  }
>>
>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx)
>> +{
>> +       if (ctx)
>> +               hdmiphy_ctx = ctx;
>> +}
>> +
>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>  {
>>         if (ctx)
>> @@ -88,6 +97,14 @@ void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
>>                 hdmi_ops = ops;
>>  }
>>
>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops)
>> +{
>> +       DRM_DEBUG_KMS("%s\n", __FILE__);
>
> Listing __FILE__ in these DRM_DEBUG_KMS traces is pretty useless. I'm
> sure we can determine the file from the function name. It would be
> slightly more useful to list __LINE__, but even more useful if you
> actually printed state relevant to the current function.
>

OK. I will correct this.

>> +
>> +       if (ops)
>> +               hdmiphy_ops = ops;
>> +}
>> +
>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
>>  {
>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>> @@ -121,7 +138,7 @@ struct edid *drm_hdmi_get_edid(struct device *dev,
>>         return NULL;
>>  }
>>
>> -static int drm_hdmi_check_timing(struct device *dev, void *timing)
>> +static int drm_hdmi_check_timing(struct device *dev, void *mode)
>>  {
>>         struct drm_hdmi_context *ctx = to_context(dev);
>>         int ret = 0;
>> @@ -129,18 +146,24 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>
>>         /*
>> -       * Both, mixer and hdmi should be able to handle the requested mode.
>> -       * If any of the two fails, return mode as BAD.
>> +       * Mixer, hdmi and hdmiphy should be able to handle the requested mode.
>> +       * If any of the them fails, return mode as BAD.
>>         */
>>
>>         if (mixer_ops && mixer_ops->check_timing)
>> -               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
>> +               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, mode);
>>
>>         if (ret)
>>                 return ret;
>>
>>         if (hdmi_ops && hdmi_ops->check_timing)
>> -               return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
>> +               ret = hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, mode);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (hdmiphy_ops && hdmiphy_ops->check_timing)
>> +               return hdmiphy_ops->check_timing(ctx->hdmiphy_ctx->ctx, mode);
>>
>>         return 0;
>>  }
>> @@ -254,6 +277,9 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
>>
>>         if (hdmi_ops && hdmi_ops->mode_set)
>>                 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
>> +
>> +       if (hdmiphy_ops && hdmiphy_ops->mode_set)
>> +               hdmiphy_ops->mode_set(ctx->hdmiphy_ctx->ctx, mode);
>>  }
>>
>>  static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
>> @@ -273,6 +299,15 @@ static void drm_hdmi_commit(struct device *subdrv_dev)
>>
>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>
>> +       if (hdmiphy_ops && hdmiphy_ops->prepare)
>> +               hdmiphy_ops->prepare(ctx->hdmiphy_ctx->ctx);
>> +
>> +       if (hdmi_ops && hdmi_ops->prepare)
>> +               hdmi_ops->prepare(ctx->hdmi_ctx->ctx);
>> +
>
> Why are you calling these prepare functions in commit? Isn't that
> precisely the reason drm has a prepare callback?

True. Currently exynos_drm_encoder_prepare is blank. I will move this to
prepare callback.

>
>> +       if (hdmiphy_ops && hdmiphy_ops->config_apply)
>> +               hdmiphy_ops->config_apply(ctx->hdmiphy_ctx->ctx);
>> +
>>         if (hdmi_ops && hdmi_ops->commit)
>>                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
>>  }
>> @@ -288,6 +323,9 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
>>
>>         if (hdmi_ops && hdmi_ops->dpms)
>>                 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
>> +
>> +       if (hdmiphy_ops && hdmiphy_ops->dpms)
>> +               hdmiphy_ops->dpms(ctx->hdmiphy_ctx->ctx, mode);
>
> I think you have to be more careful about the ordering of things here
> since you'll want to power on in a different order than you power off.
>
>>  }
>>
>>  static void drm_hdmi_apply(struct device *subdrv_dev)
>> @@ -393,6 +431,11 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>                 return -EFAULT;
>>         }
>>
>> +       if (!hdmiphy_ctx) {
>> +               DRM_ERROR("hdmiphy context not initialized.\n");
>> +               return -EFAULT;
>> +       }
>> +
>>         if (!mixer_ctx) {
>>                 DRM_ERROR("mixer context not initialized.\n");
>>                 return -EFAULT;
>> @@ -406,6 +449,7 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>         }
>>
>>         ctx->hdmi_ctx = hdmi_ctx;
>> +       ctx->hdmiphy_ctx = hdmiphy_ctx;
>>         ctx->mixer_ctx = mixer_ctx;
>>
>>         ctx->hdmi_ctx->drm_dev = drm_dev;
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>> index fd2ff9f..10db62e 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>> @@ -39,10 +39,19 @@ struct exynos_hdmi_ops {
>>         void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>         void (*get_max_resol)(void *ctx, unsigned int *width,
>>                                 unsigned int *height);
>> +       void (*prepare)(void *ctx);
>>         void (*commit)(void *ctx);
>>         void (*dpms)(void *ctx, int mode);
>>  };
>>
>> +struct exynos_hdmiphy_ops {
>> +       int (*check_timing)(void *ctx, struct drm_display_mode *mode);
>> +       void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>> +       void (*prepare)(void *ctx);
>> +       int (*config_apply)(void *ctx);
>> +       void (*dpms)(void *ctx, int mode);
>> +};
>> +
>>  struct exynos_mixer_ops {
>>         /* manager */
>>         int (*iommu_on)(void *ctx, bool enable);
>> @@ -61,7 +70,9 @@ struct exynos_mixer_ops {
>>  };
>>
>>  void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>  void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops);
>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
>>  #endif
>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
>> index a5ca2cc..7ee350d 100644
>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
>> @@ -194,7 +194,6 @@ struct hdmi_context {
>>         int                             internal_irq;
>>
>>         struct i2c_client               *ddc_port;
>> -       struct i2c_client               *hdmiphy_port;
>>
>>         /* current hdmiphy conf regs */
>>         struct hdmi_conf_regs           mode_conf;
>> @@ -206,180 +205,6 @@ struct hdmi_context {
>>         enum hdmi_type                  type;
>>  };
>>
>> -struct hdmiphy_config {
>> -       int pixel_clock;
>> -       u8 conf[32];
>> -};
>> -
>> -/* list of phy config settings */
>> -static const struct hdmiphy_config hdmiphy_v13_configs[] = {
>> -       {
>> -               .pixel_clock = 27000000,
>> -               .conf = {
>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 27027000,
>> -               .conf = {
>> -                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 74176000,
>> -               .conf = {
>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>> -                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> -                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 74250000,
>> -               .conf = {
>> -                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>> -                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>> -                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 148500000,
>> -               .conf = {
>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>> -                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> -                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>> -               },
>> -       },
>> -};
>> -
>> -static const struct hdmiphy_config hdmiphy_v14_configs[] = {
>> -       {
>> -               .pixel_clock = 25200000,
>> -               .conf = {
>> -                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>> -                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 27000000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>> -                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 27027000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>> -                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 36000000,
>> -               .conf = {
>> -                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 40000000,
>> -               .conf = {
>> -                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>> -                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 65000000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>> -                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 74176000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>> -                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 74250000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 83500000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>> -                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 106500000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>> -                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 108000000,
>> -               .conf = {
>> -                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 146250000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>> -                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>> -               },
>> -       },
>> -       {
>> -               .pixel_clock = 148500000,
>> -               .conf = {
>> -                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> -                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>> -               },
>> -       },
>> -};
>> -
>>  struct hdmi_infoframe {
>>         enum HDMI_PACKET_TYPE type;
>>         u8 ver;
>> @@ -774,46 +599,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
>>         return raw_edid;
>>  }
>>
>> -static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
>> -{
>> -       const struct hdmiphy_config *confs;
>> -       int count, i;
>> -
>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> -
>> -       if (hdata->type == HDMI_TYPE13) {
>> -               confs = hdmiphy_v13_configs;
>> -               count = ARRAY_SIZE(hdmiphy_v13_configs);
>> -       } else if (hdata->type == HDMI_TYPE14) {
>> -               confs = hdmiphy_v14_configs;
>> -               count = ARRAY_SIZE(hdmiphy_v14_configs);
>> -       } else
>> -               return -EINVAL;
>> -
>> -       for (i = 0; i < count; i++)
>> -               if (confs[i].pixel_clock == pixel_clock)
>> -                       return i;
>> -
>> -       DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
>> -       return -EINVAL;
>> -}
>> -
>> -static int hdmi_check_timing(void *ctx, struct drm_display_mode *mode)
>> -{
>> -       struct hdmi_context *hdata = ctx;
>> -       int ret;
>> -
>> -       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>> -               __func__, mode->hdisplay, mode->vdisplay,
>> -               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
>> -               false, mode->clock * 1000);
>> -
>> -       ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
>> -       if (ret < 0)
>> -               return ret;
>> -       return 0;
>> -}
>> -
>>  static void hdmi_set_acr(u32 freq, u8 *acr)
>>  {
>>         u32 n, cts;
>> @@ -1309,20 +1094,12 @@ static void hdmi_timing_apply(struct hdmi_context *hdata)
>>
>>  static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>  {
>> -       u8 buffer[2];
>>         u32 reg;
>>
>>         clk_disable(hdata->res.sclk_hdmi);
>>         clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
>>         clk_enable(hdata->res.sclk_hdmi);
>>
>> -       /* operation mode */
>> -       buffer[0] = 0x1f;
>> -       buffer[1] = 0x00;
>> -
>> -       if (hdata->hdmiphy_port)
>> -               i2c_master_send(hdata->hdmiphy_port, buffer, 2);
>> -
>>         if (hdata->type == HDMI_TYPE13)
>>                 reg = HDMI_V13_PHY_RSTOUT;
>>         else
>> @@ -1335,102 +1112,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>         usleep_range(10000, 12000);
>>  }
>>
>> -static void hdmiphy_poweron(struct hdmi_context *hdata)
>> -{
>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> -
>> -       if (hdata->type == HDMI_TYPE14)
>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
>> -                       HDMI_PHY_POWER_OFF_EN);
>> -}
>> -
>> -static void hdmiphy_poweroff(struct hdmi_context *hdata)
>> -{
>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> -
>> -       if (hdata->type == HDMI_TYPE14)
>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
>> -                       HDMI_PHY_POWER_OFF_EN);
>
> I assume these register writes are replaced with i2c operations in the
> new phy driver?

It is done for exynos5 phy. I am not having the sequence for exynos4. I
will add it later.

>
>> -}
>> -
>> -static void hdmiphy_conf_apply(struct hdmi_context *hdata)
>> -{
>> -       const u8 *hdmiphy_data;
>> -       u8 buffer[32];
>> -       u8 operation[2];
>> -       u8 read_buffer[32] = {0, };
>> -       int ret;
>> -       int i;
>> -
>> -       if (!hdata->hdmiphy_port) {
>> -               DRM_ERROR("hdmiphy is not attached\n");
>> -               return;
>> -       }
>> -
>> -       /* pixel clock */
>> -       i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
>> -       if (i < 0) {
>> -               DRM_ERROR("failed to find hdmiphy conf\n");
>> -               return;
>> -       }
>> -
>> -       if (hdata->type == HDMI_TYPE13) {
>> -               hdmiphy_data = hdmiphy_v13_configs[i].conf;
>> -       } else {
>> -               hdmiphy_data = hdmiphy_v14_configs[i].conf;
>> -       }
>> -
>> -       memcpy(buffer, hdmiphy_data, 32);
>> -       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
>> -       if (ret != 32) {
>> -               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>> -               return;
>> -       }
>> -
>> -       usleep_range(10000, 12000);
>> -
>> -       /* operation mode */
>> -       operation[0] = 0x1f;
>> -       operation[1] = 0x80;
>> -
>> -       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
>> -       if (ret != 2) {
>> -               DRM_ERROR("failed to enable hdmiphy\n");
>> -               return;
>> -       }
>> -
>> -       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
>> -       if (ret < 0) {
>> -               DRM_ERROR("failed to read hdmiphy config\n");
>> -               return;
>> -       }
>> -
>> -       for (i = 0; i < ret; i++)
>> -               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
>> -                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
>> -}
>> -
>> -static void hdmi_conf_apply(struct hdmi_context *hdata)
>> -{
>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> -
>> -       hdmiphy_conf_reset(hdata);
>> -       hdmiphy_conf_apply(hdata);
>> -
>> -       mutex_lock(&hdata->hdmi_mutex);
>> -       hdmi_conf_reset(hdata);
>> -       hdmi_conf_init(hdata);
>> -       mutex_unlock(&hdata->hdmi_mutex);
>> -
>> -       hdmi_audio_init(hdata);
>> -
>> -       /* setting core registers */
>> -       hdmi_timing_apply(hdata);
>> -       hdmi_audio_control(hdata, true);
>> -
>> -       hdmi_regs_dump(hdata, "start");
>> -}
>> -
>>  static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
>>  {
>>         int i;
>> @@ -1448,7 +1129,6 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
>>
>>         hdata->mode_conf.cea_video_id =
>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>
> I don't think you use pixel_clock in mode_conf any longer, please
> remove it from the struct.
>

Correct. I will remove pixel_clock from struct hdmi_conf_regs.

>>
>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>         hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
>> @@ -1544,7 +1224,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
>>
>>         hdata->mode_conf.cea_video_id =
>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>
>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>         hdmi_set_reg(core->v_line, 2, m->vtotal);
>> @@ -1670,13 +1349,32 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width,
>>         *height = MAX_HEIGHT;
>>  }
>>
>> +static void hdmi_commit_prepare(void *ctx)
>
> I don't know why the "_commit" is there, just hdmi_prepare will be fine.
>
Ok.

>> +{
>> +       struct hdmi_context *hdata = ctx;
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       hdmiphy_conf_reset(hdata);
>
> There's still phy-specific kruft laying around the driver. There are
> now a bunch of implicit dependencies between the hdmi driver and the
> hdmiphy driver.
>

I need to follow this sequence:

phy off (hdmiphy_prepare) -> phy reset using HDMI IP REG (hdmi_prepare) ->
phy config (hdmiphy_config_apply) -> hdmi ip config  (hdmi_commit)

Phy reset needs to be done by toggling Phy_reset bit in HDMI IP register which
cannot be accessed in hdmiphy driver. I didn't see any better way.

> Another example is the sclk_hdmiphy clock, which is still controlled
> by the hdmi driver, it should be controlled by the phy.
>

sclk_hdmiphy is one of the source clock for hdmi IP. HDMI IP needs to change the
source between sclk_hdmiphy (only if stable) else pixel clock. Above
code looks ok
to me. Please explain a bit mode on this.

>> +}
>> +
>>  static void hdmi_commit(void *ctx)
>>  {
>>         struct hdmi_context *hdata = ctx;
>>
>>         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>
>> -       hdmi_conf_apply(hdata);
>> +       mutex_lock(&hdata->hdmi_mutex);
>> +       hdmi_conf_reset(hdata);
>> +       hdmi_conf_init(hdata);
>> +       mutex_unlock(&hdata->hdmi_mutex);
>> +
>> +       hdmi_audio_init(hdata);
>> +
>> +       /* setting core registers */
>> +       hdmi_timing_apply(hdata);
>> +       hdmi_audio_control(hdata, true);
>> +
>> +       hdmi_regs_dump(hdata, "start");
>>  }
>>
>>  static void hdmi_poweron(struct hdmi_context *hdata)
>> @@ -1699,8 +1397,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
>>         clk_enable(res->hdmiphy);
>>         clk_enable(res->hdmi);
>>         clk_enable(res->sclk_hdmi);
>> -
>> -       hdmiphy_poweron(hdata);
>>  }
>>
>>  static void hdmi_poweroff(struct hdmi_context *hdata)
>> @@ -1719,7 +1415,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
>>          * its reset state seems to meet the condition.
>>          */
>>         hdmiphy_conf_reset(hdata);
>> -       hdmiphy_poweroff(hdata);
>>
>>         clk_disable(res->sclk_hdmi);
>>         clk_disable(res->hdmi);
>> @@ -1761,11 +1456,11 @@ static struct exynos_hdmi_ops hdmi_ops = {
>>         /* display */
>>         .is_connected   = hdmi_is_connected,
>>         .get_edid       = hdmi_get_edid,
>> -       .check_timing   = hdmi_check_timing,
>>
>>         /* manager */
>>         .mode_set       = hdmi_mode_set,
>>         .get_max_resol  = hdmi_get_max_resol,
>> +       .prepare                = hdmi_commit_prepare,
>>         .commit         = hdmi_commit,
>>         .dpms           = hdmi_dpms,
>>  };
>> @@ -1878,7 +1573,7 @@ fail:
>>         return -ENODEV;
>>  }
>>
>> -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
>> +static struct i2c_client *hdmi_ddc;
>>
>>  void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>  {
>> @@ -1886,12 +1581,6 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>                 hdmi_ddc = ddc;
>>  }
>>
>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
>> -{
>> -       if (hdmiphy)
>> -               hdmi_hdmiphy = hdmiphy;
>> -}
>> -
>>  #ifdef CONFIG_OF
>>  static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
>>                                         (struct device *dev)
>> @@ -2051,27 +1740,18 @@ static int hdmi_probe(struct platform_device *pdev)
>>
>>         hdata->ddc_port = hdmi_ddc;
>>
>> -       /* hdmiphy i2c driver */
>> -       if (i2c_add_driver(&hdmiphy_driver)) {
>> -               DRM_ERROR("failed to register hdmiphy i2c driver\n");
>> -               ret = -ENOENT;
>> -               goto err_ddc;
>> -       }
>> -
>> -       hdata->hdmiphy_port = hdmi_hdmiphy;
>> -
>>         hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
>>         if (hdata->external_irq < 0) {
>>                 DRM_ERROR("failed to get GPIO external irq\n");
>>                 ret = hdata->external_irq;
>> -               goto err_hdmiphy;
>> +               goto err_ddc;
>>         }
>>
>>         hdata->internal_irq = platform_get_irq(pdev, 0);
>>         if (hdata->internal_irq < 0) {
>>                 DRM_ERROR("failed to get platform internal irq\n");
>>                 ret = hdata->internal_irq;
>> -               goto err_hdmiphy;
>> +               goto err_ddc;
>>         }
>>
>>         hdata->hpd = gpio_get_value(hdata->hpd_gpio);
>> @@ -2082,7 +1762,7 @@ static int hdmi_probe(struct platform_device *pdev)
>>                         "hdmi_external", drm_hdmi_ctx);
>>         if (ret) {
>>                 DRM_ERROR("failed to register hdmi external interrupt\n");
>> -               goto err_hdmiphy;
>> +               goto err_ddc;
>>         }
>>
>>         ret = request_threaded_irq(hdata->internal_irq, NULL,
>> @@ -2105,8 +1785,6 @@ static int hdmi_probe(struct platform_device *pdev)
>>
>>  err_free_irq:
>>         free_irq(hdata->external_irq, drm_hdmi_ctx);
>> -err_hdmiphy:
>> -       i2c_del_driver(&hdmiphy_driver);
>>  err_ddc:
>>         i2c_del_driver(&ddc_driver);
>>         return ret;
>> @@ -2125,9 +1803,6 @@ static int hdmi_remove(struct platform_device *pdev)
>>         free_irq(hdata->internal_irq, hdata);
>>         free_irq(hdata->external_irq, hdata);
>>
>> -
>> -       /* hdmiphy i2c driver */
>> -       i2c_del_driver(&hdmiphy_driver);
>>         /* DDC i2c driver */
>>         i2c_del_driver(&ddc_driver);
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
>> index 0ddf395..6595d7b 100644
>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.h
>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
>> @@ -15,7 +15,6 @@
>>  #define _EXYNOS_HDMI_H_
>>
>>  void hdmi_attach_ddc_client(struct i2c_client *ddc);
>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
>>
>>  extern struct i2c_driver hdmiphy_driver;
>>  extern struct i2c_driver ddc_driver;
>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>> index ea49d13..0d47302 100644
>> --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>> +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>> @@ -16,33 +16,437 @@
>>  #include <linux/kernel.h>
>>  #include <linux/i2c.h>
>>  #include <linux/module.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/clk.h>
>>
>>  #include "exynos_drm_drv.h"
>> +#include "exynos_drm_hdmi.h"
>>  #include "exynos_hdmi.h"
>>
>> +#include "regs-hdmiphy.h"
>>
>> -static int hdmiphy_probe(struct i2c_client *client,
>> -       const struct i2c_device_id *id)
>> +#define HDMIPHY_REG_COUNT              (32)
>
> No need to include parentheses.

Ok.
>
>> +
>> +enum hdmiphy_type {
>> +       HDMIPHY_EXYNOS4210,
>> +       HDMIPHY_EXYNOS4212,
>> +};
>> +
>> +struct hdmiphy_context {
>> +       struct device           *dev;
>> +       bool                    powered;
>> +       void                    *parent_ctx;
>> +       enum hdmiphy_type               type;
>> +       const struct hdmiphy_config     *conf;
>> +       struct clk              *hdmiphy;
>> +};
>> +
>> +struct hdmiphy_config {
>> +       int pixel_clock;
>> +       u8 conf[HDMIPHY_REG_COUNT];
>> +};
>> +
>> +/* list of all required phy config settings */
>> +static const struct hdmiphy_config hdmiphy_4212_configs[] = {
>> +       {
>> +               .pixel_clock = 25200000,
>> +               .conf = {
>> +                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>> +                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 27000000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>> +                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 27027000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>> +                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 36000000,
>> +               .conf = {
>> +                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 40000000,
>> +               .conf = {
>> +                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>> +                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 65000000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>> +                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 74176000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>> +                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 74250000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 83500000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>> +                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 106500000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>> +                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 108000000,
>> +               .conf = {
>> +                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 146250000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>> +                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 148500000,
>> +               .conf = {
>> +                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>> +                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>> +               },
>> +       },
>> +};
>> +
>> +static const struct hdmiphy_config hdmiphy_4210_configs[] = {
>> +       {
>> +               .pixel_clock = 27000000,
>> +               .conf = {
>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 27027000,
>> +               .conf = {
>> +                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 74176000,
>> +               .conf = {
>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>> +                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> +                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 74250000,
>> +               .conf = {
>> +                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>> +                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>> +                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>> +               },
>> +       },
>> +       {
>> +               .pixel_clock = 148500000,
>> +               .conf = {
>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>> +                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>> +                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>> +               },
>> +       },
>> +};
>> +
>> +static const struct hdmiphy_config *hdmiphy_find_conf(void *ctx,
>> +                       const struct drm_display_mode *mode)
>> +{
>> +       struct hdmiphy_context *hdata = ctx;
>> +       const struct hdmiphy_config *confs;
>> +       int count, i;
>> +
>> +       switch (hdata->type) {
>> +       case HDMIPHY_EXYNOS4212:
>> +               confs = hdmiphy_4212_configs;
>> +               count = ARRAY_SIZE(hdmiphy_4212_configs);
>> +               break;
>> +       case HDMIPHY_EXYNOS4210:
>> +               confs = hdmiphy_4210_configs;
>> +               count = ARRAY_SIZE(hdmiphy_4210_configs);
>> +               break;
>> +       default:
>> +               return NULL;
>
> Should probably DRM_ERROR here.
>
ok. I will add that.

>> +       }
>> +
>> +       for (i = 0; i < count; i++)
>> +               if (confs[i].pixel_clock == (mode->clock * 1000))
>> +                       return &confs[i];
>> +
>> +       return NULL;
>> +}
>> +
>> +static int hdmiphy_check_timing(void *ctx, struct drm_display_mode *mode)
>>  {
>> -       hdmi_attach_hdmiphy_client(client);
>> +       const struct hdmiphy_config *conf;
>> +       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>> +               __func__, mode->hdisplay, mode->vdisplay,
>> +               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
>> +               ? true : false, mode->clock * 1000);
>
> No need to print __func__ here, DRM_DEBUG_KMS will do that for you. I
> think you should also parse the flags to a string instead of a bool
> cast to an int.
>
ok.

>>
>> -       dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
>> -               "into i2c adapter successfully\n");
>> +       conf = hdmiphy_find_conf(ctx, mode);
>> +       if (!conf) {
>> +               DRM_DEBUG_KMS("Display Mode is not supported.\n");
>> +               return -EINVAL;
>> +       }
>>
>>         return 0;
>>  }
>>
>> -static int hdmiphy_remove(struct i2c_client *client)
>> +static void hdmiphy_mode_set(void *ctx, struct drm_display_mode *mode)
>> +{
>> +       struct hdmiphy_context *hdata = ctx;
>> +
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>
> No need to print __func__ here, DRM_DEBUG_KMS will do that for you.
> This comment stand for the rest of the calls, as well.
>
>> +
>> +       hdata->conf = hdmiphy_find_conf(ctx, mode);
>> +}
>> +
>> +static void hdmiphy_config_prepare(void *ctx)
>> +{
>> +       struct hdmiphy_context *hdata = ctx;
>> +       const struct i2c_client *client = to_i2c_client(hdata->dev);
>> +       u8 buffer[2];
>> +
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       /* operation mode */
>> +       buffer[0] = HDMIPHY_MODE_SET_DONE;
>> +       buffer[1] = 0x00;
>> +
>> +       if (client)
>> +               i2c_master_send(client, buffer, 2);
>> +}
>> +
>> +static int hdmiphy_config_apply(void *ctx)
>>  {
>> -       dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
>> -               "from i2c adapter successfully\n");
>> +       struct hdmiphy_context *hdata = ctx;
>> +       struct i2c_client *client = to_i2c_client(hdata->dev);
>> +       const u8 *hdmiphy_data;
>> +       u8 buffer[HDMIPHY_REG_COUNT];
>> +       u8 operation[2];
>> +       u8 read_buffer[HDMIPHY_REG_COUNT] = {0, };
>> +       int ret;
>> +       int i;
>> +
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       /* pixel clock */
>> +       if (hdata->conf && client)
>> +               hdmiphy_data = hdata->conf->conf;
>> +       else
>> +               return -EINVAL;
>> +
>> +       memcpy(buffer, hdmiphy_data, HDMIPHY_REG_COUNT);
>> +
>> +       ret = i2c_master_send(client, buffer, HDMIPHY_REG_COUNT);
>> +       if (ret != HDMIPHY_REG_COUNT) {
>> +               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>> +               return ret;
>> +       }
>> +
>> +       usleep_range(10000, 12000);
>> +
>> +       /* operation mode */
>> +       operation[0] = HDMIPHY_MODE_SET_DONE;
>> +       operation[1] = HDMIPHY_MODE_EN;
>> +
>> +       ret = i2c_master_send(client, operation, 2);
>> +       if (ret != 2) {
>> +               DRM_ERROR("failed to enable hdmiphy\n");
>> +               return ret;
>> +       }
>> +
>> +       ret = i2c_master_recv(client, read_buffer, HDMIPHY_REG_COUNT);
>> +       if (ret < 0) {
>> +               DRM_ERROR("failed to read hdmiphy config\n");
>> +               return ret;
>> +       }
>> +
>> +       for (i = 0; i < ret; i++)
>> +               DRM_DEBUG_KMS("hdmiphy[0x%02x] wr[0x%02x], rd[0x%02x]\n",
>> +                               i, buffer[i], read_buffer[i]);
>> +       return 0;
>> +}
>> +
>> +static void hdmiphy_dpms(void *ctx, int mode)
>> +{
>> +       struct hdmiphy_context *hdata = ctx;
>> +
>> +       DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
>> +
>> +       switch (mode) {
>> +       case DRM_MODE_DPMS_ON:
>> +               if (pm_runtime_suspended(hdata->dev))
>> +                       pm_runtime_get_sync(hdata->dev);
>> +               break;
>> +       case DRM_MODE_DPMS_STANDBY:
>> +       case DRM_MODE_DPMS_SUSPEND:
>> +       case DRM_MODE_DPMS_OFF:
>> +               if (!pm_runtime_suspended(hdata->dev))
>> +                       pm_runtime_put_sync(hdata->dev);
>> +               break;
>> +       default:
>> +               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
>> +               break;
>> +       }
>> +}
>> +
>> +static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
>> +                              u8 reg, u8 mask, u8 val)
>> +{
>> +       int ret;
>> +       u8 buffer[2];
>> +
>> +       buffer[0] = reg;
>> +       buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
>> +       reg_cache[reg] = buffer[1];
>> +
>> +       ret = i2c_master_send(client, buffer, 2);
>> +       if (ret != 2)
>> +               return -EIO;
>>
>>         return 0;
>>  }
>>
>> +static int hdmiphy_4412_turn_on(struct i2c_client *client, bool on)
>
> This name is misleading. Please change it to something that doesn't
> convey a power state, such as hdmiphy_4412_power
>
ok. I will change that.

>> +{
>> +       u8 reg_cache[HDMIPHY_REG_COUNT] = { 0 };
>> +       u8 buffer[2];
>> +       int ret;
>> +
>> +       DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
>> +
>> +       /* Cache all hdmi-phy registers to make the code below faster */
>> +       buffer[0] = 0x0;
>> +       ret = i2c_master_send(client, buffer, 1);
>> +       if (ret != 1) {
>> +               ret = -EIO;
>> +               goto exit;
>> +       }
>> +       ret = i2c_master_recv(client, reg_cache, HDMIPHY_REG_COUNT);
>> +       if (ret != HDMIPHY_REG_COUNT) {
>> +               ret = -EIO;
>> +               goto exit;
>> +       }
>> +
>> +       /* Change to/from configuration from/to operation mode */
>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_MODE_SET_DONE,
>> +                               HDMIPHY_MODE_EN, on ? ~0 : 0);
>> +       if (ret)
>> +               goto exit;
>> +
>> +       /*
>> +        * Turn off "oscpad" if !on; it turns on again in
>> +        * during phy-configuration.
>> +        */
>> +       if (!on)
>> +               ret = hdmiphy_update_bits(client, reg_cache,
>> +                       HDMIPHY_4212_OSC_PAD_CON, HDMIPHY_OSC_PAD_EN, 0);
>> +               if (ret)
>> +                       goto exit;
>> +
>> +       /* Disable powerdown if on; enable if !on */
>
> This comment really isn't useful, it's just describing the code.

I will remove this comment.

>
>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>> +                       HDMIPHY_PDEN, on ? 0 : ~0);
>> +       if (ret)
>> +               goto exit;
>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>> +                       HDMIPHY_PD_ALL, on ? 0 : ~0);
>> +       if (ret)
>> +               goto exit;
>> +
>> +       /* Disable pixel clock generator block if !on */
>> +       if (!on)
>> +               ret = hdmiphy_update_bits(client, reg_cache,
>> +                       HDMIPHY_4212_PCG_CON, HDMIPHY_PCG_RESET_EN, 0);
>> +               if (ret)
>> +                       goto exit;
>> +
>> +exit:
>> +       /* Don't expect any errors so just do a single warn */
>> +       WARN_ON(ret);
>> +
>> +       return ret;
>> +}
>> +
>> +static struct exynos_hdmiphy_ops hdmiphy_ops = {
>> +       .check_timing   = hdmiphy_check_timing,
>> +       .mode_set               = hdmiphy_mode_set,
>> +       .prepare                = hdmiphy_config_prepare,
>> +       .config_apply   = hdmiphy_config_apply,
>> +       .dpms           = hdmiphy_dpms,
>> +};
>> +
>>  static const struct i2c_device_id hdmiphy_id[] = {
>> -       { "s5p_hdmiphy", 0 },
>> -       { "exynos5-hdmiphy", 0 },
>> +       { "s5p_hdmiphy", HDMIPHY_EXYNOS4210 },
>> +       { "exynos5-hdmiphy", HDMIPHY_EXYNOS4212 },
>>         { },
>>  };
>>
>> @@ -50,21 +454,183 @@ static const struct i2c_device_id hdmiphy_id[] = {
>>  static struct of_device_id hdmiphy_match_types[] = {
>>         {
>>                 .compatible = "samsung,exynos5-hdmiphy",
>> +               .data   = (void *)HDMIPHY_EXYNOS4212,
>>         }, {
>>                 /* end node */
>>         }
>>  };
>>  #endif
>>
>> +static int hdmiphy_probe(struct i2c_client *client,
>> +       const struct i2c_device_id *id)
>> +{
>> +       struct device *dev = &client->dev;
>> +       struct hdmiphy_context *hdata;
>> +       struct exynos_drm_hdmi_context *drm_hdmi_ctx;
>> +
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
>> +                                       GFP_KERNEL);
>> +       if (!drm_hdmi_ctx) {
>> +               DRM_ERROR("failed to allocate common hdmi context.\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
>> +       if (!hdata) {
>> +               DRM_ERROR("failed to allocate hdmiphy context.\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       if (dev->of_node) {
>> +               const struct of_device_id *match;
>> +               match = of_match_node(of_match_ptr(hdmiphy_match_types),
>> +                                       dev->of_node);
>> +               if (match == NULL)
>> +                       return -ENODEV;
>> +               hdata->type = (enum hdmiphy_type)match->data;
>> +       } else {
>> +               hdata->type = (enum hdmiphy_type)id->driver_data;
>> +       }
>> +
>> +       drm_hdmi_ctx->ctx = (void *)hdata;
>> +       hdata->parent_ctx = (void *)drm_hdmi_ctx;
>> +       hdata->dev = dev;
>> +
>> +       hdata->hdmiphy = devm_clk_get(dev, "hdmiphy");
>
> I don't see this call removed in the hdmi driver, am I missing something?
>

I took it wrong. I thought hdmi IP is clocked by hdmiphy clock. Actually it is
sclk_hdmiphy clock. I will remove this from hdmi driver.

>> +       if (IS_ERR_OR_NULL(hdata->hdmiphy)) {
>> +               DRM_ERROR("failed to get clock 'hdmiphy'\n");
>> +               return PTR_ERR(hdata->hdmiphy);
>> +       }
>> +
>> +       i2c_set_clientdata(client, hdata);
>> +
>> +       /* Attach HDMI-PHY Driver to common hdmi. */
>> +       exynos_hdmiphy_drv_attach(drm_hdmi_ctx);
>> +
>> +       /* register specific callbacks to common hdmi. */
>> +       exynos_hdmiphy_ops_register(&hdmiphy_ops);
>> +
>> +       pm_runtime_enable(dev);
>> +
>> +       dev_info(&client->adapter->dev,
>> +               "attached s5p_hdmiphy into i2c adapter successfully\n");
>> +
>> +       return 0;
>> +}
>> +
>> +static int hdmiphy_remove(struct i2c_client *client)
>> +{
>> +       dev_info(&client->adapter->dev,
>> +               "detached s5p_hdmiphy from i2c adapter successfully\n");
>> +
>> +       return 0;
>> +}
>> +
>> +static void hdmiphy_poweroff(struct device *dev)
>> +{
>> +       struct i2c_client *client = to_i2c_client(dev);
>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>> +
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>> +               hdmiphy_4412_turn_on(client, 0);
>> +
>> +       clk_disable(hdata->hdmiphy);
>> +}
>> +
>> +static void hdmiphy_poweron(struct device *dev)
>> +{
>> +       struct i2c_client *client = to_i2c_client(dev);
>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>> +
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       clk_enable(hdata->hdmiphy);
>> +
>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>> +               hdmiphy_4412_turn_on(client, 1);
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int hdmiphy_suspend(struct device *dev)
>> +{
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       if (pm_runtime_suspended(dev)) {
>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>> +               __func__);
>> +               return 0;
>> +       }
>> +
>> +       hdmiphy_poweroff(dev);
>> +       return 0;
>> +}
>> +
>> +static int hdmiphy_resume(struct device *dev)
>> +{
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       if (pm_runtime_suspended(dev)) {
>> +               /* dpms callback should resume the mixer. */
>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>> +               __func__);
>> +               return 0;
>> +       }
>> +
>> +       hdmiphy_poweron(dev);
>> +       return 0;
>> +}
>> +#endif
>> +
>> +
>> +#ifdef CONFIG_PM_RUNTIME
>> +static int hdmiphy_runtime_suspend(struct device *dev)
>> +{
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       hdmiphy_poweroff(dev);
>> +       return 0;
>> +}
>> +
>> +static int hdmiphy_runtime_resume(struct device *dev)
>> +{
>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>> +
>> +       hdmiphy_poweron(dev);
>> +       return 0;
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops hdmiphy_pm_ops = {
>> +       SET_SYSTEM_SLEEP_PM_OPS(hdmiphy_suspend, hdmiphy_resume)
>> +       SET_RUNTIME_PM_OPS(hdmiphy_runtime_suspend,
>> +               hdmiphy_runtime_resume, NULL)
>> +};
>> +
>>  struct i2c_driver hdmiphy_driver = {
>>         .driver = {
>>                 .name   = "exynos-hdmiphy",
>>                 .owner  = THIS_MODULE,
>>                 .of_match_table = of_match_ptr(hdmiphy_match_types),
>> +               .pm     = &hdmiphy_pm_ops,
>>         },
>>         .id_table = hdmiphy_id,
>>         .probe          = hdmiphy_probe,
>>         .remove         = hdmiphy_remove,
>>         .command                = NULL,
>>  };
>> +
>> +extern int exynos_hdmiphy_driver_register(void)
>> +{
>> +       return i2c_add_driver(&hdmiphy_driver);
>> +}
>> +
>> +extern void exynos_hdmiphy_driver_unregister(void)
>> +{
>> +       i2c_del_driver(&hdmiphy_driver);
>> +}
>> +
>>  EXPORT_SYMBOL(hdmiphy_driver);
>> diff --git a/drivers/gpu/drm/exynos/regs-hdmiphy.h b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>> new file mode 100644
>> index 0000000..8f906c6
>> --- /dev/null
>> +++ b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>> @@ -0,0 +1,61 @@
>> +/*
>> + *
>> + *  regs-hdmiphy.h
>> + *
>> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
>> + * http://www.samsung.com/
>> + *
>> + * HDMI-PHY register header file for Samsung TVOUT driver
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#ifndef SAMSUNG_REGS_HDMIPHY_H
>> +#define SAMSUNG_REGS_HDMIPHY_H
>> +
>> +/*
>> + * Register part
>> +*/
>> +
>> +/* HDMI PHY Version Common */
>> +#define HDMIPHY_MODE_SET_DONE          (0x1f)
>> +
>> +/* HDMI PHY Version 4212 */
>> +#define HDMIPHY_4212_PCG_CON           (0x04)
>> +#define HDMIPHY_4212_OSC_PAD_CON       (0x0b)
>> +#define HDMIPHY_4212_PD_CON            (0x1d)
>> +
>> +/*
>> + * Bit definition part
>> + */
>> +
>> +/* HDMIPHY_MODE_SET_DONE */
>> +#define HDMIPHY_MODE_EN                        (1 << 7)
>> +
>> +/* HDMIPHY_4212_PCG_CON */
>> +#define HDMIPHY_PCG_RESET_EN           (1 << 3)
>> +
>> +/* HDMIPHY_4212_OSC_PAD_CON */
>> +#define HDMIPHY_OSC_PAD_EN             (3 << 6)
>> +
>> +/* HDMIPHY_4212_PD_CON */
>> +#define HDMIPHY_PDEN                   (1 << 7)
>> +
>> +#define HDMIPHY_PLL_PD                 (1 << 6)
>> +#define HDMIPHY_CLKSER_PD              (1 << 5)
>> +#define HDMIPHY_CLKDRV_PD              (1 << 4)
>> +
>> +#define HDMIPHY_DRV_PD                 (1 << 2)
>> +#define HDMIPHY_SER_PD                 (1 << 1)
>> +#define HDMIPHY_ICLK_PD                        (1 << 0)
>> +
>> +#define HDMIPHY_PD_ALL                 (HDMIPHY_PLL_PD |\
>> +                                       HDMIPHY_CLKSER_PD |\
>> +                                       HDMIPHY_CLKDRV_PD|\
>> +                                       HDMIPHY_DRV_PD|\
>> +                                       HDMIPHY_SER_PD|\
>> +                                       HDMIPHY_ICLK_PD)
>> +
>> +#endif /* SAMSUNG_REGS_HDMIPHY_H */
>> --
>> 1.8.0
>>
Seung-Woo Kim March 7, 2013, 2:44 a.m. UTC | #3
On 2013? 03? 04? 23:05, Rahul Sharma wrote:
> Thanks Sean,
> 
> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>
>> s/Physicaly/Physically/
>>
>>> should be instantiated independently.
>>>
>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>> other. It is preferred to isolate them and maintained independently.
>>>
>>> This implementations is tested for:
>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>
>>
>> I don't like the idea of spawning off yet another driver in here. It
>> adds more globals, more suspend/resume ordering issues, and more
>> implicit dependencies. I understand, however, that this is the Chosen
>> Way for the exynos driver, so I will save my rant.
>>
> 
> I agree to it. splitting phy to a new driver will complicate the power related
> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
> config values, mapping (i2c/platform bus) etc. Handling this diversity
> inside hdmi driver is complicating it with unrelated changes.

Basically, I agree with the idea to split hdmiphy from hdmi. And it
seems that already existing hdmiphy i2c device is just reused and
hdmiphy_power_on is reorganized to hdmiphy dpms operation: even calling
flow of power operations is reordered.

But I'm not sure exynos_hdmiphy_driver_register() really need to be
called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough to
call exynos_hdmiphy_driver_register() from hdmi_probe() because hdmiphy
is only used from hdmi.

Thanks and Regards,
- Seung-Woo Kim

> 
> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
> we should re-factor this by explicitly calling power related callbacks
> of mixer, phy,
> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
> device. AFAIR something like this is already in place in chrome-kernel.
> 
>> I've made some comments below.
>>
>>> This patch is dependent on
>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>
>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>> ---
>>> It is based on exynos-drm-next-todo branch at
>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>
>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>

<snip>
Inki Dae March 7, 2013, 4:43 a.m. UTC | #4
2013/3/7 ??? <sw0312.kim@samsung.com>:
>
>
> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
>> Thanks Sean,
>>
>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>
>>> s/Physicaly/Physically/
>>>
>>>> should be instantiated independently.
>>>>
>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>> other. It is preferred to isolate them and maintained independently.
>>>>
>>>> This implementations is tested for:
>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>
>>>
>>> I don't like the idea of spawning off yet another driver in here. It
>>> adds more globals, more suspend/resume ordering issues, and more
>>> implicit dependencies. I understand, however, that this is the Chosen
>>> Way for the exynos driver, so I will save my rant.
>>>
>>
>> I agree to it. splitting phy to a new driver will complicate the power related
>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>> inside hdmi driver is complicating it with unrelated changes.
>
> Basically, I agree with the idea to split hdmiphy from hdmi. And it
> seems that already existing hdmiphy i2c device is just reused and
> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even calling
> flow of power operations is reordered.
>
> But I'm not sure exynos_hdmiphy_driver_register() really need to be
> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough to
> call exynos_hdmiphy_driver_register() from hdmi_probe() because hdmiphy
> is only used from hdmi.
>

I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.

Thanks,
Inki Dae

> Thanks and Regards,
> - Seung-Woo Kim
>
>>
>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>> we should re-factor this by explicitly calling power related callbacks
>> of mixer, phy,
>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>> device. AFAIR something like this is already in place in chrome-kernel.
>>
>>> I've made some comments below.
>>>
>>>> This patch is dependent on
>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>
>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>> ---
>>>> It is based on exynos-drm-next-todo branch at
>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>
>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>
>
> <snip>
>
> --
> Seung-Woo Kim
> Samsung Software R&D Center
> --
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rahul Sharma March 7, 2013, 6:45 a.m. UTC | #5
Thanks Seung Woo, Mr. Dae,

On Thu, Mar 7, 2013 at 10:13 AM, Inki Dae <inki.dae@samsung.com> wrote:
> 2013/3/7 ??? <sw0312.kim@samsung.com>:
>>
>>
>> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
>>> Thanks Sean,
>>>
>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>>
>>>> s/Physicaly/Physically/
>>>>
>>>>> should be instantiated independently.
>>>>>
>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>>> other. It is preferred to isolate them and maintained independently.
>>>>>
>>>>> This implementations is tested for:
>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>>
>>>>
>>>> I don't like the idea of spawning off yet another driver in here. It
>>>> adds more globals, more suspend/resume ordering issues, and more
>>>> implicit dependencies. I understand, however, that this is the Chosen
>>>> Way for the exynos driver, so I will save my rant.
>>>>
>>>
>>> I agree to it. splitting phy to a new driver will complicate the power related
>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>>> inside hdmi driver is complicating it with unrelated changes.
>>
>> Basically, I agree with the idea to split hdmiphy from hdmi. And it
>> seems that already existing hdmiphy i2c device is just reused and
>> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even calling
>> flow of power operations is reordered.
>>
>> But I'm not sure exynos_hdmiphy_driver_register() really need to be
>> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough to
>> call exynos_hdmiphy_driver_register() from hdmi_probe() because hdmiphy
>> is only used from hdmi.
>>
>
> I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.
>

I agree to the Seung Woo's point that hdmi-phy used to be solely accessed by
hdmi driver.  But in this RFC, hdmi-phy is not called by hdmi driver
anymore. Phy
ops will be called from drm-common-hdmi platform driver along with mixer and
hdmi ops.

The rational behind my implementation is that I am projecting hdmi-phy as
a device which is peer to hdmi ip and mixer. These 3 devices together makes
DRM HDMI subsystem.

Even physically hdmi-phy doesn't seems to be a part of hdmi ip though
configurations are listed under hdmi ip user manual. It looks like a
isolated module accessed by i2c.

Though I don't find anything wrong with Seung Woo suggestion but above
placement of hdmi-phy (parallel to hdmi and mixer) makes more sense
to me.

Please have a another look at it and let me know your opinion.

Another things which bothers me is registering mixer, hdmi driver inside
exynos_drm_init(). If we strictly follow the hierarchy inside drm,
exynos_drm_init()
should register drm-common-hdmi only. drm-common-hdmi should register
mixer and hdmi (or hdmi-phy as well).

regards,
Rahul Sharma.

> Thanks,
> Inki Dae
>
>> Thanks and Regards,
>> - Seung-Woo Kim
>>
>>>
>>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>>> we should re-factor this by explicitly calling power related callbacks
>>> of mixer, phy,
>>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>>> device. AFAIR something like this is already in place in chrome-kernel.
>>>
>>>> I've made some comments below.
>>>>
>>>>> This patch is dependent on
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>>
>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>>> ---
>>>>> It is based on exynos-drm-next-todo branch at
>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>>
>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>>
>>
>> <snip>
>>
>> --
>> Seung-Woo Kim
>> Samsung Software R&D Center
>> --
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Seung-Woo Kim March 7, 2013, 7:03 a.m. UTC | #6
On 2013? 03? 07? 15:45, Rahul Sharma wrote:
> Thanks Seung Woo, Mr. Dae,
> 
> On Thu, Mar 7, 2013 at 10:13 AM, Inki Dae <inki.dae@samsung.com> wrote:
>> 2013/3/7 ??? <sw0312.kim@samsung.com>:
>>>
>>>
>>> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
>>>> Thanks Sean,
>>>>
>>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>>>
>>>>> s/Physicaly/Physically/
>>>>>
>>>>>> should be instantiated independently.
>>>>>>
>>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>>>> other. It is preferred to isolate them and maintained independently.
>>>>>>
>>>>>> This implementations is tested for:
>>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>>>
>>>>>
>>>>> I don't like the idea of spawning off yet another driver in here. It
>>>>> adds more globals, more suspend/resume ordering issues, and more
>>>>> implicit dependencies. I understand, however, that this is the Chosen
>>>>> Way for the exynos driver, so I will save my rant.
>>>>>
>>>>
>>>> I agree to it. splitting phy to a new driver will complicate the power related
>>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>>>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>>>> inside hdmi driver is complicating it with unrelated changes.
>>>
>>> Basically, I agree with the idea to split hdmiphy from hdmi. And it
>>> seems that already existing hdmiphy i2c device is just reused and
>>> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even calling
>>> flow of power operations is reordered.
>>>
>>> But I'm not sure exynos_hdmiphy_driver_register() really need to be
>>> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough to
>>> call exynos_hdmiphy_driver_register() from hdmi_probe() because hdmiphy
>>> is only used from hdmi.
>>>
>>
>> I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.
>>
> 
> I agree to the Seung Woo's point that hdmi-phy used to be solely accessed by
> hdmi driver.  But in this RFC, hdmi-phy is not called by hdmi driver
> anymore. Phy
> ops will be called from drm-common-hdmi platform driver along with mixer and
> hdmi ops.

Considering this, exynos_drm_hdmi_probe() is more proper position.

> 
> The rational behind my implementation is that I am projecting hdmi-phy as
> a device which is peer to hdmi ip and mixer. These 3 devices together makes
> DRM HDMI subsystem.
> 
> Even physically hdmi-phy doesn't seems to be a part of hdmi ip though
> configurations are listed under hdmi ip user manual. It looks like a
> isolated module accessed by i2c.
> 
> Though I don't find anything wrong with Seung Woo suggestion but above
> placement of hdmi-phy (parallel to hdmi and mixer) makes more sense
> to me.
> 
> Please have a another look at it and let me know your opinion.
> 
> Another things which bothers me is registering mixer, hdmi driver inside
> exynos_drm_init(). If we strictly follow the hierarchy inside drm,
> exynos_drm_init()
> should register drm-common-hdmi only. drm-common-hdmi should register
> mixer and hdmi (or hdmi-phy as well).

Yes, it makes sense. All real hw blocks for hdmi including mixer, hdmi,
and hdmiphy shoulde be registered in exynos_drm_hdmi (drm-common-hdmi
for exynos).

Thanks and Regards,
- Seung-Woo Kim

> 
> regards,
> Rahul Sharma.
> 
>> Thanks,
>> Inki Dae
>>
>>> Thanks and Regards,
>>> - Seung-Woo Kim
>>>
>>>>
>>>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>>>> we should re-factor this by explicitly calling power related callbacks
>>>> of mixer, phy,
>>>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>>>> device. AFAIR something like this is already in place in chrome-kernel.
>>>>
>>>>> I've made some comments below.
>>>>>
>>>>>> This patch is dependent on
>>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>>>
>>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>>>> ---
>>>>>> It is based on exynos-drm-next-todo branch at
>>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>>>
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>>>
>>>
>>> <snip>
>>>
>>> --
>>> Seung-Woo Kim
>>> Samsung Software R&D Center
>>> --
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> 
>
Stéphane Marchesin March 7, 2013, 7:07 a.m. UTC | #7
On Wed, Mar 6, 2013 at 8:43 PM, Inki Dae <inki.dae@samsung.com> wrote:
> 2013/3/7 ??? <sw0312.kim@samsung.com>:
>>
>>
>> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
>>> Thanks Sean,
>>>
>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>>
>>>> s/Physicaly/Physically/
>>>>
>>>>> should be instantiated independently.
>>>>>
>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>>> other. It is preferred to isolate them and maintained independently.
>>>>>
>>>>> This implementations is tested for:
>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>>
>>>>
>>>> I don't like the idea of spawning off yet another driver in here. It
>>>> adds more globals, more suspend/resume ordering issues, and more
>>>> implicit dependencies. I understand, however, that this is the Chosen
>>>> Way for the exynos driver, so I will save my rant.
>>>>
>>>
>>> I agree to it. splitting phy to a new driver will complicate the power related
>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>>> inside hdmi driver is complicating it with unrelated changes.
>>
>> Basically, I agree with the idea to split hdmiphy from hdmi. And it
>> seems that already existing hdmiphy i2c device is just reused and
>> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even calling
>> flow of power operations is reordered.
>>
>> But I'm not sure exynos_hdmiphy_driver_register() really need to be
>> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough to
>> call exynos_hdmiphy_driver_register() from hdmi_probe() because hdmiphy
>> is only used from hdmi.
>>
>
> I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.
>

But this is probably going to break dpms and/or suspend/resume
functionality. Has that been tested?

Stéphane

> Thanks,
> Inki Dae
>
>> Thanks and Regards,
>> - Seung-Woo Kim
>>
>>>
>>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>>> we should re-factor this by explicitly calling power related callbacks
>>> of mixer, phy,
>>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>>> device. AFAIR something like this is already in place in chrome-kernel.
>>>
>>>> I've made some comments below.
>>>>
>>>>> This patch is dependent on
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>>
>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>>> ---
>>>>> It is based on exynos-drm-next-todo branch at
>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>>
>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>>
>>
>> <snip>
>>
>> --
>> Seung-Woo Kim
>> Samsung Software R&D Center
>> --
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Inki Dae March 7, 2013, 8:35 a.m. UTC | #8
> -----Original Message-----
> From: ??? [mailto:sw0312.kim@samsung.com]
> Sent: Thursday, March 07, 2013 4:04 PM
> To: Rahul Sharma
> Cc: Inki Dae; linux-samsung-soc@vger.kernel.org; Sean Paul; sunil joshi;
> dri-devel@lists.freedesktop.org; Rahul Sharma; sw0312.kim@samsung.com
> Subject: Re: [PATCH RFC] drm/exynos: hdmi: move hdmiphy related code to
> hdmiphy driver
> 
> 
> 
> On 2013? 03? 07? 15:45, Rahul Sharma wrote:
> > Thanks Seung Woo, Mr. Dae,
> >
> > On Thu, Mar 7, 2013 at 10:13 AM, Inki Dae <inki.dae@samsung.com> wrote:
> >> 2013/3/7 ??? <sw0312.kim@samsung.com>:
> >>>
> >>>
> >>> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
> >>>> Thanks Sean,
> >>>>
> >>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com>
> wrote:
> >>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma
> <rahul.sharma@samsung.com> wrote:
> >>>>>> Right now hdmiphy operations and configs are kept inside hdmi
> driver. hdmiphy related
> >>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are
> different devices and
> >>>>>
> >>>>> s/Physicaly/Physically/
> >>>>>
> >>>>>> should be instantiated independently.
> >>>>>>
> >>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are
> independent of each
> >>>>>> other. It is preferred to isolate them and maintained
independently.
> >>>>>>
> >>>>>> This implementations is tested for:
> >>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
> >>>>>> 2) Runtime PM and S2R scenarions for exynos5.
> >>>>>>
> >>>>>
> >>>>> I don't like the idea of spawning off yet another driver in here. It
> >>>>> adds more globals, more suspend/resume ordering issues, and more
> >>>>> implicit dependencies. I understand, however, that this is the
> Chosen
> >>>>> Way for the exynos driver, so I will save my rant.
> >>>>>
> >>>>
> >>>> I agree to it. splitting phy to a new driver will complicate the
> power related
> >>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in
> terms of
> >>>> config values, mapping (i2c/platform bus) etc. Handling this
> diversity
> >>>> inside hdmi driver is complicating it with unrelated changes.
> >>>
> >>> Basically, I agree with the idea to split hdmiphy from hdmi. And it
> >>> seems that already existing hdmiphy i2c device is just reused and
> >>> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even
> calling
> >>> flow of power operations is reordered.
> >>>
> >>> But I'm not sure exynos_hdmiphy_driver_register() really need to be
> >>> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough
> to
> >>> call exynos_hdmiphy_driver_register() from hdmi_probe() because
> hdmiphy
> >>> is only used from hdmi.
> >>>
> >>
> >> I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.
> >>
> >
> > I agree to the Seung Woo's point that hdmi-phy used to be solely
> accessed by
> > hdmi driver.  But in this RFC, hdmi-phy is not called by hdmi driver
> > anymore. Phy
> > ops will be called from drm-common-hdmi platform driver along with mixer
> and
> > hdmi ops.
> 
> Considering this, exynos_drm_hdmi_probe() is more proper position.
> 
> >
> > The rational behind my implementation is that I am projecting hdmi-phy
> as
> > a device which is peer to hdmi ip and mixer. These 3 devices together
> makes
> > DRM HDMI subsystem.
> >
> > Even physically hdmi-phy doesn't seems to be a part of hdmi ip though
> > configurations are listed under hdmi ip user manual. It looks like a
> > isolated module accessed by i2c.
> >
> > Though I don't find anything wrong with Seung Woo suggestion but above
> > placement of hdmi-phy (parallel to hdmi and mixer) makes more sense
> > to me.
> >
> > Please have a another look at it and let me know your opinion.
> >
> > Another things which bothers me is registering mixer, hdmi driver inside
> > exynos_drm_init(). If we strictly follow the hierarchy inside drm,
> > exynos_drm_init()
> > should register drm-common-hdmi only. drm-common-hdmi should register
> > mixer and hdmi (or hdmi-phy as well).
> 
> Yes, it makes sense. All real hw blocks for hdmi including mixer, hdmi,
> and hdmiphy shoulde be registered in exynos_drm_hdmi (drm-common-hdmi
> for exynos).
> 

Ideally, right. But the reason I designed and implemented hdmi subsystem
framework like this, is that there was one issue that
platform_device_register() can't be called at probe(). On other words, when
one platform device driver is being probed, anyone can't be probed. Anyway,
existing way needs to be improved. So let's find better way and improve the
hdmi subsystem framework this time. :)

Thanks,
Inki Dae

> Thanks and Regards,
> - Seung-Woo Kim
> 
> >
> > regards,
> > Rahul Sharma.
> >
> >> Thanks,
> >> Inki Dae
> >>
> >>> Thanks and Regards,
> >>> - Seung-Woo Kim
> >>>
> >>>>
> >>>> I have tested this RFC for Runtime PM / S2R. But if we see any major
> roadblock
> >>>> we should re-factor this by explicitly calling power related
> callbacks
> >>>> of mixer, phy,
> >>>> hdmi drivers in a required order. We can call them from exynos-drm-
> hdmi plf
> >>>> device. AFAIR something like this is already in place in chrome-
> kernel.
> >>>>
> >>>>> I've made some comments below.
> >>>>>
> >>>>>> This patch is dependent on
> >>>>>> http://www.mail-archive.com/dri-
> devel@lists.freedesktop.org/msg34733.html
> >>>>>> http://www.mail-archive.com/dri-
> devel@lists.freedesktop.org/msg34861.html
> >>>>>> http://www.mail-archive.com/dri-
> devel@lists.freedesktop.org/msg34862.html
> >>>>>>
> >>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> >>>>>> ---
> >>>>>> It is based on exynos-drm-next-todo branch at
> >>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-
> exynos.git
> >>>>>>
> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
> >>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++-----------------
-
> >>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
> >>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586
> ++++++++++++++++++++++++++++++-
> >>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
> >>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
> >>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
> >>>>>>
> >>>
> >>> <snip>
> >>>
> >>> --
> >>> Seung-Woo Kim
> >>> Samsung Software R&D Center
> >>> --
> >>>
> >>> _______________________________________________
> >>> dri-devel mailing list
> >>> dri-devel@lists.freedesktop.org
> >>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
> >
> >
> 
> --
> Seung-Woo Kim
> Samsung Software R&D Center
> --

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rahul Sharma March 7, 2013, 9:53 a.m. UTC | #9
On Thu, Mar 7, 2013 at 12:37 PM, Stéphane Marchesin
<stephane.marchesin@gmail.com> wrote:
> On Wed, Mar 6, 2013 at 8:43 PM, Inki Dae <inki.dae@samsung.com> wrote:
>> 2013/3/7 ??? <sw0312.kim@samsung.com>:
>>>
>>>
>>> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
>>>> Thanks Sean,
>>>>
>>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>>>
>>>>> s/Physicaly/Physically/
>>>>>
>>>>>> should be instantiated independently.
>>>>>>
>>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>>>> other. It is preferred to isolate them and maintained independently.
>>>>>>
>>>>>> This implementations is tested for:
>>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>>>
>>>>>
>>>>> I don't like the idea of spawning off yet another driver in here. It
>>>>> adds more globals, more suspend/resume ordering issues, and more
>>>>> implicit dependencies. I understand, however, that this is the Chosen
>>>>> Way for the exynos driver, so I will save my rant.
>>>>>
>>>>
>>>> I agree to it. splitting phy to a new driver will complicate the power related
>>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>>>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>>>> inside hdmi driver is complicating it with unrelated changes.
>>>
>>> Basically, I agree with the idea to split hdmiphy from hdmi. And it
>>> seems that already existing hdmiphy i2c device is just reused and
>>> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even calling
>>> flow of power operations is reordered.
>>>
>>> But I'm not sure exynos_hdmiphy_driver_register() really need to be
>>> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough to
>>> call exynos_hdmiphy_driver_register() from hdmi_probe() because hdmiphy
>>> is only used from hdmi.
>>>
>>
>> I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.
>>
>
> But this is probably going to break dpms and/or suspend/resume
> functionality. Has that been tested?
>
> Stéphane
>
Hi Stéphane

This has been tested for dpms and suspend/resume scenarios for exynos5. I
have yet to try with hdmi, mixer, phy driver registration moved to
common-drm-hdmi.

regards,
Rahul Sharma.

>> Thanks,
>> Inki Dae
>>
>>> Thanks and Regards,
>>> - Seung-Woo Kim
>>>
>>>>
>>>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>>>> we should re-factor this by explicitly calling power related callbacks
>>>> of mixer, phy,
>>>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>>>> device. AFAIR something like this is already in place in chrome-kernel.
>>>>
>>>>> I've made some comments below.
>>>>>
>>>>>> This patch is dependent on
>>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>>>
>>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>>>> ---
>>>>>> It is based on exynos-drm-next-todo branch at
>>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>>>
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>>>
>>>
>>> <snip>
>>>
>>> --
>>> Seung-Woo Kim
>>> Samsung Software R&D Center
>>> --
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rahul Sharma March 8, 2013, 8:32 a.m. UTC | #10
On Thu, Mar 7, 2013 at 2:05 PM, Inki Dae <inki.dae@samsung.com> wrote:
>
>
>> -----Original Message-----
>> From: ??? [mailto:sw0312.kim@samsung.com]
>> Sent: Thursday, March 07, 2013 4:04 PM
>> To: Rahul Sharma
>> Cc: Inki Dae; linux-samsung-soc@vger.kernel.org; Sean Paul; sunil joshi;
>> dri-devel@lists.freedesktop.org; Rahul Sharma; sw0312.kim@samsung.com
>> Subject: Re: [PATCH RFC] drm/exynos: hdmi: move hdmiphy related code to
>> hdmiphy driver
>>
>>
>>
>> On 2013? 03? 07? 15:45, Rahul Sharma wrote:
>> > Thanks Seung Woo, Mr. Dae,
>> >
>> > On Thu, Mar 7, 2013 at 10:13 AM, Inki Dae <inki.dae@samsung.com> wrote:
>> >> 2013/3/7 ??? <sw0312.kim@samsung.com>:
>> >>>
>> >>>
>> >>> On 2013? 03? 04? 23:05, Rahul Sharma wrote:
>> >>>> Thanks Sean,
>> >>>>
>> >>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com>
>> wrote:
>> >>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma
>> <rahul.sharma@samsung.com> wrote:
>> >>>>>> Right now hdmiphy operations and configs are kept inside hdmi
>> driver. hdmiphy related
>> >>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are
>> different devices and
>> >>>>>
>> >>>>> s/Physicaly/Physically/
>> >>>>>
>> >>>>>> should be instantiated independently.
>> >>>>>>
>> >>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are
>> independent of each
>> >>>>>> other. It is preferred to isolate them and maintained
> independently.
>> >>>>>>
>> >>>>>> This implementations is tested for:
>> >>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>> >>>>>> 2) Runtime PM and S2R scenarions for exynos5.
>> >>>>>>
>> >>>>>
>> >>>>> I don't like the idea of spawning off yet another driver in here. It
>> >>>>> adds more globals, more suspend/resume ordering issues, and more
>> >>>>> implicit dependencies. I understand, however, that this is the
>> Chosen
>> >>>>> Way for the exynos driver, so I will save my rant.
>> >>>>>
>> >>>>
>> >>>> I agree to it. splitting phy to a new driver will complicate the
>> power related
>> >>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in
>> terms of
>> >>>> config values, mapping (i2c/platform bus) etc. Handling this
>> diversity
>> >>>> inside hdmi driver is complicating it with unrelated changes.
>> >>>
>> >>> Basically, I agree with the idea to split hdmiphy from hdmi. And it
>> >>> seems that already existing hdmiphy i2c device is just reused and
>> >>> hdmiphy_power_on is reorganized to hdmiphy dpms operation: even
>> calling
>> >>> flow of power operations is reordered.
>> >>>
>> >>> But I'm not sure exynos_hdmiphy_driver_register() really need to be
>> >>> called from exynos_drm_init() of exynos_drm_drv.c. IMO, it is enough
>> to
>> >>> call exynos_hdmiphy_driver_register() from hdmi_probe() because
>> hdmiphy
>> >>> is only used from hdmi.
>> >>>
>> >>
>> >> I agree with Seung-Woo. The hdmiphy is just one part of HDMI subsystem.
>> >>
>> >
>> > I agree to the Seung Woo's point that hdmi-phy used to be solely
>> accessed by
>> > hdmi driver.  But in this RFC, hdmi-phy is not called by hdmi driver
>> > anymore. Phy
>> > ops will be called from drm-common-hdmi platform driver along with mixer
>> and
>> > hdmi ops.
>>
>> Considering this, exynos_drm_hdmi_probe() is more proper position.
>>
>> >
>> > The rational behind my implementation is that I am projecting hdmi-phy
>> as
>> > a device which is peer to hdmi ip and mixer. These 3 devices together
>> makes
>> > DRM HDMI subsystem.
>> >
>> > Even physically hdmi-phy doesn't seems to be a part of hdmi ip though
>> > configurations are listed under hdmi ip user manual. It looks like a
>> > isolated module accessed by i2c.
>> >
>> > Though I don't find anything wrong with Seung Woo suggestion but above
>> > placement of hdmi-phy (parallel to hdmi and mixer) makes more sense
>> > to me.
>> >
>> > Please have a another look at it and let me know your opinion.
>> >
>> > Another things which bothers me is registering mixer, hdmi driver inside
>> > exynos_drm_init(). If we strictly follow the hierarchy inside drm,
>> > exynos_drm_init()
>> > should register drm-common-hdmi only. drm-common-hdmi should register
>> > mixer and hdmi (or hdmi-phy as well).
>>
>> Yes, it makes sense. All real hw blocks for hdmi including mixer, hdmi,
>> and hdmiphy shoulde be registered in exynos_drm_hdmi (drm-common-hdmi
>> for exynos).
>>
>
> Ideally, right. But the reason I designed and implemented hdmi subsystem
> framework like this, is that there was one issue that
> platform_device_register() can't be called at probe(). On other words, when
> one platform device driver is being probed, anyone can't be probed. Anyway,
> existing way needs to be improved. So let's find better way and improve the
> hdmi subsystem framework this time. :)

Mr. Dae,

if we move exynos_drm_drv.o towards last in the makefile, we can
conveniently move all driver(or plf device for drm-comon-hdmi) registrations
from exynos_drm_init to module_init of the respective drivers.

I verified above changes for hdmi and S2R scenarios.

regards,
Rahul Sharma

>
> Thanks,
> Inki Dae
>
>> Thanks and Regards,
>> - Seung-Woo Kim
>>
>> >
>> > regards,
>> > Rahul Sharma.
>> >
>> >> Thanks,
>> >> Inki Dae
>> >>
>> >>> Thanks and Regards,
>> >>> - Seung-Woo Kim
>> >>>
>> >>>>
>> >>>> I have tested this RFC for Runtime PM / S2R. But if we see any major
>> roadblock
>> >>>> we should re-factor this by explicitly calling power related
>> callbacks
>> >>>> of mixer, phy,
>> >>>> hdmi drivers in a required order. We can call them from exynos-drm-
>> hdmi plf
>> >>>> device. AFAIR something like this is already in place in chrome-
>> kernel.
>> >>>>
>> >>>>> I've made some comments below.
>> >>>>>
>> >>>>>> This patch is dependent on
>> >>>>>> http://www.mail-archive.com/dri-
>> devel@lists.freedesktop.org/msg34733.html
>> >>>>>> http://www.mail-archive.com/dri-
>> devel@lists.freedesktop.org/msg34861.html
>> >>>>>> http://www.mail-archive.com/dri-
>> devel@lists.freedesktop.org/msg34862.html
>> >>>>>>
>> >>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>> >>>>>> ---
>> >>>>>> It is based on exynos-drm-next-todo branch at
>> >>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-
>> exynos.git
>> >>>>>>
>> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>> >>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>> >>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++-----------------
> -
>> >>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>> >>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586
>> ++++++++++++++++++++++++++++++-
>> >>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>> >>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>> >>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>> >>>>>>
>> >>>
>> >>> <snip>
>> >>>
>> >>> --
>> >>> Seung-Woo Kim
>> >>> Samsung Software R&D Center
>> >>> --
>> >>>
>> >>> _______________________________________________
>> >>> dri-devel mailing list
>> >>> dri-devel@lists.freedesktop.org
>> >>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>> >
>> >
>> >
>>
>> --
>> Seung-Woo Kim
>> Samsung Software R&D Center
>> --
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rahul Sharma March 12, 2013, 10:18 a.m. UTC | #11
On Mon, Mar 4, 2013 at 7:35 PM, Rahul Sharma <r.sh.open@gmail.com> wrote:
> Thanks Sean,
>
> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>
>> s/Physicaly/Physically/
>>
>>> should be instantiated independently.
>>>
>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>> other. It is preferred to isolate them and maintained independently.
>>>
>>> This implementations is tested for:
>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>
>>
>> I don't like the idea of spawning off yet another driver in here. It
>> adds more globals, more suspend/resume ordering issues, and more
>> implicit dependencies. I understand, however, that this is the Chosen
>> Way for the exynos driver, so I will save my rant.
>>
>
> I agree to it. splitting phy to a new driver will complicate the power related
> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
> config values, mapping (i2c/platform bus) etc. Handling this diversity
> inside hdmi driver is complicating it with unrelated changes.
>
> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
> we should re-factor this by explicitly calling power related callbacks
> of mixer, phy,
> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
> device. AFAIR something like this is already in place in chrome-kernel.
>
>> I've made some comments below.
>>
>>> This patch is dependent on
>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>
>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>> ---
>>> It is based on exynos-drm-next-todo branch at
>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>
>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>> index 3da5c2d..03acb6b 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>> @@ -334,6 +334,11 @@ static int __init exynos_drm_init(void)
>>>         ret = platform_driver_register(&hdmi_driver);
>>>         if (ret < 0)
>>>                 goto out_hdmi;
>>> +
>>> +       ret = exynos_hdmiphy_driver_register();
>>
>> Not sure why you need to obfuscate the type of driver here. All the
>> other ones are directly registered as platform drivers, I don't see
>> the harm in just doing the i2c_add_driver directly here.
>>
>
> AIMB, Phy is likely to get mapped as a platform device in later soc's.
> I want to make driver registration (inside exynos_drm_drv.c) independent
> of this changes. We can handle this inside exynos_hdmiphy_driver_register().
>
>>> +       if (ret < 0)
>>> +               goto out_hdmiphy;
>>> +
>>>         ret = platform_driver_register(&mixer_driver);
>>>         if (ret < 0)
>>>                 goto out_mixer;
>>
>>
>> I think this ordering is how you plan on enforcing the suspend/resume
>> order for hdmi/mixer/hdmiphy. In that case, however, I don't think it
>> makes sense to suspend/resume hdmiphy in between mixer and hdmi.
>> Ideally, hdmiphy should go down first and come up last.
>>
>
> I just wanted to keep, hdmi and hdmiphy things together. But what you said
> above makes sense. I will do that change.
>
>>
>>> @@ -436,6 +441,8 @@ out_common_hdmi_dev:
>>>  out_common_hdmi:
>>>         platform_driver_unregister(&mixer_driver);
>>>  out_mixer:
>>> +       exynos_hdmiphy_driver_unregister();
>>> +out_hdmiphy:
>>>         platform_driver_unregister(&hdmi_driver);
>>>  out_hdmi:
>>>  #endif
>>> @@ -479,6 +486,7 @@ static void __exit exynos_drm_exit(void)
>>>         exynos_platform_device_hdmi_unregister();
>>>         platform_driver_unregister(&exynos_drm_common_hdmi_driver);
>>>         platform_driver_unregister(&mixer_driver);
>>> +       exynos_hdmiphy_driver_unregister();
>>>         platform_driver_unregister(&hdmi_driver);
>>>  #endif
>>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>> index 4606fac..17c53e3 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>> @@ -325,6 +325,12 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
>>>  extern int exynos_platform_device_hdmi_register(void);
>>>
>>>  /*
>>> + * these functions registers/unregisters exynos drm hdmiphy driver.
>>> + */
>>> +extern int exynos_hdmiphy_driver_register(void);
>>> +extern void exynos_hdmiphy_driver_unregister(void);
>>> +
>>> +/*
>>>   * this function unregisters exynos drm hdmi platform device if it exists.
>>>   */
>>>  void exynos_platform_device_hdmi_unregister(void);
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>> index 5dc956b..45ef331 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>> @@ -32,19 +32,22 @@
>>>  /* platform device pointer for common drm hdmi device. */
>>>  static struct platform_device *exynos_drm_hdmi_pdev;
>>>
>>> -/* Common hdmi subdrv needs to access the hdmi and mixer though context.
>>> -* These should be initialied by the repective drivers */
>>> +/* Common hdmi subdrv needs to access the hdmi, hdmiphy and mixer though
>>> +*  context. These should be initialied by the repective drivers */
>>>  static struct exynos_drm_hdmi_context *hdmi_ctx;
>>> +static struct exynos_drm_hdmi_context *hdmiphy_ctx;
>>>  static struct exynos_drm_hdmi_context *mixer_ctx;
>>>
>>>  /* these callback points shoud be set by specific drivers. */
>>>  static struct exynos_hdmi_ops *hdmi_ops;
>>> +static struct exynos_hdmiphy_ops *hdmiphy_ops;
>>>  static struct exynos_mixer_ops *mixer_ops;
>>>
>>
>> This stuff really sets my teeth on edge. I can't stand these global
>> variables and the set-here, set-there, set-here dance that is done on
>> probe.
>>
>> IMO, this and the suspend/resume ordering should be enough motivation
>> to avoid separating everything out into their own drivers.
>>
>> Instead of adding more kruft to this list, we should be working to remove it.
>>
>> /rant
>>
>
> Let me think over it. Please suggest me something if you have any idea to
> correct this.
>

Hi all,

I replaced these variables with a global LIST of subdrv's with associated
type (HDMI / PHY / MIXER) and ops struct.

drm-common-hdmi callback will parse this list and based on the subdrv
type calls subdrv functions, but this adds overhead of parsing the list
multiple times.

If I cache the subdrvs in drm-common-hdmi context and parsing of the list
can be avoided.

What you think about above approach?

regards,
Rahul Sharma.

>>>  struct drm_hdmi_context {
>>>         struct exynos_drm_subdrv        subdrv;
>>>         struct exynos_drm_hdmi_context  *hdmi_ctx;
>>>         struct exynos_drm_hdmi_context  *mixer_ctx;
>>> +       struct exynos_drm_hdmi_context  *hdmiphy_ctx;
>>>
>>>         bool    enabled[MIXER_WIN_NR];
>>>  };
>>> @@ -74,6 +77,12 @@ void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>                 hdmi_ctx = ctx;
>>>  }
>>>
>>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>> +{
>>> +       if (ctx)
>>> +               hdmiphy_ctx = ctx;
>>> +}
>>> +
>>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>  {
>>>         if (ctx)
>>> @@ -88,6 +97,14 @@ void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
>>>                 hdmi_ops = ops;
>>>  }
>>>
>>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops)
>>> +{
>>> +       DRM_DEBUG_KMS("%s\n", __FILE__);
>>
>> Listing __FILE__ in these DRM_DEBUG_KMS traces is pretty useless. I'm
>> sure we can determine the file from the function name. It would be
>> slightly more useful to list __LINE__, but even more useful if you
>> actually printed state relevant to the current function.
>>
>
> OK. I will correct this.
>
>>> +
>>> +       if (ops)
>>> +               hdmiphy_ops = ops;
>>> +}
>>> +
>>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
>>>  {
>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>> @@ -121,7 +138,7 @@ struct edid *drm_hdmi_get_edid(struct device *dev,
>>>         return NULL;
>>>  }
>>>
>>> -static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>> +static int drm_hdmi_check_timing(struct device *dev, void *mode)
>>>  {
>>>         struct drm_hdmi_context *ctx = to_context(dev);
>>>         int ret = 0;
>>> @@ -129,18 +146,24 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>
>>>         /*
>>> -       * Both, mixer and hdmi should be able to handle the requested mode.
>>> -       * If any of the two fails, return mode as BAD.
>>> +       * Mixer, hdmi and hdmiphy should be able to handle the requested mode.
>>> +       * If any of the them fails, return mode as BAD.
>>>         */
>>>
>>>         if (mixer_ops && mixer_ops->check_timing)
>>> -               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
>>> +               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, mode);
>>>
>>>         if (ret)
>>>                 return ret;
>>>
>>>         if (hdmi_ops && hdmi_ops->check_timing)
>>> -               return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
>>> +               ret = hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, mode);
>>> +
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (hdmiphy_ops && hdmiphy_ops->check_timing)
>>> +               return hdmiphy_ops->check_timing(ctx->hdmiphy_ctx->ctx, mode);
>>>
>>>         return 0;
>>>  }
>>> @@ -254,6 +277,9 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
>>>
>>>         if (hdmi_ops && hdmi_ops->mode_set)
>>>                 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
>>> +
>>> +       if (hdmiphy_ops && hdmiphy_ops->mode_set)
>>> +               hdmiphy_ops->mode_set(ctx->hdmiphy_ctx->ctx, mode);
>>>  }
>>>
>>>  static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
>>> @@ -273,6 +299,15 @@ static void drm_hdmi_commit(struct device *subdrv_dev)
>>>
>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>
>>> +       if (hdmiphy_ops && hdmiphy_ops->prepare)
>>> +               hdmiphy_ops->prepare(ctx->hdmiphy_ctx->ctx);
>>> +
>>> +       if (hdmi_ops && hdmi_ops->prepare)
>>> +               hdmi_ops->prepare(ctx->hdmi_ctx->ctx);
>>> +
>>
>> Why are you calling these prepare functions in commit? Isn't that
>> precisely the reason drm has a prepare callback?
>
> True. Currently exynos_drm_encoder_prepare is blank. I will move this to
> prepare callback.
>
>>
>>> +       if (hdmiphy_ops && hdmiphy_ops->config_apply)
>>> +               hdmiphy_ops->config_apply(ctx->hdmiphy_ctx->ctx);
>>> +
>>>         if (hdmi_ops && hdmi_ops->commit)
>>>                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
>>>  }
>>> @@ -288,6 +323,9 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
>>>
>>>         if (hdmi_ops && hdmi_ops->dpms)
>>>                 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
>>> +
>>> +       if (hdmiphy_ops && hdmiphy_ops->dpms)
>>> +               hdmiphy_ops->dpms(ctx->hdmiphy_ctx->ctx, mode);
>>
>> I think you have to be more careful about the ordering of things here
>> since you'll want to power on in a different order than you power off.
>>
>>>  }
>>>
>>>  static void drm_hdmi_apply(struct device *subdrv_dev)
>>> @@ -393,6 +431,11 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>>                 return -EFAULT;
>>>         }
>>>
>>> +       if (!hdmiphy_ctx) {
>>> +               DRM_ERROR("hdmiphy context not initialized.\n");
>>> +               return -EFAULT;
>>> +       }
>>> +
>>>         if (!mixer_ctx) {
>>>                 DRM_ERROR("mixer context not initialized.\n");
>>>                 return -EFAULT;
>>> @@ -406,6 +449,7 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>>         }
>>>
>>>         ctx->hdmi_ctx = hdmi_ctx;
>>> +       ctx->hdmiphy_ctx = hdmiphy_ctx;
>>>         ctx->mixer_ctx = mixer_ctx;
>>>
>>>         ctx->hdmi_ctx->drm_dev = drm_dev;
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>> index fd2ff9f..10db62e 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>> @@ -39,10 +39,19 @@ struct exynos_hdmi_ops {
>>>         void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>>         void (*get_max_resol)(void *ctx, unsigned int *width,
>>>                                 unsigned int *height);
>>> +       void (*prepare)(void *ctx);
>>>         void (*commit)(void *ctx);
>>>         void (*dpms)(void *ctx, int mode);
>>>  };
>>>
>>> +struct exynos_hdmiphy_ops {
>>> +       int (*check_timing)(void *ctx, struct drm_display_mode *mode);
>>> +       void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>> +       void (*prepare)(void *ctx);
>>> +       int (*config_apply)(void *ctx);
>>> +       void (*dpms)(void *ctx, int mode);
>>> +};
>>> +
>>>  struct exynos_mixer_ops {
>>>         /* manager */
>>>         int (*iommu_on)(void *ctx, bool enable);
>>> @@ -61,7 +70,9 @@ struct exynos_mixer_ops {
>>>  };
>>>
>>>  void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>  void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
>>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops);
>>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
>>>  #endif
>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
>>> index a5ca2cc..7ee350d 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
>>> @@ -194,7 +194,6 @@ struct hdmi_context {
>>>         int                             internal_irq;
>>>
>>>         struct i2c_client               *ddc_port;
>>> -       struct i2c_client               *hdmiphy_port;
>>>
>>>         /* current hdmiphy conf regs */
>>>         struct hdmi_conf_regs           mode_conf;
>>> @@ -206,180 +205,6 @@ struct hdmi_context {
>>>         enum hdmi_type                  type;
>>>  };
>>>
>>> -struct hdmiphy_config {
>>> -       int pixel_clock;
>>> -       u8 conf[32];
>>> -};
>>> -
>>> -/* list of phy config settings */
>>> -static const struct hdmiphy_config hdmiphy_v13_configs[] = {
>>> -       {
>>> -               .pixel_clock = 27000000,
>>> -               .conf = {
>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 27027000,
>>> -               .conf = {
>>> -                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 74176000,
>>> -               .conf = {
>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>>> -                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> -                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 74250000,
>>> -               .conf = {
>>> -                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>>> -                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>>> -                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 148500000,
>>> -               .conf = {
>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>>> -                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> -                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>>> -               },
>>> -       },
>>> -};
>>> -
>>> -static const struct hdmiphy_config hdmiphy_v14_configs[] = {
>>> -       {
>>> -               .pixel_clock = 25200000,
>>> -               .conf = {
>>> -                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>>> -                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 27000000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>>> -                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 27027000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>>> -                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 36000000,
>>> -               .conf = {
>>> -                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 40000000,
>>> -               .conf = {
>>> -                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>>> -                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 65000000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>>> -                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 74176000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>>> -                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 74250000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 83500000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>>> -                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 106500000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>>> -                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 108000000,
>>> -               .conf = {
>>> -                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 146250000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>>> -                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>> -               },
>>> -       },
>>> -       {
>>> -               .pixel_clock = 148500000,
>>> -               .conf = {
>>> -                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> -                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>>> -               },
>>> -       },
>>> -};
>>> -
>>>  struct hdmi_infoframe {
>>>         enum HDMI_PACKET_TYPE type;
>>>         u8 ver;
>>> @@ -774,46 +599,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
>>>         return raw_edid;
>>>  }
>>>
>>> -static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
>>> -{
>>> -       const struct hdmiphy_config *confs;
>>> -       int count, i;
>>> -
>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> -
>>> -       if (hdata->type == HDMI_TYPE13) {
>>> -               confs = hdmiphy_v13_configs;
>>> -               count = ARRAY_SIZE(hdmiphy_v13_configs);
>>> -       } else if (hdata->type == HDMI_TYPE14) {
>>> -               confs = hdmiphy_v14_configs;
>>> -               count = ARRAY_SIZE(hdmiphy_v14_configs);
>>> -       } else
>>> -               return -EINVAL;
>>> -
>>> -       for (i = 0; i < count; i++)
>>> -               if (confs[i].pixel_clock == pixel_clock)
>>> -                       return i;
>>> -
>>> -       DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
>>> -       return -EINVAL;
>>> -}
>>> -
>>> -static int hdmi_check_timing(void *ctx, struct drm_display_mode *mode)
>>> -{
>>> -       struct hdmi_context *hdata = ctx;
>>> -       int ret;
>>> -
>>> -       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>>> -               __func__, mode->hdisplay, mode->vdisplay,
>>> -               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
>>> -               false, mode->clock * 1000);
>>> -
>>> -       ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
>>> -       if (ret < 0)
>>> -               return ret;
>>> -       return 0;
>>> -}
>>> -
>>>  static void hdmi_set_acr(u32 freq, u8 *acr)
>>>  {
>>>         u32 n, cts;
>>> @@ -1309,20 +1094,12 @@ static void hdmi_timing_apply(struct hdmi_context *hdata)
>>>
>>>  static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>>  {
>>> -       u8 buffer[2];
>>>         u32 reg;
>>>
>>>         clk_disable(hdata->res.sclk_hdmi);
>>>         clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
>>>         clk_enable(hdata->res.sclk_hdmi);
>>>
>>> -       /* operation mode */
>>> -       buffer[0] = 0x1f;
>>> -       buffer[1] = 0x00;
>>> -
>>> -       if (hdata->hdmiphy_port)
>>> -               i2c_master_send(hdata->hdmiphy_port, buffer, 2);
>>> -
>>>         if (hdata->type == HDMI_TYPE13)
>>>                 reg = HDMI_V13_PHY_RSTOUT;
>>>         else
>>> @@ -1335,102 +1112,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>>         usleep_range(10000, 12000);
>>>  }
>>>
>>> -static void hdmiphy_poweron(struct hdmi_context *hdata)
>>> -{
>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> -
>>> -       if (hdata->type == HDMI_TYPE14)
>>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
>>> -                       HDMI_PHY_POWER_OFF_EN);
>>> -}
>>> -
>>> -static void hdmiphy_poweroff(struct hdmi_context *hdata)
>>> -{
>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> -
>>> -       if (hdata->type == HDMI_TYPE14)
>>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
>>> -                       HDMI_PHY_POWER_OFF_EN);
>>
>> I assume these register writes are replaced with i2c operations in the
>> new phy driver?
>
> It is done for exynos5 phy. I am not having the sequence for exynos4. I
> will add it later.
>
>>
>>> -}
>>> -
>>> -static void hdmiphy_conf_apply(struct hdmi_context *hdata)
>>> -{
>>> -       const u8 *hdmiphy_data;
>>> -       u8 buffer[32];
>>> -       u8 operation[2];
>>> -       u8 read_buffer[32] = {0, };
>>> -       int ret;
>>> -       int i;
>>> -
>>> -       if (!hdata->hdmiphy_port) {
>>> -               DRM_ERROR("hdmiphy is not attached\n");
>>> -               return;
>>> -       }
>>> -
>>> -       /* pixel clock */
>>> -       i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
>>> -       if (i < 0) {
>>> -               DRM_ERROR("failed to find hdmiphy conf\n");
>>> -               return;
>>> -       }
>>> -
>>> -       if (hdata->type == HDMI_TYPE13) {
>>> -               hdmiphy_data = hdmiphy_v13_configs[i].conf;
>>> -       } else {
>>> -               hdmiphy_data = hdmiphy_v14_configs[i].conf;
>>> -       }
>>> -
>>> -       memcpy(buffer, hdmiphy_data, 32);
>>> -       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
>>> -       if (ret != 32) {
>>> -               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>>> -               return;
>>> -       }
>>> -
>>> -       usleep_range(10000, 12000);
>>> -
>>> -       /* operation mode */
>>> -       operation[0] = 0x1f;
>>> -       operation[1] = 0x80;
>>> -
>>> -       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
>>> -       if (ret != 2) {
>>> -               DRM_ERROR("failed to enable hdmiphy\n");
>>> -               return;
>>> -       }
>>> -
>>> -       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
>>> -       if (ret < 0) {
>>> -               DRM_ERROR("failed to read hdmiphy config\n");
>>> -               return;
>>> -       }
>>> -
>>> -       for (i = 0; i < ret; i++)
>>> -               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
>>> -                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
>>> -}
>>> -
>>> -static void hdmi_conf_apply(struct hdmi_context *hdata)
>>> -{
>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> -
>>> -       hdmiphy_conf_reset(hdata);
>>> -       hdmiphy_conf_apply(hdata);
>>> -
>>> -       mutex_lock(&hdata->hdmi_mutex);
>>> -       hdmi_conf_reset(hdata);
>>> -       hdmi_conf_init(hdata);
>>> -       mutex_unlock(&hdata->hdmi_mutex);
>>> -
>>> -       hdmi_audio_init(hdata);
>>> -
>>> -       /* setting core registers */
>>> -       hdmi_timing_apply(hdata);
>>> -       hdmi_audio_control(hdata, true);
>>> -
>>> -       hdmi_regs_dump(hdata, "start");
>>> -}
>>> -
>>>  static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
>>>  {
>>>         int i;
>>> @@ -1448,7 +1129,6 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
>>>
>>>         hdata->mode_conf.cea_video_id =
>>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>
>> I don't think you use pixel_clock in mode_conf any longer, please
>> remove it from the struct.
>>
>
> Correct. I will remove pixel_clock from struct hdmi_conf_regs.
>
>>>
>>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>>         hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
>>> @@ -1544,7 +1224,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
>>>
>>>         hdata->mode_conf.cea_video_id =
>>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>>
>>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>>         hdmi_set_reg(core->v_line, 2, m->vtotal);
>>> @@ -1670,13 +1349,32 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width,
>>>         *height = MAX_HEIGHT;
>>>  }
>>>
>>> +static void hdmi_commit_prepare(void *ctx)
>>
>> I don't know why the "_commit" is there, just hdmi_prepare will be fine.
>>
> Ok.
>
>>> +{
>>> +       struct hdmi_context *hdata = ctx;
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       hdmiphy_conf_reset(hdata);
>>
>> There's still phy-specific kruft laying around the driver. There are
>> now a bunch of implicit dependencies between the hdmi driver and the
>> hdmiphy driver.
>>
>
> I need to follow this sequence:
>
> phy off (hdmiphy_prepare) -> phy reset using HDMI IP REG (hdmi_prepare) ->
> phy config (hdmiphy_config_apply) -> hdmi ip config  (hdmi_commit)
>
> Phy reset needs to be done by toggling Phy_reset bit in HDMI IP register which
> cannot be accessed in hdmiphy driver. I didn't see any better way.
>
>> Another example is the sclk_hdmiphy clock, which is still controlled
>> by the hdmi driver, it should be controlled by the phy.
>>
>
> sclk_hdmiphy is one of the source clock for hdmi IP. HDMI IP needs to change the
> source between sclk_hdmiphy (only if stable) else pixel clock. Above
> code looks ok
> to me. Please explain a bit mode on this.
>
>>> +}
>>> +
>>>  static void hdmi_commit(void *ctx)
>>>  {
>>>         struct hdmi_context *hdata = ctx;
>>>
>>>         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>
>>> -       hdmi_conf_apply(hdata);
>>> +       mutex_lock(&hdata->hdmi_mutex);
>>> +       hdmi_conf_reset(hdata);
>>> +       hdmi_conf_init(hdata);
>>> +       mutex_unlock(&hdata->hdmi_mutex);
>>> +
>>> +       hdmi_audio_init(hdata);
>>> +
>>> +       /* setting core registers */
>>> +       hdmi_timing_apply(hdata);
>>> +       hdmi_audio_control(hdata, true);
>>> +
>>> +       hdmi_regs_dump(hdata, "start");
>>>  }
>>>
>>>  static void hdmi_poweron(struct hdmi_context *hdata)
>>> @@ -1699,8 +1397,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
>>>         clk_enable(res->hdmiphy);
>>>         clk_enable(res->hdmi);
>>>         clk_enable(res->sclk_hdmi);
>>> -
>>> -       hdmiphy_poweron(hdata);
>>>  }
>>>
>>>  static void hdmi_poweroff(struct hdmi_context *hdata)
>>> @@ -1719,7 +1415,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
>>>          * its reset state seems to meet the condition.
>>>          */
>>>         hdmiphy_conf_reset(hdata);
>>> -       hdmiphy_poweroff(hdata);
>>>
>>>         clk_disable(res->sclk_hdmi);
>>>         clk_disable(res->hdmi);
>>> @@ -1761,11 +1456,11 @@ static struct exynos_hdmi_ops hdmi_ops = {
>>>         /* display */
>>>         .is_connected   = hdmi_is_connected,
>>>         .get_edid       = hdmi_get_edid,
>>> -       .check_timing   = hdmi_check_timing,
>>>
>>>         /* manager */
>>>         .mode_set       = hdmi_mode_set,
>>>         .get_max_resol  = hdmi_get_max_resol,
>>> +       .prepare                = hdmi_commit_prepare,
>>>         .commit         = hdmi_commit,
>>>         .dpms           = hdmi_dpms,
>>>  };
>>> @@ -1878,7 +1573,7 @@ fail:
>>>         return -ENODEV;
>>>  }
>>>
>>> -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
>>> +static struct i2c_client *hdmi_ddc;
>>>
>>>  void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>>  {
>>> @@ -1886,12 +1581,6 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>>                 hdmi_ddc = ddc;
>>>  }
>>>
>>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
>>> -{
>>> -       if (hdmiphy)
>>> -               hdmi_hdmiphy = hdmiphy;
>>> -}
>>> -
>>>  #ifdef CONFIG_OF
>>>  static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
>>>                                         (struct device *dev)
>>> @@ -2051,27 +1740,18 @@ static int hdmi_probe(struct platform_device *pdev)
>>>
>>>         hdata->ddc_port = hdmi_ddc;
>>>
>>> -       /* hdmiphy i2c driver */
>>> -       if (i2c_add_driver(&hdmiphy_driver)) {
>>> -               DRM_ERROR("failed to register hdmiphy i2c driver\n");
>>> -               ret = -ENOENT;
>>> -               goto err_ddc;
>>> -       }
>>> -
>>> -       hdata->hdmiphy_port = hdmi_hdmiphy;
>>> -
>>>         hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
>>>         if (hdata->external_irq < 0) {
>>>                 DRM_ERROR("failed to get GPIO external irq\n");
>>>                 ret = hdata->external_irq;
>>> -               goto err_hdmiphy;
>>> +               goto err_ddc;
>>>         }
>>>
>>>         hdata->internal_irq = platform_get_irq(pdev, 0);
>>>         if (hdata->internal_irq < 0) {
>>>                 DRM_ERROR("failed to get platform internal irq\n");
>>>                 ret = hdata->internal_irq;
>>> -               goto err_hdmiphy;
>>> +               goto err_ddc;
>>>         }
>>>
>>>         hdata->hpd = gpio_get_value(hdata->hpd_gpio);
>>> @@ -2082,7 +1762,7 @@ static int hdmi_probe(struct platform_device *pdev)
>>>                         "hdmi_external", drm_hdmi_ctx);
>>>         if (ret) {
>>>                 DRM_ERROR("failed to register hdmi external interrupt\n");
>>> -               goto err_hdmiphy;
>>> +               goto err_ddc;
>>>         }
>>>
>>>         ret = request_threaded_irq(hdata->internal_irq, NULL,
>>> @@ -2105,8 +1785,6 @@ static int hdmi_probe(struct platform_device *pdev)
>>>
>>>  err_free_irq:
>>>         free_irq(hdata->external_irq, drm_hdmi_ctx);
>>> -err_hdmiphy:
>>> -       i2c_del_driver(&hdmiphy_driver);
>>>  err_ddc:
>>>         i2c_del_driver(&ddc_driver);
>>>         return ret;
>>> @@ -2125,9 +1803,6 @@ static int hdmi_remove(struct platform_device *pdev)
>>>         free_irq(hdata->internal_irq, hdata);
>>>         free_irq(hdata->external_irq, hdata);
>>>
>>> -
>>> -       /* hdmiphy i2c driver */
>>> -       i2c_del_driver(&hdmiphy_driver);
>>>         /* DDC i2c driver */
>>>         i2c_del_driver(&ddc_driver);
>>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
>>> index 0ddf395..6595d7b 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.h
>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
>>> @@ -15,7 +15,6 @@
>>>  #define _EXYNOS_HDMI_H_
>>>
>>>  void hdmi_attach_ddc_client(struct i2c_client *ddc);
>>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
>>>
>>>  extern struct i2c_driver hdmiphy_driver;
>>>  extern struct i2c_driver ddc_driver;
>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>> index ea49d13..0d47302 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>> @@ -16,33 +16,437 @@
>>>  #include <linux/kernel.h>
>>>  #include <linux/i2c.h>
>>>  #include <linux/module.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/clk.h>
>>>
>>>  #include "exynos_drm_drv.h"
>>> +#include "exynos_drm_hdmi.h"
>>>  #include "exynos_hdmi.h"
>>>
>>> +#include "regs-hdmiphy.h"
>>>
>>> -static int hdmiphy_probe(struct i2c_client *client,
>>> -       const struct i2c_device_id *id)
>>> +#define HDMIPHY_REG_COUNT              (32)
>>
>> No need to include parentheses.
>
> Ok.
>>
>>> +
>>> +enum hdmiphy_type {
>>> +       HDMIPHY_EXYNOS4210,
>>> +       HDMIPHY_EXYNOS4212,
>>> +};
>>> +
>>> +struct hdmiphy_context {
>>> +       struct device           *dev;
>>> +       bool                    powered;
>>> +       void                    *parent_ctx;
>>> +       enum hdmiphy_type               type;
>>> +       const struct hdmiphy_config     *conf;
>>> +       struct clk              *hdmiphy;
>>> +};
>>> +
>>> +struct hdmiphy_config {
>>> +       int pixel_clock;
>>> +       u8 conf[HDMIPHY_REG_COUNT];
>>> +};
>>> +
>>> +/* list of all required phy config settings */
>>> +static const struct hdmiphy_config hdmiphy_4212_configs[] = {
>>> +       {
>>> +               .pixel_clock = 25200000,
>>> +               .conf = {
>>> +                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>>> +                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 27000000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>>> +                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 27027000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>>> +                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 36000000,
>>> +               .conf = {
>>> +                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 40000000,
>>> +               .conf = {
>>> +                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>>> +                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 65000000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>>> +                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 74176000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>>> +                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 74250000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 83500000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>>> +                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 106500000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>>> +                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 108000000,
>>> +               .conf = {
>>> +                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 146250000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>>> +                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 148500000,
>>> +               .conf = {
>>> +                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>> +                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>>> +               },
>>> +       },
>>> +};
>>> +
>>> +static const struct hdmiphy_config hdmiphy_4210_configs[] = {
>>> +       {
>>> +               .pixel_clock = 27000000,
>>> +               .conf = {
>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 27027000,
>>> +               .conf = {
>>> +                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 74176000,
>>> +               .conf = {
>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>>> +                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> +                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 74250000,
>>> +               .conf = {
>>> +                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>>> +                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>>> +                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>>> +               },
>>> +       },
>>> +       {
>>> +               .pixel_clock = 148500000,
>>> +               .conf = {
>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>>> +                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>> +                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>>> +               },
>>> +       },
>>> +};
>>> +
>>> +static const struct hdmiphy_config *hdmiphy_find_conf(void *ctx,
>>> +                       const struct drm_display_mode *mode)
>>> +{
>>> +       struct hdmiphy_context *hdata = ctx;
>>> +       const struct hdmiphy_config *confs;
>>> +       int count, i;
>>> +
>>> +       switch (hdata->type) {
>>> +       case HDMIPHY_EXYNOS4212:
>>> +               confs = hdmiphy_4212_configs;
>>> +               count = ARRAY_SIZE(hdmiphy_4212_configs);
>>> +               break;
>>> +       case HDMIPHY_EXYNOS4210:
>>> +               confs = hdmiphy_4210_configs;
>>> +               count = ARRAY_SIZE(hdmiphy_4210_configs);
>>> +               break;
>>> +       default:
>>> +               return NULL;
>>
>> Should probably DRM_ERROR here.
>>
> ok. I will add that.
>
>>> +       }
>>> +
>>> +       for (i = 0; i < count; i++)
>>> +               if (confs[i].pixel_clock == (mode->clock * 1000))
>>> +                       return &confs[i];
>>> +
>>> +       return NULL;
>>> +}
>>> +
>>> +static int hdmiphy_check_timing(void *ctx, struct drm_display_mode *mode)
>>>  {
>>> -       hdmi_attach_hdmiphy_client(client);
>>> +       const struct hdmiphy_config *conf;
>>> +       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>>> +               __func__, mode->hdisplay, mode->vdisplay,
>>> +               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>> +               ? true : false, mode->clock * 1000);
>>
>> No need to print __func__ here, DRM_DEBUG_KMS will do that for you. I
>> think you should also parse the flags to a string instead of a bool
>> cast to an int.
>>
> ok.
>
>>>
>>> -       dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
>>> -               "into i2c adapter successfully\n");
>>> +       conf = hdmiphy_find_conf(ctx, mode);
>>> +       if (!conf) {
>>> +               DRM_DEBUG_KMS("Display Mode is not supported.\n");
>>> +               return -EINVAL;
>>> +       }
>>>
>>>         return 0;
>>>  }
>>>
>>> -static int hdmiphy_remove(struct i2c_client *client)
>>> +static void hdmiphy_mode_set(void *ctx, struct drm_display_mode *mode)
>>> +{
>>> +       struct hdmiphy_context *hdata = ctx;
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>
>> No need to print __func__ here, DRM_DEBUG_KMS will do that for you.
>> This comment stand for the rest of the calls, as well.
>>
>>> +
>>> +       hdata->conf = hdmiphy_find_conf(ctx, mode);
>>> +}
>>> +
>>> +static void hdmiphy_config_prepare(void *ctx)
>>> +{
>>> +       struct hdmiphy_context *hdata = ctx;
>>> +       const struct i2c_client *client = to_i2c_client(hdata->dev);
>>> +       u8 buffer[2];
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       /* operation mode */
>>> +       buffer[0] = HDMIPHY_MODE_SET_DONE;
>>> +       buffer[1] = 0x00;
>>> +
>>> +       if (client)
>>> +               i2c_master_send(client, buffer, 2);
>>> +}
>>> +
>>> +static int hdmiphy_config_apply(void *ctx)
>>>  {
>>> -       dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
>>> -               "from i2c adapter successfully\n");
>>> +       struct hdmiphy_context *hdata = ctx;
>>> +       struct i2c_client *client = to_i2c_client(hdata->dev);
>>> +       const u8 *hdmiphy_data;
>>> +       u8 buffer[HDMIPHY_REG_COUNT];
>>> +       u8 operation[2];
>>> +       u8 read_buffer[HDMIPHY_REG_COUNT] = {0, };
>>> +       int ret;
>>> +       int i;
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       /* pixel clock */
>>> +       if (hdata->conf && client)
>>> +               hdmiphy_data = hdata->conf->conf;
>>> +       else
>>> +               return -EINVAL;
>>> +
>>> +       memcpy(buffer, hdmiphy_data, HDMIPHY_REG_COUNT);
>>> +
>>> +       ret = i2c_master_send(client, buffer, HDMIPHY_REG_COUNT);
>>> +       if (ret != HDMIPHY_REG_COUNT) {
>>> +               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       usleep_range(10000, 12000);
>>> +
>>> +       /* operation mode */
>>> +       operation[0] = HDMIPHY_MODE_SET_DONE;
>>> +       operation[1] = HDMIPHY_MODE_EN;
>>> +
>>> +       ret = i2c_master_send(client, operation, 2);
>>> +       if (ret != 2) {
>>> +               DRM_ERROR("failed to enable hdmiphy\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       ret = i2c_master_recv(client, read_buffer, HDMIPHY_REG_COUNT);
>>> +       if (ret < 0) {
>>> +               DRM_ERROR("failed to read hdmiphy config\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       for (i = 0; i < ret; i++)
>>> +               DRM_DEBUG_KMS("hdmiphy[0x%02x] wr[0x%02x], rd[0x%02x]\n",
>>> +                               i, buffer[i], read_buffer[i]);
>>> +       return 0;
>>> +}
>>> +
>>> +static void hdmiphy_dpms(void *ctx, int mode)
>>> +{
>>> +       struct hdmiphy_context *hdata = ctx;
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
>>> +
>>> +       switch (mode) {
>>> +       case DRM_MODE_DPMS_ON:
>>> +               if (pm_runtime_suspended(hdata->dev))
>>> +                       pm_runtime_get_sync(hdata->dev);
>>> +               break;
>>> +       case DRM_MODE_DPMS_STANDBY:
>>> +       case DRM_MODE_DPMS_SUSPEND:
>>> +       case DRM_MODE_DPMS_OFF:
>>> +               if (!pm_runtime_suspended(hdata->dev))
>>> +                       pm_runtime_put_sync(hdata->dev);
>>> +               break;
>>> +       default:
>>> +               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
>>> +               break;
>>> +       }
>>> +}
>>> +
>>> +static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
>>> +                              u8 reg, u8 mask, u8 val)
>>> +{
>>> +       int ret;
>>> +       u8 buffer[2];
>>> +
>>> +       buffer[0] = reg;
>>> +       buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
>>> +       reg_cache[reg] = buffer[1];
>>> +
>>> +       ret = i2c_master_send(client, buffer, 2);
>>> +       if (ret != 2)
>>> +               return -EIO;
>>>
>>>         return 0;
>>>  }
>>>
>>> +static int hdmiphy_4412_turn_on(struct i2c_client *client, bool on)
>>
>> This name is misleading. Please change it to something that doesn't
>> convey a power state, such as hdmiphy_4412_power
>>
> ok. I will change that.
>
>>> +{
>>> +       u8 reg_cache[HDMIPHY_REG_COUNT] = { 0 };
>>> +       u8 buffer[2];
>>> +       int ret;
>>> +
>>> +       DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
>>> +
>>> +       /* Cache all hdmi-phy registers to make the code below faster */
>>> +       buffer[0] = 0x0;
>>> +       ret = i2c_master_send(client, buffer, 1);
>>> +       if (ret != 1) {
>>> +               ret = -EIO;
>>> +               goto exit;
>>> +       }
>>> +       ret = i2c_master_recv(client, reg_cache, HDMIPHY_REG_COUNT);
>>> +       if (ret != HDMIPHY_REG_COUNT) {
>>> +               ret = -EIO;
>>> +               goto exit;
>>> +       }
>>> +
>>> +       /* Change to/from configuration from/to operation mode */
>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_MODE_SET_DONE,
>>> +                               HDMIPHY_MODE_EN, on ? ~0 : 0);
>>> +       if (ret)
>>> +               goto exit;
>>> +
>>> +       /*
>>> +        * Turn off "oscpad" if !on; it turns on again in
>>> +        * during phy-configuration.
>>> +        */
>>> +       if (!on)
>>> +               ret = hdmiphy_update_bits(client, reg_cache,
>>> +                       HDMIPHY_4212_OSC_PAD_CON, HDMIPHY_OSC_PAD_EN, 0);
>>> +               if (ret)
>>> +                       goto exit;
>>> +
>>> +       /* Disable powerdown if on; enable if !on */
>>
>> This comment really isn't useful, it's just describing the code.
>
> I will remove this comment.
>
>>
>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>>> +                       HDMIPHY_PDEN, on ? 0 : ~0);
>>> +       if (ret)
>>> +               goto exit;
>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>>> +                       HDMIPHY_PD_ALL, on ? 0 : ~0);
>>> +       if (ret)
>>> +               goto exit;
>>> +
>>> +       /* Disable pixel clock generator block if !on */
>>> +       if (!on)
>>> +               ret = hdmiphy_update_bits(client, reg_cache,
>>> +                       HDMIPHY_4212_PCG_CON, HDMIPHY_PCG_RESET_EN, 0);
>>> +               if (ret)
>>> +                       goto exit;
>>> +
>>> +exit:
>>> +       /* Don't expect any errors so just do a single warn */
>>> +       WARN_ON(ret);
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static struct exynos_hdmiphy_ops hdmiphy_ops = {
>>> +       .check_timing   = hdmiphy_check_timing,
>>> +       .mode_set               = hdmiphy_mode_set,
>>> +       .prepare                = hdmiphy_config_prepare,
>>> +       .config_apply   = hdmiphy_config_apply,
>>> +       .dpms           = hdmiphy_dpms,
>>> +};
>>> +
>>>  static const struct i2c_device_id hdmiphy_id[] = {
>>> -       { "s5p_hdmiphy", 0 },
>>> -       { "exynos5-hdmiphy", 0 },
>>> +       { "s5p_hdmiphy", HDMIPHY_EXYNOS4210 },
>>> +       { "exynos5-hdmiphy", HDMIPHY_EXYNOS4212 },
>>>         { },
>>>  };
>>>
>>> @@ -50,21 +454,183 @@ static const struct i2c_device_id hdmiphy_id[] = {
>>>  static struct of_device_id hdmiphy_match_types[] = {
>>>         {
>>>                 .compatible = "samsung,exynos5-hdmiphy",
>>> +               .data   = (void *)HDMIPHY_EXYNOS4212,
>>>         }, {
>>>                 /* end node */
>>>         }
>>>  };
>>>  #endif
>>>
>>> +static int hdmiphy_probe(struct i2c_client *client,
>>> +       const struct i2c_device_id *id)
>>> +{
>>> +       struct device *dev = &client->dev;
>>> +       struct hdmiphy_context *hdata;
>>> +       struct exynos_drm_hdmi_context *drm_hdmi_ctx;
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
>>> +                                       GFP_KERNEL);
>>> +       if (!drm_hdmi_ctx) {
>>> +               DRM_ERROR("failed to allocate common hdmi context.\n");
>>> +               return -ENOMEM;
>>> +       }
>>> +
>>> +       hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
>>> +       if (!hdata) {
>>> +               DRM_ERROR("failed to allocate hdmiphy context.\n");
>>> +               return -ENOMEM;
>>> +       }
>>> +
>>> +       if (dev->of_node) {
>>> +               const struct of_device_id *match;
>>> +               match = of_match_node(of_match_ptr(hdmiphy_match_types),
>>> +                                       dev->of_node);
>>> +               if (match == NULL)
>>> +                       return -ENODEV;
>>> +               hdata->type = (enum hdmiphy_type)match->data;
>>> +       } else {
>>> +               hdata->type = (enum hdmiphy_type)id->driver_data;
>>> +       }
>>> +
>>> +       drm_hdmi_ctx->ctx = (void *)hdata;
>>> +       hdata->parent_ctx = (void *)drm_hdmi_ctx;
>>> +       hdata->dev = dev;
>>> +
>>> +       hdata->hdmiphy = devm_clk_get(dev, "hdmiphy");
>>
>> I don't see this call removed in the hdmi driver, am I missing something?
>>
>
> I took it wrong. I thought hdmi IP is clocked by hdmiphy clock. Actually it is
> sclk_hdmiphy clock. I will remove this from hdmi driver.
>
>>> +       if (IS_ERR_OR_NULL(hdata->hdmiphy)) {
>>> +               DRM_ERROR("failed to get clock 'hdmiphy'\n");
>>> +               return PTR_ERR(hdata->hdmiphy);
>>> +       }
>>> +
>>> +       i2c_set_clientdata(client, hdata);
>>> +
>>> +       /* Attach HDMI-PHY Driver to common hdmi. */
>>> +       exynos_hdmiphy_drv_attach(drm_hdmi_ctx);
>>> +
>>> +       /* register specific callbacks to common hdmi. */
>>> +       exynos_hdmiphy_ops_register(&hdmiphy_ops);
>>> +
>>> +       pm_runtime_enable(dev);
>>> +
>>> +       dev_info(&client->adapter->dev,
>>> +               "attached s5p_hdmiphy into i2c adapter successfully\n");
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int hdmiphy_remove(struct i2c_client *client)
>>> +{
>>> +       dev_info(&client->adapter->dev,
>>> +               "detached s5p_hdmiphy from i2c adapter successfully\n");
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static void hdmiphy_poweroff(struct device *dev)
>>> +{
>>> +       struct i2c_client *client = to_i2c_client(dev);
>>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>>> +               hdmiphy_4412_turn_on(client, 0);
>>> +
>>> +       clk_disable(hdata->hdmiphy);
>>> +}
>>> +
>>> +static void hdmiphy_poweron(struct device *dev)
>>> +{
>>> +       struct i2c_client *client = to_i2c_client(dev);
>>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>>> +
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       clk_enable(hdata->hdmiphy);
>>> +
>>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>>> +               hdmiphy_4412_turn_on(client, 1);
>>> +}
>>> +
>>> +#ifdef CONFIG_PM_SLEEP
>>> +static int hdmiphy_suspend(struct device *dev)
>>> +{
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       if (pm_runtime_suspended(dev)) {
>>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>>> +               __func__);
>>> +               return 0;
>>> +       }
>>> +
>>> +       hdmiphy_poweroff(dev);
>>> +       return 0;
>>> +}
>>> +
>>> +static int hdmiphy_resume(struct device *dev)
>>> +{
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       if (pm_runtime_suspended(dev)) {
>>> +               /* dpms callback should resume the mixer. */
>>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>>> +               __func__);
>>> +               return 0;
>>> +       }
>>> +
>>> +       hdmiphy_poweron(dev);
>>> +       return 0;
>>> +}
>>> +#endif
>>> +
>>> +
>>> +#ifdef CONFIG_PM_RUNTIME
>>> +static int hdmiphy_runtime_suspend(struct device *dev)
>>> +{
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       hdmiphy_poweroff(dev);
>>> +       return 0;
>>> +}
>>> +
>>> +static int hdmiphy_runtime_resume(struct device *dev)
>>> +{
>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>> +
>>> +       hdmiphy_poweron(dev);
>>> +       return 0;
>>> +}
>>> +#endif
>>> +
>>> +static const struct dev_pm_ops hdmiphy_pm_ops = {
>>> +       SET_SYSTEM_SLEEP_PM_OPS(hdmiphy_suspend, hdmiphy_resume)
>>> +       SET_RUNTIME_PM_OPS(hdmiphy_runtime_suspend,
>>> +               hdmiphy_runtime_resume, NULL)
>>> +};
>>> +
>>>  struct i2c_driver hdmiphy_driver = {
>>>         .driver = {
>>>                 .name   = "exynos-hdmiphy",
>>>                 .owner  = THIS_MODULE,
>>>                 .of_match_table = of_match_ptr(hdmiphy_match_types),
>>> +               .pm     = &hdmiphy_pm_ops,
>>>         },
>>>         .id_table = hdmiphy_id,
>>>         .probe          = hdmiphy_probe,
>>>         .remove         = hdmiphy_remove,
>>>         .command                = NULL,
>>>  };
>>> +
>>> +extern int exynos_hdmiphy_driver_register(void)
>>> +{
>>> +       return i2c_add_driver(&hdmiphy_driver);
>>> +}
>>> +
>>> +extern void exynos_hdmiphy_driver_unregister(void)
>>> +{
>>> +       i2c_del_driver(&hdmiphy_driver);
>>> +}
>>> +
>>>  EXPORT_SYMBOL(hdmiphy_driver);
>>> diff --git a/drivers/gpu/drm/exynos/regs-hdmiphy.h b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>>> new file mode 100644
>>> index 0000000..8f906c6
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>>> @@ -0,0 +1,61 @@
>>> +/*
>>> + *
>>> + *  regs-hdmiphy.h
>>> + *
>>> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
>>> + * http://www.samsung.com/
>>> + *
>>> + * HDMI-PHY register header file for Samsung TVOUT driver
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> +*/
>>> +
>>> +#ifndef SAMSUNG_REGS_HDMIPHY_H
>>> +#define SAMSUNG_REGS_HDMIPHY_H
>>> +
>>> +/*
>>> + * Register part
>>> +*/
>>> +
>>> +/* HDMI PHY Version Common */
>>> +#define HDMIPHY_MODE_SET_DONE          (0x1f)
>>> +
>>> +/* HDMI PHY Version 4212 */
>>> +#define HDMIPHY_4212_PCG_CON           (0x04)
>>> +#define HDMIPHY_4212_OSC_PAD_CON       (0x0b)
>>> +#define HDMIPHY_4212_PD_CON            (0x1d)
>>> +
>>> +/*
>>> + * Bit definition part
>>> + */
>>> +
>>> +/* HDMIPHY_MODE_SET_DONE */
>>> +#define HDMIPHY_MODE_EN                        (1 << 7)
>>> +
>>> +/* HDMIPHY_4212_PCG_CON */
>>> +#define HDMIPHY_PCG_RESET_EN           (1 << 3)
>>> +
>>> +/* HDMIPHY_4212_OSC_PAD_CON */
>>> +#define HDMIPHY_OSC_PAD_EN             (3 << 6)
>>> +
>>> +/* HDMIPHY_4212_PD_CON */
>>> +#define HDMIPHY_PDEN                   (1 << 7)
>>> +
>>> +#define HDMIPHY_PLL_PD                 (1 << 6)
>>> +#define HDMIPHY_CLKSER_PD              (1 << 5)
>>> +#define HDMIPHY_CLKDRV_PD              (1 << 4)
>>> +
>>> +#define HDMIPHY_DRV_PD                 (1 << 2)
>>> +#define HDMIPHY_SER_PD                 (1 << 1)
>>> +#define HDMIPHY_ICLK_PD                        (1 << 0)
>>> +
>>> +#define HDMIPHY_PD_ALL                 (HDMIPHY_PLL_PD |\
>>> +                                       HDMIPHY_CLKSER_PD |\
>>> +                                       HDMIPHY_CLKDRV_PD|\
>>> +                                       HDMIPHY_DRV_PD|\
>>> +                                       HDMIPHY_SER_PD|\
>>> +                                       HDMIPHY_ICLK_PD)
>>> +
>>> +#endif /* SAMSUNG_REGS_HDMIPHY_H */
>>> --
>>> 1.8.0
>>>
>
> --
> Regards,
> Rahul Sharma,
> email to: rahul.sharma@samsung.com
> Samsung India.
Inki Dae March 12, 2013, 12:07 p.m. UTC | #12
2013/3/12 Rahul Sharma <r.sh.open@gmail.com>:
> On Mon, Mar 4, 2013 at 7:35 PM, Rahul Sharma <r.sh.open@gmail.com> wrote:
>> Thanks Sean,
>>
>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>
>>> s/Physicaly/Physically/
>>>
>>>> should be instantiated independently.
>>>>
>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>> other. It is preferred to isolate them and maintained independently.
>>>>
>>>> This implementations is tested for:
>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>
>>>
>>> I don't like the idea of spawning off yet another driver in here. It
>>> adds more globals, more suspend/resume ordering issues, and more
>>> implicit dependencies. I understand, however, that this is the Chosen
>>> Way for the exynos driver, so I will save my rant.
>>>
>>
>> I agree to it. splitting phy to a new driver will complicate the power related
>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>> inside hdmi driver is complicating it with unrelated changes.
>>
>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>> we should re-factor this by explicitly calling power related callbacks
>> of mixer, phy,
>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>> device. AFAIR something like this is already in place in chrome-kernel.
>>
>>> I've made some comments below.
>>>
>>>> This patch is dependent on
>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>
>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>> ---
>>>> It is based on exynos-drm-next-todo branch at
>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>
>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> index 3da5c2d..03acb6b 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> @@ -334,6 +334,11 @@ static int __init exynos_drm_init(void)
>>>>         ret = platform_driver_register(&hdmi_driver);
>>>>         if (ret < 0)
>>>>                 goto out_hdmi;
>>>> +
>>>> +       ret = exynos_hdmiphy_driver_register();
>>>
>>> Not sure why you need to obfuscate the type of driver here. All the
>>> other ones are directly registered as platform drivers, I don't see
>>> the harm in just doing the i2c_add_driver directly here.
>>>
>>
>> AIMB, Phy is likely to get mapped as a platform device in later soc's.
>> I want to make driver registration (inside exynos_drm_drv.c) independent
>> of this changes. We can handle this inside exynos_hdmiphy_driver_register().
>>
>>>> +       if (ret < 0)
>>>> +               goto out_hdmiphy;
>>>> +
>>>>         ret = platform_driver_register(&mixer_driver);
>>>>         if (ret < 0)
>>>>                 goto out_mixer;
>>>
>>>
>>> I think this ordering is how you plan on enforcing the suspend/resume
>>> order for hdmi/mixer/hdmiphy. In that case, however, I don't think it
>>> makes sense to suspend/resume hdmiphy in between mixer and hdmi.
>>> Ideally, hdmiphy should go down first and come up last.
>>>
>>
>> I just wanted to keep, hdmi and hdmiphy things together. But what you said
>> above makes sense. I will do that change.
>>
>>>
>>>> @@ -436,6 +441,8 @@ out_common_hdmi_dev:
>>>>  out_common_hdmi:
>>>>         platform_driver_unregister(&mixer_driver);
>>>>  out_mixer:
>>>> +       exynos_hdmiphy_driver_unregister();
>>>> +out_hdmiphy:
>>>>         platform_driver_unregister(&hdmi_driver);
>>>>  out_hdmi:
>>>>  #endif
>>>> @@ -479,6 +486,7 @@ static void __exit exynos_drm_exit(void)
>>>>         exynos_platform_device_hdmi_unregister();
>>>>         platform_driver_unregister(&exynos_drm_common_hdmi_driver);
>>>>         platform_driver_unregister(&mixer_driver);
>>>> +       exynos_hdmiphy_driver_unregister();
>>>>         platform_driver_unregister(&hdmi_driver);
>>>>  #endif
>>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> index 4606fac..17c53e3 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> @@ -325,6 +325,12 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
>>>>  extern int exynos_platform_device_hdmi_register(void);
>>>>
>>>>  /*
>>>> + * these functions registers/unregisters exynos drm hdmiphy driver.
>>>> + */
>>>> +extern int exynos_hdmiphy_driver_register(void);
>>>> +extern void exynos_hdmiphy_driver_unregister(void);
>>>> +
>>>> +/*
>>>>   * this function unregisters exynos drm hdmi platform device if it exists.
>>>>   */
>>>>  void exynos_platform_device_hdmi_unregister(void);
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>>> index 5dc956b..45ef331 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>>> @@ -32,19 +32,22 @@
>>>>  /* platform device pointer for common drm hdmi device. */
>>>>  static struct platform_device *exynos_drm_hdmi_pdev;
>>>>
>>>> -/* Common hdmi subdrv needs to access the hdmi and mixer though context.
>>>> -* These should be initialied by the repective drivers */
>>>> +/* Common hdmi subdrv needs to access the hdmi, hdmiphy and mixer though
>>>> +*  context. These should be initialied by the repective drivers */
>>>>  static struct exynos_drm_hdmi_context *hdmi_ctx;
>>>> +static struct exynos_drm_hdmi_context *hdmiphy_ctx;
>>>>  static struct exynos_drm_hdmi_context *mixer_ctx;
>>>>
>>>>  /* these callback points shoud be set by specific drivers. */
>>>>  static struct exynos_hdmi_ops *hdmi_ops;
>>>> +static struct exynos_hdmiphy_ops *hdmiphy_ops;
>>>>  static struct exynos_mixer_ops *mixer_ops;
>>>>
>>>
>>> This stuff really sets my teeth on edge. I can't stand these global
>>> variables and the set-here, set-there, set-here dance that is done on
>>> probe.
>>>
>>> IMO, this and the suspend/resume ordering should be enough motivation
>>> to avoid separating everything out into their own drivers.
>>>
>>> Instead of adding more kruft to this list, we should be working to remove it.
>>>
>>> /rant
>>>
>>
>> Let me think over it. Please suggest me something if you have any idea to
>> correct this.
>>
>
> Hi all,
>
> I replaced these variables with a global LIST of subdrv's with associated
> type (HDMI / PHY / MIXER) and ops struct.
>
> drm-common-hdmi callback will parse this list and based on the subdrv
> type calls subdrv functions, but this adds overhead of parsing the list
> multiple times.
>
> If I cache the subdrvs in drm-common-hdmi context and parsing of the list
> can be avoided.
>
> What you think about above approach?
>

I think it's good to use existing exynos drm core framework. But I'm
anxious about probing point and existing subdrv callbacks with this.
Could you share that patch as RFC?

Thanks,
Inki Dae

> regards,
> Rahul Sharma.
>
>>>>  struct drm_hdmi_context {
>>>>         struct exynos_drm_subdrv        subdrv;
>>>>         struct exynos_drm_hdmi_context  *hdmi_ctx;
>>>>         struct exynos_drm_hdmi_context  *mixer_ctx;
>>>> +       struct exynos_drm_hdmi_context  *hdmiphy_ctx;
>>>>
>>>>         bool    enabled[MIXER_WIN_NR];
>>>>  };
>>>> @@ -74,6 +77,12 @@ void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>>                 hdmi_ctx = ctx;
>>>>  }
>>>>
>>>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>> +{
>>>> +       if (ctx)
>>>> +               hdmiphy_ctx = ctx;
>>>> +}
>>>> +
>>>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>>  {
>>>>         if (ctx)
>>>> @@ -88,6 +97,14 @@ void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
>>>>                 hdmi_ops = ops;
>>>>  }
>>>>
>>>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops)
>>>> +{
>>>> +       DRM_DEBUG_KMS("%s\n", __FILE__);
>>>
>>> Listing __FILE__ in these DRM_DEBUG_KMS traces is pretty useless. I'm
>>> sure we can determine the file from the function name. It would be
>>> slightly more useful to list __LINE__, but even more useful if you
>>> actually printed state relevant to the current function.
>>>
>>
>> OK. I will correct this.
>>
>>>> +
>>>> +       if (ops)
>>>> +               hdmiphy_ops = ops;
>>>> +}
>>>> +
>>>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
>>>>  {
>>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>> @@ -121,7 +138,7 @@ struct edid *drm_hdmi_get_edid(struct device *dev,
>>>>         return NULL;
>>>>  }
>>>>
>>>> -static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>>> +static int drm_hdmi_check_timing(struct device *dev, void *mode)
>>>>  {
>>>>         struct drm_hdmi_context *ctx = to_context(dev);
>>>>         int ret = 0;
>>>> @@ -129,18 +146,24 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>>
>>>>         /*
>>>> -       * Both, mixer and hdmi should be able to handle the requested mode.
>>>> -       * If any of the two fails, return mode as BAD.
>>>> +       * Mixer, hdmi and hdmiphy should be able to handle the requested mode.
>>>> +       * If any of the them fails, return mode as BAD.
>>>>         */
>>>>
>>>>         if (mixer_ops && mixer_ops->check_timing)
>>>> -               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
>>>> +               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, mode);
>>>>
>>>>         if (ret)
>>>>                 return ret;
>>>>
>>>>         if (hdmi_ops && hdmi_ops->check_timing)
>>>> -               return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
>>>> +               ret = hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, mode);
>>>> +
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       if (hdmiphy_ops && hdmiphy_ops->check_timing)
>>>> +               return hdmiphy_ops->check_timing(ctx->hdmiphy_ctx->ctx, mode);
>>>>
>>>>         return 0;
>>>>  }
>>>> @@ -254,6 +277,9 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
>>>>
>>>>         if (hdmi_ops && hdmi_ops->mode_set)
>>>>                 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
>>>> +
>>>> +       if (hdmiphy_ops && hdmiphy_ops->mode_set)
>>>> +               hdmiphy_ops->mode_set(ctx->hdmiphy_ctx->ctx, mode);
>>>>  }
>>>>
>>>>  static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
>>>> @@ -273,6 +299,15 @@ static void drm_hdmi_commit(struct device *subdrv_dev)
>>>>
>>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>>
>>>> +       if (hdmiphy_ops && hdmiphy_ops->prepare)
>>>> +               hdmiphy_ops->prepare(ctx->hdmiphy_ctx->ctx);
>>>> +
>>>> +       if (hdmi_ops && hdmi_ops->prepare)
>>>> +               hdmi_ops->prepare(ctx->hdmi_ctx->ctx);
>>>> +
>>>
>>> Why are you calling these prepare functions in commit? Isn't that
>>> precisely the reason drm has a prepare callback?
>>
>> True. Currently exynos_drm_encoder_prepare is blank. I will move this to
>> prepare callback.
>>
>>>
>>>> +       if (hdmiphy_ops && hdmiphy_ops->config_apply)
>>>> +               hdmiphy_ops->config_apply(ctx->hdmiphy_ctx->ctx);
>>>> +
>>>>         if (hdmi_ops && hdmi_ops->commit)
>>>>                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
>>>>  }
>>>> @@ -288,6 +323,9 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
>>>>
>>>>         if (hdmi_ops && hdmi_ops->dpms)
>>>>                 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
>>>> +
>>>> +       if (hdmiphy_ops && hdmiphy_ops->dpms)
>>>> +               hdmiphy_ops->dpms(ctx->hdmiphy_ctx->ctx, mode);
>>>
>>> I think you have to be more careful about the ordering of things here
>>> since you'll want to power on in a different order than you power off.
>>>
>>>>  }
>>>>
>>>>  static void drm_hdmi_apply(struct device *subdrv_dev)
>>>> @@ -393,6 +431,11 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>>>                 return -EFAULT;
>>>>         }
>>>>
>>>> +       if (!hdmiphy_ctx) {
>>>> +               DRM_ERROR("hdmiphy context not initialized.\n");
>>>> +               return -EFAULT;
>>>> +       }
>>>> +
>>>>         if (!mixer_ctx) {
>>>>                 DRM_ERROR("mixer context not initialized.\n");
>>>>                 return -EFAULT;
>>>> @@ -406,6 +449,7 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>>>         }
>>>>
>>>>         ctx->hdmi_ctx = hdmi_ctx;
>>>> +       ctx->hdmiphy_ctx = hdmiphy_ctx;
>>>>         ctx->mixer_ctx = mixer_ctx;
>>>>
>>>>         ctx->hdmi_ctx->drm_dev = drm_dev;
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>>> index fd2ff9f..10db62e 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>>> @@ -39,10 +39,19 @@ struct exynos_hdmi_ops {
>>>>         void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>>>         void (*get_max_resol)(void *ctx, unsigned int *width,
>>>>                                 unsigned int *height);
>>>> +       void (*prepare)(void *ctx);
>>>>         void (*commit)(void *ctx);
>>>>         void (*dpms)(void *ctx, int mode);
>>>>  };
>>>>
>>>> +struct exynos_hdmiphy_ops {
>>>> +       int (*check_timing)(void *ctx, struct drm_display_mode *mode);
>>>> +       void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>>> +       void (*prepare)(void *ctx);
>>>> +       int (*config_apply)(void *ctx);
>>>> +       void (*dpms)(void *ctx, int mode);
>>>> +};
>>>> +
>>>>  struct exynos_mixer_ops {
>>>>         /* manager */
>>>>         int (*iommu_on)(void *ctx, bool enable);
>>>> @@ -61,7 +70,9 @@ struct exynos_mixer_ops {
>>>>  };
>>>>
>>>>  void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>>  void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
>>>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops);
>>>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
>>>>  #endif
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
>>>> index a5ca2cc..7ee350d 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
>>>> @@ -194,7 +194,6 @@ struct hdmi_context {
>>>>         int                             internal_irq;
>>>>
>>>>         struct i2c_client               *ddc_port;
>>>> -       struct i2c_client               *hdmiphy_port;
>>>>
>>>>         /* current hdmiphy conf regs */
>>>>         struct hdmi_conf_regs           mode_conf;
>>>> @@ -206,180 +205,6 @@ struct hdmi_context {
>>>>         enum hdmi_type                  type;
>>>>  };
>>>>
>>>> -struct hdmiphy_config {
>>>> -       int pixel_clock;
>>>> -       u8 conf[32];
>>>> -};
>>>> -
>>>> -/* list of phy config settings */
>>>> -static const struct hdmiphy_config hdmiphy_v13_configs[] = {
>>>> -       {
>>>> -               .pixel_clock = 27000000,
>>>> -               .conf = {
>>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>>>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 27027000,
>>>> -               .conf = {
>>>> -                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>>>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 74176000,
>>>> -               .conf = {
>>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>>>> -                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> -                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 74250000,
>>>> -               .conf = {
>>>> -                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>>>> -                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>>>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>>>> -                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 148500000,
>>>> -               .conf = {
>>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>>>> -                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>>>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> -                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>>>> -               },
>>>> -       },
>>>> -};
>>>> -
>>>> -static const struct hdmiphy_config hdmiphy_v14_configs[] = {
>>>> -       {
>>>> -               .pixel_clock = 25200000,
>>>> -               .conf = {
>>>> -                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>>>> -                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 27000000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>>>> -                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 27027000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>>>> -                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 36000000,
>>>> -               .conf = {
>>>> -                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 40000000,
>>>> -               .conf = {
>>>> -                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>> -                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 65000000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>>>> -                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 74176000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>>>> -                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 74250000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>>>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 83500000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>>>> -                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 106500000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>>>> -                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 108000000,
>>>> -               .conf = {
>>>> -                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>>>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 146250000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>>>> -                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>> -               },
>>>> -       },
>>>> -       {
>>>> -               .pixel_clock = 148500000,
>>>> -               .conf = {
>>>> -                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>>>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> -                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>>>> -               },
>>>> -       },
>>>> -};
>>>> -
>>>>  struct hdmi_infoframe {
>>>>         enum HDMI_PACKET_TYPE type;
>>>>         u8 ver;
>>>> @@ -774,46 +599,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
>>>>         return raw_edid;
>>>>  }
>>>>
>>>> -static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
>>>> -{
>>>> -       const struct hdmiphy_config *confs;
>>>> -       int count, i;
>>>> -
>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> -
>>>> -       if (hdata->type == HDMI_TYPE13) {
>>>> -               confs = hdmiphy_v13_configs;
>>>> -               count = ARRAY_SIZE(hdmiphy_v13_configs);
>>>> -       } else if (hdata->type == HDMI_TYPE14) {
>>>> -               confs = hdmiphy_v14_configs;
>>>> -               count = ARRAY_SIZE(hdmiphy_v14_configs);
>>>> -       } else
>>>> -               return -EINVAL;
>>>> -
>>>> -       for (i = 0; i < count; i++)
>>>> -               if (confs[i].pixel_clock == pixel_clock)
>>>> -                       return i;
>>>> -
>>>> -       DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
>>>> -       return -EINVAL;
>>>> -}
>>>> -
>>>> -static int hdmi_check_timing(void *ctx, struct drm_display_mode *mode)
>>>> -{
>>>> -       struct hdmi_context *hdata = ctx;
>>>> -       int ret;
>>>> -
>>>> -       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>>>> -               __func__, mode->hdisplay, mode->vdisplay,
>>>> -               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
>>>> -               false, mode->clock * 1000);
>>>> -
>>>> -       ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
>>>> -       if (ret < 0)
>>>> -               return ret;
>>>> -       return 0;
>>>> -}
>>>> -
>>>>  static void hdmi_set_acr(u32 freq, u8 *acr)
>>>>  {
>>>>         u32 n, cts;
>>>> @@ -1309,20 +1094,12 @@ static void hdmi_timing_apply(struct hdmi_context *hdata)
>>>>
>>>>  static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>>>  {
>>>> -       u8 buffer[2];
>>>>         u32 reg;
>>>>
>>>>         clk_disable(hdata->res.sclk_hdmi);
>>>>         clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
>>>>         clk_enable(hdata->res.sclk_hdmi);
>>>>
>>>> -       /* operation mode */
>>>> -       buffer[0] = 0x1f;
>>>> -       buffer[1] = 0x00;
>>>> -
>>>> -       if (hdata->hdmiphy_port)
>>>> -               i2c_master_send(hdata->hdmiphy_port, buffer, 2);
>>>> -
>>>>         if (hdata->type == HDMI_TYPE13)
>>>>                 reg = HDMI_V13_PHY_RSTOUT;
>>>>         else
>>>> @@ -1335,102 +1112,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>>>         usleep_range(10000, 12000);
>>>>  }
>>>>
>>>> -static void hdmiphy_poweron(struct hdmi_context *hdata)
>>>> -{
>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> -
>>>> -       if (hdata->type == HDMI_TYPE14)
>>>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
>>>> -                       HDMI_PHY_POWER_OFF_EN);
>>>> -}
>>>> -
>>>> -static void hdmiphy_poweroff(struct hdmi_context *hdata)
>>>> -{
>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> -
>>>> -       if (hdata->type == HDMI_TYPE14)
>>>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
>>>> -                       HDMI_PHY_POWER_OFF_EN);
>>>
>>> I assume these register writes are replaced with i2c operations in the
>>> new phy driver?
>>
>> It is done for exynos5 phy. I am not having the sequence for exynos4. I
>> will add it later.
>>
>>>
>>>> -}
>>>> -
>>>> -static void hdmiphy_conf_apply(struct hdmi_context *hdata)
>>>> -{
>>>> -       const u8 *hdmiphy_data;
>>>> -       u8 buffer[32];
>>>> -       u8 operation[2];
>>>> -       u8 read_buffer[32] = {0, };
>>>> -       int ret;
>>>> -       int i;
>>>> -
>>>> -       if (!hdata->hdmiphy_port) {
>>>> -               DRM_ERROR("hdmiphy is not attached\n");
>>>> -               return;
>>>> -       }
>>>> -
>>>> -       /* pixel clock */
>>>> -       i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
>>>> -       if (i < 0) {
>>>> -               DRM_ERROR("failed to find hdmiphy conf\n");
>>>> -               return;
>>>> -       }
>>>> -
>>>> -       if (hdata->type == HDMI_TYPE13) {
>>>> -               hdmiphy_data = hdmiphy_v13_configs[i].conf;
>>>> -       } else {
>>>> -               hdmiphy_data = hdmiphy_v14_configs[i].conf;
>>>> -       }
>>>> -
>>>> -       memcpy(buffer, hdmiphy_data, 32);
>>>> -       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
>>>> -       if (ret != 32) {
>>>> -               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>>>> -               return;
>>>> -       }
>>>> -
>>>> -       usleep_range(10000, 12000);
>>>> -
>>>> -       /* operation mode */
>>>> -       operation[0] = 0x1f;
>>>> -       operation[1] = 0x80;
>>>> -
>>>> -       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
>>>> -       if (ret != 2) {
>>>> -               DRM_ERROR("failed to enable hdmiphy\n");
>>>> -               return;
>>>> -       }
>>>> -
>>>> -       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
>>>> -       if (ret < 0) {
>>>> -               DRM_ERROR("failed to read hdmiphy config\n");
>>>> -               return;
>>>> -       }
>>>> -
>>>> -       for (i = 0; i < ret; i++)
>>>> -               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
>>>> -                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
>>>> -}
>>>> -
>>>> -static void hdmi_conf_apply(struct hdmi_context *hdata)
>>>> -{
>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> -
>>>> -       hdmiphy_conf_reset(hdata);
>>>> -       hdmiphy_conf_apply(hdata);
>>>> -
>>>> -       mutex_lock(&hdata->hdmi_mutex);
>>>> -       hdmi_conf_reset(hdata);
>>>> -       hdmi_conf_init(hdata);
>>>> -       mutex_unlock(&hdata->hdmi_mutex);
>>>> -
>>>> -       hdmi_audio_init(hdata);
>>>> -
>>>> -       /* setting core registers */
>>>> -       hdmi_timing_apply(hdata);
>>>> -       hdmi_audio_control(hdata, true);
>>>> -
>>>> -       hdmi_regs_dump(hdata, "start");
>>>> -}
>>>> -
>>>>  static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
>>>>  {
>>>>         int i;
>>>> @@ -1448,7 +1129,6 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
>>>>
>>>>         hdata->mode_conf.cea_video_id =
>>>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>>>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>>
>>> I don't think you use pixel_clock in mode_conf any longer, please
>>> remove it from the struct.
>>>
>>
>> Correct. I will remove pixel_clock from struct hdmi_conf_regs.
>>
>>>>
>>>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>>>         hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
>>>> @@ -1544,7 +1224,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
>>>>
>>>>         hdata->mode_conf.cea_video_id =
>>>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>>>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>>>
>>>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>>>         hdmi_set_reg(core->v_line, 2, m->vtotal);
>>>> @@ -1670,13 +1349,32 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width,
>>>>         *height = MAX_HEIGHT;
>>>>  }
>>>>
>>>> +static void hdmi_commit_prepare(void *ctx)
>>>
>>> I don't know why the "_commit" is there, just hdmi_prepare will be fine.
>>>
>> Ok.
>>
>>>> +{
>>>> +       struct hdmi_context *hdata = ctx;
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       hdmiphy_conf_reset(hdata);
>>>
>>> There's still phy-specific kruft laying around the driver. There are
>>> now a bunch of implicit dependencies between the hdmi driver and the
>>> hdmiphy driver.
>>>
>>
>> I need to follow this sequence:
>>
>> phy off (hdmiphy_prepare) -> phy reset using HDMI IP REG (hdmi_prepare) ->
>> phy config (hdmiphy_config_apply) -> hdmi ip config  (hdmi_commit)
>>
>> Phy reset needs to be done by toggling Phy_reset bit in HDMI IP register which
>> cannot be accessed in hdmiphy driver. I didn't see any better way.
>>
>>> Another example is the sclk_hdmiphy clock, which is still controlled
>>> by the hdmi driver, it should be controlled by the phy.
>>>
>>
>> sclk_hdmiphy is one of the source clock for hdmi IP. HDMI IP needs to change the
>> source between sclk_hdmiphy (only if stable) else pixel clock. Above
>> code looks ok
>> to me. Please explain a bit mode on this.
>>
>>>> +}
>>>> +
>>>>  static void hdmi_commit(void *ctx)
>>>>  {
>>>>         struct hdmi_context *hdata = ctx;
>>>>
>>>>         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>
>>>> -       hdmi_conf_apply(hdata);
>>>> +       mutex_lock(&hdata->hdmi_mutex);
>>>> +       hdmi_conf_reset(hdata);
>>>> +       hdmi_conf_init(hdata);
>>>> +       mutex_unlock(&hdata->hdmi_mutex);
>>>> +
>>>> +       hdmi_audio_init(hdata);
>>>> +
>>>> +       /* setting core registers */
>>>> +       hdmi_timing_apply(hdata);
>>>> +       hdmi_audio_control(hdata, true);
>>>> +
>>>> +       hdmi_regs_dump(hdata, "start");
>>>>  }
>>>>
>>>>  static void hdmi_poweron(struct hdmi_context *hdata)
>>>> @@ -1699,8 +1397,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
>>>>         clk_enable(res->hdmiphy);
>>>>         clk_enable(res->hdmi);
>>>>         clk_enable(res->sclk_hdmi);
>>>> -
>>>> -       hdmiphy_poweron(hdata);
>>>>  }
>>>>
>>>>  static void hdmi_poweroff(struct hdmi_context *hdata)
>>>> @@ -1719,7 +1415,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
>>>>          * its reset state seems to meet the condition.
>>>>          */
>>>>         hdmiphy_conf_reset(hdata);
>>>> -       hdmiphy_poweroff(hdata);
>>>>
>>>>         clk_disable(res->sclk_hdmi);
>>>>         clk_disable(res->hdmi);
>>>> @@ -1761,11 +1456,11 @@ static struct exynos_hdmi_ops hdmi_ops = {
>>>>         /* display */
>>>>         .is_connected   = hdmi_is_connected,
>>>>         .get_edid       = hdmi_get_edid,
>>>> -       .check_timing   = hdmi_check_timing,
>>>>
>>>>         /* manager */
>>>>         .mode_set       = hdmi_mode_set,
>>>>         .get_max_resol  = hdmi_get_max_resol,
>>>> +       .prepare                = hdmi_commit_prepare,
>>>>         .commit         = hdmi_commit,
>>>>         .dpms           = hdmi_dpms,
>>>>  };
>>>> @@ -1878,7 +1573,7 @@ fail:
>>>>         return -ENODEV;
>>>>  }
>>>>
>>>> -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
>>>> +static struct i2c_client *hdmi_ddc;
>>>>
>>>>  void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>>>  {
>>>> @@ -1886,12 +1581,6 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>>>                 hdmi_ddc = ddc;
>>>>  }
>>>>
>>>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
>>>> -{
>>>> -       if (hdmiphy)
>>>> -               hdmi_hdmiphy = hdmiphy;
>>>> -}
>>>> -
>>>>  #ifdef CONFIG_OF
>>>>  static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
>>>>                                         (struct device *dev)
>>>> @@ -2051,27 +1740,18 @@ static int hdmi_probe(struct platform_device *pdev)
>>>>
>>>>         hdata->ddc_port = hdmi_ddc;
>>>>
>>>> -       /* hdmiphy i2c driver */
>>>> -       if (i2c_add_driver(&hdmiphy_driver)) {
>>>> -               DRM_ERROR("failed to register hdmiphy i2c driver\n");
>>>> -               ret = -ENOENT;
>>>> -               goto err_ddc;
>>>> -       }
>>>> -
>>>> -       hdata->hdmiphy_port = hdmi_hdmiphy;
>>>> -
>>>>         hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
>>>>         if (hdata->external_irq < 0) {
>>>>                 DRM_ERROR("failed to get GPIO external irq\n");
>>>>                 ret = hdata->external_irq;
>>>> -               goto err_hdmiphy;
>>>> +               goto err_ddc;
>>>>         }
>>>>
>>>>         hdata->internal_irq = platform_get_irq(pdev, 0);
>>>>         if (hdata->internal_irq < 0) {
>>>>                 DRM_ERROR("failed to get platform internal irq\n");
>>>>                 ret = hdata->internal_irq;
>>>> -               goto err_hdmiphy;
>>>> +               goto err_ddc;
>>>>         }
>>>>
>>>>         hdata->hpd = gpio_get_value(hdata->hpd_gpio);
>>>> @@ -2082,7 +1762,7 @@ static int hdmi_probe(struct platform_device *pdev)
>>>>                         "hdmi_external", drm_hdmi_ctx);
>>>>         if (ret) {
>>>>                 DRM_ERROR("failed to register hdmi external interrupt\n");
>>>> -               goto err_hdmiphy;
>>>> +               goto err_ddc;
>>>>         }
>>>>
>>>>         ret = request_threaded_irq(hdata->internal_irq, NULL,
>>>> @@ -2105,8 +1785,6 @@ static int hdmi_probe(struct platform_device *pdev)
>>>>
>>>>  err_free_irq:
>>>>         free_irq(hdata->external_irq, drm_hdmi_ctx);
>>>> -err_hdmiphy:
>>>> -       i2c_del_driver(&hdmiphy_driver);
>>>>  err_ddc:
>>>>         i2c_del_driver(&ddc_driver);
>>>>         return ret;
>>>> @@ -2125,9 +1803,6 @@ static int hdmi_remove(struct platform_device *pdev)
>>>>         free_irq(hdata->internal_irq, hdata);
>>>>         free_irq(hdata->external_irq, hdata);
>>>>
>>>> -
>>>> -       /* hdmiphy i2c driver */
>>>> -       i2c_del_driver(&hdmiphy_driver);
>>>>         /* DDC i2c driver */
>>>>         i2c_del_driver(&ddc_driver);
>>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
>>>> index 0ddf395..6595d7b 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.h
>>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
>>>> @@ -15,7 +15,6 @@
>>>>  #define _EXYNOS_HDMI_H_
>>>>
>>>>  void hdmi_attach_ddc_client(struct i2c_client *ddc);
>>>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
>>>>
>>>>  extern struct i2c_driver hdmiphy_driver;
>>>>  extern struct i2c_driver ddc_driver;
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>>> index ea49d13..0d47302 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>>> @@ -16,33 +16,437 @@
>>>>  #include <linux/kernel.h>
>>>>  #include <linux/i2c.h>
>>>>  #include <linux/module.h>
>>>> +#include <linux/pm_runtime.h>
>>>> +#include <linux/clk.h>
>>>>
>>>>  #include "exynos_drm_drv.h"
>>>> +#include "exynos_drm_hdmi.h"
>>>>  #include "exynos_hdmi.h"
>>>>
>>>> +#include "regs-hdmiphy.h"
>>>>
>>>> -static int hdmiphy_probe(struct i2c_client *client,
>>>> -       const struct i2c_device_id *id)
>>>> +#define HDMIPHY_REG_COUNT              (32)
>>>
>>> No need to include parentheses.
>>
>> Ok.
>>>
>>>> +
>>>> +enum hdmiphy_type {
>>>> +       HDMIPHY_EXYNOS4210,
>>>> +       HDMIPHY_EXYNOS4212,
>>>> +};
>>>> +
>>>> +struct hdmiphy_context {
>>>> +       struct device           *dev;
>>>> +       bool                    powered;
>>>> +       void                    *parent_ctx;
>>>> +       enum hdmiphy_type               type;
>>>> +       const struct hdmiphy_config     *conf;
>>>> +       struct clk              *hdmiphy;
>>>> +};
>>>> +
>>>> +struct hdmiphy_config {
>>>> +       int pixel_clock;
>>>> +       u8 conf[HDMIPHY_REG_COUNT];
>>>> +};
>>>> +
>>>> +/* list of all required phy config settings */
>>>> +static const struct hdmiphy_config hdmiphy_4212_configs[] = {
>>>> +       {
>>>> +               .pixel_clock = 25200000,
>>>> +               .conf = {
>>>> +                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>>>> +                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 27000000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>>>> +                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 27027000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>>>> +                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 36000000,
>>>> +               .conf = {
>>>> +                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 40000000,
>>>> +               .conf = {
>>>> +                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>> +                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 65000000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>>>> +                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 74176000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>>>> +                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 74250000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>>>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 83500000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>>>> +                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 106500000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>>>> +                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 108000000,
>>>> +               .conf = {
>>>> +                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>>>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 146250000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>>>> +                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 148500000,
>>>> +               .conf = {
>>>> +                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>>>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>> +                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>>>> +               },
>>>> +       },
>>>> +};
>>>> +
>>>> +static const struct hdmiphy_config hdmiphy_4210_configs[] = {
>>>> +       {
>>>> +               .pixel_clock = 27000000,
>>>> +               .conf = {
>>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>>>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 27027000,
>>>> +               .conf = {
>>>> +                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>>>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 74176000,
>>>> +               .conf = {
>>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>>>> +                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> +                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 74250000,
>>>> +               .conf = {
>>>> +                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>>>> +                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>>>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>>>> +                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>> +               },
>>>> +       },
>>>> +       {
>>>> +               .pixel_clock = 148500000,
>>>> +               .conf = {
>>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>>>> +                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>>>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>> +                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>>>> +               },
>>>> +       },
>>>> +};
>>>> +
>>>> +static const struct hdmiphy_config *hdmiphy_find_conf(void *ctx,
>>>> +                       const struct drm_display_mode *mode)
>>>> +{
>>>> +       struct hdmiphy_context *hdata = ctx;
>>>> +       const struct hdmiphy_config *confs;
>>>> +       int count, i;
>>>> +
>>>> +       switch (hdata->type) {
>>>> +       case HDMIPHY_EXYNOS4212:
>>>> +               confs = hdmiphy_4212_configs;
>>>> +               count = ARRAY_SIZE(hdmiphy_4212_configs);
>>>> +               break;
>>>> +       case HDMIPHY_EXYNOS4210:
>>>> +               confs = hdmiphy_4210_configs;
>>>> +               count = ARRAY_SIZE(hdmiphy_4210_configs);
>>>> +               break;
>>>> +       default:
>>>> +               return NULL;
>>>
>>> Should probably DRM_ERROR here.
>>>
>> ok. I will add that.
>>
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < count; i++)
>>>> +               if (confs[i].pixel_clock == (mode->clock * 1000))
>>>> +                       return &confs[i];
>>>> +
>>>> +       return NULL;
>>>> +}
>>>> +
>>>> +static int hdmiphy_check_timing(void *ctx, struct drm_display_mode *mode)
>>>>  {
>>>> -       hdmi_attach_hdmiphy_client(client);
>>>> +       const struct hdmiphy_config *conf;
>>>> +       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>>>> +               __func__, mode->hdisplay, mode->vdisplay,
>>>> +               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>>> +               ? true : false, mode->clock * 1000);
>>>
>>> No need to print __func__ here, DRM_DEBUG_KMS will do that for you. I
>>> think you should also parse the flags to a string instead of a bool
>>> cast to an int.
>>>
>> ok.
>>
>>>>
>>>> -       dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
>>>> -               "into i2c adapter successfully\n");
>>>> +       conf = hdmiphy_find_conf(ctx, mode);
>>>> +       if (!conf) {
>>>> +               DRM_DEBUG_KMS("Display Mode is not supported.\n");
>>>> +               return -EINVAL;
>>>> +       }
>>>>
>>>>         return 0;
>>>>  }
>>>>
>>>> -static int hdmiphy_remove(struct i2c_client *client)
>>>> +static void hdmiphy_mode_set(void *ctx, struct drm_display_mode *mode)
>>>> +{
>>>> +       struct hdmiphy_context *hdata = ctx;
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>
>>> No need to print __func__ here, DRM_DEBUG_KMS will do that for you.
>>> This comment stand for the rest of the calls, as well.
>>>
>>>> +
>>>> +       hdata->conf = hdmiphy_find_conf(ctx, mode);
>>>> +}
>>>> +
>>>> +static void hdmiphy_config_prepare(void *ctx)
>>>> +{
>>>> +       struct hdmiphy_context *hdata = ctx;
>>>> +       const struct i2c_client *client = to_i2c_client(hdata->dev);
>>>> +       u8 buffer[2];
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       /* operation mode */
>>>> +       buffer[0] = HDMIPHY_MODE_SET_DONE;
>>>> +       buffer[1] = 0x00;
>>>> +
>>>> +       if (client)
>>>> +               i2c_master_send(client, buffer, 2);
>>>> +}
>>>> +
>>>> +static int hdmiphy_config_apply(void *ctx)
>>>>  {
>>>> -       dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
>>>> -               "from i2c adapter successfully\n");
>>>> +       struct hdmiphy_context *hdata = ctx;
>>>> +       struct i2c_client *client = to_i2c_client(hdata->dev);
>>>> +       const u8 *hdmiphy_data;
>>>> +       u8 buffer[HDMIPHY_REG_COUNT];
>>>> +       u8 operation[2];
>>>> +       u8 read_buffer[HDMIPHY_REG_COUNT] = {0, };
>>>> +       int ret;
>>>> +       int i;
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       /* pixel clock */
>>>> +       if (hdata->conf && client)
>>>> +               hdmiphy_data = hdata->conf->conf;
>>>> +       else
>>>> +               return -EINVAL;
>>>> +
>>>> +       memcpy(buffer, hdmiphy_data, HDMIPHY_REG_COUNT);
>>>> +
>>>> +       ret = i2c_master_send(client, buffer, HDMIPHY_REG_COUNT);
>>>> +       if (ret != HDMIPHY_REG_COUNT) {
>>>> +               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       usleep_range(10000, 12000);
>>>> +
>>>> +       /* operation mode */
>>>> +       operation[0] = HDMIPHY_MODE_SET_DONE;
>>>> +       operation[1] = HDMIPHY_MODE_EN;
>>>> +
>>>> +       ret = i2c_master_send(client, operation, 2);
>>>> +       if (ret != 2) {
>>>> +               DRM_ERROR("failed to enable hdmiphy\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ret = i2c_master_recv(client, read_buffer, HDMIPHY_REG_COUNT);
>>>> +       if (ret < 0) {
>>>> +               DRM_ERROR("failed to read hdmiphy config\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       for (i = 0; i < ret; i++)
>>>> +               DRM_DEBUG_KMS("hdmiphy[0x%02x] wr[0x%02x], rd[0x%02x]\n",
>>>> +                               i, buffer[i], read_buffer[i]);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static void hdmiphy_dpms(void *ctx, int mode)
>>>> +{
>>>> +       struct hdmiphy_context *hdata = ctx;
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
>>>> +
>>>> +       switch (mode) {
>>>> +       case DRM_MODE_DPMS_ON:
>>>> +               if (pm_runtime_suspended(hdata->dev))
>>>> +                       pm_runtime_get_sync(hdata->dev);
>>>> +               break;
>>>> +       case DRM_MODE_DPMS_STANDBY:
>>>> +       case DRM_MODE_DPMS_SUSPEND:
>>>> +       case DRM_MODE_DPMS_OFF:
>>>> +               if (!pm_runtime_suspended(hdata->dev))
>>>> +                       pm_runtime_put_sync(hdata->dev);
>>>> +               break;
>>>> +       default:
>>>> +               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
>>>> +               break;
>>>> +       }
>>>> +}
>>>> +
>>>> +static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
>>>> +                              u8 reg, u8 mask, u8 val)
>>>> +{
>>>> +       int ret;
>>>> +       u8 buffer[2];
>>>> +
>>>> +       buffer[0] = reg;
>>>> +       buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
>>>> +       reg_cache[reg] = buffer[1];
>>>> +
>>>> +       ret = i2c_master_send(client, buffer, 2);
>>>> +       if (ret != 2)
>>>> +               return -EIO;
>>>>
>>>>         return 0;
>>>>  }
>>>>
>>>> +static int hdmiphy_4412_turn_on(struct i2c_client *client, bool on)
>>>
>>> This name is misleading. Please change it to something that doesn't
>>> convey a power state, such as hdmiphy_4412_power
>>>
>> ok. I will change that.
>>
>>>> +{
>>>> +       u8 reg_cache[HDMIPHY_REG_COUNT] = { 0 };
>>>> +       u8 buffer[2];
>>>> +       int ret;
>>>> +
>>>> +       DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
>>>> +
>>>> +       /* Cache all hdmi-phy registers to make the code below faster */
>>>> +       buffer[0] = 0x0;
>>>> +       ret = i2c_master_send(client, buffer, 1);
>>>> +       if (ret != 1) {
>>>> +               ret = -EIO;
>>>> +               goto exit;
>>>> +       }
>>>> +       ret = i2c_master_recv(client, reg_cache, HDMIPHY_REG_COUNT);
>>>> +       if (ret != HDMIPHY_REG_COUNT) {
>>>> +               ret = -EIO;
>>>> +               goto exit;
>>>> +       }
>>>> +
>>>> +       /* Change to/from configuration from/to operation mode */
>>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_MODE_SET_DONE,
>>>> +                               HDMIPHY_MODE_EN, on ? ~0 : 0);
>>>> +       if (ret)
>>>> +               goto exit;
>>>> +
>>>> +       /*
>>>> +        * Turn off "oscpad" if !on; it turns on again in
>>>> +        * during phy-configuration.
>>>> +        */
>>>> +       if (!on)
>>>> +               ret = hdmiphy_update_bits(client, reg_cache,
>>>> +                       HDMIPHY_4212_OSC_PAD_CON, HDMIPHY_OSC_PAD_EN, 0);
>>>> +               if (ret)
>>>> +                       goto exit;
>>>> +
>>>> +       /* Disable powerdown if on; enable if !on */
>>>
>>> This comment really isn't useful, it's just describing the code.
>>
>> I will remove this comment.
>>
>>>
>>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>>>> +                       HDMIPHY_PDEN, on ? 0 : ~0);
>>>> +       if (ret)
>>>> +               goto exit;
>>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>>>> +                       HDMIPHY_PD_ALL, on ? 0 : ~0);
>>>> +       if (ret)
>>>> +               goto exit;
>>>> +
>>>> +       /* Disable pixel clock generator block if !on */
>>>> +       if (!on)
>>>> +               ret = hdmiphy_update_bits(client, reg_cache,
>>>> +                       HDMIPHY_4212_PCG_CON, HDMIPHY_PCG_RESET_EN, 0);
>>>> +               if (ret)
>>>> +                       goto exit;
>>>> +
>>>> +exit:
>>>> +       /* Don't expect any errors so just do a single warn */
>>>> +       WARN_ON(ret);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static struct exynos_hdmiphy_ops hdmiphy_ops = {
>>>> +       .check_timing   = hdmiphy_check_timing,
>>>> +       .mode_set               = hdmiphy_mode_set,
>>>> +       .prepare                = hdmiphy_config_prepare,
>>>> +       .config_apply   = hdmiphy_config_apply,
>>>> +       .dpms           = hdmiphy_dpms,
>>>> +};
>>>> +
>>>>  static const struct i2c_device_id hdmiphy_id[] = {
>>>> -       { "s5p_hdmiphy", 0 },
>>>> -       { "exynos5-hdmiphy", 0 },
>>>> +       { "s5p_hdmiphy", HDMIPHY_EXYNOS4210 },
>>>> +       { "exynos5-hdmiphy", HDMIPHY_EXYNOS4212 },
>>>>         { },
>>>>  };
>>>>
>>>> @@ -50,21 +454,183 @@ static const struct i2c_device_id hdmiphy_id[] = {
>>>>  static struct of_device_id hdmiphy_match_types[] = {
>>>>         {
>>>>                 .compatible = "samsung,exynos5-hdmiphy",
>>>> +               .data   = (void *)HDMIPHY_EXYNOS4212,
>>>>         }, {
>>>>                 /* end node */
>>>>         }
>>>>  };
>>>>  #endif
>>>>
>>>> +static int hdmiphy_probe(struct i2c_client *client,
>>>> +       const struct i2c_device_id *id)
>>>> +{
>>>> +       struct device *dev = &client->dev;
>>>> +       struct hdmiphy_context *hdata;
>>>> +       struct exynos_drm_hdmi_context *drm_hdmi_ctx;
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
>>>> +                                       GFP_KERNEL);
>>>> +       if (!drm_hdmi_ctx) {
>>>> +               DRM_ERROR("failed to allocate common hdmi context.\n");
>>>> +               return -ENOMEM;
>>>> +       }
>>>> +
>>>> +       hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
>>>> +       if (!hdata) {
>>>> +               DRM_ERROR("failed to allocate hdmiphy context.\n");
>>>> +               return -ENOMEM;
>>>> +       }
>>>> +
>>>> +       if (dev->of_node) {
>>>> +               const struct of_device_id *match;
>>>> +               match = of_match_node(of_match_ptr(hdmiphy_match_types),
>>>> +                                       dev->of_node);
>>>> +               if (match == NULL)
>>>> +                       return -ENODEV;
>>>> +               hdata->type = (enum hdmiphy_type)match->data;
>>>> +       } else {
>>>> +               hdata->type = (enum hdmiphy_type)id->driver_data;
>>>> +       }
>>>> +
>>>> +       drm_hdmi_ctx->ctx = (void *)hdata;
>>>> +       hdata->parent_ctx = (void *)drm_hdmi_ctx;
>>>> +       hdata->dev = dev;
>>>> +
>>>> +       hdata->hdmiphy = devm_clk_get(dev, "hdmiphy");
>>>
>>> I don't see this call removed in the hdmi driver, am I missing something?
>>>
>>
>> I took it wrong. I thought hdmi IP is clocked by hdmiphy clock. Actually it is
>> sclk_hdmiphy clock. I will remove this from hdmi driver.
>>
>>>> +       if (IS_ERR_OR_NULL(hdata->hdmiphy)) {
>>>> +               DRM_ERROR("failed to get clock 'hdmiphy'\n");
>>>> +               return PTR_ERR(hdata->hdmiphy);
>>>> +       }
>>>> +
>>>> +       i2c_set_clientdata(client, hdata);
>>>> +
>>>> +       /* Attach HDMI-PHY Driver to common hdmi. */
>>>> +       exynos_hdmiphy_drv_attach(drm_hdmi_ctx);
>>>> +
>>>> +       /* register specific callbacks to common hdmi. */
>>>> +       exynos_hdmiphy_ops_register(&hdmiphy_ops);
>>>> +
>>>> +       pm_runtime_enable(dev);
>>>> +
>>>> +       dev_info(&client->adapter->dev,
>>>> +               "attached s5p_hdmiphy into i2c adapter successfully\n");
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int hdmiphy_remove(struct i2c_client *client)
>>>> +{
>>>> +       dev_info(&client->adapter->dev,
>>>> +               "detached s5p_hdmiphy from i2c adapter successfully\n");
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static void hdmiphy_poweroff(struct device *dev)
>>>> +{
>>>> +       struct i2c_client *client = to_i2c_client(dev);
>>>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>>>> +               hdmiphy_4412_turn_on(client, 0);
>>>> +
>>>> +       clk_disable(hdata->hdmiphy);
>>>> +}
>>>> +
>>>> +static void hdmiphy_poweron(struct device *dev)
>>>> +{
>>>> +       struct i2c_client *client = to_i2c_client(dev);
>>>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>>>> +
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       clk_enable(hdata->hdmiphy);
>>>> +
>>>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>>>> +               hdmiphy_4412_turn_on(client, 1);
>>>> +}
>>>> +
>>>> +#ifdef CONFIG_PM_SLEEP
>>>> +static int hdmiphy_suspend(struct device *dev)
>>>> +{
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       if (pm_runtime_suspended(dev)) {
>>>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>>>> +               __func__);
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       hdmiphy_poweroff(dev);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int hdmiphy_resume(struct device *dev)
>>>> +{
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       if (pm_runtime_suspended(dev)) {
>>>> +               /* dpms callback should resume the mixer. */
>>>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>>>> +               __func__);
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       hdmiphy_poweron(dev);
>>>> +       return 0;
>>>> +}
>>>> +#endif
>>>> +
>>>> +
>>>> +#ifdef CONFIG_PM_RUNTIME
>>>> +static int hdmiphy_runtime_suspend(struct device *dev)
>>>> +{
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       hdmiphy_poweroff(dev);
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int hdmiphy_runtime_resume(struct device *dev)
>>>> +{
>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>> +
>>>> +       hdmiphy_poweron(dev);
>>>> +       return 0;
>>>> +}
>>>> +#endif
>>>> +
>>>> +static const struct dev_pm_ops hdmiphy_pm_ops = {
>>>> +       SET_SYSTEM_SLEEP_PM_OPS(hdmiphy_suspend, hdmiphy_resume)
>>>> +       SET_RUNTIME_PM_OPS(hdmiphy_runtime_suspend,
>>>> +               hdmiphy_runtime_resume, NULL)
>>>> +};
>>>> +
>>>>  struct i2c_driver hdmiphy_driver = {
>>>>         .driver = {
>>>>                 .name   = "exynos-hdmiphy",
>>>>                 .owner  = THIS_MODULE,
>>>>                 .of_match_table = of_match_ptr(hdmiphy_match_types),
>>>> +               .pm     = &hdmiphy_pm_ops,
>>>>         },
>>>>         .id_table = hdmiphy_id,
>>>>         .probe          = hdmiphy_probe,
>>>>         .remove         = hdmiphy_remove,
>>>>         .command                = NULL,
>>>>  };
>>>> +
>>>> +extern int exynos_hdmiphy_driver_register(void)
>>>> +{
>>>> +       return i2c_add_driver(&hdmiphy_driver);
>>>> +}
>>>> +
>>>> +extern void exynos_hdmiphy_driver_unregister(void)
>>>> +{
>>>> +       i2c_del_driver(&hdmiphy_driver);
>>>> +}
>>>> +
>>>>  EXPORT_SYMBOL(hdmiphy_driver);
>>>> diff --git a/drivers/gpu/drm/exynos/regs-hdmiphy.h b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>> new file mode 100644
>>>> index 0000000..8f906c6
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>> @@ -0,0 +1,61 @@
>>>> +/*
>>>> + *
>>>> + *  regs-hdmiphy.h
>>>> + *
>>>> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
>>>> + * http://www.samsung.com/
>>>> + *
>>>> + * HDMI-PHY register header file for Samsung TVOUT driver
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> +*/
>>>> +
>>>> +#ifndef SAMSUNG_REGS_HDMIPHY_H
>>>> +#define SAMSUNG_REGS_HDMIPHY_H
>>>> +
>>>> +/*
>>>> + * Register part
>>>> +*/
>>>> +
>>>> +/* HDMI PHY Version Common */
>>>> +#define HDMIPHY_MODE_SET_DONE          (0x1f)
>>>> +
>>>> +/* HDMI PHY Version 4212 */
>>>> +#define HDMIPHY_4212_PCG_CON           (0x04)
>>>> +#define HDMIPHY_4212_OSC_PAD_CON       (0x0b)
>>>> +#define HDMIPHY_4212_PD_CON            (0x1d)
>>>> +
>>>> +/*
>>>> + * Bit definition part
>>>> + */
>>>> +
>>>> +/* HDMIPHY_MODE_SET_DONE */
>>>> +#define HDMIPHY_MODE_EN                        (1 << 7)
>>>> +
>>>> +/* HDMIPHY_4212_PCG_CON */
>>>> +#define HDMIPHY_PCG_RESET_EN           (1 << 3)
>>>> +
>>>> +/* HDMIPHY_4212_OSC_PAD_CON */
>>>> +#define HDMIPHY_OSC_PAD_EN             (3 << 6)
>>>> +
>>>> +/* HDMIPHY_4212_PD_CON */
>>>> +#define HDMIPHY_PDEN                   (1 << 7)
>>>> +
>>>> +#define HDMIPHY_PLL_PD                 (1 << 6)
>>>> +#define HDMIPHY_CLKSER_PD              (1 << 5)
>>>> +#define HDMIPHY_CLKDRV_PD              (1 << 4)
>>>> +
>>>> +#define HDMIPHY_DRV_PD                 (1 << 2)
>>>> +#define HDMIPHY_SER_PD                 (1 << 1)
>>>> +#define HDMIPHY_ICLK_PD                        (1 << 0)
>>>> +
>>>> +#define HDMIPHY_PD_ALL                 (HDMIPHY_PLL_PD |\
>>>> +                                       HDMIPHY_CLKSER_PD |\
>>>> +                                       HDMIPHY_CLKDRV_PD|\
>>>> +                                       HDMIPHY_DRV_PD|\
>>>> +                                       HDMIPHY_SER_PD|\
>>>> +                                       HDMIPHY_ICLK_PD)
>>>> +
>>>> +#endif /* SAMSUNG_REGS_HDMIPHY_H */
>>>> --
>>>> 1.8.0
>>>>
>>
>> --
>> Regards,
>> Rahul Sharma,
>> email to: rahul.sharma@samsung.com
>> Samsung India.
>
>
>
> --
> Regards,
> Rahul Sharma,
> email to: rahul.sharma@samsung.com
> Samsung India.
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rahul Sharma March 14, 2013, 9:40 a.m. UTC | #13
On Tue, Mar 12, 2013 at 5:37 PM, Inki Dae <inki.dae@samsung.com> wrote:
> 2013/3/12 Rahul Sharma <r.sh.open@gmail.com>:
>> On Mon, Mar 4, 2013 at 7:35 PM, Rahul Sharma <r.sh.open@gmail.com> wrote:
>>> Thanks Sean,
>>>
>>> On Wed, Feb 27, 2013 at 9:47 PM, Sean Paul <seanpaul@google.com> wrote:
>>>> On Wed, Feb 27, 2013 at 8:22 AM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>>>>> Right now hdmiphy operations and configs are kept inside hdmi driver. hdmiphy related
>>>>> code is tightly coupled with hdmi ip driver. Physicaly they are different devices and
>>>>
>>>> s/Physicaly/Physically/
>>>>
>>>>> should be instantiated independently.
>>>>>
>>>>> In terms of versions/mapping/configurations Hdmi and hdmiphy are independent of each
>>>>> other. It is preferred to isolate them and maintained independently.
>>>>>
>>>>> This implementations is tested for:
>>>>> 1) Resolutions supported by exynos4 and 5 hdmi.
>>>>> 2) Runtime PM and S2R scenarions for exynos5.
>>>>>
>>>>
>>>> I don't like the idea of spawning off yet another driver in here. It
>>>> adds more globals, more suspend/resume ordering issues, and more
>>>> implicit dependencies. I understand, however, that this is the Chosen
>>>> Way for the exynos driver, so I will save my rant.
>>>>
>>>
>>> I agree to it. splitting phy to a new driver will complicate the power related
>>> scenarios. But in upcoming SoC,s, Phy is changing considerably in terms of
>>> config values, mapping (i2c/platform bus) etc. Handling this diversity
>>> inside hdmi driver is complicating it with unrelated changes.
>>>
>>> I have tested this RFC for Runtime PM / S2R. But if we see any major roadblock
>>> we should re-factor this by explicitly calling power related callbacks
>>> of mixer, phy,
>>> hdmi drivers in a required order. We can call them from exynos-drm-hdmi plf
>>> device. AFAIR something like this is already in place in chrome-kernel.
>>>
>>>> I've made some comments below.
>>>>
>>>>> This patch is dependent on
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34733.html
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34861.html
>>>>> http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg34862.html
>>>>>
>>>>> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
>>>>> ---
>>>>> It is based on exynos-drm-next-todo branch at
>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos.git
>>>>>
>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.c  |   8 +
>>>>>  drivers/gpu/drm/exynos/exynos_drm_drv.h  |   6 +
>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.c |  58 ++-
>>>>>  drivers/gpu/drm/exynos/exynos_drm_hdmi.h |  11 +
>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.c     | 375 ++------------------
>>>>>  drivers/gpu/drm/exynos/exynos_hdmi.h     |   1 -
>>>>>  drivers/gpu/drm/exynos/exynos_hdmiphy.c  | 586 ++++++++++++++++++++++++++++++-
>>>>>  drivers/gpu/drm/exynos/regs-hdmiphy.h    |  61 ++++
>>>>>  8 files changed, 738 insertions(+), 368 deletions(-)
>>>>>  create mode 100644 drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>>
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>> index 3da5c2d..03acb6b 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>> @@ -334,6 +334,11 @@ static int __init exynos_drm_init(void)
>>>>>         ret = platform_driver_register(&hdmi_driver);
>>>>>         if (ret < 0)
>>>>>                 goto out_hdmi;
>>>>> +
>>>>> +       ret = exynos_hdmiphy_driver_register();
>>>>
>>>> Not sure why you need to obfuscate the type of driver here. All the
>>>> other ones are directly registered as platform drivers, I don't see
>>>> the harm in just doing the i2c_add_driver directly here.
>>>>
>>>
>>> AIMB, Phy is likely to get mapped as a platform device in later soc's.
>>> I want to make driver registration (inside exynos_drm_drv.c) independent
>>> of this changes. We can handle this inside exynos_hdmiphy_driver_register().
>>>
>>>>> +       if (ret < 0)
>>>>> +               goto out_hdmiphy;
>>>>> +
>>>>>         ret = platform_driver_register(&mixer_driver);
>>>>>         if (ret < 0)
>>>>>                 goto out_mixer;
>>>>
>>>>
>>>> I think this ordering is how you plan on enforcing the suspend/resume
>>>> order for hdmi/mixer/hdmiphy. In that case, however, I don't think it
>>>> makes sense to suspend/resume hdmiphy in between mixer and hdmi.
>>>> Ideally, hdmiphy should go down first and come up last.
>>>>
>>>
>>> I just wanted to keep, hdmi and hdmiphy things together. But what you said
>>> above makes sense. I will do that change.
>>>
>>>>
>>>>> @@ -436,6 +441,8 @@ out_common_hdmi_dev:
>>>>>  out_common_hdmi:
>>>>>         platform_driver_unregister(&mixer_driver);
>>>>>  out_mixer:
>>>>> +       exynos_hdmiphy_driver_unregister();
>>>>> +out_hdmiphy:
>>>>>         platform_driver_unregister(&hdmi_driver);
>>>>>  out_hdmi:
>>>>>  #endif
>>>>> @@ -479,6 +486,7 @@ static void __exit exynos_drm_exit(void)
>>>>>         exynos_platform_device_hdmi_unregister();
>>>>>         platform_driver_unregister(&exynos_drm_common_hdmi_driver);
>>>>>         platform_driver_unregister(&mixer_driver);
>>>>> +       exynos_hdmiphy_driver_unregister();
>>>>>         platform_driver_unregister(&hdmi_driver);
>>>>>  #endif
>>>>>
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>> index 4606fac..17c53e3 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>> @@ -325,6 +325,12 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
>>>>>  extern int exynos_platform_device_hdmi_register(void);
>>>>>
>>>>>  /*
>>>>> + * these functions registers/unregisters exynos drm hdmiphy driver.
>>>>> + */
>>>>> +extern int exynos_hdmiphy_driver_register(void);
>>>>> +extern void exynos_hdmiphy_driver_unregister(void);
>>>>> +
>>>>> +/*
>>>>>   * this function unregisters exynos drm hdmi platform device if it exists.
>>>>>   */
>>>>>  void exynos_platform_device_hdmi_unregister(void);
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>>>> index 5dc956b..45ef331 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
>>>>> @@ -32,19 +32,22 @@
>>>>>  /* platform device pointer for common drm hdmi device. */
>>>>>  static struct platform_device *exynos_drm_hdmi_pdev;
>>>>>
>>>>> -/* Common hdmi subdrv needs to access the hdmi and mixer though context.
>>>>> -* These should be initialied by the repective drivers */
>>>>> +/* Common hdmi subdrv needs to access the hdmi, hdmiphy and mixer though
>>>>> +*  context. These should be initialied by the repective drivers */
>>>>>  static struct exynos_drm_hdmi_context *hdmi_ctx;
>>>>> +static struct exynos_drm_hdmi_context *hdmiphy_ctx;
>>>>>  static struct exynos_drm_hdmi_context *mixer_ctx;
>>>>>
>>>>>  /* these callback points shoud be set by specific drivers. */
>>>>>  static struct exynos_hdmi_ops *hdmi_ops;
>>>>> +static struct exynos_hdmiphy_ops *hdmiphy_ops;
>>>>>  static struct exynos_mixer_ops *mixer_ops;
>>>>>
>>>>
>>>> This stuff really sets my teeth on edge. I can't stand these global
>>>> variables and the set-here, set-there, set-here dance that is done on
>>>> probe.
>>>>
>>>> IMO, this and the suspend/resume ordering should be enough motivation
>>>> to avoid separating everything out into their own drivers.
>>>>
>>>> Instead of adding more kruft to this list, we should be working to remove it.
>>>>
>>>> /rant
>>>>
>>>
>>> Let me think over it. Please suggest me something if you have any idea to
>>> correct this.
>>>
>>
>> Hi all,
>>
>> I replaced these variables with a global LIST of subdrv's with associated
>> type (HDMI / PHY / MIXER) and ops struct.
>>
>> drm-common-hdmi callback will parse this list and based on the subdrv
>> type calls subdrv functions, but this adds overhead of parsing the list
>> multiple times.
>>
>> If I cache the subdrvs in drm-common-hdmi context and parsing of the list
>> can be avoided.
>>
>> What you think about above approach?
>>
>
> I think it's good to use existing exynos drm core framework. But I'm
> anxious about probing point and existing subdrv callbacks with this.
> Could you share that patch as RFC?

Mr. Dae,

Drm sub-drivers including drm-fimd, drm-common-hdmi will continue using existing
exynos drm framework. If I use same for HDMI, PHY and MIXER drivers, I will be
bypassing the followed hierarchy  i.e.
DRM--> EXYNOS-DRM --> Drm-common-hdmi --> (hdmi, mixer, phy).
Secondly we need to invoke callbacks in sequence for above three. That
condition can
not be met if moved to exynos-drm.

I modified drm-common-hdmi to create provision for HDMI, PHY and MIXER
sub-driver registrations. Then we don't need to maintain so many
global variables.

If you feel this approach is reasonable I will post the RFC patch.

regards,
Rahul Sharma.

>
> Thanks,
> Inki Dae
>
>> regards,
>> Rahul Sharma.
>>
>>>>>  struct drm_hdmi_context {
>>>>>         struct exynos_drm_subdrv        subdrv;
>>>>>         struct exynos_drm_hdmi_context  *hdmi_ctx;
>>>>>         struct exynos_drm_hdmi_context  *mixer_ctx;
>>>>> +       struct exynos_drm_hdmi_context  *hdmiphy_ctx;
>>>>>
>>>>>         bool    enabled[MIXER_WIN_NR];
>>>>>  };
>>>>> @@ -74,6 +77,12 @@ void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>>>                 hdmi_ctx = ctx;
>>>>>  }
>>>>>
>>>>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>>> +{
>>>>> +       if (ctx)
>>>>> +               hdmiphy_ctx = ctx;
>>>>> +}
>>>>> +
>>>>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
>>>>>  {
>>>>>         if (ctx)
>>>>> @@ -88,6 +97,14 @@ void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
>>>>>                 hdmi_ops = ops;
>>>>>  }
>>>>>
>>>>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops)
>>>>> +{
>>>>> +       DRM_DEBUG_KMS("%s\n", __FILE__);
>>>>
>>>> Listing __FILE__ in these DRM_DEBUG_KMS traces is pretty useless. I'm
>>>> sure we can determine the file from the function name. It would be
>>>> slightly more useful to list __LINE__, but even more useful if you
>>>> actually printed state relevant to the current function.
>>>>
>>>
>>> OK. I will correct this.
>>>
>>>>> +
>>>>> +       if (ops)
>>>>> +               hdmiphy_ops = ops;
>>>>> +}
>>>>> +
>>>>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
>>>>>  {
>>>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>>> @@ -121,7 +138,7 @@ struct edid *drm_hdmi_get_edid(struct device *dev,
>>>>>         return NULL;
>>>>>  }
>>>>>
>>>>> -static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>>>> +static int drm_hdmi_check_timing(struct device *dev, void *mode)
>>>>>  {
>>>>>         struct drm_hdmi_context *ctx = to_context(dev);
>>>>>         int ret = 0;
>>>>> @@ -129,18 +146,24 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing)
>>>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>>>
>>>>>         /*
>>>>> -       * Both, mixer and hdmi should be able to handle the requested mode.
>>>>> -       * If any of the two fails, return mode as BAD.
>>>>> +       * Mixer, hdmi and hdmiphy should be able to handle the requested mode.
>>>>> +       * If any of the them fails, return mode as BAD.
>>>>>         */
>>>>>
>>>>>         if (mixer_ops && mixer_ops->check_timing)
>>>>> -               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
>>>>> +               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, mode);
>>>>>
>>>>>         if (ret)
>>>>>                 return ret;
>>>>>
>>>>>         if (hdmi_ops && hdmi_ops->check_timing)
>>>>> -               return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
>>>>> +               ret = hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, mode);
>>>>> +
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       if (hdmiphy_ops && hdmiphy_ops->check_timing)
>>>>> +               return hdmiphy_ops->check_timing(ctx->hdmiphy_ctx->ctx, mode);
>>>>>
>>>>>         return 0;
>>>>>  }
>>>>> @@ -254,6 +277,9 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
>>>>>
>>>>>         if (hdmi_ops && hdmi_ops->mode_set)
>>>>>                 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
>>>>> +
>>>>> +       if (hdmiphy_ops && hdmiphy_ops->mode_set)
>>>>> +               hdmiphy_ops->mode_set(ctx->hdmiphy_ctx->ctx, mode);
>>>>>  }
>>>>>
>>>>>  static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
>>>>> @@ -273,6 +299,15 @@ static void drm_hdmi_commit(struct device *subdrv_dev)
>>>>>
>>>>>         DRM_DEBUG_KMS("%s\n", __FILE__);
>>>>>
>>>>> +       if (hdmiphy_ops && hdmiphy_ops->prepare)
>>>>> +               hdmiphy_ops->prepare(ctx->hdmiphy_ctx->ctx);
>>>>> +
>>>>> +       if (hdmi_ops && hdmi_ops->prepare)
>>>>> +               hdmi_ops->prepare(ctx->hdmi_ctx->ctx);
>>>>> +
>>>>
>>>> Why are you calling these prepare functions in commit? Isn't that
>>>> precisely the reason drm has a prepare callback?
>>>
>>> True. Currently exynos_drm_encoder_prepare is blank. I will move this to
>>> prepare callback.
>>>
>>>>
>>>>> +       if (hdmiphy_ops && hdmiphy_ops->config_apply)
>>>>> +               hdmiphy_ops->config_apply(ctx->hdmiphy_ctx->ctx);
>>>>> +
>>>>>         if (hdmi_ops && hdmi_ops->commit)
>>>>>                 hdmi_ops->commit(ctx->hdmi_ctx->ctx);
>>>>>  }
>>>>> @@ -288,6 +323,9 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
>>>>>
>>>>>         if (hdmi_ops && hdmi_ops->dpms)
>>>>>                 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
>>>>> +
>>>>> +       if (hdmiphy_ops && hdmiphy_ops->dpms)
>>>>> +               hdmiphy_ops->dpms(ctx->hdmiphy_ctx->ctx, mode);
>>>>
>>>> I think you have to be more careful about the ordering of things here
>>>> since you'll want to power on in a different order than you power off.
>>>>
>>>>>  }
>>>>>
>>>>>  static void drm_hdmi_apply(struct device *subdrv_dev)
>>>>> @@ -393,6 +431,11 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>>>>                 return -EFAULT;
>>>>>         }
>>>>>
>>>>> +       if (!hdmiphy_ctx) {
>>>>> +               DRM_ERROR("hdmiphy context not initialized.\n");
>>>>> +               return -EFAULT;
>>>>> +       }
>>>>> +
>>>>>         if (!mixer_ctx) {
>>>>>                 DRM_ERROR("mixer context not initialized.\n");
>>>>>                 return -EFAULT;
>>>>> @@ -406,6 +449,7 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
>>>>>         }
>>>>>
>>>>>         ctx->hdmi_ctx = hdmi_ctx;
>>>>> +       ctx->hdmiphy_ctx = hdmiphy_ctx;
>>>>>         ctx->mixer_ctx = mixer_ctx;
>>>>>
>>>>>         ctx->hdmi_ctx->drm_dev = drm_dev;
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>>>> index fd2ff9f..10db62e 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
>>>>> @@ -39,10 +39,19 @@ struct exynos_hdmi_ops {
>>>>>         void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>>>>         void (*get_max_resol)(void *ctx, unsigned int *width,
>>>>>                                 unsigned int *height);
>>>>> +       void (*prepare)(void *ctx);
>>>>>         void (*commit)(void *ctx);
>>>>>         void (*dpms)(void *ctx, int mode);
>>>>>  };
>>>>>
>>>>> +struct exynos_hdmiphy_ops {
>>>>> +       int (*check_timing)(void *ctx, struct drm_display_mode *mode);
>>>>> +       void (*mode_set)(void *ctx, struct drm_display_mode *mode);
>>>>> +       void (*prepare)(void *ctx);
>>>>> +       int (*config_apply)(void *ctx);
>>>>> +       void (*dpms)(void *ctx, int mode);
>>>>> +};
>>>>> +
>>>>>  struct exynos_mixer_ops {
>>>>>         /* manager */
>>>>>         int (*iommu_on)(void *ctx, bool enable);
>>>>> @@ -61,7 +70,9 @@ struct exynos_mixer_ops {
>>>>>  };
>>>>>
>>>>>  void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>>> +void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>>>  void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
>>>>>  void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
>>>>> +void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops);
>>>>>  void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
>>>>>  #endif
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
>>>>> index a5ca2cc..7ee350d 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
>>>>> @@ -194,7 +194,6 @@ struct hdmi_context {
>>>>>         int                             internal_irq;
>>>>>
>>>>>         struct i2c_client               *ddc_port;
>>>>> -       struct i2c_client               *hdmiphy_port;
>>>>>
>>>>>         /* current hdmiphy conf regs */
>>>>>         struct hdmi_conf_regs           mode_conf;
>>>>> @@ -206,180 +205,6 @@ struct hdmi_context {
>>>>>         enum hdmi_type                  type;
>>>>>  };
>>>>>
>>>>> -struct hdmiphy_config {
>>>>> -       int pixel_clock;
>>>>> -       u8 conf[32];
>>>>> -};
>>>>> -
>>>>> -/* list of phy config settings */
>>>>> -static const struct hdmiphy_config hdmiphy_v13_configs[] = {
>>>>> -       {
>>>>> -               .pixel_clock = 27000000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>>>>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 27027000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>>>>> -                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> -                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 74176000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>>>>> -                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>>>>> -                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> -                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 74250000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>>>>> -                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>>>>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>>>>> -                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 148500000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>>>>> -                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>>>>> -                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> -                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -};
>>>>> -
>>>>> -static const struct hdmiphy_config hdmiphy_v14_configs[] = {
>>>>> -       {
>>>>> -               .pixel_clock = 25200000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>>>>> -                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 27000000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>>>>> -                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 27027000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>>>>> -                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 36000000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 40000000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>>> -                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 65000000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>>>>> -                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 74176000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>>>>> -                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 74250000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>>>>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 83500000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>>>>> -                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 106500000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>>>>> -                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 108000000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>>>>> -                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 146250000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>>>>> -                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>>> -               },
>>>>> -       },
>>>>> -       {
>>>>> -               .pixel_clock = 148500000,
>>>>> -               .conf = {
>>>>> -                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>>>>> -                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> -                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> -                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>>>>> -               },
>>>>> -       },
>>>>> -};
>>>>> -
>>>>>  struct hdmi_infoframe {
>>>>>         enum HDMI_PACKET_TYPE type;
>>>>>         u8 ver;
>>>>> @@ -774,46 +599,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
>>>>>         return raw_edid;
>>>>>  }
>>>>>
>>>>> -static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
>>>>> -{
>>>>> -       const struct hdmiphy_config *confs;
>>>>> -       int count, i;
>>>>> -
>>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> -
>>>>> -       if (hdata->type == HDMI_TYPE13) {
>>>>> -               confs = hdmiphy_v13_configs;
>>>>> -               count = ARRAY_SIZE(hdmiphy_v13_configs);
>>>>> -       } else if (hdata->type == HDMI_TYPE14) {
>>>>> -               confs = hdmiphy_v14_configs;
>>>>> -               count = ARRAY_SIZE(hdmiphy_v14_configs);
>>>>> -       } else
>>>>> -               return -EINVAL;
>>>>> -
>>>>> -       for (i = 0; i < count; i++)
>>>>> -               if (confs[i].pixel_clock == pixel_clock)
>>>>> -                       return i;
>>>>> -
>>>>> -       DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
>>>>> -       return -EINVAL;
>>>>> -}
>>>>> -
>>>>> -static int hdmi_check_timing(void *ctx, struct drm_display_mode *mode)
>>>>> -{
>>>>> -       struct hdmi_context *hdata = ctx;
>>>>> -       int ret;
>>>>> -
>>>>> -       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>>>>> -               __func__, mode->hdisplay, mode->vdisplay,
>>>>> -               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
>>>>> -               false, mode->clock * 1000);
>>>>> -
>>>>> -       ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
>>>>> -       if (ret < 0)
>>>>> -               return ret;
>>>>> -       return 0;
>>>>> -}
>>>>> -
>>>>>  static void hdmi_set_acr(u32 freq, u8 *acr)
>>>>>  {
>>>>>         u32 n, cts;
>>>>> @@ -1309,20 +1094,12 @@ static void hdmi_timing_apply(struct hdmi_context *hdata)
>>>>>
>>>>>  static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>>>>  {
>>>>> -       u8 buffer[2];
>>>>>         u32 reg;
>>>>>
>>>>>         clk_disable(hdata->res.sclk_hdmi);
>>>>>         clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
>>>>>         clk_enable(hdata->res.sclk_hdmi);
>>>>>
>>>>> -       /* operation mode */
>>>>> -       buffer[0] = 0x1f;
>>>>> -       buffer[1] = 0x00;
>>>>> -
>>>>> -       if (hdata->hdmiphy_port)
>>>>> -               i2c_master_send(hdata->hdmiphy_port, buffer, 2);
>>>>> -
>>>>>         if (hdata->type == HDMI_TYPE13)
>>>>>                 reg = HDMI_V13_PHY_RSTOUT;
>>>>>         else
>>>>> @@ -1335,102 +1112,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
>>>>>         usleep_range(10000, 12000);
>>>>>  }
>>>>>
>>>>> -static void hdmiphy_poweron(struct hdmi_context *hdata)
>>>>> -{
>>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> -
>>>>> -       if (hdata->type == HDMI_TYPE14)
>>>>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
>>>>> -                       HDMI_PHY_POWER_OFF_EN);
>>>>> -}
>>>>> -
>>>>> -static void hdmiphy_poweroff(struct hdmi_context *hdata)
>>>>> -{
>>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> -
>>>>> -       if (hdata->type == HDMI_TYPE14)
>>>>> -               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
>>>>> -                       HDMI_PHY_POWER_OFF_EN);
>>>>
>>>> I assume these register writes are replaced with i2c operations in the
>>>> new phy driver?
>>>
>>> It is done for exynos5 phy. I am not having the sequence for exynos4. I
>>> will add it later.
>>>
>>>>
>>>>> -}
>>>>> -
>>>>> -static void hdmiphy_conf_apply(struct hdmi_context *hdata)
>>>>> -{
>>>>> -       const u8 *hdmiphy_data;
>>>>> -       u8 buffer[32];
>>>>> -       u8 operation[2];
>>>>> -       u8 read_buffer[32] = {0, };
>>>>> -       int ret;
>>>>> -       int i;
>>>>> -
>>>>> -       if (!hdata->hdmiphy_port) {
>>>>> -               DRM_ERROR("hdmiphy is not attached\n");
>>>>> -               return;
>>>>> -       }
>>>>> -
>>>>> -       /* pixel clock */
>>>>> -       i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
>>>>> -       if (i < 0) {
>>>>> -               DRM_ERROR("failed to find hdmiphy conf\n");
>>>>> -               return;
>>>>> -       }
>>>>> -
>>>>> -       if (hdata->type == HDMI_TYPE13) {
>>>>> -               hdmiphy_data = hdmiphy_v13_configs[i].conf;
>>>>> -       } else {
>>>>> -               hdmiphy_data = hdmiphy_v14_configs[i].conf;
>>>>> -       }
>>>>> -
>>>>> -       memcpy(buffer, hdmiphy_data, 32);
>>>>> -       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
>>>>> -       if (ret != 32) {
>>>>> -               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>>>>> -               return;
>>>>> -       }
>>>>> -
>>>>> -       usleep_range(10000, 12000);
>>>>> -
>>>>> -       /* operation mode */
>>>>> -       operation[0] = 0x1f;
>>>>> -       operation[1] = 0x80;
>>>>> -
>>>>> -       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
>>>>> -       if (ret != 2) {
>>>>> -               DRM_ERROR("failed to enable hdmiphy\n");
>>>>> -               return;
>>>>> -       }
>>>>> -
>>>>> -       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
>>>>> -       if (ret < 0) {
>>>>> -               DRM_ERROR("failed to read hdmiphy config\n");
>>>>> -               return;
>>>>> -       }
>>>>> -
>>>>> -       for (i = 0; i < ret; i++)
>>>>> -               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
>>>>> -                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
>>>>> -}
>>>>> -
>>>>> -static void hdmi_conf_apply(struct hdmi_context *hdata)
>>>>> -{
>>>>> -       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> -
>>>>> -       hdmiphy_conf_reset(hdata);
>>>>> -       hdmiphy_conf_apply(hdata);
>>>>> -
>>>>> -       mutex_lock(&hdata->hdmi_mutex);
>>>>> -       hdmi_conf_reset(hdata);
>>>>> -       hdmi_conf_init(hdata);
>>>>> -       mutex_unlock(&hdata->hdmi_mutex);
>>>>> -
>>>>> -       hdmi_audio_init(hdata);
>>>>> -
>>>>> -       /* setting core registers */
>>>>> -       hdmi_timing_apply(hdata);
>>>>> -       hdmi_audio_control(hdata, true);
>>>>> -
>>>>> -       hdmi_regs_dump(hdata, "start");
>>>>> -}
>>>>> -
>>>>>  static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
>>>>>  {
>>>>>         int i;
>>>>> @@ -1448,7 +1129,6 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
>>>>>
>>>>>         hdata->mode_conf.cea_video_id =
>>>>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>>>>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>>>
>>>> I don't think you use pixel_clock in mode_conf any longer, please
>>>> remove it from the struct.
>>>>
>>>
>>> Correct. I will remove pixel_clock from struct hdmi_conf_regs.
>>>
>>>>>
>>>>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>>>>         hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
>>>>> @@ -1544,7 +1224,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
>>>>>
>>>>>         hdata->mode_conf.cea_video_id =
>>>>>                 drm_match_cea_mode((struct drm_display_mode *)m);
>>>>> -       hdata->mode_conf.pixel_clock = m->clock * 1000;
>>>>>
>>>>>         hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
>>>>>         hdmi_set_reg(core->v_line, 2, m->vtotal);
>>>>> @@ -1670,13 +1349,32 @@ static void hdmi_get_max_resol(void *ctx, unsigned int *width,
>>>>>         *height = MAX_HEIGHT;
>>>>>  }
>>>>>
>>>>> +static void hdmi_commit_prepare(void *ctx)
>>>>
>>>> I don't know why the "_commit" is there, just hdmi_prepare will be fine.
>>>>
>>> Ok.
>>>
>>>>> +{
>>>>> +       struct hdmi_context *hdata = ctx;
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       hdmiphy_conf_reset(hdata);
>>>>
>>>> There's still phy-specific kruft laying around the driver. There are
>>>> now a bunch of implicit dependencies between the hdmi driver and the
>>>> hdmiphy driver.
>>>>
>>>
>>> I need to follow this sequence:
>>>
>>> phy off (hdmiphy_prepare) -> phy reset using HDMI IP REG (hdmi_prepare) ->
>>> phy config (hdmiphy_config_apply) -> hdmi ip config  (hdmi_commit)
>>>
>>> Phy reset needs to be done by toggling Phy_reset bit in HDMI IP register which
>>> cannot be accessed in hdmiphy driver. I didn't see any better way.
>>>
>>>> Another example is the sclk_hdmiphy clock, which is still controlled
>>>> by the hdmi driver, it should be controlled by the phy.
>>>>
>>>
>>> sclk_hdmiphy is one of the source clock for hdmi IP. HDMI IP needs to change the
>>> source between sclk_hdmiphy (only if stable) else pixel clock. Above
>>> code looks ok
>>> to me. Please explain a bit mode on this.
>>>
>>>>> +}
>>>>> +
>>>>>  static void hdmi_commit(void *ctx)
>>>>>  {
>>>>>         struct hdmi_context *hdata = ctx;
>>>>>
>>>>>         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>>
>>>>> -       hdmi_conf_apply(hdata);
>>>>> +       mutex_lock(&hdata->hdmi_mutex);
>>>>> +       hdmi_conf_reset(hdata);
>>>>> +       hdmi_conf_init(hdata);
>>>>> +       mutex_unlock(&hdata->hdmi_mutex);
>>>>> +
>>>>> +       hdmi_audio_init(hdata);
>>>>> +
>>>>> +       /* setting core registers */
>>>>> +       hdmi_timing_apply(hdata);
>>>>> +       hdmi_audio_control(hdata, true);
>>>>> +
>>>>> +       hdmi_regs_dump(hdata, "start");
>>>>>  }
>>>>>
>>>>>  static void hdmi_poweron(struct hdmi_context *hdata)
>>>>> @@ -1699,8 +1397,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
>>>>>         clk_enable(res->hdmiphy);
>>>>>         clk_enable(res->hdmi);
>>>>>         clk_enable(res->sclk_hdmi);
>>>>> -
>>>>> -       hdmiphy_poweron(hdata);
>>>>>  }
>>>>>
>>>>>  static void hdmi_poweroff(struct hdmi_context *hdata)
>>>>> @@ -1719,7 +1415,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
>>>>>          * its reset state seems to meet the condition.
>>>>>          */
>>>>>         hdmiphy_conf_reset(hdata);
>>>>> -       hdmiphy_poweroff(hdata);
>>>>>
>>>>>         clk_disable(res->sclk_hdmi);
>>>>>         clk_disable(res->hdmi);
>>>>> @@ -1761,11 +1456,11 @@ static struct exynos_hdmi_ops hdmi_ops = {
>>>>>         /* display */
>>>>>         .is_connected   = hdmi_is_connected,
>>>>>         .get_edid       = hdmi_get_edid,
>>>>> -       .check_timing   = hdmi_check_timing,
>>>>>
>>>>>         /* manager */
>>>>>         .mode_set       = hdmi_mode_set,
>>>>>         .get_max_resol  = hdmi_get_max_resol,
>>>>> +       .prepare                = hdmi_commit_prepare,
>>>>>         .commit         = hdmi_commit,
>>>>>         .dpms           = hdmi_dpms,
>>>>>  };
>>>>> @@ -1878,7 +1573,7 @@ fail:
>>>>>         return -ENODEV;
>>>>>  }
>>>>>
>>>>> -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
>>>>> +static struct i2c_client *hdmi_ddc;
>>>>>
>>>>>  void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>>>>  {
>>>>> @@ -1886,12 +1581,6 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
>>>>>                 hdmi_ddc = ddc;
>>>>>  }
>>>>>
>>>>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
>>>>> -{
>>>>> -       if (hdmiphy)
>>>>> -               hdmi_hdmiphy = hdmiphy;
>>>>> -}
>>>>> -
>>>>>  #ifdef CONFIG_OF
>>>>>  static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
>>>>>                                         (struct device *dev)
>>>>> @@ -2051,27 +1740,18 @@ static int hdmi_probe(struct platform_device *pdev)
>>>>>
>>>>>         hdata->ddc_port = hdmi_ddc;
>>>>>
>>>>> -       /* hdmiphy i2c driver */
>>>>> -       if (i2c_add_driver(&hdmiphy_driver)) {
>>>>> -               DRM_ERROR("failed to register hdmiphy i2c driver\n");
>>>>> -               ret = -ENOENT;
>>>>> -               goto err_ddc;
>>>>> -       }
>>>>> -
>>>>> -       hdata->hdmiphy_port = hdmi_hdmiphy;
>>>>> -
>>>>>         hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
>>>>>         if (hdata->external_irq < 0) {
>>>>>                 DRM_ERROR("failed to get GPIO external irq\n");
>>>>>                 ret = hdata->external_irq;
>>>>> -               goto err_hdmiphy;
>>>>> +               goto err_ddc;
>>>>>         }
>>>>>
>>>>>         hdata->internal_irq = platform_get_irq(pdev, 0);
>>>>>         if (hdata->internal_irq < 0) {
>>>>>                 DRM_ERROR("failed to get platform internal irq\n");
>>>>>                 ret = hdata->internal_irq;
>>>>> -               goto err_hdmiphy;
>>>>> +               goto err_ddc;
>>>>>         }
>>>>>
>>>>>         hdata->hpd = gpio_get_value(hdata->hpd_gpio);
>>>>> @@ -2082,7 +1762,7 @@ static int hdmi_probe(struct platform_device *pdev)
>>>>>                         "hdmi_external", drm_hdmi_ctx);
>>>>>         if (ret) {
>>>>>                 DRM_ERROR("failed to register hdmi external interrupt\n");
>>>>> -               goto err_hdmiphy;
>>>>> +               goto err_ddc;
>>>>>         }
>>>>>
>>>>>         ret = request_threaded_irq(hdata->internal_irq, NULL,
>>>>> @@ -2105,8 +1785,6 @@ static int hdmi_probe(struct platform_device *pdev)
>>>>>
>>>>>  err_free_irq:
>>>>>         free_irq(hdata->external_irq, drm_hdmi_ctx);
>>>>> -err_hdmiphy:
>>>>> -       i2c_del_driver(&hdmiphy_driver);
>>>>>  err_ddc:
>>>>>         i2c_del_driver(&ddc_driver);
>>>>>         return ret;
>>>>> @@ -2125,9 +1803,6 @@ static int hdmi_remove(struct platform_device *pdev)
>>>>>         free_irq(hdata->internal_irq, hdata);
>>>>>         free_irq(hdata->external_irq, hdata);
>>>>>
>>>>> -
>>>>> -       /* hdmiphy i2c driver */
>>>>> -       i2c_del_driver(&hdmiphy_driver);
>>>>>         /* DDC i2c driver */
>>>>>         i2c_del_driver(&ddc_driver);
>>>>>
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
>>>>> index 0ddf395..6595d7b 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.h
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
>>>>> @@ -15,7 +15,6 @@
>>>>>  #define _EXYNOS_HDMI_H_
>>>>>
>>>>>  void hdmi_attach_ddc_client(struct i2c_client *ddc);
>>>>> -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
>>>>>
>>>>>  extern struct i2c_driver hdmiphy_driver;
>>>>>  extern struct i2c_driver ddc_driver;
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>>>> index ea49d13..0d47302 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
>>>>> @@ -16,33 +16,437 @@
>>>>>  #include <linux/kernel.h>
>>>>>  #include <linux/i2c.h>
>>>>>  #include <linux/module.h>
>>>>> +#include <linux/pm_runtime.h>
>>>>> +#include <linux/clk.h>
>>>>>
>>>>>  #include "exynos_drm_drv.h"
>>>>> +#include "exynos_drm_hdmi.h"
>>>>>  #include "exynos_hdmi.h"
>>>>>
>>>>> +#include "regs-hdmiphy.h"
>>>>>
>>>>> -static int hdmiphy_probe(struct i2c_client *client,
>>>>> -       const struct i2c_device_id *id)
>>>>> +#define HDMIPHY_REG_COUNT              (32)
>>>>
>>>> No need to include parentheses.
>>>
>>> Ok.
>>>>
>>>>> +
>>>>> +enum hdmiphy_type {
>>>>> +       HDMIPHY_EXYNOS4210,
>>>>> +       HDMIPHY_EXYNOS4212,
>>>>> +};
>>>>> +
>>>>> +struct hdmiphy_context {
>>>>> +       struct device           *dev;
>>>>> +       bool                    powered;
>>>>> +       void                    *parent_ctx;
>>>>> +       enum hdmiphy_type               type;
>>>>> +       const struct hdmiphy_config     *conf;
>>>>> +       struct clk              *hdmiphy;
>>>>> +};
>>>>> +
>>>>> +struct hdmiphy_config {
>>>>> +       int pixel_clock;
>>>>> +       u8 conf[HDMIPHY_REG_COUNT];
>>>>> +};
>>>>> +
>>>>> +/* list of all required phy config settings */
>>>>> +static const struct hdmiphy_config hdmiphy_4212_configs[] = {
>>>>> +       {
>>>>> +               .pixel_clock = 25200000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
>>>>> +                       0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 27000000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
>>>>> +                       0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 27027000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
>>>>> +                       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 36000000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 40000000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
>>>>> +                       0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 65000000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
>>>>> +                       0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 74176000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
>>>>> +                       0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 74250000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
>>>>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 83500000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
>>>>> +                       0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 106500000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
>>>>> +                       0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 108000000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
>>>>> +                       0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 146250000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
>>>>> +                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 148500000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
>>>>> +                       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
>>>>> +                       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
>>>>> +                       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +};
>>>>> +
>>>>> +static const struct hdmiphy_config hdmiphy_4210_configs[] = {
>>>>> +       {
>>>>> +               .pixel_clock = 27000000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
>>>>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 27027000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
>>>>> +                       0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
>>>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> +                       0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 74176000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
>>>>> +                       0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
>>>>> +                       0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> +                       0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 74250000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
>>>>> +                       0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
>>>>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
>>>>> +                       0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +       {
>>>>> +               .pixel_clock = 148500000,
>>>>> +               .conf = {
>>>>> +                       0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
>>>>> +                       0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
>>>>> +                       0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
>>>>> +                       0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
>>>>> +               },
>>>>> +       },
>>>>> +};
>>>>> +
>>>>> +static const struct hdmiphy_config *hdmiphy_find_conf(void *ctx,
>>>>> +                       const struct drm_display_mode *mode)
>>>>> +{
>>>>> +       struct hdmiphy_context *hdata = ctx;
>>>>> +       const struct hdmiphy_config *confs;
>>>>> +       int count, i;
>>>>> +
>>>>> +       switch (hdata->type) {
>>>>> +       case HDMIPHY_EXYNOS4212:
>>>>> +               confs = hdmiphy_4212_configs;
>>>>> +               count = ARRAY_SIZE(hdmiphy_4212_configs);
>>>>> +               break;
>>>>> +       case HDMIPHY_EXYNOS4210:
>>>>> +               confs = hdmiphy_4210_configs;
>>>>> +               count = ARRAY_SIZE(hdmiphy_4210_configs);
>>>>> +               break;
>>>>> +       default:
>>>>> +               return NULL;
>>>>
>>>> Should probably DRM_ERROR here.
>>>>
>>> ok. I will add that.
>>>
>>>>> +       }
>>>>> +
>>>>> +       for (i = 0; i < count; i++)
>>>>> +               if (confs[i].pixel_clock == (mode->clock * 1000))
>>>>> +                       return &confs[i];
>>>>> +
>>>>> +       return NULL;
>>>>> +}
>>>>> +
>>>>> +static int hdmiphy_check_timing(void *ctx, struct drm_display_mode *mode)
>>>>>  {
>>>>> -       hdmi_attach_hdmiphy_client(client);
>>>>> +       const struct hdmiphy_config *conf;
>>>>> +       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
>>>>> +               __func__, mode->hdisplay, mode->vdisplay,
>>>>> +               mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>>>> +               ? true : false, mode->clock * 1000);
>>>>
>>>> No need to print __func__ here, DRM_DEBUG_KMS will do that for you. I
>>>> think you should also parse the flags to a string instead of a bool
>>>> cast to an int.
>>>>
>>> ok.
>>>
>>>>>
>>>>> -       dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
>>>>> -               "into i2c adapter successfully\n");
>>>>> +       conf = hdmiphy_find_conf(ctx, mode);
>>>>> +       if (!conf) {
>>>>> +               DRM_DEBUG_KMS("Display Mode is not supported.\n");
>>>>> +               return -EINVAL;
>>>>> +       }
>>>>>
>>>>>         return 0;
>>>>>  }
>>>>>
>>>>> -static int hdmiphy_remove(struct i2c_client *client)
>>>>> +static void hdmiphy_mode_set(void *ctx, struct drm_display_mode *mode)
>>>>> +{
>>>>> +       struct hdmiphy_context *hdata = ctx;
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>
>>>> No need to print __func__ here, DRM_DEBUG_KMS will do that for you.
>>>> This comment stand for the rest of the calls, as well.
>>>>
>>>>> +
>>>>> +       hdata->conf = hdmiphy_find_conf(ctx, mode);
>>>>> +}
>>>>> +
>>>>> +static void hdmiphy_config_prepare(void *ctx)
>>>>> +{
>>>>> +       struct hdmiphy_context *hdata = ctx;
>>>>> +       const struct i2c_client *client = to_i2c_client(hdata->dev);
>>>>> +       u8 buffer[2];
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       /* operation mode */
>>>>> +       buffer[0] = HDMIPHY_MODE_SET_DONE;
>>>>> +       buffer[1] = 0x00;
>>>>> +
>>>>> +       if (client)
>>>>> +               i2c_master_send(client, buffer, 2);
>>>>> +}
>>>>> +
>>>>> +static int hdmiphy_config_apply(void *ctx)
>>>>>  {
>>>>> -       dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
>>>>> -               "from i2c adapter successfully\n");
>>>>> +       struct hdmiphy_context *hdata = ctx;
>>>>> +       struct i2c_client *client = to_i2c_client(hdata->dev);
>>>>> +       const u8 *hdmiphy_data;
>>>>> +       u8 buffer[HDMIPHY_REG_COUNT];
>>>>> +       u8 operation[2];
>>>>> +       u8 read_buffer[HDMIPHY_REG_COUNT] = {0, };
>>>>> +       int ret;
>>>>> +       int i;
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       /* pixel clock */
>>>>> +       if (hdata->conf && client)
>>>>> +               hdmiphy_data = hdata->conf->conf;
>>>>> +       else
>>>>> +               return -EINVAL;
>>>>> +
>>>>> +       memcpy(buffer, hdmiphy_data, HDMIPHY_REG_COUNT);
>>>>> +
>>>>> +       ret = i2c_master_send(client, buffer, HDMIPHY_REG_COUNT);
>>>>> +       if (ret != HDMIPHY_REG_COUNT) {
>>>>> +               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
>>>>> +               return ret;
>>>>> +       }
>>>>> +
>>>>> +       usleep_range(10000, 12000);
>>>>> +
>>>>> +       /* operation mode */
>>>>> +       operation[0] = HDMIPHY_MODE_SET_DONE;
>>>>> +       operation[1] = HDMIPHY_MODE_EN;
>>>>> +
>>>>> +       ret = i2c_master_send(client, operation, 2);
>>>>> +       if (ret != 2) {
>>>>> +               DRM_ERROR("failed to enable hdmiphy\n");
>>>>> +               return ret;
>>>>> +       }
>>>>> +
>>>>> +       ret = i2c_master_recv(client, read_buffer, HDMIPHY_REG_COUNT);
>>>>> +       if (ret < 0) {
>>>>> +               DRM_ERROR("failed to read hdmiphy config\n");
>>>>> +               return ret;
>>>>> +       }
>>>>> +
>>>>> +       for (i = 0; i < ret; i++)
>>>>> +               DRM_DEBUG_KMS("hdmiphy[0x%02x] wr[0x%02x], rd[0x%02x]\n",
>>>>> +                               i, buffer[i], read_buffer[i]);
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static void hdmiphy_dpms(void *ctx, int mode)
>>>>> +{
>>>>> +       struct hdmiphy_context *hdata = ctx;
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
>>>>> +
>>>>> +       switch (mode) {
>>>>> +       case DRM_MODE_DPMS_ON:
>>>>> +               if (pm_runtime_suspended(hdata->dev))
>>>>> +                       pm_runtime_get_sync(hdata->dev);
>>>>> +               break;
>>>>> +       case DRM_MODE_DPMS_STANDBY:
>>>>> +       case DRM_MODE_DPMS_SUSPEND:
>>>>> +       case DRM_MODE_DPMS_OFF:
>>>>> +               if (!pm_runtime_suspended(hdata->dev))
>>>>> +                       pm_runtime_put_sync(hdata->dev);
>>>>> +               break;
>>>>> +       default:
>>>>> +               DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
>>>>> +               break;
>>>>> +       }
>>>>> +}
>>>>> +
>>>>> +static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
>>>>> +                              u8 reg, u8 mask, u8 val)
>>>>> +{
>>>>> +       int ret;
>>>>> +       u8 buffer[2];
>>>>> +
>>>>> +       buffer[0] = reg;
>>>>> +       buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
>>>>> +       reg_cache[reg] = buffer[1];
>>>>> +
>>>>> +       ret = i2c_master_send(client, buffer, 2);
>>>>> +       if (ret != 2)
>>>>> +               return -EIO;
>>>>>
>>>>>         return 0;
>>>>>  }
>>>>>
>>>>> +static int hdmiphy_4412_turn_on(struct i2c_client *client, bool on)
>>>>
>>>> This name is misleading. Please change it to something that doesn't
>>>> convey a power state, such as hdmiphy_4412_power
>>>>
>>> ok. I will change that.
>>>
>>>>> +{
>>>>> +       u8 reg_cache[HDMIPHY_REG_COUNT] = { 0 };
>>>>> +       u8 buffer[2];
>>>>> +       int ret;
>>>>> +
>>>>> +       DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
>>>>> +
>>>>> +       /* Cache all hdmi-phy registers to make the code below faster */
>>>>> +       buffer[0] = 0x0;
>>>>> +       ret = i2c_master_send(client, buffer, 1);
>>>>> +       if (ret != 1) {
>>>>> +               ret = -EIO;
>>>>> +               goto exit;
>>>>> +       }
>>>>> +       ret = i2c_master_recv(client, reg_cache, HDMIPHY_REG_COUNT);
>>>>> +       if (ret != HDMIPHY_REG_COUNT) {
>>>>> +               ret = -EIO;
>>>>> +               goto exit;
>>>>> +       }
>>>>> +
>>>>> +       /* Change to/from configuration from/to operation mode */
>>>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_MODE_SET_DONE,
>>>>> +                               HDMIPHY_MODE_EN, on ? ~0 : 0);
>>>>> +       if (ret)
>>>>> +               goto exit;
>>>>> +
>>>>> +       /*
>>>>> +        * Turn off "oscpad" if !on; it turns on again in
>>>>> +        * during phy-configuration.
>>>>> +        */
>>>>> +       if (!on)
>>>>> +               ret = hdmiphy_update_bits(client, reg_cache,
>>>>> +                       HDMIPHY_4212_OSC_PAD_CON, HDMIPHY_OSC_PAD_EN, 0);
>>>>> +               if (ret)
>>>>> +                       goto exit;
>>>>> +
>>>>> +       /* Disable powerdown if on; enable if !on */
>>>>
>>>> This comment really isn't useful, it's just describing the code.
>>>
>>> I will remove this comment.
>>>
>>>>
>>>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>>>>> +                       HDMIPHY_PDEN, on ? 0 : ~0);
>>>>> +       if (ret)
>>>>> +               goto exit;
>>>>> +       ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
>>>>> +                       HDMIPHY_PD_ALL, on ? 0 : ~0);
>>>>> +       if (ret)
>>>>> +               goto exit;
>>>>> +
>>>>> +       /* Disable pixel clock generator block if !on */
>>>>> +       if (!on)
>>>>> +               ret = hdmiphy_update_bits(client, reg_cache,
>>>>> +                       HDMIPHY_4212_PCG_CON, HDMIPHY_PCG_RESET_EN, 0);
>>>>> +               if (ret)
>>>>> +                       goto exit;
>>>>> +
>>>>> +exit:
>>>>> +       /* Don't expect any errors so just do a single warn */
>>>>> +       WARN_ON(ret);
>>>>> +
>>>>> +       return ret;
>>>>> +}
>>>>> +
>>>>> +static struct exynos_hdmiphy_ops hdmiphy_ops = {
>>>>> +       .check_timing   = hdmiphy_check_timing,
>>>>> +       .mode_set               = hdmiphy_mode_set,
>>>>> +       .prepare                = hdmiphy_config_prepare,
>>>>> +       .config_apply   = hdmiphy_config_apply,
>>>>> +       .dpms           = hdmiphy_dpms,
>>>>> +};
>>>>> +
>>>>>  static const struct i2c_device_id hdmiphy_id[] = {
>>>>> -       { "s5p_hdmiphy", 0 },
>>>>> -       { "exynos5-hdmiphy", 0 },
>>>>> +       { "s5p_hdmiphy", HDMIPHY_EXYNOS4210 },
>>>>> +       { "exynos5-hdmiphy", HDMIPHY_EXYNOS4212 },
>>>>>         { },
>>>>>  };
>>>>>
>>>>> @@ -50,21 +454,183 @@ static const struct i2c_device_id hdmiphy_id[] = {
>>>>>  static struct of_device_id hdmiphy_match_types[] = {
>>>>>         {
>>>>>                 .compatible = "samsung,exynos5-hdmiphy",
>>>>> +               .data   = (void *)HDMIPHY_EXYNOS4212,
>>>>>         }, {
>>>>>                 /* end node */
>>>>>         }
>>>>>  };
>>>>>  #endif
>>>>>
>>>>> +static int hdmiphy_probe(struct i2c_client *client,
>>>>> +       const struct i2c_device_id *id)
>>>>> +{
>>>>> +       struct device *dev = &client->dev;
>>>>> +       struct hdmiphy_context *hdata;
>>>>> +       struct exynos_drm_hdmi_context *drm_hdmi_ctx;
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
>>>>> +                                       GFP_KERNEL);
>>>>> +       if (!drm_hdmi_ctx) {
>>>>> +               DRM_ERROR("failed to allocate common hdmi context.\n");
>>>>> +               return -ENOMEM;
>>>>> +       }
>>>>> +
>>>>> +       hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
>>>>> +       if (!hdata) {
>>>>> +               DRM_ERROR("failed to allocate hdmiphy context.\n");
>>>>> +               return -ENOMEM;
>>>>> +       }
>>>>> +
>>>>> +       if (dev->of_node) {
>>>>> +               const struct of_device_id *match;
>>>>> +               match = of_match_node(of_match_ptr(hdmiphy_match_types),
>>>>> +                                       dev->of_node);
>>>>> +               if (match == NULL)
>>>>> +                       return -ENODEV;
>>>>> +               hdata->type = (enum hdmiphy_type)match->data;
>>>>> +       } else {
>>>>> +               hdata->type = (enum hdmiphy_type)id->driver_data;
>>>>> +       }
>>>>> +
>>>>> +       drm_hdmi_ctx->ctx = (void *)hdata;
>>>>> +       hdata->parent_ctx = (void *)drm_hdmi_ctx;
>>>>> +       hdata->dev = dev;
>>>>> +
>>>>> +       hdata->hdmiphy = devm_clk_get(dev, "hdmiphy");
>>>>
>>>> I don't see this call removed in the hdmi driver, am I missing something?
>>>>
>>>
>>> I took it wrong. I thought hdmi IP is clocked by hdmiphy clock. Actually it is
>>> sclk_hdmiphy clock. I will remove this from hdmi driver.
>>>
>>>>> +       if (IS_ERR_OR_NULL(hdata->hdmiphy)) {
>>>>> +               DRM_ERROR("failed to get clock 'hdmiphy'\n");
>>>>> +               return PTR_ERR(hdata->hdmiphy);
>>>>> +       }
>>>>> +
>>>>> +       i2c_set_clientdata(client, hdata);
>>>>> +
>>>>> +       /* Attach HDMI-PHY Driver to common hdmi. */
>>>>> +       exynos_hdmiphy_drv_attach(drm_hdmi_ctx);
>>>>> +
>>>>> +       /* register specific callbacks to common hdmi. */
>>>>> +       exynos_hdmiphy_ops_register(&hdmiphy_ops);
>>>>> +
>>>>> +       pm_runtime_enable(dev);
>>>>> +
>>>>> +       dev_info(&client->adapter->dev,
>>>>> +               "attached s5p_hdmiphy into i2c adapter successfully\n");
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static int hdmiphy_remove(struct i2c_client *client)
>>>>> +{
>>>>> +       dev_info(&client->adapter->dev,
>>>>> +               "detached s5p_hdmiphy from i2c adapter successfully\n");
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static void hdmiphy_poweroff(struct device *dev)
>>>>> +{
>>>>> +       struct i2c_client *client = to_i2c_client(dev);
>>>>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>>>>> +               hdmiphy_4412_turn_on(client, 0);
>>>>> +
>>>>> +       clk_disable(hdata->hdmiphy);
>>>>> +}
>>>>> +
>>>>> +static void hdmiphy_poweron(struct device *dev)
>>>>> +{
>>>>> +       struct i2c_client *client = to_i2c_client(dev);
>>>>> +       struct hdmiphy_context *hdata = i2c_get_clientdata(client);
>>>>> +
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       clk_enable(hdata->hdmiphy);
>>>>> +
>>>>> +       if (hdata->type == HDMIPHY_EXYNOS4212)
>>>>> +               hdmiphy_4412_turn_on(client, 1);
>>>>> +}
>>>>> +
>>>>> +#ifdef CONFIG_PM_SLEEP
>>>>> +static int hdmiphy_suspend(struct device *dev)
>>>>> +{
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       if (pm_runtime_suspended(dev)) {
>>>>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>>>>> +               __func__);
>>>>> +               return 0;
>>>>> +       }
>>>>> +
>>>>> +       hdmiphy_poweroff(dev);
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static int hdmiphy_resume(struct device *dev)
>>>>> +{
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       if (pm_runtime_suspended(dev)) {
>>>>> +               /* dpms callback should resume the mixer. */
>>>>> +               DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
>>>>> +               __func__);
>>>>> +               return 0;
>>>>> +       }
>>>>> +
>>>>> +       hdmiphy_poweron(dev);
>>>>> +       return 0;
>>>>> +}
>>>>> +#endif
>>>>> +
>>>>> +
>>>>> +#ifdef CONFIG_PM_RUNTIME
>>>>> +static int hdmiphy_runtime_suspend(struct device *dev)
>>>>> +{
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       hdmiphy_poweroff(dev);
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static int hdmiphy_runtime_resume(struct device *dev)
>>>>> +{
>>>>> +       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
>>>>> +
>>>>> +       hdmiphy_poweron(dev);
>>>>> +       return 0;
>>>>> +}
>>>>> +#endif
>>>>> +
>>>>> +static const struct dev_pm_ops hdmiphy_pm_ops = {
>>>>> +       SET_SYSTEM_SLEEP_PM_OPS(hdmiphy_suspend, hdmiphy_resume)
>>>>> +       SET_RUNTIME_PM_OPS(hdmiphy_runtime_suspend,
>>>>> +               hdmiphy_runtime_resume, NULL)
>>>>> +};
>>>>> +
>>>>>  struct i2c_driver hdmiphy_driver = {
>>>>>         .driver = {
>>>>>                 .name   = "exynos-hdmiphy",
>>>>>                 .owner  = THIS_MODULE,
>>>>>                 .of_match_table = of_match_ptr(hdmiphy_match_types),
>>>>> +               .pm     = &hdmiphy_pm_ops,
>>>>>         },
>>>>>         .id_table = hdmiphy_id,
>>>>>         .probe          = hdmiphy_probe,
>>>>>         .remove         = hdmiphy_remove,
>>>>>         .command                = NULL,
>>>>>  };
>>>>> +
>>>>> +extern int exynos_hdmiphy_driver_register(void)
>>>>> +{
>>>>> +       return i2c_add_driver(&hdmiphy_driver);
>>>>> +}
>>>>> +
>>>>> +extern void exynos_hdmiphy_driver_unregister(void)
>>>>> +{
>>>>> +       i2c_del_driver(&hdmiphy_driver);
>>>>> +}
>>>>> +
>>>>>  EXPORT_SYMBOL(hdmiphy_driver);
>>>>> diff --git a/drivers/gpu/drm/exynos/regs-hdmiphy.h b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>> new file mode 100644
>>>>> index 0000000..8f906c6
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/exynos/regs-hdmiphy.h
>>>>> @@ -0,0 +1,61 @@
>>>>> +/*
>>>>> + *
>>>>> + *  regs-hdmiphy.h
>>>>> + *
>>>>> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
>>>>> + * http://www.samsung.com/
>>>>> + *
>>>>> + * HDMI-PHY register header file for Samsung TVOUT driver
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>> + * published by the Free Software Foundation.
>>>>> +*/
>>>>> +
>>>>> +#ifndef SAMSUNG_REGS_HDMIPHY_H
>>>>> +#define SAMSUNG_REGS_HDMIPHY_H
>>>>> +
>>>>> +/*
>>>>> + * Register part
>>>>> +*/
>>>>> +
>>>>> +/* HDMI PHY Version Common */
>>>>> +#define HDMIPHY_MODE_SET_DONE          (0x1f)
>>>>> +
>>>>> +/* HDMI PHY Version 4212 */
>>>>> +#define HDMIPHY_4212_PCG_CON           (0x04)
>>>>> +#define HDMIPHY_4212_OSC_PAD_CON       (0x0b)
>>>>> +#define HDMIPHY_4212_PD_CON            (0x1d)
>>>>> +
>>>>> +/*
>>>>> + * Bit definition part
>>>>> + */
>>>>> +
>>>>> +/* HDMIPHY_MODE_SET_DONE */
>>>>> +#define HDMIPHY_MODE_EN                        (1 << 7)
>>>>> +
>>>>> +/* HDMIPHY_4212_PCG_CON */
>>>>> +#define HDMIPHY_PCG_RESET_EN           (1 << 3)
>>>>> +
>>>>> +/* HDMIPHY_4212_OSC_PAD_CON */
>>>>> +#define HDMIPHY_OSC_PAD_EN             (3 << 6)
>>>>> +
>>>>> +/* HDMIPHY_4212_PD_CON */
>>>>> +#define HDMIPHY_PDEN                   (1 << 7)
>>>>> +
>>>>> +#define HDMIPHY_PLL_PD                 (1 << 6)
>>>>> +#define HDMIPHY_CLKSER_PD              (1 << 5)
>>>>> +#define HDMIPHY_CLKDRV_PD              (1 << 4)
>>>>> +
>>>>> +#define HDMIPHY_DRV_PD                 (1 << 2)
>>>>> +#define HDMIPHY_SER_PD                 (1 << 1)
>>>>> +#define HDMIPHY_ICLK_PD                        (1 << 0)
>>>>> +
>>>>> +#define HDMIPHY_PD_ALL                 (HDMIPHY_PLL_PD |\
>>>>> +                                       HDMIPHY_CLKSER_PD |\
>>>>> +                                       HDMIPHY_CLKDRV_PD|\
>>>>> +                                       HDMIPHY_DRV_PD|\
>>>>> +                                       HDMIPHY_SER_PD|\
>>>>> +                                       HDMIPHY_ICLK_PD)
>>>>> +
>>>>> +#endif /* SAMSUNG_REGS_HDMIPHY_H */
>>>>> --
>>>>> 1.8.0
>>>>>
>>>
>>> --
>>> Regards,
>>> Rahul Sharma,
>>> email to: rahul.sharma@samsung.com
>>> Samsung India.
>>
>>
>>
>> --
>> Regards,
>> Rahul Sharma,
>> email to: rahul.sharma@samsung.com
>> Samsung India.
>> _______________________________________________
>> 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/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 3da5c2d..03acb6b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -334,6 +334,11 @@  static int __init exynos_drm_init(void)
 	ret = platform_driver_register(&hdmi_driver);
 	if (ret < 0)
 		goto out_hdmi;
+
+	ret = exynos_hdmiphy_driver_register();
+	if (ret < 0)
+		goto out_hdmiphy;
+
 	ret = platform_driver_register(&mixer_driver);
 	if (ret < 0)
 		goto out_mixer;
@@ -436,6 +441,8 @@  out_common_hdmi_dev:
 out_common_hdmi:
 	platform_driver_unregister(&mixer_driver);
 out_mixer:
+	exynos_hdmiphy_driver_unregister();
+out_hdmiphy:
 	platform_driver_unregister(&hdmi_driver);
 out_hdmi:
 #endif
@@ -479,6 +486,7 @@  static void __exit exynos_drm_exit(void)
 	exynos_platform_device_hdmi_unregister();
 	platform_driver_unregister(&exynos_drm_common_hdmi_driver);
 	platform_driver_unregister(&mixer_driver);
+	exynos_hdmiphy_driver_unregister();
 	platform_driver_unregister(&hdmi_driver);
 #endif
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 4606fac..17c53e3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -325,6 +325,12 @@  void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
 extern int exynos_platform_device_hdmi_register(void);
 
 /*
+ * these functions registers/unregisters exynos drm hdmiphy driver.
+ */
+extern int exynos_hdmiphy_driver_register(void);
+extern void exynos_hdmiphy_driver_unregister(void);
+
+/*
  * this function unregisters exynos drm hdmi platform device if it exists.
  */
 void exynos_platform_device_hdmi_unregister(void);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
index 5dc956b..45ef331 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
@@ -32,19 +32,22 @@ 
 /* platform device pointer for common drm hdmi device. */
 static struct platform_device *exynos_drm_hdmi_pdev;
 
-/* Common hdmi subdrv needs to access the hdmi and mixer though context.
-* These should be initialied by the repective drivers */
+/* Common hdmi subdrv needs to access the hdmi, hdmiphy and mixer though
+*  context. These should be initialied by the repective drivers */
 static struct exynos_drm_hdmi_context *hdmi_ctx;
+static struct exynos_drm_hdmi_context *hdmiphy_ctx;
 static struct exynos_drm_hdmi_context *mixer_ctx;
 
 /* these callback points shoud be set by specific drivers. */
 static struct exynos_hdmi_ops *hdmi_ops;
+static struct exynos_hdmiphy_ops *hdmiphy_ops;
 static struct exynos_mixer_ops *mixer_ops;
 
 struct drm_hdmi_context {
 	struct exynos_drm_subdrv	subdrv;
 	struct exynos_drm_hdmi_context	*hdmi_ctx;
 	struct exynos_drm_hdmi_context	*mixer_ctx;
+	struct exynos_drm_hdmi_context	*hdmiphy_ctx;
 
 	bool	enabled[MIXER_WIN_NR];
 };
@@ -74,6 +77,12 @@  void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
 		hdmi_ctx = ctx;
 }
 
+void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx)
+{
+	if (ctx)
+		hdmiphy_ctx = ctx;
+}
+
 void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
 {
 	if (ctx)
@@ -88,6 +97,14 @@  void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
 		hdmi_ops = ops;
 }
 
+void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops)
+{
+	DRM_DEBUG_KMS("%s\n", __FILE__);
+
+	if (ops)
+		hdmiphy_ops = ops;
+}
+
 void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
 {
 	DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -121,7 +138,7 @@  struct edid *drm_hdmi_get_edid(struct device *dev,
 	return NULL;
 }
 
-static int drm_hdmi_check_timing(struct device *dev, void *timing)
+static int drm_hdmi_check_timing(struct device *dev, void *mode)
 {
 	struct drm_hdmi_context *ctx = to_context(dev);
 	int ret = 0;
@@ -129,18 +146,24 @@  static int drm_hdmi_check_timing(struct device *dev, void *timing)
 	DRM_DEBUG_KMS("%s\n", __FILE__);
 
 	/*
-	* Both, mixer and hdmi should be able to handle the requested mode.
-	* If any of the two fails, return mode as BAD.
+	* Mixer, hdmi and hdmiphy should be able to handle the requested mode.
+	* If any of the them fails, return mode as BAD.
 	*/
 
 	if (mixer_ops && mixer_ops->check_timing)
-		ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
+		ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, mode);
 
 	if (ret)
 		return ret;
 
 	if (hdmi_ops && hdmi_ops->check_timing)
-		return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
+		ret = hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, mode);
+
+	if (ret)
+		return ret;
+
+	if (hdmiphy_ops && hdmiphy_ops->check_timing)
+		return hdmiphy_ops->check_timing(ctx->hdmiphy_ctx->ctx, mode);
 
 	return 0;
 }
@@ -254,6 +277,9 @@  static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
 
 	if (hdmi_ops && hdmi_ops->mode_set)
 		hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
+
+	if (hdmiphy_ops && hdmiphy_ops->mode_set)
+		hdmiphy_ops->mode_set(ctx->hdmiphy_ctx->ctx, mode);
 }
 
 static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
@@ -273,6 +299,15 @@  static void drm_hdmi_commit(struct device *subdrv_dev)
 
 	DRM_DEBUG_KMS("%s\n", __FILE__);
 
+	if (hdmiphy_ops && hdmiphy_ops->prepare)
+		hdmiphy_ops->prepare(ctx->hdmiphy_ctx->ctx);
+
+	if (hdmi_ops && hdmi_ops->prepare)
+		hdmi_ops->prepare(ctx->hdmi_ctx->ctx);
+
+	if (hdmiphy_ops && hdmiphy_ops->config_apply)
+		hdmiphy_ops->config_apply(ctx->hdmiphy_ctx->ctx);
+
 	if (hdmi_ops && hdmi_ops->commit)
 		hdmi_ops->commit(ctx->hdmi_ctx->ctx);
 }
@@ -288,6 +323,9 @@  static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
 
 	if (hdmi_ops && hdmi_ops->dpms)
 		hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
+
+	if (hdmiphy_ops && hdmiphy_ops->dpms)
+		hdmiphy_ops->dpms(ctx->hdmiphy_ctx->ctx, mode);
 }
 
 static void drm_hdmi_apply(struct device *subdrv_dev)
@@ -393,6 +431,11 @@  static int hdmi_subdrv_probe(struct drm_device *drm_dev,
 		return -EFAULT;
 	}
 
+	if (!hdmiphy_ctx) {
+		DRM_ERROR("hdmiphy context not initialized.\n");
+		return -EFAULT;
+	}
+
 	if (!mixer_ctx) {
 		DRM_ERROR("mixer context not initialized.\n");
 		return -EFAULT;
@@ -406,6 +449,7 @@  static int hdmi_subdrv_probe(struct drm_device *drm_dev,
 	}
 
 	ctx->hdmi_ctx = hdmi_ctx;
+	ctx->hdmiphy_ctx = hdmiphy_ctx;
 	ctx->mixer_ctx = mixer_ctx;
 
 	ctx->hdmi_ctx->drm_dev = drm_dev;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
index fd2ff9f..10db62e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
@@ -39,10 +39,19 @@  struct exynos_hdmi_ops {
 	void (*mode_set)(void *ctx, struct drm_display_mode *mode);
 	void (*get_max_resol)(void *ctx, unsigned int *width,
 				unsigned int *height);
+	void (*prepare)(void *ctx);
 	void (*commit)(void *ctx);
 	void (*dpms)(void *ctx, int mode);
 };
 
+struct exynos_hdmiphy_ops {
+	int (*check_timing)(void *ctx, struct drm_display_mode *mode);
+	void (*mode_set)(void *ctx, struct drm_display_mode *mode);
+	void (*prepare)(void *ctx);
+	int (*config_apply)(void *ctx);
+	void (*dpms)(void *ctx, int mode);
+};
+
 struct exynos_mixer_ops {
 	/* manager */
 	int (*iommu_on)(void *ctx, bool enable);
@@ -61,7 +70,9 @@  struct exynos_mixer_ops {
 };
 
 void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
+void exynos_hdmiphy_drv_attach(struct exynos_drm_hdmi_context *ctx);
 void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
 void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
+void exynos_hdmiphy_ops_register(struct exynos_hdmiphy_ops *ops);
 void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
 #endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index a5ca2cc..7ee350d 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -194,7 +194,6 @@  struct hdmi_context {
 	int				internal_irq;
 
 	struct i2c_client		*ddc_port;
-	struct i2c_client		*hdmiphy_port;
 
 	/* current hdmiphy conf regs */
 	struct hdmi_conf_regs		mode_conf;
@@ -206,180 +205,6 @@  struct hdmi_context {
 	enum hdmi_type			type;
 };
 
-struct hdmiphy_config {
-	int pixel_clock;
-	u8 conf[32];
-};
-
-/* list of phy config settings */
-static const struct hdmiphy_config hdmiphy_v13_configs[] = {
-	{
-		.pixel_clock = 27000000,
-		.conf = {
-			0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
-			0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
-			0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
-			0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
-		},
-	},
-	{
-		.pixel_clock = 27027000,
-		.conf = {
-			0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
-			0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
-			0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
-			0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
-		},
-	},
-	{
-		.pixel_clock = 74176000,
-		.conf = {
-			0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
-			0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
-			0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
-			0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
-		},
-	},
-	{
-		.pixel_clock = 74250000,
-		.conf = {
-			0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
-			0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
-			0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
-			0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
-		},
-	},
-	{
-		.pixel_clock = 148500000,
-		.conf = {
-			0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
-			0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
-			0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
-			0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
-		},
-	},
-};
-
-static const struct hdmiphy_config hdmiphy_v14_configs[] = {
-	{
-		.pixel_clock = 25200000,
-		.conf = {
-			0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
-			0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 27000000,
-		.conf = {
-			0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
-			0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-			0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 27027000,
-		.conf = {
-			0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
-			0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
-		},
-	},
-	{
-		.pixel_clock = 36000000,
-		.conf = {
-			0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
-			0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 40000000,
-		.conf = {
-			0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
-			0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 65000000,
-		.conf = {
-			0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
-			0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 74176000,
-		.conf = {
-			0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
-			0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 74250000,
-		.conf = {
-			0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
-			0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-			0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
-		},
-	},
-	{
-		.pixel_clock = 83500000,
-		.conf = {
-			0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
-			0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 106500000,
-		.conf = {
-			0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
-			0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 108000000,
-		.conf = {
-			0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
-			0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 146250000,
-		.conf = {
-			0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
-			0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
-		},
-	},
-	{
-		.pixel_clock = 148500000,
-		.conf = {
-			0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
-			0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-			0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-			0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
-		},
-	},
-};
-
 struct hdmi_infoframe {
 	enum HDMI_PACKET_TYPE type;
 	u8 ver;
@@ -774,46 +599,6 @@  static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
 	return raw_edid;
 }
 
-static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
-{
-	const struct hdmiphy_config *confs;
-	int count, i;
-
-	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-	if (hdata->type == HDMI_TYPE13) {
-		confs = hdmiphy_v13_configs;
-		count = ARRAY_SIZE(hdmiphy_v13_configs);
-	} else if (hdata->type == HDMI_TYPE14) {
-		confs = hdmiphy_v14_configs;
-		count = ARRAY_SIZE(hdmiphy_v14_configs);
-	} else
-		return -EINVAL;
-
-	for (i = 0; i < count; i++)
-		if (confs[i].pixel_clock == pixel_clock)
-			return i;
-
-	DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
-	return -EINVAL;
-}
-
-static int hdmi_check_timing(void *ctx, struct drm_display_mode *mode)
-{
-	struct hdmi_context *hdata = ctx;
-	int ret;
-
-	DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
-		__func__, mode->hdisplay, mode->vdisplay,
-		mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
-		false, mode->clock * 1000);
-
-	ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
-	if (ret < 0)
-		return ret;
-	return 0;
-}
-
 static void hdmi_set_acr(u32 freq, u8 *acr)
 {
 	u32 n, cts;
@@ -1309,20 +1094,12 @@  static void hdmi_timing_apply(struct hdmi_context *hdata)
 
 static void hdmiphy_conf_reset(struct hdmi_context *hdata)
 {
-	u8 buffer[2];
 	u32 reg;
 
 	clk_disable(hdata->res.sclk_hdmi);
 	clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
 	clk_enable(hdata->res.sclk_hdmi);
 
-	/* operation mode */
-	buffer[0] = 0x1f;
-	buffer[1] = 0x00;
-
-	if (hdata->hdmiphy_port)
-		i2c_master_send(hdata->hdmiphy_port, buffer, 2);
-
 	if (hdata->type == HDMI_TYPE13)
 		reg = HDMI_V13_PHY_RSTOUT;
 	else
@@ -1335,102 +1112,6 @@  static void hdmiphy_conf_reset(struct hdmi_context *hdata)
 	usleep_range(10000, 12000);
 }
 
-static void hdmiphy_poweron(struct hdmi_context *hdata)
-{
-	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-	if (hdata->type == HDMI_TYPE14)
-		hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
-			HDMI_PHY_POWER_OFF_EN);
-}
-
-static void hdmiphy_poweroff(struct hdmi_context *hdata)
-{
-	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-	if (hdata->type == HDMI_TYPE14)
-		hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
-			HDMI_PHY_POWER_OFF_EN);
-}
-
-static void hdmiphy_conf_apply(struct hdmi_context *hdata)
-{
-	const u8 *hdmiphy_data;
-	u8 buffer[32];
-	u8 operation[2];
-	u8 read_buffer[32] = {0, };
-	int ret;
-	int i;
-
-	if (!hdata->hdmiphy_port) {
-		DRM_ERROR("hdmiphy is not attached\n");
-		return;
-	}
-
-	/* pixel clock */
-	i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
-	if (i < 0) {
-		DRM_ERROR("failed to find hdmiphy conf\n");
-		return;
-	}
-
-	if (hdata->type == HDMI_TYPE13) {
-		hdmiphy_data = hdmiphy_v13_configs[i].conf;
-	} else {
-		hdmiphy_data = hdmiphy_v14_configs[i].conf;
-	}
-
-	memcpy(buffer, hdmiphy_data, 32);
-	ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
-	if (ret != 32) {
-		DRM_ERROR("failed to configure HDMIPHY via I2C\n");
-		return;
-	}
-
-	usleep_range(10000, 12000);
-
-	/* operation mode */
-	operation[0] = 0x1f;
-	operation[1] = 0x80;
-
-	ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
-	if (ret != 2) {
-		DRM_ERROR("failed to enable hdmiphy\n");
-		return;
-	}
-
-	ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
-	if (ret < 0) {
-		DRM_ERROR("failed to read hdmiphy config\n");
-		return;
-	}
-
-	for (i = 0; i < ret; i++)
-		DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
-			"recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
-}
-
-static void hdmi_conf_apply(struct hdmi_context *hdata)
-{
-	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-	hdmiphy_conf_reset(hdata);
-	hdmiphy_conf_apply(hdata);
-
-	mutex_lock(&hdata->hdmi_mutex);
-	hdmi_conf_reset(hdata);
-	hdmi_conf_init(hdata);
-	mutex_unlock(&hdata->hdmi_mutex);
-
-	hdmi_audio_init(hdata);
-
-	/* setting core registers */
-	hdmi_timing_apply(hdata);
-	hdmi_audio_control(hdata, true);
-
-	hdmi_regs_dump(hdata, "start");
-}
-
 static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
 {
 	int i;
@@ -1448,7 +1129,6 @@  static void hdmi_v13_mode_set(struct hdmi_context *hdata,
 
 	hdata->mode_conf.cea_video_id =
 		drm_match_cea_mode((struct drm_display_mode *)m);
-	hdata->mode_conf.pixel_clock = m->clock * 1000;
 
 	hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
 	hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
@@ -1544,7 +1224,6 @@  static void hdmi_v14_mode_set(struct hdmi_context *hdata,
 
 	hdata->mode_conf.cea_video_id =
 		drm_match_cea_mode((struct drm_display_mode *)m);
-	hdata->mode_conf.pixel_clock = m->clock * 1000;
 
 	hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
 	hdmi_set_reg(core->v_line, 2, m->vtotal);
@@ -1670,13 +1349,32 @@  static void hdmi_get_max_resol(void *ctx, unsigned int *width,
 	*height = MAX_HEIGHT;
 }
 
+static void hdmi_commit_prepare(void *ctx)
+{
+	struct hdmi_context *hdata = ctx;
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	hdmiphy_conf_reset(hdata);
+}
+
 static void hdmi_commit(void *ctx)
 {
 	struct hdmi_context *hdata = ctx;
 
 	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-	hdmi_conf_apply(hdata);
+	mutex_lock(&hdata->hdmi_mutex);
+	hdmi_conf_reset(hdata);
+	hdmi_conf_init(hdata);
+	mutex_unlock(&hdata->hdmi_mutex);
+
+	hdmi_audio_init(hdata);
+
+	/* setting core registers */
+	hdmi_timing_apply(hdata);
+	hdmi_audio_control(hdata, true);
+
+	hdmi_regs_dump(hdata, "start");
 }
 
 static void hdmi_poweron(struct hdmi_context *hdata)
@@ -1699,8 +1397,6 @@  static void hdmi_poweron(struct hdmi_context *hdata)
 	clk_enable(res->hdmiphy);
 	clk_enable(res->hdmi);
 	clk_enable(res->sclk_hdmi);
-
-	hdmiphy_poweron(hdata);
 }
 
 static void hdmi_poweroff(struct hdmi_context *hdata)
@@ -1719,7 +1415,6 @@  static void hdmi_poweroff(struct hdmi_context *hdata)
 	 * its reset state seems to meet the condition.
 	 */
 	hdmiphy_conf_reset(hdata);
-	hdmiphy_poweroff(hdata);
 
 	clk_disable(res->sclk_hdmi);
 	clk_disable(res->hdmi);
@@ -1761,11 +1456,11 @@  static struct exynos_hdmi_ops hdmi_ops = {
 	/* display */
 	.is_connected	= hdmi_is_connected,
 	.get_edid	= hdmi_get_edid,
-	.check_timing	= hdmi_check_timing,
 
 	/* manager */
 	.mode_set	= hdmi_mode_set,
 	.get_max_resol	= hdmi_get_max_resol,
+	.prepare		= hdmi_commit_prepare,
 	.commit		= hdmi_commit,
 	.dpms		= hdmi_dpms,
 };
@@ -1878,7 +1573,7 @@  fail:
 	return -ENODEV;
 }
 
-static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
+static struct i2c_client *hdmi_ddc;
 
 void hdmi_attach_ddc_client(struct i2c_client *ddc)
 {
@@ -1886,12 +1581,6 @@  void hdmi_attach_ddc_client(struct i2c_client *ddc)
 		hdmi_ddc = ddc;
 }
 
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
-{
-	if (hdmiphy)
-		hdmi_hdmiphy = hdmiphy;
-}
-
 #ifdef CONFIG_OF
 static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
 					(struct device *dev)
@@ -2051,27 +1740,18 @@  static int hdmi_probe(struct platform_device *pdev)
 
 	hdata->ddc_port = hdmi_ddc;
 
-	/* hdmiphy i2c driver */
-	if (i2c_add_driver(&hdmiphy_driver)) {
-		DRM_ERROR("failed to register hdmiphy i2c driver\n");
-		ret = -ENOENT;
-		goto err_ddc;
-	}
-
-	hdata->hdmiphy_port = hdmi_hdmiphy;
-
 	hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
 	if (hdata->external_irq < 0) {
 		DRM_ERROR("failed to get GPIO external irq\n");
 		ret = hdata->external_irq;
-		goto err_hdmiphy;
+		goto err_ddc;
 	}
 
 	hdata->internal_irq = platform_get_irq(pdev, 0);
 	if (hdata->internal_irq < 0) {
 		DRM_ERROR("failed to get platform internal irq\n");
 		ret = hdata->internal_irq;
-		goto err_hdmiphy;
+		goto err_ddc;
 	}
 
 	hdata->hpd = gpio_get_value(hdata->hpd_gpio);
@@ -2082,7 +1762,7 @@  static int hdmi_probe(struct platform_device *pdev)
 			"hdmi_external", drm_hdmi_ctx);
 	if (ret) {
 		DRM_ERROR("failed to register hdmi external interrupt\n");
-		goto err_hdmiphy;
+		goto err_ddc;
 	}
 
 	ret = request_threaded_irq(hdata->internal_irq, NULL,
@@ -2105,8 +1785,6 @@  static int hdmi_probe(struct platform_device *pdev)
 
 err_free_irq:
 	free_irq(hdata->external_irq, drm_hdmi_ctx);
-err_hdmiphy:
-	i2c_del_driver(&hdmiphy_driver);
 err_ddc:
 	i2c_del_driver(&ddc_driver);
 	return ret;
@@ -2125,9 +1803,6 @@  static int hdmi_remove(struct platform_device *pdev)
 	free_irq(hdata->internal_irq, hdata);
 	free_irq(hdata->external_irq, hdata);
 
-
-	/* hdmiphy i2c driver */
-	i2c_del_driver(&hdmiphy_driver);
 	/* DDC i2c driver */
 	i2c_del_driver(&ddc_driver);
 
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
index 0ddf395..6595d7b 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
@@ -15,7 +15,6 @@ 
 #define _EXYNOS_HDMI_H_
 
 void hdmi_attach_ddc_client(struct i2c_client *ddc);
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
 
 extern struct i2c_driver hdmiphy_driver;
 extern struct i2c_driver ddc_driver;
diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
index ea49d13..0d47302 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
@@ -16,33 +16,437 @@ 
 #include <linux/kernel.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
 
 #include "exynos_drm_drv.h"
+#include "exynos_drm_hdmi.h"
 #include "exynos_hdmi.h"
 
+#include "regs-hdmiphy.h"
 
-static int hdmiphy_probe(struct i2c_client *client,
-	const struct i2c_device_id *id)
+#define HDMIPHY_REG_COUNT		(32)
+
+enum hdmiphy_type {
+	HDMIPHY_EXYNOS4210,
+	HDMIPHY_EXYNOS4212,
+};
+
+struct hdmiphy_context {
+	struct device		*dev;
+	bool			powered;
+	void			*parent_ctx;
+	enum hdmiphy_type		type;
+	const struct hdmiphy_config	*conf;
+	struct clk		*hdmiphy;
+};
+
+struct hdmiphy_config {
+	int pixel_clock;
+	u8 conf[HDMIPHY_REG_COUNT];
+};
+
+/* list of all required phy config settings */
+static const struct hdmiphy_config hdmiphy_4212_configs[] = {
+	{
+		.pixel_clock = 25200000,
+		.conf = {
+			0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
+			0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 27000000,
+		.conf = {
+			0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
+			0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+			0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 27027000,
+		.conf = {
+			0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
+			0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
+		},
+	},
+	{
+		.pixel_clock = 36000000,
+		.conf = {
+			0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
+			0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 40000000,
+		.conf = {
+			0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
+			0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 65000000,
+		.conf = {
+			0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
+			0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 74176000,
+		.conf = {
+			0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
+			0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 74250000,
+		.conf = {
+			0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
+			0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+			0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
+		},
+	},
+	{
+		.pixel_clock = 83500000,
+		.conf = {
+			0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
+			0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 106500000,
+		.conf = {
+			0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
+			0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 108000000,
+		.conf = {
+			0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
+			0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 146250000,
+		.conf = {
+			0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
+			0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+			0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+		},
+	},
+	{
+		.pixel_clock = 148500000,
+		.conf = {
+			0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
+			0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+			0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+			0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+		},
+	},
+};
+
+static const struct hdmiphy_config hdmiphy_4210_configs[] = {
+	{
+		.pixel_clock = 27000000,
+		.conf = {
+			0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
+			0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
+			0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+			0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
+		},
+	},
+	{
+		.pixel_clock = 27027000,
+		.conf = {
+			0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
+			0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
+			0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+			0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
+		},
+	},
+	{
+		.pixel_clock = 74176000,
+		.conf = {
+			0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
+			0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
+			0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+			0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
+		},
+	},
+	{
+		.pixel_clock = 74250000,
+		.conf = {
+			0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
+			0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
+			0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
+			0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
+		},
+	},
+	{
+		.pixel_clock = 148500000,
+		.conf = {
+			0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
+			0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
+			0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+			0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
+		},
+	},
+};
+
+static const struct hdmiphy_config *hdmiphy_find_conf(void *ctx,
+			const struct drm_display_mode *mode)
+{
+	struct hdmiphy_context *hdata = ctx;
+	const struct hdmiphy_config *confs;
+	int count, i;
+
+	switch (hdata->type) {
+	case HDMIPHY_EXYNOS4212:
+		confs = hdmiphy_4212_configs;
+		count = ARRAY_SIZE(hdmiphy_4212_configs);
+		break;
+	case HDMIPHY_EXYNOS4210:
+		confs = hdmiphy_4210_configs;
+		count = ARRAY_SIZE(hdmiphy_4210_configs);
+		break;
+	default:
+		return NULL;
+	}
+
+	for (i = 0; i < count; i++)
+		if (confs[i].pixel_clock == (mode->clock * 1000))
+			return &confs[i];
+
+	return NULL;
+}
+
+static int hdmiphy_check_timing(void *ctx, struct drm_display_mode *mode)
 {
-	hdmi_attach_hdmiphy_client(client);
+	const struct hdmiphy_config *conf;
+	DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
+		__func__, mode->hdisplay, mode->vdisplay,
+		mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		? true : false, mode->clock * 1000);
 
-	dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
-		"into i2c adapter successfully\n");
+	conf = hdmiphy_find_conf(ctx, mode);
+	if (!conf) {
+		DRM_DEBUG_KMS("Display Mode is not supported.\n");
+		return -EINVAL;
+	}
 
 	return 0;
 }
 
-static int hdmiphy_remove(struct i2c_client *client)
+static void hdmiphy_mode_set(void *ctx, struct drm_display_mode *mode)
+{
+	struct hdmiphy_context *hdata = ctx;
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	hdata->conf = hdmiphy_find_conf(ctx, mode);
+}
+
+static void hdmiphy_config_prepare(void *ctx)
+{
+	struct hdmiphy_context *hdata = ctx;
+	const struct i2c_client *client = to_i2c_client(hdata->dev);
+	u8 buffer[2];
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	/* operation mode */
+	buffer[0] = HDMIPHY_MODE_SET_DONE;
+	buffer[1] = 0x00;
+
+	if (client)
+		i2c_master_send(client, buffer, 2);
+}
+
+static int hdmiphy_config_apply(void *ctx)
 {
-	dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
-		"from i2c adapter successfully\n");
+	struct hdmiphy_context *hdata = ctx;
+	struct i2c_client *client = to_i2c_client(hdata->dev);
+	const u8 *hdmiphy_data;
+	u8 buffer[HDMIPHY_REG_COUNT];
+	u8 operation[2];
+	u8 read_buffer[HDMIPHY_REG_COUNT] = {0, };
+	int ret;
+	int i;
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	/* pixel clock */
+	if (hdata->conf && client)
+		hdmiphy_data = hdata->conf->conf;
+	else
+		return -EINVAL;
+
+	memcpy(buffer, hdmiphy_data, HDMIPHY_REG_COUNT);
+
+	ret = i2c_master_send(client, buffer, HDMIPHY_REG_COUNT);
+	if (ret != HDMIPHY_REG_COUNT) {
+		DRM_ERROR("failed to configure HDMIPHY via I2C\n");
+		return ret;
+	}
+
+	usleep_range(10000, 12000);
+
+	/* operation mode */
+	operation[0] = HDMIPHY_MODE_SET_DONE;
+	operation[1] = HDMIPHY_MODE_EN;
+
+	ret = i2c_master_send(client, operation, 2);
+	if (ret != 2) {
+		DRM_ERROR("failed to enable hdmiphy\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, read_buffer, HDMIPHY_REG_COUNT);
+	if (ret < 0) {
+		DRM_ERROR("failed to read hdmiphy config\n");
+		return ret;
+	}
+
+	for (i = 0; i < ret; i++)
+		DRM_DEBUG_KMS("hdmiphy[0x%02x] wr[0x%02x], rd[0x%02x]\n",
+				i, buffer[i], read_buffer[i]);
+	return 0;
+}
+
+static void hdmiphy_dpms(void *ctx, int mode)
+{
+	struct hdmiphy_context *hdata = ctx;
+
+	DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (pm_runtime_suspended(hdata->dev))
+			pm_runtime_get_sync(hdata->dev);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (!pm_runtime_suspended(hdata->dev))
+			pm_runtime_put_sync(hdata->dev);
+		break;
+	default:
+		DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
+		break;
+	}
+}
+
+static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
+			       u8 reg, u8 mask, u8 val)
+{
+	int ret;
+	u8 buffer[2];
+
+	buffer[0] = reg;
+	buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
+	reg_cache[reg] = buffer[1];
+
+	ret = i2c_master_send(client, buffer, 2);
+	if (ret != 2)
+		return -EIO;
 
 	return 0;
 }
 
+static int hdmiphy_4412_turn_on(struct i2c_client *client, bool on)
+{
+	u8 reg_cache[HDMIPHY_REG_COUNT] = { 0 };
+	u8 buffer[2];
+	int ret;
+
+	DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
+
+	/* Cache all hdmi-phy registers to make the code below faster */
+	buffer[0] = 0x0;
+	ret = i2c_master_send(client, buffer, 1);
+	if (ret != 1) {
+		ret = -EIO;
+		goto exit;
+	}
+	ret = i2c_master_recv(client, reg_cache, HDMIPHY_REG_COUNT);
+	if (ret != HDMIPHY_REG_COUNT) {
+		ret = -EIO;
+		goto exit;
+	}
+
+	/* Change to/from configuration from/to operation mode */
+	ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_MODE_SET_DONE,
+				HDMIPHY_MODE_EN, on ? ~0 : 0);
+	if (ret)
+		goto exit;
+
+	/*
+	 * Turn off "oscpad" if !on; it turns on again in
+	 * during phy-configuration.
+	 */
+	if (!on)
+		ret = hdmiphy_update_bits(client, reg_cache,
+			HDMIPHY_4212_OSC_PAD_CON, HDMIPHY_OSC_PAD_EN, 0);
+		if (ret)
+			goto exit;
+
+	/* Disable powerdown if on; enable if !on */
+	ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
+			HDMIPHY_PDEN, on ? 0 : ~0);
+	if (ret)
+		goto exit;
+	ret = hdmiphy_update_bits(client, reg_cache, HDMIPHY_4212_PD_CON,
+			HDMIPHY_PD_ALL, on ? 0 : ~0);
+	if (ret)
+		goto exit;
+
+	/* Disable pixel clock generator block if !on */
+	if (!on)
+		ret = hdmiphy_update_bits(client, reg_cache,
+			HDMIPHY_4212_PCG_CON, HDMIPHY_PCG_RESET_EN, 0);
+		if (ret)
+			goto exit;
+
+exit:
+	/* Don't expect any errors so just do a single warn */
+	WARN_ON(ret);
+
+	return ret;
+}
+
+static struct exynos_hdmiphy_ops hdmiphy_ops = {
+	.check_timing	= hdmiphy_check_timing,
+	.mode_set		= hdmiphy_mode_set,
+	.prepare		= hdmiphy_config_prepare,
+	.config_apply	= hdmiphy_config_apply,
+	.dpms		= hdmiphy_dpms,
+};
+
 static const struct i2c_device_id hdmiphy_id[] = {
-	{ "s5p_hdmiphy", 0 },
-	{ "exynos5-hdmiphy", 0 },
+	{ "s5p_hdmiphy", HDMIPHY_EXYNOS4210 },
+	{ "exynos5-hdmiphy", HDMIPHY_EXYNOS4212 },
 	{ },
 };
 
@@ -50,21 +454,183 @@  static const struct i2c_device_id hdmiphy_id[] = {
 static struct of_device_id hdmiphy_match_types[] = {
 	{
 		.compatible = "samsung,exynos5-hdmiphy",
+		.data	= (void	*)HDMIPHY_EXYNOS4212,
 	}, {
 		/* end node */
 	}
 };
 #endif
 
+static int hdmiphy_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct hdmiphy_context *hdata;
+	struct exynos_drm_hdmi_context *drm_hdmi_ctx;
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
+					GFP_KERNEL);
+	if (!drm_hdmi_ctx) {
+		DRM_ERROR("failed to allocate common hdmi context.\n");
+		return -ENOMEM;
+	}
+
+	hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
+	if (!hdata) {
+		DRM_ERROR("failed to allocate hdmiphy context.\n");
+		return -ENOMEM;
+	}
+
+	if (dev->of_node) {
+		const struct of_device_id *match;
+		match = of_match_node(of_match_ptr(hdmiphy_match_types),
+					dev->of_node);
+		if (match == NULL)
+			return -ENODEV;
+		hdata->type = (enum hdmiphy_type)match->data;
+	} else {
+		hdata->type = (enum hdmiphy_type)id->driver_data;
+	}
+
+	drm_hdmi_ctx->ctx = (void *)hdata;
+	hdata->parent_ctx = (void *)drm_hdmi_ctx;
+	hdata->dev = dev;
+
+	hdata->hdmiphy = devm_clk_get(dev, "hdmiphy");
+	if (IS_ERR_OR_NULL(hdata->hdmiphy)) {
+		DRM_ERROR("failed to get clock 'hdmiphy'\n");
+		return PTR_ERR(hdata->hdmiphy);
+	}
+
+	i2c_set_clientdata(client, hdata);
+
+	/* Attach HDMI-PHY Driver to common hdmi. */
+	exynos_hdmiphy_drv_attach(drm_hdmi_ctx);
+
+	/* register specific callbacks to common hdmi. */
+	exynos_hdmiphy_ops_register(&hdmiphy_ops);
+
+	pm_runtime_enable(dev);
+
+	dev_info(&client->adapter->dev,
+		"attached s5p_hdmiphy into i2c adapter successfully\n");
+
+	return 0;
+}
+
+static int hdmiphy_remove(struct i2c_client *client)
+{
+	dev_info(&client->adapter->dev,
+		"detached s5p_hdmiphy from i2c adapter successfully\n");
+
+	return 0;
+}
+
+static void hdmiphy_poweroff(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct hdmiphy_context *hdata = i2c_get_clientdata(client);
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	if (hdata->type == HDMIPHY_EXYNOS4212)
+		hdmiphy_4412_turn_on(client, 0);
+
+	clk_disable(hdata->hdmiphy);
+}
+
+static void hdmiphy_poweron(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct hdmiphy_context *hdata = i2c_get_clientdata(client);
+
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	clk_enable(hdata->hdmiphy);
+
+	if (hdata->type == HDMIPHY_EXYNOS4212)
+		hdmiphy_4412_turn_on(client, 1);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hdmiphy_suspend(struct device *dev)
+{
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	if (pm_runtime_suspended(dev)) {
+		DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
+		__func__);
+		return 0;
+	}
+
+	hdmiphy_poweroff(dev);
+	return 0;
+}
+
+static int hdmiphy_resume(struct device *dev)
+{
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	if (pm_runtime_suspended(dev)) {
+		/* dpms callback should resume the mixer. */
+		DRM_DEBUG_KMS("%s: already runtime-suspended.\n",
+		__func__);
+		return 0;
+	}
+
+	hdmiphy_poweron(dev);
+	return 0;
+}
+#endif
+
+
+#ifdef CONFIG_PM_RUNTIME
+static int hdmiphy_runtime_suspend(struct device *dev)
+{
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	hdmiphy_poweroff(dev);
+	return 0;
+}
+
+static int hdmiphy_runtime_resume(struct device *dev)
+{
+	DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+	hdmiphy_poweron(dev);
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops hdmiphy_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(hdmiphy_suspend, hdmiphy_resume)
+	SET_RUNTIME_PM_OPS(hdmiphy_runtime_suspend,
+		hdmiphy_runtime_resume, NULL)
+};
+
 struct i2c_driver hdmiphy_driver = {
 	.driver = {
 		.name	= "exynos-hdmiphy",
 		.owner	= THIS_MODULE,
 		.of_match_table = of_match_ptr(hdmiphy_match_types),
+		.pm	= &hdmiphy_pm_ops,
 	},
 	.id_table = hdmiphy_id,
 	.probe		= hdmiphy_probe,
 	.remove		= hdmiphy_remove,
 	.command		= NULL,
 };
+
+extern int exynos_hdmiphy_driver_register(void)
+{
+	return i2c_add_driver(&hdmiphy_driver);
+}
+
+extern void exynos_hdmiphy_driver_unregister(void)
+{
+	i2c_del_driver(&hdmiphy_driver);
+}
+
 EXPORT_SYMBOL(hdmiphy_driver);
diff --git a/drivers/gpu/drm/exynos/regs-hdmiphy.h b/drivers/gpu/drm/exynos/regs-hdmiphy.h
new file mode 100644
index 0000000..8f906c6
--- /dev/null
+++ b/drivers/gpu/drm/exynos/regs-hdmiphy.h
@@ -0,0 +1,61 @@ 
+/*
+ *
+ *  regs-hdmiphy.h
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * HDMI-PHY register header file for Samsung TVOUT driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef SAMSUNG_REGS_HDMIPHY_H
+#define SAMSUNG_REGS_HDMIPHY_H
+
+/*
+ * Register part
+*/
+
+/* HDMI PHY Version Common */
+#define HDMIPHY_MODE_SET_DONE		(0x1f)
+
+/* HDMI PHY Version 4212 */
+#define HDMIPHY_4212_PCG_CON		(0x04)
+#define HDMIPHY_4212_OSC_PAD_CON	(0x0b)
+#define HDMIPHY_4212_PD_CON		(0x1d)
+
+/*
+ * Bit definition part
+ */
+
+/* HDMIPHY_MODE_SET_DONE */
+#define HDMIPHY_MODE_EN			(1 << 7)
+
+/* HDMIPHY_4212_PCG_CON */
+#define HDMIPHY_PCG_RESET_EN		(1 << 3)
+
+/* HDMIPHY_4212_OSC_PAD_CON */
+#define HDMIPHY_OSC_PAD_EN		(3 << 6)
+
+/* HDMIPHY_4212_PD_CON */
+#define HDMIPHY_PDEN			(1 << 7)
+
+#define HDMIPHY_PLL_PD			(1 << 6)
+#define HDMIPHY_CLKSER_PD		(1 << 5)
+#define HDMIPHY_CLKDRV_PD		(1 << 4)
+
+#define HDMIPHY_DRV_PD			(1 << 2)
+#define HDMIPHY_SER_PD			(1 << 1)
+#define HDMIPHY_ICLK_PD			(1 << 0)
+
+#define HDMIPHY_PD_ALL			(HDMIPHY_PLL_PD |\
+					HDMIPHY_CLKSER_PD |\
+					HDMIPHY_CLKDRV_PD|\
+					HDMIPHY_DRV_PD|\
+					HDMIPHY_SER_PD|\
+					HDMIPHY_ICLK_PD)
+
+#endif /* SAMSUNG_REGS_HDMIPHY_H */