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, 4096000, 23, 14, 1, false}, 106 {19200000, 24576000, 3, 30, 3, false}, 107 }; 108 109 /** 110 * rl6231_pll_calc - Calcualte PLL M/N/K code. 111 * @freq_in: external clock provided to codec. 112 * @freq_out: target clock which codec works on. 113 * @pll_code: Pointer to structure with M, N, K and bypass flag. 114 * 115 * Calcualte M/N/K code to configure PLL for codec. 116 * 117 * Returns 0 for success or negative error code. 118 */ 119 int rl6231_pll_calc(const unsigned int freq_in, 120 const unsigned int freq_out, struct rl6231_pll_code *pll_code) 121 { 122 int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX; 123 int i, k, red, n_t, pll_out, in_t, out_t; 124 int n = 0, m = 0, m_t = 0; 125 int red_t = abs(freq_out - freq_in); 126 bool bypass = false; 127 128 if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in) 129 return -EINVAL; 130 131 for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { 132 if (freq_in == pll_preset_table[i].pll_in && 133 freq_out == pll_preset_table[i].pll_out) { 134 k = pll_preset_table[i].k; 135 m = pll_preset_table[i].m; 136 n = pll_preset_table[i].n; 137 bypass = pll_preset_table[i].m_bp; 138 pr_debug("Use preset PLL parameter table\n"); 139 goto code_find; 140 } 141 } 142 143 k = 100000000 / freq_out - 2; 144 if (k > RL6231_PLL_K_MAX) 145 k = RL6231_PLL_K_MAX; 146 for (n_t = 0; n_t <= max_n; n_t++) { 147 in_t = freq_in / (k + 2); 148 pll_out = freq_out / (n_t + 2); 149 if (in_t < 0) 150 continue; 151 if (in_t == pll_out) { 152 bypass = true; 153 n = n_t; 154 goto code_find; 155 } 156 red = abs(in_t - pll_out); 157 if (red < red_t) { 158 bypass = true; 159 n = n_t; 160 m = m_t; 161 if (red == 0) 162 goto code_find; 163 red_t = red; 164 } 165 for (m_t = 0; m_t <= max_m; m_t++) { 166 out_t = in_t / (m_t + 2); 167 red = abs(out_t - pll_out); 168 if (red < red_t) { 169 bypass = false; 170 n = n_t; 171 m = m_t; 172 if (red == 0) 173 goto code_find; 174 red_t = red; 175 } 176 } 177 } 178 pr_debug("Only get approximation about PLL\n"); 179 180 code_find: 181 182 pll_code->m_bp = bypass; 183 pll_code->m_code = m; 184 pll_code->n_code = n; 185 pll_code->k_code = k; 186 return 0; 187 } 188 EXPORT_SYMBOL_GPL(rl6231_pll_calc); 189 190 int rl6231_get_clk_info(int sclk, int rate) 191 { 192 int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; 193 194 if (sclk <= 0 || rate <= 0) 195 return -EINVAL; 196 197 rate = rate << 8; 198 for (i = 0; i < ARRAY_SIZE(pd); i++) 199 if (sclk == rate * pd[i]) 200 return i; 201 202 return -EINVAL; 203 } 204 EXPORT_SYMBOL_GPL(rl6231_get_clk_info); 205 206 MODULE_DESCRIPTION("RL6231 class device shared support"); 207 MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); 208 MODULE_LICENSE("GPL v2"); 209