drm/msm/hdmi: Manage HDMI PLL through PHY driver
authorArchit Taneja <architt@codeaurora.org>
Thu, 25 Feb 2016 05:52:39 +0000 (11:22 +0530)
committerRob Clark <robdclark@gmail.com>
Mon, 29 Feb 2016 14:48:30 +0000 (09:48 -0500)
Add a helper to initialize PLL in the PHY driver. HDMI PLLs are going to
have their own mmio base different from that of PHY.

For the clock code in hdmi_phy_8960.c, some changes were needed for it to
work with the updated register offsets. Create a copy of the updated clock
code in hdmi_pll_8960.c, instead of rewriting it in hdmi_phy_8960.c
itself. This removes the need to place CONFIG_COMMON_CLOCK checks all
around, makes the code more legible, and also removes some old checkpatch
warnings with the original code.

The older hdmi pll clock ops in hdmi_phy_8960.c will be removed later. The
driver will use these until the HDMI PHY/PLL register offsets aren't
considered as separate domains (i.e. their offsets start from 0).

Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/hdmi/hdmi.h
drivers/gpu/drm/msm/hdmi/hdmi_phy.c
drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c [new file with mode: 0644]

index 6ad0f7e625e7cdf73052390da1dc3e37dc83e35b..0c5c9c2c2049cc0f91064acb462160483dd592a1 100644 (file)
@@ -53,6 +53,7 @@ msm-y := \
 
 msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
 msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
+msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
 
 msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
                        mdp/mdp4/mdp4_dsi_encoder.o \
index 3404235bb24dc4381f7b650c0dbfc816f21064ac..1beace8bcb4b389e2453107ac2cd2ae8647093a5 100644 (file)
@@ -198,6 +198,15 @@ void hdmi_phy_powerdown(struct hdmi_phy *phy);
 void __init hdmi_phy_driver_register(void);
 void __exit hdmi_phy_driver_unregister(void);
 
+#ifdef CONFIG_COMMON_CLK
+int hdmi_pll_8960_init(struct platform_device *pdev);
+#else
+int hdmi_pll_8960_init(struct platform_device *pdev);
+{
+       return -ENODEV;
+}
+#endif
+
 /*
  * audio:
  */
index de3f0f504ede5687118c27d22d5b6b0cc0d70852..9ad4573b6fd5d5f8d6ffc9f8dd24f6a2204d6b2d 100644 (file)
@@ -118,6 +118,28 @@ void hdmi_phy_powerdown(struct hdmi_phy *phy)
        phy->cfg->powerdown(phy);
 }
 
+static int hdmi_phy_pll_init(struct platform_device *pdev,
+                            enum hdmi_phy_type type)
+{
+       int ret;
+
+       switch (type) {
+       case MSM_HDMI_PHY_8960:
+               ret = hdmi_pll_8960_init(pdev);
+               break;
+       /*
+        * we don't have PLL support for these, don't report an error for now
+        */
+       case MSM_HDMI_PHY_8x60:
+       case MSM_HDMI_PHY_8x74:
+       default:
+               ret = 0;
+               break;
+       }
+
+       return ret;
+}
+
 static int hdmi_phy_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -146,6 +168,19 @@ static int hdmi_phy_probe(struct platform_device *pdev)
 
        pm_runtime_enable(&pdev->dev);
 
+       ret = hdmi_phy_resource_enable(phy);
+       if (ret)
+               return ret;
+
+       ret = hdmi_phy_pll_init(pdev, phy->cfg->type);
+       if (ret) {
+               dev_err(dev, "couldn't init PLL\n");
+               hdmi_phy_resource_disable(phy);
+               return ret;
+       }
+
+       hdmi_phy_resource_disable(phy);
+
        platform_set_drvdata(pdev, phy);
 
        return 0;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c
