diff options
Diffstat (limited to 'sound/soc/codecs/rl6231.c')
-rw-r--r-- | sound/soc/codecs/rl6231.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c new file mode 100644 index 000000000000..7b82fbe0d14c --- /dev/null +++ b/sound/soc/codecs/rl6231.c | |||
@@ -0,0 +1,152 @@ | |||
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/moduleparam.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/pm.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/regmap.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/of_gpio.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | #include <linux/acpi.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/pcm_params.h> | ||
29 | #include <sound/soc.h> | ||
30 | #include <sound/soc-dapm.h> | ||
31 | #include <sound/initval.h> | ||
32 | #include <sound/tlv.h> | ||
33 | |||
34 | #include "rl6231.h" | ||
35 | |||
36 | /** | ||
37 | * rl6231_calc_dmic_clk - Calculate the parameter of dmic. | ||
38 | * | ||
39 | * @rate: base clock rate. | ||
40 | * | ||
41 | * Choose dmic clock between 1MHz and 3MHz. | ||
42 | * It is better for clock to approximate 3MHz. | ||
43 | */ | ||
44 | int rl6231_calc_dmic_clk(int rate) | ||
45 | { | ||
46 | int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL; | ||
47 | int i, red, bound, temp; | ||
48 | |||
49 | red = 3000000 * 12; | ||
50 | for (i = 0; i < ARRAY_SIZE(div); i++) { | ||
51 | bound = div[i] * 3000000; | ||
52 | if (rate > bound) | ||
53 | continue; | ||
54 | temp = bound - rate; | ||
55 | if (temp < red) { | ||
56 | red = temp; | ||
57 | idx = i; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | return idx; | ||
62 | } | ||
63 | EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk); | ||
64 | |||
65 | /** | ||
66 | * rl6231_pll_calc - Calcualte PLL M/N/K code. | ||
67 | * @freq_in: external clock provided to codec. | ||
68 | * @freq_out: target clock which codec works on. | ||
69 | * @pll_code: Pointer to structure with M, N, K and bypass flag. | ||
70 | * | ||
71 | * Calcualte M/N/K code to configure PLL for codec. | ||
72 | * | ||
73 | * Returns 0 for success or negative error code. | ||
74 | */ | ||
75 | int rl6231_pll_calc(const unsigned int freq_in, | ||
76 | const unsigned int freq_out, struct rl6231_pll_code *pll_code) | ||
77 | { | ||
78 | int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX; | ||
79 | int k, red, n_t, pll_out, in_t, out_t; | ||
80 | int n = 0, m = 0, m_t = 0; | ||
81 | int red_t = abs(freq_out - freq_in); | ||
82 | bool bypass = false; | ||
83 | |||
84 | if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in) | ||
85 | return -EINVAL; | ||
86 | |||
87 | k = 100000000 / freq_out - 2; | ||
88 | if (k > RL6231_PLL_K_MAX) | ||
89 | k = RL6231_PLL_K_MAX; | ||
90 | for (n_t = 0; n_t <= max_n; n_t++) { | ||
91 | in_t = freq_in / (k + 2); | ||
92 | pll_out = freq_out / (n_t + 2); | ||
93 | if (in_t < 0) | ||
94 | continue; | ||
95 | if (in_t == pll_out) { | ||
96 | bypass = true; | ||
97 | n = n_t; | ||
98 | goto code_find; | ||
99 | } | ||
100 | red = abs(in_t - pll_out); | ||
101 | if (red < red_t) { | ||
102 | bypass = true; | ||
103 | n = n_t; | ||
104 | m = m_t; | ||
105 | if (red == 0) | ||
106 | goto code_find; | ||
107 | red_t = red; | ||
108 | } | ||
109 | for (m_t = 0; m_t <= max_m; m_t++) { | ||
110 | out_t = in_t / (m_t + 2); | ||
111 | red = abs(out_t - pll_out); | ||
112 | if (red < red_t) { | ||
113 | bypass = false; | ||
114 | n = n_t; | ||
115 | m = m_t; | ||
116 | if (red == 0) | ||
117 | goto code_find; | ||
118 | red_t = red; | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | pr_debug("Only get approximation about PLL\n"); | ||
123 | |||
124 | code_find: | ||
125 | |||
126 | pll_code->m_bp = bypass; | ||
127 | pll_code->m_code = m; | ||
128 | pll_code->n_code = n; | ||
129 | pll_code->k_code = k; | ||
130 | return 0; | ||
131 | } | ||
132 | EXPORT_SYMBOL_GPL(rl6231_pll_calc); | ||
133 | |||
134 | int rl6231_get_clk_info(int sclk, int rate) | ||
135 | { | ||
136 | int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; | ||
137 | |||
138 | if (sclk <= 0 || rate <= 0) | ||
139 | return -EINVAL; | ||
140 | |||
141 | rate = rate << 8; | ||
142 | for (i = 0; i < ARRAY_SIZE(pd); i++) | ||
143 | if (sclk == rate * pd[i]) | ||
144 | return i; | ||
145 | |||
146 | return -EINVAL; | ||
147 | } | ||
148 | EXPORT_SYMBOL_GPL(rl6231_get_clk_info); | ||
149 | |||
150 | MODULE_DESCRIPTION("RL6231 class device shared support"); | ||
151 | MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); | ||
152 | MODULE_LICENSE("GPL v2"); | ||