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