1 /* 2 * rl6231.c - RL6231 class device shared support 3 * 4 * Copyright 2014 Realtek Semiconductor Corp. 5 * 6 * Author: Oder Chiou <oder_chiou@realtek.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/regmap.h> 15 16 #include "rl6231.h" 17 18 /** 19 * rl6231_get_pre_div - Return the value of pre divider. 20 * 21 * @map: map for setting. 22 * @reg: register. 23 * @sft: shift. 24 * 25 * Return the value of pre divider from given register value. 26 * Return negative error code for unexpected register value. 27 */ 28 int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft) 29 { 30 int pd, val; 31 32 regmap_read(map, reg, &val); 33 34 val = (val >> sft) & 0x7; 35 36 switch (val) { 37 case 0: 38 case 1: 39 case 2: 40 case 3: 41 pd = val + 1; 42 break; 43 case 4: 44 pd = 6; 45 break; 46 case 5: 47 pd = 8; 48 break; 49 case 6: 50 pd = 12; 51 break; 52 case 7: 53 pd = 16; 54 break; 55 default: 56 pd = -EINVAL; 57 break; 58 } 59 60 return pd; 61 } 62 EXPORT_SYMBOL_GPL(rl6231_get_pre_div); 63 64 /** 65 * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic. 66 * 67 * @rate: base clock rate. 68 * 69 * Choose divider parameter that gives the highest possible DMIC frequency in 70 * 1MHz - 3MHz range. 71 */ 72 int rl6231_calc_dmic_clk(int rate) 73 { 74 int div[] = {2, 3, 4, 6, 8, 12}; 75 int i; 76 77 if (rate < 1000000 * div[0]) { 78 pr_warn("Base clock rate %d is too low\n", rate); 79 return -EINVAL; 80 } 81 82 for (i = 0; i < ARRAY_SIZE(div); i++) { 83 if ((div[i] % 3) == 0) 84 continue; 85 /* find divider that gives DMIC frequency below 3.072MHz */ 86 if (3072000 * div[i] >= rate) 87 return i; 88 } 89 90 pr_warn("Base clock rate %d is too high\n", rate); 91 return -EINVAL; 92 } 93 EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk); 94 95 struct pll_calc_map { 96 unsigned int pll_in; 97 unsigned int pll_out; 98 int k; 99 int n; 100 int m; 101 bool m_bp; 102 }; 103 104 static const struct pll_calc_map pll_preset_table[] = { 105 {19200000, 24576000, 3, 30, 3, false}, 106 }; 107 108 /** 109 * rl6231_pll_calc - Calcualte PLL M/N/K code. 110 * @freq_in: external clock provided to codec. 111 * @freq_out: target clock which codec works on. 112 * @pll_code: Pointer to structure with M, N, K and bypass flag. 113 * 114 * Calcualte M/N/K code to configure PLL for codec. 115 * 116 * Returns 0 for success or negative error code. 117 */ 118 int rl6231_pll_calc(const unsigned int freq_in, 119 const unsigned int freq_out, struct rl6231_pll_code *pll_code) 120 { 121 int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX; 122 int i, k, red, n_t, pll_out, in_t, out_t; 123 int n = 0, m = 0, m_t = 0; 124 int red_t = abs(freq_out - freq_in); 125 bool bypass = false; 126 127 if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in) 128 return -EINVAL; 129 130 for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { 131 if (freq_in == pll_preset_table[i].pll_in && 132 freq_out == pll_preset_table[i].pll_out) { 133 k = pll_preset_table[i].k; 134 m = pll_preset_table[i].m; 135 n = pll_preset_table[i].n; 136 bypass = pll_preset_table[i].m_bp; 137 pr_debug("Use preset PLL parameter table\n"); 138 goto code_find; 139 } 140 } 141 142 k = 100000000 / freq_out - 2; 143 if (k > RL6231_PLL_K_MAX) 144 k = RL6231_PLL_K_MAX; 145 for (n_t = 0; n_t <= max_n; n_t++) { 146 in_t = freq_in / (k + 2); 147 pll_out = freq_out / (n_t + 2); 148 if (in_t < 0) 149 continue; 150 if (in_t == pll_out) { 151 bypass = true; 152 n = n_t; 153 goto code_find; 154 } 155 red = abs(in_t - pll_out); 156 if (red < red_t) { 157 bypass = true; 158 n = n_t; 159 m = m_t; 160 if (red == 0) 161 goto code_find; 162 red_t = red; 163 } 164 for (m_t = 0; m_t <= max_m; m_t++) { 165 out_t = in_t / (m_t + 2); 166 red = abs(out_t - pll_out); 167 if (red < red_t) { 168 bypass = false; 169 n = n_t; 170 m = m_t; 171 if (red == 0) 172 goto code_find; 173 red_t = red; 174 } 175 } 176 } 177 pr_debug("Only get approximation about PLL\n"); 178 179 code_find: 180 181 pll_code->m_bp = bypass; 182 pll_code->m_code = m; 183 pll_code->n_code = n; 184 pll_code->k_code = k; 185 return 0; 186 } 187 EXPORT_SYMBOL_GPL(rl6231_pll_calc); 188 189 int rl6231_get_clk_info(int sclk, int rate) 190 { 191 int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; 192 193 if (sclk <= 0 || rate <= 0) 194 return -EINVAL; 195 196 rate = rate << 8; 197 for (i = 0; i < ARRAY_SIZE(pd); i++) 198 if (sclk == rate * pd[i]) 199 return i; 200 201 return -EINVAL; 202 } 203 EXPORT_SYMBOL_GPL(rl6231_get_clk_info); 204 205 MODULE_DESCRIPTION("RL6231 class device shared support"); 206 MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); 207 MODULE_LICENSE("GPL v2"); 208