hdmi_pll_8960.c 15 KB

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