Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2016, The Linux Foundation. All rights reserved.
0004  * Copyright (C) 2013 Red Hat
0005  * Author: Rob Clark <robdclark@gmail.com>
0006  */
0007 
0008 #include <linux/clk-provider.h>
0009 #include <linux/delay.h>
0010 
0011 #include "hdmi.h"
0012 
0013 struct hdmi_pll_8960 {
0014     struct platform_device *pdev;
0015     struct clk_hw clk_hw;
0016     void __iomem *mmio;
0017 
0018     unsigned long pixclk;
0019 };
0020 
0021 #define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw)
0022 
0023 /*
0024  * HDMI PLL:
0025  *
0026  * To get the parent clock setup properly, we need to plug in hdmi pll
0027  * configuration into common-clock-framework.
0028  */
0029 
0030 struct pll_rate {
0031     unsigned long rate;
0032     int num_reg;
0033     struct {
0034         u32 val;
0035         u32 reg;
0036     } conf[32];
0037 };
0038 
0039 /* NOTE: keep sorted highest freq to lowest: */
0040 static const struct pll_rate freqtbl[] = {
0041     { 154000000, 14, {
0042         { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0043         { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0044         { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0045         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0046         { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0047         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0048         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0049         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0050         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0051         { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0052         { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0053         { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0054         { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0055         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0056             }
0057     },
0058     /* 1080p60/1080p50 case */
0059     { 148500000, 27, {
0060         { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0061         { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
0062         { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0063         { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0064         { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
0065         { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
0066         { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
0067         { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0068         { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0069         { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0070         { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0071         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0072         { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
0073         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
0074         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
0075         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
0076         { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
0077         { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
0078         { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
0079         { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0080         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0081         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0082         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0083         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0084         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0085         { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
0086         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
0087             }
0088     },
0089     { 108000000, 13, {
0090         { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0091         { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0092         { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0093         { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0094         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0095         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0096         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0097         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0098         { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0099         { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0100         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0101         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0102         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0103             }
0104     },
0105     /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */
0106     { 74250000, 8, {
0107         { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
0108         { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0109         { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0110         { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0111         { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0112         { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0113         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0114         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0115             }
0116     },
0117     { 74176000, 14, {
0118         { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0119         { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0120         { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0121         { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0122         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0123         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0124         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0125         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0126         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0127         { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0128         { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0129         { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0130         { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0131         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0132             }
0133     },
0134     { 65000000, 14, {
0135         { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0136         { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0137         { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0138         { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0139         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0140         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0141         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0142         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0143         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0144         { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0145         { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0146         { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0147         { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0148         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0149             }
0150     },
0151     /* 480p60/480i60 */
0152     { 27030000, 18, {
0153         { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
0154         { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0155         { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
0156         { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0157         { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0158         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0159         { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0160         { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0161         { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0162         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0163         { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0164         { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0165         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0166         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0167         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0168         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0169         { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
0170         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
0171             }
0172     },
0173     /* 576p50/576i50 */
0174     { 27000000, 27, {
0175         { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0176         { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
0177         { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0178         { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0179         { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
0180         { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
0181         { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
0182         { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0183         { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0184         { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0185         { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0186         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0187         { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
0188         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
0189         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
0190         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
0191         { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
0192         { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
0193         { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
0194         { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0195         { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0196         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0197         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0198         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0199         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0200         { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
0201         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
0202             }
0203     },
0204     /* 640x480p60 */
0205     { 25200000, 27, {
0206         { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
0207         { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
0208         { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
0209         { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
0210         { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
0211         { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
0212         { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
0213         { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
0214         { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
0215         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
0216         { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
0217         { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
0218         { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
0219         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
0220         { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
0221         { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
0222         { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
0223         { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
0224         { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
0225         { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
0226         { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
0227         { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
0228         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
0229         { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
0230         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
0231         { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
0232         { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
0233             }
0234     },
0235 };
0236 
0237 static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data)
0238 {
0239     msm_writel(data, pll->mmio + reg);
0240 }
0241 
0242 static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg)
0243 {
0244     return msm_readl(pll->mmio + reg);
0245 }
0246 
0247 static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll)
0248 {
0249     return platform_get_drvdata(pll->pdev);
0250 }
0251 
0252 static int hdmi_pll_enable(struct clk_hw *hw)
0253 {
0254     struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
0255     struct hdmi_phy *phy = pll_get_phy(pll);
0256     int timeout_count, pll_lock_retry = 10;
0257     unsigned int val;
0258 
0259     DBG("");
0260 
0261     /* Assert PLL S/W reset */
0262     pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
0263     pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
0264     pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a);
0265 
0266     /* Wait for a short time before de-asserting
0267      * to allow the hardware to complete its job.
0268      * This much of delay should be fine for hardware
0269      * to assert and de-assert.
0270      */
0271     udelay(10);
0272 
0273     /* De-assert PLL S/W reset */
0274     pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
0275 
0276     val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
0277     val |= HDMI_8960_PHY_REG12_SW_RESET;
0278     /* Assert PHY S/W reset */
0279     hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
0280     val &= ~HDMI_8960_PHY_REG12_SW_RESET;
0281     /*
0282      * Wait for a short time before de-asserting to allow the hardware to
0283      * complete its job. This much of delay should be fine for hardware to
0284      * assert and de-assert.
0285      */
0286     udelay(10);
0287     /* De-assert PHY S/W reset */
0288     hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
0289     hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2,  0x3f);
0290 
0291     val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
0292     val |= HDMI_8960_PHY_REG12_PWRDN_B;
0293     hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
0294     /* Wait 10 us for enabling global power for PHY */
0295     mb();
0296     udelay(10);
0297 
0298     val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
0299     val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
0300     val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL;
0301     pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
0302     hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x80);
0303 
0304     timeout_count = 1000;
0305     while (--pll_lock_retry > 0) {
0306         /* are we there yet? */
0307         val = pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0);
0308         if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK)
0309             break;
0310 
0311         udelay(1);
0312 
0313         if (--timeout_count > 0)
0314             continue;
0315 
0316         /*
0317          * PLL has still not locked.
0318          * Do a software reset and try again
0319          * Assert PLL S/W reset first
0320          */
0321         pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
0322         udelay(10);
0323         pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
0324 
0325         /*
0326          * Wait for a short duration for the PLL calibration
0327          * before checking if the PLL gets locked
0328          */
0329         udelay(350);
0330 
0331         timeout_count = 1000;
0332     }
0333 
0334     return 0;
0335 }
0336 
0337 static void hdmi_pll_disable(struct clk_hw *hw)
0338 {
0339     struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
0340     struct hdmi_phy *phy = pll_get_phy(pll);
0341     unsigned int val;
0342 
0343     DBG("");
0344 
0345     val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
0346     val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
0347     hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
0348 
0349     val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
0350     val |= HDMI_8960_PHY_REG12_SW_RESET;
0351     val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
0352     pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
0353     /* Make sure HDMI PHY/PLL are powered down */
0354     mb();
0355 }
0356 
0357 static const struct pll_rate *find_rate(unsigned long rate)
0358 {
0359     int i;
0360 
0361     for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
0362         if (rate > freqtbl[i].rate)
0363             return &freqtbl[i - 1];
0364 
0365     return &freqtbl[i - 1];
0366 }
0367 
0368 static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
0369                       unsigned long parent_rate)
0370 {
0371     struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
0372 
0373     return pll->pixclk;
0374 }
0375 
0376 static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
0377                 unsigned long *parent_rate)
0378 {
0379     const struct pll_rate *pll_rate = find_rate(rate);
0380 
0381     return pll_rate->rate;
0382 }
0383 
0384 static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
0385                  unsigned long parent_rate)
0386 {
0387     struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
0388     const struct pll_rate *pll_rate = find_rate(rate);
0389     int i;
0390 
0391     DBG("rate=%lu", rate);
0392 
0393     for (i = 0; i < pll_rate->num_reg; i++)
0394         pll_write(pll, pll_rate->conf[i].reg, pll_rate->conf[i].val);
0395 
0396     pll->pixclk = rate;
0397 
0398     return 0;
0399 }
0400 
0401 static const struct clk_ops hdmi_pll_ops = {
0402     .enable = hdmi_pll_enable,
0403     .disable = hdmi_pll_disable,
0404     .recalc_rate = hdmi_pll_recalc_rate,
0405     .round_rate = hdmi_pll_round_rate,
0406     .set_rate = hdmi_pll_set_rate,
0407 };
0408 
0409 static const char * const hdmi_pll_parents[] = {
0410     "pxo",
0411 };
0412 
0413 static struct clk_init_data pll_init = {
0414     .name = "hdmi_pll",
0415     .ops = &hdmi_pll_ops,
0416     .parent_names = hdmi_pll_parents,
0417     .num_parents = ARRAY_SIZE(hdmi_pll_parents),
0418     .flags = CLK_IGNORE_UNUSED,
0419 };
0420 
0421 int msm_hdmi_pll_8960_init(struct platform_device *pdev)
0422 {
0423     struct device *dev = &pdev->dev;
0424     struct hdmi_pll_8960 *pll;
0425     struct clk *clk;
0426     int i;
0427 
0428     /* sanity check: */
0429     for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
0430         if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate))
0431             return -EINVAL;
0432 
0433     pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
0434     if (!pll)
0435         return -ENOMEM;
0436 
0437     pll->mmio = msm_ioremap(pdev, "hdmi_pll");
0438     if (IS_ERR(pll->mmio)) {
0439         DRM_DEV_ERROR(dev, "failed to map pll base\n");
0440         return -ENOMEM;
0441     }
0442 
0443     pll->pdev = pdev;
0444     pll->clk_hw.init = &pll_init;
0445 
0446     clk = devm_clk_register(dev, &pll->clk_hw);
0447     if (IS_ERR(clk)) {
0448         DRM_DEV_ERROR(dev, "failed to register pll clock\n");
0449         return -EINVAL;
0450     }
0451 
0452     return 0;
0453 }