new file mode 100644 (file)
index 0000000..35830a9
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk-provider.h>
+#include "hdmi.h"
+
+struct hdmi_pll_8960 {
+       struct platform_device *pdev;
+       struct clk_hw clk_hw;
+       void __iomem *mmio;
+
+       unsigned long pixclk;
+};
+
+#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw)
+
+/*
+ * HDMI PLL:
+ *
+ * To get the parent clock setup properly, we need to plug in hdmi pll
+ * configuration into common-clock-framework.
+ */
+
+struct pll_rate {
+       unsigned long rate;
+       int num_reg;
+       struct {
+               u32 val;
+               u32 reg;
+       } conf[32];
+};
+
+/* NOTE: keep sorted highest freq to lowest: */
+static const struct pll_rate freqtbl[] = {
+       { 154000000, 14, {
+               { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+                       }
+       },
+       /* 1080p60/1080p50 case */
+       { 148500000, 27, {
+               { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+               { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
+               { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
+               { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
+               { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+               { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
+               { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
+               { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
+               { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
+               { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
+                       }
+       },
+       { 108000000, 13, {
+               { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+                       }
+       },
+       /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */
+       { 74250000, 8, {
+               { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
+               { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+                       }
+       },
+       { 74176000, 14, {
+               { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+                       }
+       },
+       { 65000000, 14, {
+               { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+                       }
+       },
+       /* 480p60/480i60 */
+       { 27030000, 18, {
+               { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
+               { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+               { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+               { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
+                       }
+       },
+       /* 576p50/576i50 */
+       { 27000000, 27, {
+               { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+               { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
+               { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
+               { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
+               { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+               { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
+               { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
+               { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
+               { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
+               { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
+                       }
+       },
+       /* 640x480p60 */
+       { 25200000, 27, {
+               { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
+               { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+               { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+               { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+               { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
+               { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
+               { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
+               { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
+               { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
+               { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
+               { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
+               { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
+               { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
+               { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
+               { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
+               { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
+               { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
+               { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
+               { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
+               { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
+               { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
+               { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
+                       }
+       },
+};
+
+static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data)
+{
+       msm_writel(data, pll->mmio + reg);
+}
+
+static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg)
+{
+       return msm_readl(pll->mmio + reg);
+}
+
+static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll)
+{
+       return platform_get_drvdata(pll->pdev);
+}
+
+static int hdmi_pll_enable(struct clk_hw *hw)
+{
+       struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
+       struct hdmi_phy *phy = pll_get_phy(pll);
+       int timeout_count, pll_lock_retry = 10;
+       unsigned int val;
+
+       DBG("");
+
+       /* Assert PLL S/W reset */
+       pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
+       pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
+       pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a);
+
+       /* Wait for a short time before de-asserting
+        * to allow the hardware to complete its job.
+        * This much of delay should be fine for hardware
+        * to assert and de-assert.
+        */
+       udelay(10);
+
+       /* De-assert PLL S/W reset */
+       pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
+
+       val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
+       val |= HDMI_8960_PHY_REG12_SW_RESET;
+       /* Assert PHY S/W reset */
+       hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+       val &= ~HDMI_8960_PHY_REG12_SW_RESET;
+       /*
+        * Wait for a short time before de-asserting to allow the hardware to
+        * complete its job. This much of delay should be fine for hardware to
+        * assert and de-assert.
+        */
+       udelay(10);
+       /* De-assert PHY S/W reset */
+       hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+       hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2,  0x3f);
+
+       val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
+       val |= HDMI_8960_PHY_REG12_PWRDN_B;
+       hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+       /* Wait 10 us for enabling global power for PHY */
+       mb();
+       udelay(10);
+
+       val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
+       val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
+       val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL;
+       pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
+       hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x80);
+
+       timeout_count = 1000;
+       while (--pll_lock_retry > 0) {
+               /* are we there yet? */
+               val = pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0);
+               if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK)
+                       break;
+
+               udelay(1);
+
+               if (--timeout_count > 0)
+                       continue;
+
+               /*
+                * PLL has still not locked.
+                * Do a software reset and try again
+                * Assert PLL S/W reset first
+                */
+               pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
+               udelay(10);
+               pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
+
+               /*
+                * Wait for a short duration for the PLL calibration
+                * before checking if the PLL gets locked
+                */
+               udelay(350);
+
+               timeout_count = 1000;
+       }
+
+       return 0;
+}
+
+static void hdmi_pll_disable(struct clk_hw *hw)
+{
+       struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
+       struct hdmi_phy *phy = pll_get_phy(pll);
+       unsigned int val;
+
+       DBG("");
+
+       val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
+       val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
+       hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+
+       val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
+       val |= HDMI_8960_PHY_REG12_SW_RESET;
+       val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
+       pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
+       /* Make sure HDMI PHY/PLL are powered down */
+       mb();
+}
+
+static const struct pll_rate *find_rate(unsigned long rate)
+{
+       int i;
+
+       for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
+               if (rate > freqtbl[i].rate)
+                       return &freqtbl[i - 1];
+
+       return &freqtbl[i - 1];
+}
+
+static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
+                                         unsigned long parent_rate)
+{
+       struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
+
+       return pll->pixclk;
+}
+
+static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long *parent_rate)
+{
+       const struct pll_rate *pll_rate = find_rate(rate);
+
+       return pll_rate->rate;
+}
+
+static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+                            unsigned long parent_rate)
+{
+       struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
+       const struct pll_rate *pll_rate = find_rate(rate);
+       int i;
+
+       DBG("rate=%lu", rate);
+
+       for (i = 0; i < pll_rate->num_reg; i++)
+               pll_write(pll, pll_rate->conf[i].reg, pll_rate->conf[i].val);
+
+       pll->pixclk = rate;
+
+       return 0;
+}
+
+static const struct clk_ops hdmi_pll_ops = {
+       .enable = hdmi_pll_enable,
+       .disable = hdmi_pll_disable,
+       .recalc_rate = hdmi_pll_recalc_rate,
+       .round_rate = hdmi_pll_round_rate,
+       .set_rate = hdmi_pll_set_rate,
+};
+
+static const char * const hdmi_pll_parents[] = {
+       "pxo",
+};
+
+static struct clk_init_data pll_init = {
+       .name = "hdmi_pll",
+       .ops = &hdmi_pll_ops,
+       .parent_names = hdmi_pll_parents,
+       .num_parents = ARRAY_SIZE(hdmi_pll_parents),
+};
+
+int hdmi_pll_8960_init(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct hdmi_pll_8960 *pll;
+       struct clk *clk;
+       int i;
+
+       /* sanity check: */
+       for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
+               if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate))
+                       return -EINVAL;
+
+       pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+       if (!pll)
+               return -ENOMEM;
+
+       pll->mmio = msm_ioremap(pdev, "hdmi_pll", "HDMI_PLL");
+       if (IS_ERR(pll->mmio)) {
+               dev_err(dev, "failed to map pll base\n");
+               return -ENOMEM;
+       }
+
+       pll->pdev = pdev;
+       pll->clk_hw.init = &pll_init;
+
+       clk = devm_clk_register(dev, &pll->clk_hw);
+       if (IS_ERR(clk)) {
+               dev_err(dev, "failed to register pll clock\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}