1/* 2 * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14#include "dsi_pll.h" 15 16static int dsi_pll_enable(struct msm_dsi_pll *pll) 17{ 18 int i, ret = 0; 19 20 /* 21 * Certain PLLs do not allow VCO rate update when it is on. 22 * Keep track of their status to turn on/off after set rate success. 23 */ 24 if (unlikely(pll->pll_on)) 25 return 0; 26 27 /* Try all enable sequences until one succeeds */ 28 for (i = 0; i < pll->en_seq_cnt; i++) { 29 ret = pll->enable_seqs[i](pll); 30 DBG("DSI PLL %s after sequence #%d", 31 ret ? "unlocked" : "locked", i + 1); 32 if (!ret) 33 break; 34 } 35 36 if (ret) { 37 DRM_ERROR("DSI PLL failed to lock\n"); 38 return ret; 39 } 40 41 pll->pll_on = true; 42 43 return 0; 44} 45 46static void dsi_pll_disable(struct msm_dsi_pll *pll) 47{ 48 if (unlikely(!pll->pll_on)) 49 return; 50 51 pll->disable_seq(pll); 52 53 pll->pll_on = false; 54} 55 56/* 57 * DSI PLL Helper functions 58 */ 59long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw, 60 unsigned long rate, unsigned long *parent_rate) 61{ 62 struct msm_dsi_pll *pll = hw_clk_to_pll(hw); 63 64 if (rate < pll->min_rate) 65 return pll->min_rate; 66 else if (rate > pll->max_rate) 67 return pll->max_rate; 68 else 69 return rate; 70} 71 72int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw) 73{ 74 struct msm_dsi_pll *pll = hw_clk_to_pll(hw); 75 76 return dsi_pll_enable(pll); 77} 78 79void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw) 80{ 81 struct msm_dsi_pll *pll = hw_clk_to_pll(hw); 82 83 dsi_pll_disable(pll); 84} 85 86void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev, 87 struct clk **clks, u32 num_clks) 88{ 89 of_clk_del_provider(pdev->dev.of_node); 90 91 if (!num_clks || !clks) 92 return; 93 94 do { 95 clk_unregister(clks[--num_clks]); 96 clks[num_clks] = NULL; 97 } while (num_clks); 98} 99 100/* 101 * DSI PLL API 102 */ 103int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, 104 struct clk **byte_clk_provider, struct clk **pixel_clk_provider) 105{ 106 if (pll->get_provider) 107 return pll->get_provider(pll, 108 byte_clk_provider, 109 pixel_clk_provider); 110 111 return -EINVAL; 112} 113 114void msm_dsi_pll_destroy(struct msm_dsi_pll *pll) 115{ 116 if (pll->destroy) 117 pll->destroy(pll); 118} 119 120void msm_dsi_pll_save_state(struct msm_dsi_pll *pll) 121{ 122 if (pll->save_state) { 123 pll->save_state(pll); 124 pll->state_saved = true; 125 } 126} 127 128int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) 129{ 130 int ret; 131 132 if (pll->restore_state && pll->state_saved) { 133 ret = pll->restore_state(pll); 134 if (ret) 135 return ret; 136 137 pll->state_saved = false; 138 } 139 140 return 0; 141} 142 143struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, 144 enum msm_dsi_phy_type type, int id) 145{ 146 struct device *dev = &pdev->dev; 147 struct msm_dsi_pll *pll; 148 149 switch (type) { 150 case MSM_DSI_PHY_28NM_HPM: 151 case MSM_DSI_PHY_28NM_LP: 152 pll = msm_dsi_pll_28nm_init(pdev, type, id); 153 break; 154 default: 155 pll = ERR_PTR(-ENXIO); 156 break; 157 } 158 159 if (IS_ERR(pll)) { 160 dev_err(dev, "%s: failed to init DSI PLL\n", __func__); 161 return NULL; 162 } 163 164 pll->type = type; 165 166 DBG("DSI:%d PLL registered", id); 167 168 return pll; 169} 170 171