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