Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * rl6231.c - RL6231 class device shared support
0004  *
0005  * Copyright 2014 Realtek Semiconductor Corp.
0006  *
0007  * Author: Oder Chiou <oder_chiou@realtek.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/regmap.h>
0012 
0013 #include <linux/gcd.h>
0014 #include "rl6231.h"
0015 
0016 /**
0017  * rl6231_get_pre_div - Return the value of pre divider.
0018  *
0019  * @map: map for setting.
0020  * @reg: register.
0021  * @sft: shift.
0022  *
0023  * Return the value of pre divider from given register value.
0024  * Return negative error code for unexpected register value.
0025  */
0026 int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft)
0027 {
0028     int pd, val;
0029 
0030     regmap_read(map, reg, &val);
0031 
0032     val = (val >> sft) & 0x7;
0033 
0034     switch (val) {
0035     case 0:
0036     case 1:
0037     case 2:
0038     case 3:
0039         pd = val + 1;
0040         break;
0041     case 4:
0042         pd = 6;
0043         break;
0044     case 5:
0045         pd = 8;
0046         break;
0047     case 6:
0048         pd = 12;
0049         break;
0050     case 7:
0051         pd = 16;
0052         break;
0053     default:
0054         pd = -EINVAL;
0055         break;
0056     }
0057 
0058     return pd;
0059 }
0060 EXPORT_SYMBOL_GPL(rl6231_get_pre_div);
0061 
0062 /**
0063  * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
0064  *
0065  * @rate: base clock rate.
0066  *
0067  * Choose divider parameter that gives the highest possible DMIC frequency in
0068  * 1MHz - 3MHz range.
0069  */
0070 int rl6231_calc_dmic_clk(int rate)
0071 {
0072     static const int div[] = {2, 3, 4, 6, 8, 12};
0073     int i;
0074 
0075     if (rate < 1000000 * div[0]) {
0076         pr_warn("Base clock rate %d is too low\n", rate);
0077         return -EINVAL;
0078     }
0079 
0080     for (i = 0; i < ARRAY_SIZE(div); i++) {
0081         if ((div[i] % 3) == 0)
0082             continue;
0083         /* find divider that gives DMIC frequency below 1.536MHz */
0084         if (1536000 * div[i] >= rate)
0085             return i;
0086     }
0087 
0088     pr_warn("Base clock rate %d is too high\n", rate);
0089     return -EINVAL;
0090 }
0091 EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
0092 
0093 struct pll_calc_map {
0094     unsigned int pll_in;
0095     unsigned int pll_out;
0096     int k;
0097     int n;
0098     int m;
0099     bool m_bp;
0100     bool k_bp;
0101 };
0102 
0103 static const struct pll_calc_map pll_preset_table[] = {
0104     {19200000,  4096000,  23, 14, 1, false, false},
0105     {19200000,  24576000,  3, 30, 3, false, false},
0106     {48000000,  3840000,  23,  2, 0, false, false},
0107     {3840000,   24576000,  3, 30, 0, true, false},
0108     {3840000,   22579200,  3,  5, 0, true, false},
0109 };
0110 
0111 static unsigned int find_best_div(unsigned int in,
0112     unsigned int max, unsigned int div)
0113 {
0114     unsigned int d;
0115 
0116     if (in <= max)
0117         return 1;
0118 
0119     d = in / max;
0120     if (in % max)
0121         d++;
0122 
0123     while (div % d != 0)
0124         d++;
0125 
0126 
0127     return d;
0128 }
0129 
0130 /**
0131  * rl6231_pll_calc - Calcualte PLL M/N/K code.
0132  * @freq_in: external clock provided to codec.
0133  * @freq_out: target clock which codec works on.
0134  * @pll_code: Pointer to structure with M, N, K, m_bypass and k_bypass flag.
0135  *
0136  * Calcualte M/N/K code to configure PLL for codec.
0137  *
0138  * Returns 0 for success or negative error code.
0139  */
0140 int rl6231_pll_calc(const unsigned int freq_in,
0141     const unsigned int freq_out, struct rl6231_pll_code *pll_code)
0142 {
0143     int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
0144     int i, k, n_t;
0145     int k_t, min_k, max_k, n = 0, m = 0, m_t = 0;
0146     unsigned int red, pll_out, in_t, out_t, div, div_t;
0147     unsigned int red_t = abs(freq_out - freq_in);
0148     unsigned int f_in, f_out, f_max;
0149     bool m_bypass = false, k_bypass = false;
0150 
0151     if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
0152         return -EINVAL;
0153 
0154     for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) {
0155         if (freq_in == pll_preset_table[i].pll_in &&
0156             freq_out == pll_preset_table[i].pll_out) {
0157             k = pll_preset_table[i].k;
0158             m = pll_preset_table[i].m;
0159             n = pll_preset_table[i].n;
0160             m_bypass = pll_preset_table[i].m_bp;
0161             k_bypass = pll_preset_table[i].k_bp;
0162             pr_debug("Use preset PLL parameter table\n");
0163             goto code_find;
0164         }
0165     }
0166 
0167     min_k = 80000000 / freq_out - 2;
0168     max_k = 150000000 / freq_out - 2;
0169     if (max_k > RL6231_PLL_K_MAX)
0170         max_k = RL6231_PLL_K_MAX;
0171     if (min_k > RL6231_PLL_K_MAX)
0172         min_k = max_k = RL6231_PLL_K_MAX;
0173     div_t = gcd(freq_in, freq_out);
0174     f_max = 0xffffffff / RL6231_PLL_N_MAX;
0175     div = find_best_div(freq_in, f_max, div_t);
0176     f_in = freq_in / div;
0177     f_out = freq_out / div;
0178     k = min_k;
0179     if (min_k < -1)
0180         min_k = -1;
0181     for (k_t = min_k; k_t <= max_k; k_t++) {
0182         for (n_t = 0; n_t <= max_n; n_t++) {
0183             in_t = f_in * (n_t + 2);
0184             pll_out = f_out * (k_t + 2);
0185             if (in_t == pll_out) {
0186                 m_bypass = true;
0187                 n = n_t;
0188                 k = k_t;
0189                 goto code_find;
0190             }
0191             out_t = in_t / (k_t + 2);
0192             red = abs(f_out - out_t);
0193             if (red < red_t) {
0194                 m_bypass = true;
0195                 n = n_t;
0196                 m = 0;
0197                 k = k_t;
0198                 if (red == 0)
0199                     goto code_find;
0200                 red_t = red;
0201             }
0202             for (m_t = 0; m_t <= max_m; m_t++) {
0203                 out_t = in_t / ((m_t + 2) * (k_t + 2));
0204                 red = abs(f_out - out_t);
0205                 if (red < red_t) {
0206                     m_bypass = false;
0207                     n = n_t;
0208                     m = m_t;
0209                     k = k_t;
0210                     if (red == 0)
0211                         goto code_find;
0212                     red_t = red;
0213                 }
0214             }
0215         }
0216     }
0217     pr_debug("Only get approximation about PLL\n");
0218 
0219 code_find:
0220     if (k == -1) {
0221         k_bypass = true;
0222         k = 0;
0223     }
0224 
0225     pll_code->m_bp = m_bypass;
0226     pll_code->k_bp = k_bypass;
0227     pll_code->m_code = m;
0228     pll_code->n_code = n;
0229     pll_code->k_code = k;
0230     return 0;
0231 }
0232 EXPORT_SYMBOL_GPL(rl6231_pll_calc);
0233 
0234 int rl6231_get_clk_info(int sclk, int rate)
0235 {
0236     int i;
0237     static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
0238 
0239     if (sclk <= 0 || rate <= 0)
0240         return -EINVAL;
0241 
0242     rate = rate << 8;
0243     for (i = 0; i < ARRAY_SIZE(pd); i++)
0244         if (sclk == rate * pd[i])
0245             return i;
0246 
0247     return -EINVAL;
0248 }
0249 EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
0250 
0251 MODULE_DESCRIPTION("RL6231 class device shared support");
0252 MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
0253 MODULE_LICENSE("GPL v2");