diff options
-rw-r--r-- | sound/soc/codecs/tas2552.c | 138 | ||||
-rw-r--r-- | sound/soc/codecs/tas2552.h | 11 |
2 files changed, 95 insertions, 54 deletions
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 891e2c529df3..0ca55aaeaaf2 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c | |||
@@ -77,7 +77,9 @@ struct tas2552_data { | |||
77 | struct gpio_desc *enable_gpio; | 77 | struct gpio_desc *enable_gpio; |
78 | unsigned char regs[TAS2552_VBAT_DATA]; | 78 | unsigned char regs[TAS2552_VBAT_DATA]; |
79 | unsigned int pll_clkin; | 79 | unsigned int pll_clkin; |
80 | int pll_clk_id; | ||
80 | unsigned int pdm_clk; | 81 | unsigned int pdm_clk; |
82 | int pdm_clk_id; | ||
81 | 83 | ||
82 | unsigned int dai_fmt; | 84 | unsigned int dai_fmt; |
83 | unsigned int tdm_delay; | 85 | unsigned int tdm_delay; |
@@ -158,16 +160,90 @@ static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) | |||
158 | } | 160 | } |
159 | #endif | 161 | #endif |
160 | 162 | ||
163 | static int tas2552_setup_pll(struct snd_soc_codec *codec, | ||
164 | struct snd_pcm_hw_params *params) | ||
165 | { | ||
166 | struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); | ||
167 | bool bypass_pll = false; | ||
168 | unsigned int pll_clk = params_rate(params) * 512; | ||
169 | unsigned int pll_clkin = tas2552->pll_clkin; | ||
170 | u8 pll_enable; | ||
171 | |||
172 | if (!pll_clkin) { | ||
173 | if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK) | ||
174 | return -EINVAL; | ||
175 | |||
176 | pll_clkin = snd_soc_params_to_bclk(params); | ||
177 | pll_clkin += tas2552->tdm_delay; | ||
178 | } | ||
179 | |||
180 | pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE; | ||
181 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); | ||
182 | |||
183 | if (pll_clkin == pll_clk) | ||
184 | bypass_pll = true; | ||
185 | |||
186 | if (bypass_pll) { | ||
187 | /* By pass the PLL configuration */ | ||
188 | snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, | ||
189 | TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS); | ||
190 | } else { | ||
191 | /* Fill in the PLL control registers for J & D | ||
192 | * pll_clk = (.5 * pll_clkin * J.D) / 2^p | ||
193 | * Need to fill in J and D here based on incoming freq | ||
194 | */ | ||
195 | unsigned int d; | ||
196 | u8 j; | ||
197 | u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK; | ||
198 | u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); | ||
199 | |||
200 | p = (p >> 7); | ||
201 | |||
202 | recalc: | ||
203 | j = (pll_clk * 2 * (1 << p)) / pll_clkin; | ||
204 | d = (pll_clk * 2 * (1 << p)) % pll_clkin; | ||
205 | d /= (pll_clkin / 10000); | ||
206 | |||
207 | if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) { | ||
208 | if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) { | ||
209 | pll_clkin = 1800000; | ||
210 | pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) & | ||
211 | TAS2552_PLL_SRC_MASK; | ||
212 | } else { | ||
213 | pll_clkin = snd_soc_params_to_bclk(params); | ||
214 | pll_clkin += tas2552->tdm_delay; | ||
215 | pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) & | ||
216 | TAS2552_PLL_SRC_MASK; | ||
217 | } | ||
218 | goto recalc; | ||
219 | } | ||
220 | |||
221 | snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK, | ||
222 | pll_sel); | ||
223 | |||
224 | snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, | ||
225 | TAS2552_PLL_J_MASK, j); | ||
226 | /* Will clear the PLL_BYPASS bit */ | ||
227 | snd_soc_write(codec, TAS2552_PLL_CTRL_2, | ||
228 | TAS2552_PLL_D_UPPER(d)); | ||
229 | snd_soc_write(codec, TAS2552_PLL_CTRL_3, | ||
230 | TAS2552_PLL_D_LOWER(d)); | ||
231 | } | ||
232 | |||
233 | /* Restore PLL status */ | ||
234 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, | ||
235 | pll_enable); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
161 | static int tas2552_hw_params(struct snd_pcm_substream *substream, | 240 | static int tas2552_hw_params(struct snd_pcm_substream *substream, |
162 | struct snd_pcm_hw_params *params, | 241 | struct snd_pcm_hw_params *params, |
163 | struct snd_soc_dai *dai) | 242 | struct snd_soc_dai *dai) |
164 | { | 243 | { |
165 | struct snd_soc_codec *codec = dai->codec; | 244 | struct snd_soc_codec *codec = dai->codec; |
166 | struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); | 245 | struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); |
167 | int sample_rate, pll_clk; | ||
168 | int d; | ||
169 | int cpf; | 246 | int cpf; |
170 | u8 p, j; | ||
171 | u8 ser_ctrl1_reg, wclk_rate; | 247 | u8 ser_ctrl1_reg, wclk_rate; |
172 | 248 | ||
173 | switch (params_width(params)) { | 249 | switch (params_width(params)) { |
@@ -245,49 +321,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, | |||
245 | snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, | 321 | snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, |
246 | wclk_rate); | 322 | wclk_rate); |
247 | 323 | ||
248 | if (!tas2552->pll_clkin) | 324 | return tas2552_setup_pll(codec, params); |
249 | return -EINVAL; | ||
250 | |||
251 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); | ||
252 | |||
253 | if (tas2552->pll_clkin == TAS2552_245MHZ_CLK || | ||
254 | tas2552->pll_clkin == TAS2552_225MHZ_CLK) { | ||
255 | /* By pass the PLL configuration */ | ||
256 | snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, | ||
257 | TAS2552_PLL_BYPASS_MASK, | ||
258 | TAS2552_PLL_BYPASS); | ||
259 | } else { | ||
260 | /* Fill in the PLL control registers for J & D | ||
261 | * PLL_CLK = (.5 * freq * J.D) / 2^p | ||
262 | * Need to fill in J and D here based on incoming freq | ||
263 | */ | ||
264 | p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); | ||
265 | p = (p >> 7); | ||
266 | sample_rate = params_rate(params); | ||
267 | |||
268 | if (sample_rate == 48000) | ||
269 | pll_clk = TAS2552_245MHZ_CLK; | ||
270 | else if (sample_rate == 44100) | ||
271 | pll_clk = TAS2552_225MHZ_CLK; | ||
272 | else { | ||
273 | dev_vdbg(codec->dev, "Substream sample rate is not found %i\n", | ||
274 | params_rate(params)); | ||
275 | return -EINVAL; | ||
276 | } | ||
277 | |||
278 | j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin; | ||
279 | d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin; | ||
280 | |||
281 | snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, | ||
282 | TAS2552_PLL_J_MASK, j); | ||
283 | snd_soc_write(codec, TAS2552_PLL_CTRL_2, | ||
284 | (d >> 7) & TAS2552_PLL_D_UPPER_MASK); | ||
285 | snd_soc_write(codec, TAS2552_PLL_CTRL_3, | ||
286 | d & TAS2552_PLL_D_LOWER_MASK); | ||
287 | |||
288 | } | ||
289 | |||
290 | return 0; | ||
291 | } | 325 | } |
292 | 326 | ||
293 | #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ | 327 | #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ |
@@ -370,12 +404,21 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, | |||
370 | 404 | ||
371 | switch (clk_id) { | 405 | switch (clk_id) { |
372 | case TAS2552_PLL_CLKIN_MCLK: | 406 | case TAS2552_PLL_CLKIN_MCLK: |
373 | case TAS2552_PLL_CLKIN_BCLK: | ||
374 | case TAS2552_PLL_CLKIN_IVCLKIN: | 407 | case TAS2552_PLL_CLKIN_IVCLKIN: |
408 | if (freq < 512000 || freq > 24576000) { | ||
409 | /* out of range PLL_CLKIN, fall back to use BCLK */ | ||
410 | dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n", | ||
411 | freq); | ||
412 | clk_id = TAS2552_PLL_CLKIN_BCLK; | ||
413 | freq = 0; | ||
414 | } | ||
415 | /* fall through */ | ||
416 | case TAS2552_PLL_CLKIN_BCLK: | ||
375 | case TAS2552_PLL_CLKIN_1_8_FIXED: | 417 | case TAS2552_PLL_CLKIN_1_8_FIXED: |
376 | mask = TAS2552_PLL_SRC_MASK; | 418 | mask = TAS2552_PLL_SRC_MASK; |
377 | val = (clk_id << 3) & mask; /* bit 4:5 in the register */ | 419 | val = (clk_id << 3) & mask; /* bit 4:5 in the register */ |
378 | reg = TAS2552_CFG_1; | 420 | reg = TAS2552_CFG_1; |
421 | tas2552->pll_clk_id = clk_id; | ||
379 | tas2552->pll_clkin = freq; | 422 | tas2552->pll_clkin = freq; |
380 | break; | 423 | break; |
381 | case TAS2552_PDM_CLK_PLL: | 424 | case TAS2552_PDM_CLK_PLL: |
@@ -385,6 +428,7 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, | |||
385 | mask = TAS2552_PDM_CLK_SEL_MASK; | 428 | mask = TAS2552_PDM_CLK_SEL_MASK; |
386 | val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ | 429 | val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ |
387 | reg = TAS2552_PDM_CFG; | 430 | reg = TAS2552_PDM_CFG; |
431 | tas2552->pdm_clk_id = clk_id; | ||
388 | tas2552->pdm_clk = freq; | 432 | tas2552->pdm_clk = freq; |
389 | break; | 433 | break; |
390 | default: | 434 | default: |
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h index bbb820495516..6806516a62cd 100644 --- a/sound/soc/codecs/tas2552.h +++ b/sound/soc/codecs/tas2552.h | |||
@@ -128,12 +128,9 @@ | |||
128 | #define TAS2552_APT_THRESH_2_1_7 (0x11 << 2) | 128 | #define TAS2552_APT_THRESH_2_1_7 (0x11 << 2) |
129 | 129 | ||
130 | /* PLL Control Register */ | 130 | /* PLL Control Register */ |
131 | #define TAS2552_245MHZ_CLK 24576000 | 131 | #define TAS2552_PLL_J_MASK 0x7f |
132 | #define TAS2552_225MHZ_CLK 22579200 | 132 | #define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f) |
133 | #define TAS2552_PLL_J_MASK 0x7f | 133 | #define TAS2552_PLL_D_LOWER(x) ((x) & 0xff) |
134 | #define TAS2552_PLL_D_UPPER_MASK 0x3f | 134 | #define TAS2552_PLL_BYPASS (1 << 7) |
135 | #define TAS2552_PLL_D_LOWER_MASK 0xff | ||
136 | #define TAS2552_PLL_BYPASS_MASK 0x80 | ||
137 | #define TAS2552_PLL_BYPASS 0x80 | ||
138 | 135 | ||
139 | #endif | 136 | #endif |