1/* 2 * Copyright (C) 2014 Red Hat 3 * Author: Rob Clark <robdclark@gmail.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published by 7 * the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18#include <linux/clk.h> 19#include <linux/clk-provider.h> 20 21#include "mdp4_kms.h" 22 23struct mdp4_lvds_pll { 24 struct clk_hw pll_hw; 25 struct drm_device *dev; 26 unsigned long pixclk; 27}; 28#define to_mdp4_lvds_pll(x) container_of(x, struct mdp4_lvds_pll, pll_hw) 29 30static struct mdp4_kms *get_kms(struct mdp4_lvds_pll *lvds_pll) 31{ 32 struct msm_drm_private *priv = lvds_pll->dev->dev_private; 33 return to_mdp4_kms(to_mdp_kms(priv->kms)); 34} 35 36struct pll_rate { 37 unsigned long rate; 38 struct { 39 uint32_t val; 40 uint32_t reg; 41 } conf[32]; 42}; 43 44/* NOTE: keep sorted highest freq to lowest: */ 45static const struct pll_rate freqtbl[] = { 46 { 72000000, { 47 { 0x8f, REG_MDP4_LVDS_PHY_PLL_CTRL_1 }, 48 { 0x30, REG_MDP4_LVDS_PHY_PLL_CTRL_2 }, 49 { 0xc6, REG_MDP4_LVDS_PHY_PLL_CTRL_3 }, 50 { 0x10, REG_MDP4_LVDS_PHY_PLL_CTRL_5 }, 51 { 0x07, REG_MDP4_LVDS_PHY_PLL_CTRL_6 }, 52 { 0x62, REG_MDP4_LVDS_PHY_PLL_CTRL_7 }, 53 { 0x41, REG_MDP4_LVDS_PHY_PLL_CTRL_8 }, 54 { 0x0d, REG_MDP4_LVDS_PHY_PLL_CTRL_9 }, 55 { 0, 0 } } 56 }, 57}; 58 59static const struct pll_rate *find_rate(unsigned long rate) 60{ 61 int i; 62 for (i = 1; i < ARRAY_SIZE(freqtbl); i++) 63 if (rate > freqtbl[i].rate) 64 return &freqtbl[i-1]; 65 return &freqtbl[i-1]; 66} 67 68static int mpd4_lvds_pll_enable(struct clk_hw *hw) 69{ 70 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); 71 struct mdp4_kms *mdp4_kms = get_kms(lvds_pll); 72 const struct pll_rate *pll_rate = find_rate(lvds_pll->pixclk); 73 int i; 74 75 DBG("pixclk=%lu (%lu)", lvds_pll->pixclk, pll_rate->rate); 76 77 if (WARN_ON(!pll_rate)) 78 return -EINVAL; 79 80 mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_PHY_RESET, 0x33); 81 82 for (i = 0; pll_rate->conf[i].reg; i++) 83 mdp4_write(mdp4_kms, pll_rate->conf[i].reg, pll_rate->conf[i].val); 84 85 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x01); 86 87 /* Wait until LVDS PLL is locked and ready */ 88 while (!mdp4_read(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_LOCKED)) 89 cpu_relax(); 90 91 return 0; 92} 93 94static void mpd4_lvds_pll_disable(struct clk_hw *hw) 95{ 96 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); 97 struct mdp4_kms *mdp4_kms = get_kms(lvds_pll); 98 99 DBG(""); 100 101 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, 0x0); 102 mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x0); 103} 104 105static unsigned long mpd4_lvds_pll_recalc_rate(struct clk_hw *hw, 106 unsigned long parent_rate) 107{ 108 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); 109 return lvds_pll->pixclk; 110} 111 112static long mpd4_lvds_pll_round_rate(struct clk_hw *hw, unsigned long rate, 113 unsigned long *parent_rate) 114{ 115 const struct pll_rate *pll_rate = find_rate(rate); 116 return pll_rate->rate; 117} 118 119static int mpd4_lvds_pll_set_rate(struct clk_hw *hw, unsigned long rate, 120 unsigned long parent_rate) 121{ 122 struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); 123 lvds_pll->pixclk = rate; 124 return 0; 125} 126 127 128static const struct clk_ops mpd4_lvds_pll_ops = { 129 .enable = mpd4_lvds_pll_enable, 130 .disable = mpd4_lvds_pll_disable, 131 .recalc_rate = mpd4_lvds_pll_recalc_rate, 132 .round_rate = mpd4_lvds_pll_round_rate, 133 .set_rate = mpd4_lvds_pll_set_rate, 134}; 135 136static const char *mpd4_lvds_pll_parents[] = { 137 "pxo", 138}; 139 140static struct clk_init_data pll_init = { 141 .name = "mpd4_lvds_pll", 142 .ops = &mpd4_lvds_pll_ops, 143 .parent_names = mpd4_lvds_pll_parents, 144 .num_parents = ARRAY_SIZE(mpd4_lvds_pll_parents), 145}; 146 147struct clk *mpd4_lvds_pll_init(struct drm_device *dev) 148{ 149 struct mdp4_lvds_pll *lvds_pll; 150 struct clk *clk; 151 int ret; 152 153 lvds_pll = devm_kzalloc(dev->dev, sizeof(*lvds_pll), GFP_KERNEL); 154 if (!lvds_pll) { 155 ret = -ENOMEM; 156 goto fail; 157 } 158 159 lvds_pll->dev = dev; 160 161 lvds_pll->pll_hw.init = &pll_init; 162 clk = devm_clk_register(dev->dev, &lvds_pll->pll_hw); 163 if (IS_ERR(clk)) { 164 ret = PTR_ERR(clk); 165 goto fail; 166 } 167 168 return clk; 169 170fail: 171 return ERR_PTR(ret); 172} 173