linux/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
   3 * Copyright (C) 2013 Red Hat
   4 * Author: Rob Clark <robdclark@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License version 2 as published by
   8 * the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License along with
  16 * this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include <linux/clk-provider.h>
  20#include "hdmi.h"
  21
  22struct hdmi_pll_8960 {
  23        struct platform_device *pdev;
  24        struct clk_hw clk_hw;
  25        void __iomem *mmio;
  26
  27        unsigned long pixclk;
  28};
  29
  30#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw)
  31
  32/*
  33 * HDMI PLL:
  34 *
  35 * To get the parent clock setup properly, we need to plug in hdmi pll
  36 * configuration into common-clock-framework.
  37 */
  38
  39struct pll_rate {
  40        unsigned long rate;
  41        int num_reg;
  42        struct {
  43                u32 val;
  44                u32 reg;
  45        } conf[32];
  46};
  47
  48/* NOTE: keep sorted highest freq to lowest: */
  49static const struct pll_rate freqtbl[] = {
  50        { 154000000, 14, {
  51                { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
  52                { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
  53                { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
  54                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
  55                { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
  56                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
  57                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
  58                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
  59                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
  60                { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
  61                { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
  62                { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
  63                { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
  64                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
  65                        }
  66        },
  67        /* 1080p60/1080p50 case */
  68        { 148500000, 27, {
  69                { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
  70                { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
  71                { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
  72                { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
  73                { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
  74                { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
  75                { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
  76                { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
  77                { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
  78                { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
  79                { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
  80                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
  81                { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
  82                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
  83                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
  84                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
  85                { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
  86                { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
  87                { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
  88                { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
  89                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
  90                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
  91                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
  92                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
  93                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
  94                { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
  95                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
  96                        }
  97        },
  98        { 108000000, 13, {
  99                { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 100                { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 101                { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 102                { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 103                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 104                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 105                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
 106                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
 107                { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 108                { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
 109                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
 110                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
 111                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
 112                        }
 113        },
 114        /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */
 115        { 74250000, 8, {
 116                { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
 117                { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 118                { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 119                { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 120                { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 121                { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 122                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 123                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 124                        }
 125        },
 126        { 74176000, 14, {
 127                { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 128                { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 129                { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 130                { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 131                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 132                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 133                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
 134                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
 135                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
 136                { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 137                { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
 138                { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
 139                { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
 140                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
 141                        }
 142        },
 143        { 65000000, 14, {
 144                { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 145                { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 146                { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 147                { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 148                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 149                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 150                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
 151                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
 152                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
 153                { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 154                { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
 155                { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
 156                { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
 157                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
 158                        }
 159        },
 160        /* 480p60/480i60 */
 161        { 27030000, 18, {
 162                { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
 163                { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 164                { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
 165                { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 166                { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 167                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 168                { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
 169                { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
 170                { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
 171                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
 172                { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 173                { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 174                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 175                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
 176                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
 177                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
 178                { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
 179                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
 180                        }
 181        },
 182        /* 576p50/576i50 */
 183        { 27000000, 27, {
 184                { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 185                { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
 186                { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 187                { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 188                { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
 189                { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
 190                { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
 191                { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 192                { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
 193                { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
 194                { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
 195                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
 196                { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
 197                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
 198                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
 199                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
 200                { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
 201                { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
 202                { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
 203                { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 204                { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 205                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 206                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
 207                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
 208                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
 209                { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
 210                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
 211                        }
 212        },
 213        /* 640x480p60 */
 214        { 25200000, 27, {
 215                { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
 216                { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
 217                { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
 218                { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
 219                { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
 220                { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
 221                { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
 222                { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
 223                { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
 224                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
 225                { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
 226                { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
 227                { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
 228                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
 229                { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
 230                { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
 231                { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
 232                { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
 233                { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
 234                { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
 235                { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
 236                { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
 237                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
 238                { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
 239                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
 240                { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
 241                { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
 242                        }
 243        },
 244};
 245
 246static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data)
 247{
 248        msm_writel(data, pll->mmio + reg);
 249}
 250
 251static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg)
 252{
 253        return msm_readl(pll->mmio + reg);
 254}
 255
 256static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll)
 257{
 258        return platform_get_drvdata(pll->pdev);
 259}
 260
 261static int hdmi_pll_enable(struct clk_hw *hw)
 262{
 263        struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
 264        struct hdmi_phy *phy = pll_get_phy(pll);
 265        int timeout_count, pll_lock_retry = 10;
 266        unsigned int val;
 267
 268        DBG("");
 269
 270        /* Assert PLL S/W reset */
 271        pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
 272        pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
 273        pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a);
 274
 275        /* Wait for a short time before de-asserting
 276         * to allow the hardware to complete its job.
 277         * This much of delay should be fine for hardware
 278         * to assert and de-assert.
 279         */
 280        udelay(10);
 281
 282        /* De-assert PLL S/W reset */
 283        pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
 284
 285        val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
 286        val |= HDMI_8960_PHY_REG12_SW_RESET;
 287        /* Assert PHY S/W reset */
 288        hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
 289        val &= ~HDMI_8960_PHY_REG12_SW_RESET;
 290        /*
 291         * Wait for a short time before de-asserting to allow the hardware to
 292         * complete its job. This much of delay should be fine for hardware to
 293         * assert and de-assert.
 294         */
 295        udelay(10);
 296        /* De-assert PHY S/W reset */
 297        hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
 298        hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2,  0x3f);
 299
 300        val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
 301        val |= HDMI_8960_PHY_REG12_PWRDN_B;
 302        hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
 303        /* Wait 10 us for enabling global power for PHY */
 304        mb();
 305        udelay(10);
 306
 307        val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
 308        val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
 309        val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL;
 310        pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
 311        hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x80);
 312
 313        timeout_count = 1000;
 314        while (--pll_lock_retry > 0) {
 315                /* are we there yet? */
 316                val = pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0);
 317                if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK)
 318                        break;
 319
 320                udelay(1);
 321
 322                if (--timeout_count > 0)
 323                        continue;
 324
 325                /*
 326                 * PLL has still not locked.
 327                 * Do a software reset and try again
 328                 * Assert PLL S/W reset first
 329                 */
 330                pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
 331                udelay(10);
 332                pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
 333
 334                /*
 335                 * Wait for a short duration for the PLL calibration
 336                 * before checking if the PLL gets locked
 337                 */
 338                udelay(350);
 339
 340                timeout_count = 1000;
 341        }
 342
 343        return 0;
 344}
 345
 346static void hdmi_pll_disable(struct clk_hw *hw)
 347{
 348        struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
 349        struct hdmi_phy *phy = pll_get_phy(pll);
 350        unsigned int val;
 351
 352        DBG("");
 353
 354        val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
 355        val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
 356        hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
 357
 358        val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
 359        val |= HDMI_8960_PHY_REG12_SW_RESET;
 360        val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
 361        pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
 362        /* Make sure HDMI PHY/PLL are powered down */
 363        mb();
 364}
 365
 366static const struct pll_rate *find_rate(unsigned long rate)
 367{
 368        int i;
 369
 370        for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
 371                if (rate > freqtbl[i].rate)
 372                        return &freqtbl[i - 1];
 373
 374        return &freqtbl[i - 1];
 375}
 376
 377static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
 378                                          unsigned long parent_rate)
 379{
 380        struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
 381
 382        return pll->pixclk;
 383}
 384
 385static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 386                                unsigned long *parent_rate)
 387{
 388        const struct pll_rate *pll_rate = find_rate(rate);
 389
 390        return pll_rate->rate;
 391}
 392
 393static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 394                             unsigned long parent_rate)
 395{
 396        struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
 397        const struct pll_rate *pll_rate = find_rate(rate);
 398        int i;
 399
 400        DBG("rate=%lu", rate);
 401
 402        for (i = 0; i < pll_rate->num_reg; i++)
 403                pll_write(pll, pll_rate->conf[i].reg, pll_rate->conf[i].val);
 404
 405        pll->pixclk = rate;
 406
 407        return 0;
 408}
 409
 410static const struct clk_ops hdmi_pll_ops = {
 411        .enable = hdmi_pll_enable,
 412        .disable = hdmi_pll_disable,
 413        .recalc_rate = hdmi_pll_recalc_rate,
 414        .round_rate = hdmi_pll_round_rate,
 415        .set_rate = hdmi_pll_set_rate,
 416};
 417
 418static const char * const hdmi_pll_parents[] = {
 419        "pxo",
 420};
 421
 422static struct clk_init_data pll_init = {
 423        .name = "hdmi_pll",
 424        .ops = &hdmi_pll_ops,
 425        .parent_names = hdmi_pll_parents,
 426        .num_parents = ARRAY_SIZE(hdmi_pll_parents),
 427        .flags = CLK_IGNORE_UNUSED,
 428};
 429
 430int msm_hdmi_pll_8960_init(struct platform_device *pdev)
 431{
 432        struct device *dev = &pdev->dev;
 433        struct hdmi_pll_8960 *pll;
 434        struct clk *clk;
 435        int i;
 436
 437        /* sanity check: */
 438        for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
 439                if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate))
 440                        return -EINVAL;
 441
 442        pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
 443        if (!pll)
 444                return -ENOMEM;
 445
 446        pll->mmio = msm_ioremap(pdev, "hdmi_pll", "HDMI_PLL");
 447        if (IS_ERR(pll->mmio)) {
 448                dev_err(dev, "failed to map pll base\n");
 449                return -ENOMEM;
 450        }
 451
 452        pll->pdev = pdev;
 453        pll->clk_hw.init = &pll_init;
 454
 455        clk = devm_clk_register(dev, &pll->clk_hw);
 456        if (IS_ERR(clk)) {
 457                dev_err(dev, "failed to register pll clock\n");
 458                return -EINVAL;
 459        }
 460
 461        return 0;
 462}
 463